]> git.mxchange.org Git - friendica.git/blob - src/Core/Addon.php
bfb2a1e8fddbdd01f11e93125b65004bd2ba4ea8
[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                 $condition = ['hook' => $hook, 'file' => $file, 'function' => $function];
143                 $exists = DBA::exists('hook', $condition);
144                 if ($exists) {
145                         return true;
146                 }
147
148                 $r = DBA::insert('hook', ['hook' => $hook, 'file' => $file, 'function' => $function, 'priority' => $priority]);
149
150                 return $r;
151         }
152
153         /**
154          * @brief unregisters a hook.
155          *
156          * @param string $hook the name of the hook
157          * @param string $file the name of the file that hooks into
158          * @param string $function the name of the function that the hook called
159          * @return array
160          */
161         public static function unregisterHook($hook, $file, $function)
162         {
163                 $condition = ['hook' => $hook, 'file' => $file, 'function' => $function];
164                 $r = DBA::delete('hook', $condition);
165                 return $r;
166         }
167
168         /**
169          * Load hooks
170          */
171         public static function loadHooks()
172         {
173                 $a = get_app();
174                 $a->hooks = [];
175                 $r = DBA::select('hook', ['hook', 'file', 'function'], [], ['order' => ['priority' => 'desc', 'file']]);
176
177                 while ($rr = DBA::fetch($r)) {
178                         if (! array_key_exists($rr['hook'], $a->hooks)) {
179                                 $a->hooks[$rr['hook']] = [];
180                         }
181                         $a->hooks[$rr['hook']][] = [$rr['file'],$rr['function']];
182                 }
183                 DBA::close($r);
184         }
185
186         /**
187          * @brief Forks a hook.
188          *
189          * Use this function when you want to fork a hook via the worker.
190          *
191          * @param string $name of the hook to call
192          * @param string|array $data to transmit to the callback handler
193          */
194         public static function forkHooks($priority, $name, $data = null)
195         {
196                 $a = get_app();
197
198                 if (is_array($a->hooks) && array_key_exists($name, $a->hooks)) {
199                         foreach ($a->hooks[$name] as $hook) {
200                                 Worker::add($priority, 'ForkHook', $name, $hook, $data);
201                         }
202                 }
203         }
204
205         /**
206          * @brief Calls a hook.
207          *
208          * Use this function when you want to be able to allow a hook to manipulate
209          * the provided data.
210          *
211          * @param string $name of the hook to call
212          * @param string|array &$data to transmit to the callback handler
213          */
214         public static function callHooks($name, &$data = null)
215         {
216                 $a = get_app();
217
218                 if (is_array($a->hooks) && array_key_exists($name, $a->hooks)) {
219                         foreach ($a->hooks[$name] as $hook) {
220                                 self::callSingleHook($a, $name, $hook, $data);
221                         }
222                 }
223         }
224
225         /**
226          * @brief Calls a single hook.
227          *
228          * @param App $a
229          * @param string         $name of the hook to call
230          * @param array          $hook Hook data
231          * @param string|array   &$data to transmit to the callback handler
232          */
233         public static function callSingleHook(App $a, $name, $hook, &$data = null)
234         {
235                 // Don't run a theme's hook if the user isn't using the theme
236                 if (strpos($hook[0], 'view/theme/') !== false && strpos($hook[0], 'view/theme/' . $a->getCurrentTheme()) === false) {
237                         return;
238                 }
239
240                 @include_once($hook[0]);
241                 if (function_exists($hook[1])) {
242                         $func = $hook[1];
243                         $func($a, $data);
244                 } else {
245                         // remove orphan hooks
246                         $condition = ['hook' => $name, 'file' => $hook[0], 'function' => $hook[1]];
247                         DBA::delete('hook', $condition, ['cascade' => false]);
248                 }
249         }
250
251         /**
252          * check if an app_menu hook exist for addon $name.
253          * Return true if the addon is an app
254          */
255         public static function isApp($name)
256         {
257                 $a = get_app();
258
259                 if (is_array($a->hooks) && (array_key_exists('app_menu', $a->hooks))) {
260                         foreach ($a->hooks['app_menu'] as $hook) {
261                                 if ($hook[0] == 'addon/'.$name.'/'.$name.'.php') {
262                                         return true;
263                                 }
264                         }
265                 }
266
267                 return false;
268         }
269
270         /**
271          * @brief Parse addon comment in search of addon infos.
272          *
273          * like
274          * \code
275          *   * Name: addon
276          *   * Description: An addon which plugs in
277          * . * Version: 1.2.3
278          *   * Author: John <profile url>
279          *   * Author: Jane <email>
280          *   * Maintainer: Jess <email>
281          *   *
282          *   *\endcode
283          * @param string $addon the name of the addon
284          * @return array with the addon information
285          */
286         public static function getInfo($addon)
287         {
288                 $a = get_app();
289
290                 $info = [
291                         'name' => $addon,
292                         'description' => "",
293                         'author' => [],
294                         'maintainer' => [],
295                         'version' => "",
296                         'status' => ""
297                 ];
298
299                 if (!is_file("addon/$addon/$addon.php")) {
300                         return $info;
301                 }
302
303                 $stamp1 = microtime(true);
304                 $f = file_get_contents("addon/$addon/$addon.php");
305                 $a->save_timestamp($stamp1, "file");
306
307                 $r = preg_match("|/\*.*\*/|msU", $f, $m);
308
309                 if ($r) {
310                         $ll = explode("\n", $m[0]);
311                         foreach ($ll as $l) {
312                                 $l = trim($l, "\t\n\r */");
313                                 if ($l != "") {
314                                         list($type, $v) = array_map("trim", explode(":", $l, 2));
315                                         $type = strtolower($type);
316                                         if ($type == "author" || $type == "maintainer") {
317                                                 $r = preg_match("|([^<]+)<([^>]+)>|", $v, $m);
318                                                 if ($r) {
319                                                         $info[$type][] = ['name' => $m[1], 'link' => $m[2]];
320                                                 } else {
321                                                         $info[$type][] = ['name' => $v];
322                                                 }
323                                         } else {
324                                                 if (array_key_exists($type, $info)) {
325                                                         $info[$type] = $v;
326                                                 }
327                                         }
328                                 }
329                         }
330                 }
331                 return $info;
332         }
333 }