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