]> git.mxchange.org Git - friendica.git/blob - src/Core/Addon.php
Cleanups: isResult() more used, readability improved (#5608)
[friendica.git] / src / Core / Addon.php
1 <?php
2 /**
3  * @file src/Core/Addon.php
4  */
5 namespace Friendica\Core;
6
7 use Friendica\App;
8 use Friendica\Database\DBA;
9
10 require_once 'include/dba.php';
11
12 /**
13  * Some functions to handle addons
14  */
15 class Addon
16 {
17         /**
18          * @brief uninstalls an addon.
19          *
20          * @param string $addon name of the addon
21          * @return boolean
22          */
23         public static function uninstall($addon)
24         {
25                 logger("Addons: uninstalling " . $addon);
26                 DBA::delete('addon', ['name' => $addon]);
27
28                 @include_once('addon/' . $addon . '/' . $addon . '.php');
29                 if (function_exists($addon . '_uninstall')) {
30                         $func = $addon . '_uninstall';
31                         $func();
32                 }
33         }
34
35         /**
36          * @brief installs an addon.
37          *
38          * @param string $addon name of the addon
39          * @return bool
40          */
41         public static function install($addon)
42         {
43                 // silently fail if addon was removed
44
45                 if (!file_exists('addon/' . $addon . '/' . $addon . '.php')) {
46                         return false;
47                 }
48                 logger("Addons: installing " . $addon);
49                 $t = @filemtime('addon/' . $addon . '/' . $addon . '.php');
50                 @include_once('addon/' . $addon . '/' . $addon . '.php');
51                 if (function_exists($addon . '_install')) {
52                         $func = $addon . '_install';
53                         $func();
54
55                         $addon_admin = (function_exists($addon."_addon_admin") ? 1 : 0);
56
57                         DBA::insert('addon', ['name' => $addon, 'installed' => true,
58                                                 'timestamp' => $t, 'plugin_admin' => $addon_admin]);
59
60                         // we can add the following with the previous SQL
61                         // once most site tables have been updated.
62                         // This way the system won't fall over dead during the update.
63
64                         if (file_exists('addon/' . $addon . '/.hidden')) {
65                                 DBA::update('addon', ['hidden' => true], ['name' => $addon]);
66                         }
67                         return true;
68                 } else {
69                         logger("Addons: FAILED installing " . $addon);
70                         return false;
71                 }
72         }
73
74         /**
75          * reload all updated addons
76          */
77         public static function reload()
78         {
79                 $addons = Config::get('system', 'addon');
80                 if (strlen($addons)) {
81                         $r = DBA::select('addon', [], ['installed' => 1]);
82                         if (DBA::isResult($r)) {
83                                 $installed = DBA::toArray($r);
84                         } else {
85                                 $installed = [];
86                         }
87
88                         $addon_list = explode(',', $addons);
89
90                         if (count($addon_list)) {
91                                 foreach ($addon_list as $addon) {
92                                         $addon = trim($addon);
93                                         $fname = 'addon/' . $addon . '/' . $addon . '.php';
94
95                                         if (file_exists($fname)) {
96                                                 $t = @filemtime($fname);
97                                                 foreach ($installed as $i) {
98                                                         if (($i['name'] == $addon) && ($i['timestamp'] != $t)) {
99                                                                 logger('Reloading addon: ' . $i['name']);
100                                                                 @include_once($fname);
101
102                                                                 if (function_exists($addon . '_uninstall')) {
103                                                                         $func = $addon . '_uninstall';
104                                                                         $func();
105                                                                 }
106                                                                 if (function_exists($addon . '_install')) {
107                                                                         $func = $addon . '_install';
108                                                                         $func();
109                                                                 }
110                                                                 DBA::update('addon', ['timestamp' => $t], ['id' => $i['id']]);
111                                                         }
112                                                 }
113                                         }
114                                 }
115                         }
116                 }
117         }
118
119         /**
120          * @brief check if addon is enabled
121          *
122          * @param string $addon
123          * @return boolean
124          */
125         public static function isEnabled($addon)
126         {
127                 return DBA::exists('addon', ['installed' => true, 'name' => $addon]);
128         }
129
130
131         /**
132          * @brief registers a hook.
133          *
134          * @param string $hook the name of the hook
135          * @param string $file the name of the file that hooks into
136          * @param string $function the name of the function that the hook will call
137          * @param int $priority A priority (defaults to 0)
138          * @return mixed|bool
139          */
140         public static function registerHook($hook, $file, $function, $priority = 0)
141         {
142                 $file = str_replace(get_app()->get_basepath() . DIRECTORY_SEPARATOR, '', $file);
143
144                 $condition = ['hook' => $hook, 'file' => $file, 'function' => $function];
145                 $exists = DBA::exists('hook', $condition);
146                 if ($exists) {
147                         return true;
148                 }
149
150                 $r = DBA::insert('hook', ['hook' => $hook, 'file' => $file, 'function' => $function, 'priority' => $priority]);
151
152                 return $r;
153         }
154
155         /**
156          * @brief unregisters a hook.
157          *
158          * @param string $hook the name of the hook
159          * @param string $file the name of the file that hooks into
160          * @param string $function the name of the function that the hook called
161          * @return array
162          */
163         public static function unregisterHook($hook, $file, $function)
164         {
165                 $relative_file = str_replace(get_app()->get_basepath() . DIRECTORY_SEPARATOR, '', $file);
166
167                 // This here is only needed for fixing a problem that existed on the develop branch
168                 $condition = ['hook' => $hook, 'file' => $file, 'function' => $function];
169                 DBA::delete('hook', $condition);
170
171                 $condition = ['hook' => $hook, 'file' => $relative_file, 'function' => $function];
172                 $r = DBA::delete('hook', $condition);
173                 return $r;
174         }
175
176         /**
177          * Load hooks
178          */
179         public static function loadHooks()
180         {
181                 $a = get_app();
182                 $a->hooks = [];
183                 $r = DBA::select('hook', ['hook', 'file', 'function'], [], ['order' => ['priority' => 'desc', 'file']]);
184
185                 while ($rr = DBA::fetch($r)) {
186                         if (! array_key_exists($rr['hook'], $a->hooks)) {
187                                 $a->hooks[$rr['hook']] = [];
188                         }
189                         $a->hooks[$rr['hook']][] = [$rr['file'],$rr['function']];
190                 }
191                 DBA::close($r);
192         }
193
194         /**
195          * @brief Forks a hook.
196          *
197          * Use this function when you want to fork a hook via the worker.
198          *
199          * @param string $name of the hook to call
200          * @param string|array $data to transmit to the callback handler
201          */
202         public static function forkHooks($priority, $name, $data = null)
203         {
204                 $a = get_app();
205
206                 if (is_array($a->hooks) && array_key_exists($name, $a->hooks)) {
207                         foreach ($a->hooks[$name] as $hook) {
208                                 Worker::add($priority, 'ForkHook', $name, $hook, $data);
209                         }
210                 }
211         }
212
213         /**
214          * @brief Calls a hook.
215          *
216          * Use this function when you want to be able to allow a hook to manipulate
217          * the provided data.
218          *
219          * @param string $name of the hook to call
220          * @param string|array &$data to transmit to the callback handler
221          */
222         public static function callHooks($name, &$data = null)
223         {
224                 $a = get_app();
225
226                 if (is_array($a->hooks) && array_key_exists($name, $a->hooks)) {
227                         foreach ($a->hooks[$name] as $hook) {
228                                 self::callSingleHook($a, $name, $hook, $data);
229                         }
230                 }
231         }
232
233         /**
234          * @brief Calls a single hook.
235          *
236          * @param App $a
237          * @param string         $name of the hook to call
238          * @param array          $hook Hook data
239          * @param string|array   &$data to transmit to the callback handler
240          */
241         public static function callSingleHook(App $a, $name, $hook, &$data = null)
242         {
243                 // Don't run a theme's hook if the user isn't using the theme
244                 if (strpos($hook[0], 'view/theme/') !== false && strpos($hook[0], 'view/theme/' . $a->getCurrentTheme()) === false) {
245                         return;
246                 }
247
248                 @include_once($hook[0]);
249                 if (function_exists($hook[1])) {
250                         $func = $hook[1];
251                         $func($a, $data);
252                 } else {
253                         // remove orphan hooks
254                         $condition = ['hook' => $name, 'file' => $hook[0], 'function' => $hook[1]];
255                         DBA::delete('hook', $condition, ['cascade' => false]);
256                 }
257         }
258
259         /**
260          * check if an app_menu hook exist for addon $name.
261          * Return true if the addon is an app
262          */
263         public static function isApp($name)
264         {
265                 $a = get_app();
266
267                 if (is_array($a->hooks) && (array_key_exists('app_menu', $a->hooks))) {
268                         foreach ($a->hooks['app_menu'] as $hook) {
269                                 if ($hook[0] == 'addon/'.$name.'/'.$name.'.php') {
270                                         return true;
271                                 }
272                         }
273                 }
274
275                 return false;
276         }
277
278         /**
279          * @brief Parse addon comment in search of addon infos.
280          *
281          * like
282          * \code
283          *   * Name: addon
284          *   * Description: An addon which plugs in
285          * . * Version: 1.2.3
286          *   * Author: John <profile url>
287          *   * Author: Jane <email>
288          *   * Maintainer: Jess <email>
289          *   *
290          *   *\endcode
291          * @param string $addon the name of the addon
292          * @return array with the addon information
293          */
294         public static function getInfo($addon)
295         {
296                 $a = get_app();
297
298                 $info = [
299                         'name' => $addon,
300                         'description' => "",
301                         'author' => [],
302                         'maintainer' => [],
303                         'version' => "",
304                         'status' => ""
305                 ];
306
307                 if (!is_file("addon/$addon/$addon.php")) {
308                         return $info;
309                 }
310
311                 $stamp1 = microtime(true);
312                 $f = file_get_contents("addon/$addon/$addon.php");
313                 $a->save_timestamp($stamp1, "file");
314
315                 $r = preg_match("|/\*.*\*/|msU", $f, $m);
316
317                 if ($r) {
318                         $ll = explode("\n", $m[0]);
319                         foreach ($ll as $l) {
320                                 $l = trim($l, "\t\n\r */");
321                                 if ($l != "") {
322                                         $addon_info = array_map("trim", explode(":", $l, 2));
323                                         if (count($addon_info) < 2) {
324                                                 continue;
325                                         }
326
327                                         list($type, $v) = $addon_info;
328                                         $type = strtolower($type);
329                                         if ($type == "author" || $type == "maintainer") {
330                                                 $r = preg_match("|([^<]+)<([^>]+)>|", $v, $m);
331                                                 if ($r) {
332                                                         $info[$type][] = ['name' => $m[1], 'link' => $m[2]];
333                                                 } else {
334                                                         $info[$type][] = ['name' => $v];
335                                                 }
336                                         } else {
337                                                 if (array_key_exists($type, $info)) {
338                                                         $info[$type] = $v;
339                                                 }
340                                         }
341                                 }
342                         }
343                 }
344                 return $info;
345         }
346 }