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.

340 lines
8.1KB

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