3 * @file src/Core/Addon.php
5 namespace Friendica\Core;
7 use Friendica\BaseObject;
8 use Friendica\Database\DBA;
11 * Some functions to handle addons
13 class Addon extends BaseObject
16 * List of the names of enabled addons
20 private static $addons = [];
23 * @brief Synchronize addons:
25 * system.addon contains a comma-separated list of names
26 * of addons which are used on this system.
27 * Go through the database list of already installed addons, and if we have
28 * an entry, but it isn't in the config list, call the uninstall procedure
29 * and mark it uninstalled in the database (for now we'll remove it).
30 * Then go through the config list and if we have a addon that isn't installed,
31 * call the install procedure and add it to the database.
34 public static function loadAddons()
36 $installed_addons = [];
38 $r = DBA::select('addon', [], ['installed' => 1]);
39 if (DBA::isResult($r)) {
40 $installed_addons = DBA::toArray($r);
43 $addons = Config::get('system', 'addon');
47 $addons_arr = explode(',', str_replace(' ', '', $addons));
50 self::$addons = $addons_arr;
54 foreach ($installed_addons as $addon) {
55 if (!self::isEnabled($addon['name'])) {
56 self::uninstall($addon['name']);
58 $installed_arr[] = $addon['name'];
62 foreach (self::$addons as $p) {
63 if (!in_array($p, $installed_arr)) {
70 * @brief uninstalls an addon.
72 * @param string $addon name of the addon
76 public static function uninstall($addon)
78 Logger::notice("Addon {addon}: {action}", ['action' => 'uninstall', 'addon' => $addon]);
79 DBA::delete('addon', ['name' => $addon]);
81 @include_once('addon/' . $addon . '/' . $addon . '.php');
82 if (function_exists($addon . '_uninstall')) {
83 $func = $addon . '_uninstall';
87 unset(self::$addons[array_search($addon, self::$addons)]);
91 * @brief installs an addon.
93 * @param string $addon name of the addon
97 public static function install($addon)
99 // silently fail if addon was removed
101 if (!file_exists('addon/' . $addon . '/' . $addon . '.php')) {
104 Logger::notice("Addon {addon}: {action}", ['action' => 'install', 'addon' => $addon]);
105 $t = @filemtime('addon/' . $addon . '/' . $addon . '.php');
106 @include_once('addon/' . $addon . '/' . $addon . '.php');
107 if (function_exists($addon . '_install')) {
108 $func = $addon . '_install';
111 $addon_admin = (function_exists($addon . "_addon_admin") ? 1 : 0);
113 DBA::insert('addon', ['name' => $addon, 'installed' => true,
114 'timestamp' => $t, 'plugin_admin' => $addon_admin]);
116 // we can add the following with the previous SQL
117 // once most site tables have been updated.
118 // This way the system won't fall over dead during the update.
120 if (file_exists('addon/' . $addon . '/.hidden')) {
121 DBA::update('addon', ['hidden' => true], ['name' => $addon]);
124 if (!self::isEnabled($addon)) {
125 self::$addons[] = $addon;
129 Logger::error("Addon {addon}: {action} failed", ['action' => 'uninstall', 'addon' => $addon]);
135 * reload all updated addons
137 public static function reload()
139 $addons = Config::get('system', 'addon');
140 if (strlen($addons)) {
141 $r = DBA::select('addon', [], ['installed' => 1]);
142 if (DBA::isResult($r)) {
143 $installed = DBA::toArray($r);
148 $addon_list = explode(',', $addons);
150 if (count($addon_list)) {
151 foreach ($addon_list as $addon) {
152 $addon = trim($addon);
153 $fname = 'addon/' . $addon . '/' . $addon . '.php';
155 if (file_exists($fname)) {
156 $t = @filemtime($fname);
157 foreach ($installed as $i) {
158 if (($i['name'] == $addon) && ($i['timestamp'] != $t)) {
160 Logger::notice("Addon {addon}: {action}", ['action' => 'reload', 'addon' => $i['name']]);
161 @include_once($fname);
163 if (function_exists($addon . '_uninstall')) {
164 $func = $addon . '_uninstall';
167 if (function_exists($addon . '_install')) {
168 $func = $addon . '_install';
171 DBA::update('addon', ['timestamp' => $t], ['id' => $i['id']]);
181 * @brief Parse addon comment in search of addon infos.
186 * * Description: An addon which plugs in
188 * * Author: John <profile url>
189 * * Author: Jane <email>
190 * * Maintainer: Jess <email>
193 * @param string $addon the name of the addon
194 * @return array with the addon information
197 public static function getInfo($addon)
210 if (!is_file("addon/$addon/$addon.php")) {
214 $stamp1 = microtime(true);
215 $f = file_get_contents("addon/$addon/$addon.php");
216 $a->saveTimestamp($stamp1, "file");
218 $r = preg_match("|/\*.*\*/|msU", $f, $m);
221 $ll = explode("\n", $m[0]);
222 foreach ($ll as $l) {
223 $l = trim($l, "\t\n\r */");
225 $addon_info = array_map("trim", explode(":", $l, 2));
226 if (count($addon_info) < 2) {
230 list($type, $v) = $addon_info;
231 $type = strtolower($type);
232 if ($type == "author" || $type == "maintainer") {
233 $r = preg_match("|([^<]+)<([^>]+)>|", $v, $m);
235 $info[$type][] = ['name' => $m[1], 'link' => $m[2]];
237 $info[$type][] = ['name' => $v];
240 if (array_key_exists($type, $info)) {
251 * Checks if the provided addon is enabled
253 * @param string $addon
256 public static function isEnabled($addon)
258 return in_array($addon, self::$addons);
262 * Returns a list of the enabled addon names
266 public static function getEnabledList()
268 return self::$addons;
272 * Saves the current enabled addon list in the system.addon config key
275 * @throws \Friendica\Network\HTTPException\InternalServerErrorException
277 public static function saveEnabledList()
279 return Config::set("system", "addon", implode(", ", self::$addons));
283 * Returns the list of non-hidden enabled addon names
288 public static function getVisibleList()
290 $visible_addons = [];
291 $stmt = DBA::select('addon', ['name'], ['hidden' => false, 'installed' => true]);
292 if (DBA::isResult($stmt)) {
293 foreach (DBA::toArray($stmt) as $addon) {
294 $visible_addons[] = $addon['name'];
298 return $visible_addons;
302 * Shim of Hook::register left for backward compatibility purpose.
304 * @see Hook::register
305 * @deprecated since version 2018.12
306 * @param string $hook the name of the hook
307 * @param string $file the name of the file that hooks into
308 * @param string $function the name of the function that the hook will call
309 * @param int $priority A priority (defaults to 0)
311 * @throws \Friendica\Network\HTTPException\InternalServerErrorException
313 public static function registerHook($hook, $file, $function, $priority = 0)
315 return Hook::register($hook, $file, $function, $priority);
319 * Shim of Hook::unregister left for backward compatibility purpose.
321 * @see Hook::unregister
322 * @deprecated since version 2018.12
323 * @param string $hook the name of the hook
324 * @param string $file the name of the file that hooks into
325 * @param string $function the name of the function that the hook called
327 * @throws \Friendica\Network\HTTPException\InternalServerErrorException
329 public static function unregisterHook($hook, $file, $function)
331 return Hook::unregister($hook, $file, $function);
335 * Shim of Hook::callAll left for backward-compatibility purpose.
338 * @deprecated since version 2018.12
339 * @param string $name of the hook to call
340 * @param string|array &$data to transmit to the callback handler
341 * @throws \Friendica\Network\HTTPException\InternalServerErrorException
343 public static function callHooks($name, &$data = null)
345 Hook::callAll($name, $data);