Friendica Communications Platform (please note that this is a clone of the repository at github, issues are handled there) https://friendi.ca
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

341 lines
8.0KB

  1. <?php
  2. /**
  3. * @file src/Core/Addon.php
  4. */
  5. namespace Friendica\Core;
  6. use Friendica\App;
  7. use Friendica\BaseObject;
  8. use Friendica\Core\Logger;
  9. use Friendica\Database\DBA;
  10. /**
  11. * Some functions to handle addons
  12. */
  13. class Addon extends BaseObject
  14. {
  15. /**
  16. * List of the names of enabled addons
  17. *
  18. * @var array
  19. */
  20. private static $addons = [];
  21. /**
  22. * @brief Synchronize addons:
  23. *
  24. * system.addon contains a comma-separated list of names
  25. * of addons which are used on this system.
  26. * Go through the database list of already installed addons, and if we have
  27. * an entry, but it isn't in the config list, call the uninstall procedure
  28. * and mark it uninstalled in the database (for now we'll remove it).
  29. * Then go through the config list and if we have a addon that isn't installed,
  30. * call the install procedure and add it to the database.
  31. *
  32. */
  33. public static function loadAddons()
  34. {
  35. $installed_addons = [];
  36. $r = DBA::select('addon', [], ['installed' => 1]);
  37. if (DBA::isResult($r)) {
  38. $installed_addons = DBA::toArray($r);
  39. }
  40. $addons = Config::get('system', 'addon');
  41. $addons_arr = [];
  42. if ($addons) {
  43. $addons_arr = explode(',', str_replace(' ', '', $addons));
  44. }
  45. self::$addons = $addons_arr;
  46. $installed_arr = [];
  47. foreach ($installed_addons as $addon) {
  48. if (!self::isEnabled($addon['name'])) {
  49. self::uninstall($addon['name']);
  50. } else {
  51. $installed_arr[] = $addon['name'];
  52. }
  53. }
  54. foreach (self::$addons as $p) {
  55. if (!in_array($p, $installed_arr)) {
  56. self::install($p);
  57. }
  58. }
  59. }
  60. /**
  61. * @brief uninstalls an addon.
  62. *
  63. * @param string $addon name of the addon
  64. * @return boolean
  65. */
  66. public static function uninstall($addon)
  67. {
  68. Logger::log("Addons: uninstalling " . $addon);
  69. DBA::delete('addon', ['name' => $addon]);
  70. @include_once('addon/' . $addon . '/' . $addon . '.php');
  71. if (function_exists($addon . '_uninstall')) {
  72. $func = $addon . '_uninstall';
  73. $func();
  74. }
  75. unset(self::$addons[array_search($addon, self::$addons)]);
  76. }
  77. /**
  78. * @brief installs an addon.
  79. *
  80. * @param string $addon name of the addon
  81. * @return bool
  82. */
  83. public static function install($addon)
  84. {
  85. // silently fail if addon was removed
  86. if (!file_exists('addon/' . $addon . '/' . $addon . '.php')) {
  87. return false;
  88. }
  89. Logger::log("Addons: installing " . $addon);
  90. $t = @filemtime('addon/' . $addon . '/' . $addon . '.php');
  91. @include_once('addon/' . $addon . '/' . $addon . '.php');
  92. if (function_exists($addon . '_install')) {
  93. $func = $addon . '_install';
  94. $func();
  95. $addon_admin = (function_exists($addon . "_addon_admin") ? 1 : 0);
  96. DBA::insert('addon', ['name' => $addon, 'installed' => true,
  97. 'timestamp' => $t, 'plugin_admin' => $addon_admin]);
  98. // we can add the following with the previous SQL
  99. // once most site tables have been updated.
  100. // This way the system won't fall over dead during the update.
  101. if (file_exists('addon/' . $addon . '/.hidden')) {
  102. DBA::update('addon', ['hidden' => true], ['name' => $addon]);
  103. }
  104. if (!self::isEnabled($addon)) {
  105. self::$addons[] = $addon;
  106. }
  107. return true;
  108. } else {
  109. Logger::log("Addons: FAILED installing " . $addon);
  110. return false;
  111. }
  112. }
  113. /**
  114. * reload all updated addons
  115. */
  116. public static function reload()
  117. {
  118. $addons = Config::get('system', 'addon');
  119. if (strlen($addons)) {
  120. $r = DBA::select('addon', [], ['installed' => 1]);
  121. if (DBA::isResult($r)) {
  122. $installed = DBA::toArray($r);
  123. } else {
  124. $installed = [];
  125. }
  126. $addon_list = explode(',', $addons);
  127. if (count($addon_list)) {
  128. foreach ($addon_list as $addon) {
  129. $addon = trim($addon);
  130. $fname = 'addon/' . $addon . '/' . $addon . '.php';
  131. if (file_exists($fname)) {
  132. $t = @filemtime($fname);
  133. foreach ($installed as $i) {
  134. if (($i['name'] == $addon) && ($i['timestamp'] != $t)) {
  135. Logger::log('Reloading addon: ' . $i['name']);
  136. @include_once($fname);
  137. if (function_exists($addon . '_uninstall')) {
  138. $func = $addon . '_uninstall';
  139. $func();
  140. }
  141. if (function_exists($addon . '_install')) {
  142. $func = $addon . '_install';
  143. $func();
  144. }
  145. DBA::update('addon', ['timestamp' => $t], ['id' => $i['id']]);
  146. }
  147. }
  148. }
  149. }
  150. }
  151. }
  152. }
  153. /**
  154. * @brief Parse addon comment in search of addon infos.
  155. *
  156. * like
  157. * \code
  158. * * Name: addon
  159. * * Description: An addon which plugs in
  160. * . * Version: 1.2.3
  161. * * Author: John <profile url>
  162. * * Author: Jane <email>
  163. * * Maintainer: Jess <email>
  164. * *
  165. * *\endcode
  166. * @param string $addon the name of the addon
  167. * @return array with the addon information
  168. */
  169. public static function getInfo($addon)
  170. {
  171. $a = self::getApp();
  172. $info = [
  173. 'name' => $addon,
  174. 'description' => "",
  175. 'author' => [],
  176. 'maintainer' => [],
  177. 'version' => "",
  178. 'status' => ""
  179. ];
  180. if (!is_file("addon/$addon/$addon.php")) {
  181. return $info;
  182. }
  183. $stamp1 = microtime(true);
  184. $f = file_get_contents("addon/$addon/$addon.php");
  185. $a->saveTimestamp($stamp1, "file");
  186. $r = preg_match("|/\*.*\*/|msU", $f, $m);
  187. if ($r) {
  188. $ll = explode("\n", $m[0]);
  189. foreach ($ll as $l) {
  190. $l = trim($l, "\t\n\r */");
  191. if ($l != "") {
  192. $addon_info = array_map("trim", explode(":", $l, 2));
  193. if (count($addon_info) < 2) {
  194. continue;
  195. }
  196. list($type, $v) = $addon_info;
  197. $type = strtolower($type);
  198. if ($type == "author" || $type == "maintainer") {
  199. $r = preg_match("|([^<]+)<([^>]+)>|", $v, $m);
  200. if ($r) {
  201. $info[$type][] = ['name' => $m[1], 'link' => $m[2]];
  202. } else {
  203. $info[$type][] = ['name' => $v];
  204. }
  205. } else {
  206. if (array_key_exists($type, $info)) {
  207. $info[$type] = $v;
  208. }
  209. }
  210. }
  211. }
  212. }
  213. return $info;
  214. }
  215. /**
  216. * Checks if the provided addon is enabled
  217. *
  218. * @param string $addon
  219. * @return boolean
  220. */
  221. public static function isEnabled($addon)
  222. {
  223. return in_array($addon, self::$addons);
  224. }
  225. /**
  226. * Returns a list of the enabled addon names
  227. *
  228. * @return array
  229. */
  230. public static function getEnabledList()
  231. {
  232. return self::$addons;
  233. }
  234. /**
  235. * Saves the current enabled addon list in the system.addon config key
  236. *
  237. * @return boolean
  238. */
  239. public static function saveEnabledList()
  240. {
  241. return Config::set("system", "addon", implode(", ", self::$addons));
  242. }
  243. /**
  244. * Returns the list of non-hidden enabled addon names
  245. *
  246. * @return array
  247. */
  248. public static function getVisibleList()
  249. {
  250. $visible_addons = [];
  251. $stmt = DBA::select('addon', ['name'], ['hidden' => false, 'installed' => true]);
  252. if (DBA::isResult($stmt)) {
  253. foreach (DBA::toArray($stmt) as $addon) {
  254. $visible_addons[] = $addon['name'];
  255. }
  256. }
  257. return $visible_addons;
  258. }
  259. /**
  260. * Shim of Hook::register left for backward compatibility purpose.
  261. *
  262. * @see Hook::register
  263. * @deprecated since version 2018.12
  264. * @param string $hook the name of the hook
  265. * @param string $file the name of the file that hooks into
  266. * @param string $function the name of the function that the hook will call
  267. * @param int $priority A priority (defaults to 0)
  268. * @return mixed|bool
  269. */
  270. public static function registerHook($hook, $file, $function, $priority = 0)
  271. {
  272. return Hook::register($hook, $file, $function, $priority);
  273. }
  274. /**
  275. * Shim of Hook::unregister left for backward compatibility purpose.
  276. *
  277. * @see Hook::unregister
  278. * @deprecated since version 2018.12
  279. * @param string $hook the name of the hook
  280. * @param string $file the name of the file that hooks into
  281. * @param string $function the name of the function that the hook called
  282. * @return boolean
  283. */
  284. public static function unregisterHook($hook, $file, $function)
  285. {
  286. return Hook::unregister($hook, $file, $function);
  287. }
  288. /**
  289. * Shim of Hook::callAll left for backward-compatibility purpose.
  290. *
  291. * @see Hook::callAll
  292. * @deprecated since version 2018.12
  293. * @param string $name of the hook to call
  294. * @param string|array &$data to transmit to the callback handler
  295. */
  296. public static function callHooks($name, &$data = null)
  297. {
  298. Hook::callAll($name, $data);
  299. }
  300. }