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