source: trunk/include/functions_plugins.inc.php @ 28651

Last change on this file since 28651 was 28651, checked in by mistic100, 10 years ago

feature 3076: Enhance plugin update system

  • Property svn:eol-style set to LF
File size: 11.0 KB
Line 
1<?php
2// +-----------------------------------------------------------------------+
3// | Piwigo - a PHP based photo gallery                                    |
4// +-----------------------------------------------------------------------+
5// | Copyright(C) 2008-2014 Piwigo Team                  http://piwigo.org |
6// | Copyright(C) 2003-2008 PhpWebGallery Team    http://phpwebgallery.net |
7// | Copyright(C) 2002-2003 Pierrick LE GALL   http://le-gall.net/pierrick |
8// +-----------------------------------------------------------------------+
9// | This program is free software; you can redistribute it and/or modify  |
10// | it under the terms of the GNU General Public License as published by  |
11// | the Free Software Foundation                                          |
12// |                                                                       |
13// | This program is distributed in the hope that it will be useful, but   |
14// | WITHOUT ANY WARRANTY; without even the implied warranty of            |
15// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU      |
16// | General Public License for more details.                              |
17// |                                                                       |
18// | You should have received a copy of the GNU General Public License     |
19// | along with this program; if not, write to the Free Software           |
20// | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, |
21// | USA.                                                                  |
22// +-----------------------------------------------------------------------+
23
24/**
25 * @package functions\plugins
26 */
27
28
29/** base directory of plugins */
30define('PHPWG_PLUGINS_PATH', PHPWG_ROOT_PATH.'plugins/');
31/** default priority for plugins handlers */
32define('EVENT_HANDLER_PRIORITY_NEUTRAL', 50);
33
34
35/**
36 * Used to declare maintenance methods of a plugin.
37 */
38class PluginMaintain
39{
40  /** @var string $plugin_id */
41  protected $plugin_id;
42
43  /**
44   * @param string $id
45   */
46  function __construct($id)
47  {
48    $this->plugin_id = $id;
49  }
50
51  /**
52   * @param string $plugin_version
53   * @param array &$errors - used to return error messages
54   */
55  function install($plugin_version, &$errors=array()) {}
56
57  /**
58   * @param string $plugin_version
59   * @param array &$errors - used to return error messages
60   */
61  function activate($plugin_version, &$errors=array()) {}
62
63  function deactivate() {}
64
65  function uninstall() {}
66
67  /**
68   * @param string $old_version
69   * @param string $new_version
70   * @param array &$errors - used to return error messages
71   */
72  function update($old_version, $new_version, &$errors=array()) {}
73}
74
75/**
76 * Used to declare maintenance methods of a theme.
77 */
78class ThemeMaintain
79{
80  /** @var string $theme_id */
81  protected $theme_id;
82
83  /**
84   * @param string $id
85   */
86  function __construct($id)
87  {
88    $this->theme_id = $id;
89  }
90
91  /**
92   * @param string $theme_version
93   * @param array &$errors - used to return error messages
94   */
95  function activate($theme_version, &$errors=array()) {}
96
97  function deactivate() {}
98
99  function delete() {}
100}
101
102
103/**
104 * Register an event handler.
105 *
106 * @param string $event the name of the event to listen to
107 * @param Callable $func the callback function
108 * @param int $priority greater priority will be executed at last
109 * @param string $include_path file to include before executing the callback
110 * @return bool false is handler already exists
111 */
112function add_event_handler($event, $func,
113    $priority=EVENT_HANDLER_PRIORITY_NEUTRAL, $include_path=null)
114{
115  global $pwg_event_handlers;
116
117  if (isset($pwg_event_handlers[$event][$priority]))
118  {
119    foreach ($pwg_event_handlers[$event][$priority] as $handler)
120    {
121      if ($handler['function'] == $func)
122      {
123        return false;
124      }
125    }
126  }
127
128  $pwg_event_handlers[$event][$priority][] = array(
129    'function' => $func,
130    'include_path' => is_string($include_path) ? $include_path : null,
131    );
132
133  ksort($pwg_event_handlers[$event]);
134  return true;
135}
136
137/**
138 * Removes an event handler.
139 * @see add_event_handler()
140 *
141 * @param string $event
142 * @param Callable $func
143 * @param int $priority
144 */
145function remove_event_handler($event, $func,
146   $priority=EVENT_HANDLER_PRIORITY_NEUTRAL)
147{
148  global $pwg_event_handlers;
149
150  if (!isset($pwg_event_handlers[$event][$priority]))
151  {
152    return false;
153  }
154  for ($i=0; $i<count($pwg_event_handlers[$event][$priority]); $i++)
155  {
156    if ($pwg_event_handlers[$event][$priority][$i]['function']==$func)
157    {
158      unset($pwg_event_handlers[$event][$priority][$i]);
159      $pwg_event_handlers[$event][$priority] =
160        array_values($pwg_event_handlers[$event][$priority]);
161
162      if (empty($pwg_event_handlers[$event][$priority]))
163      {
164        unset($pwg_event_handlers[$event][$priority]);
165        if (empty($pwg_event_handlers[$event]))
166        {
167          unset($pwg_event_handlers[$event]);
168        }
169      }
170      return true;
171    }
172  }
173  return false;
174}
175
176/**
177 * Triggers a modifier event and calls all registered event handlers.
178 * trigger_change() is used as a modifier: it allows to transmit _$data_
179 * through all handlers, thus each handler MUST return a value,
180 * optional _$args_ are not transmitted.
181 *
182 * @since 2.6
183 *
184 * @param string $event
185 * @param mixed $data data to transmit to all handlers
186 * @param mixed $args,... optional arguments
187 * @return mixed $data
188 */
189function trigger_change($event, $data=null)
190{
191  global $pwg_event_handlers;
192
193  if (isset($pwg_event_handlers['trigger']))
194  {// debugging
195    trigger_notify('trigger',
196      array('type'=>'event', 'event'=>$event, 'data'=>$data)
197      );
198  }
199
200  if (!isset($pwg_event_handlers[$event]))
201  {
202    return $data;
203  }
204  $args = func_get_args();
205  array_shift($args);
206
207  foreach ($pwg_event_handlers[$event] as $priority => $handlers)
208  {
209    foreach ($handlers as $handler)
210    {
211      $args[0] = $data;
212
213      if (!empty($handler['include_path']))
214      {
215        include_once($handler['include_path']);
216      }
217
218      $data = call_user_func_array($handler['function'], $args);
219    }
220  }
221
222  if (isset($pwg_event_handlers['trigger']))
223  {// debugging
224    trigger_notify('trigger',
225      array('type'=>'post_event', 'event'=>$event, 'data'=>$data)
226      );
227  }
228
229  return $data;
230}
231
232/**
233 * Triggers a notifier event and calls all registered event handlers.
234 * trigger_notify() is only used as a notifier, no modification of data is possible
235 *
236 * @since 2.6
237 *
238 * @param string $event
239 * @param mixed $args,... optional arguments
240 */
241function trigger_notify($event)
242{
243  global $pwg_event_handlers;
244
245  if (isset($pwg_event_handlers['trigger']) and $event!='trigger')
246  {// debugging - avoid recursive calls
247    trigger_notify('trigger',
248      array('type'=>'action', 'event'=>$event, 'data'=>null)
249      );
250  }
251
252  if (!isset($pwg_event_handlers[$event]))
253  {
254    return;
255  }
256  $args = func_get_args();
257  array_shift($args);
258
259  foreach ($pwg_event_handlers[$event] as $priority => $handlers)
260  {
261    foreach ($handlers as $handler)
262    {
263      if (!empty($handler['include_path']))
264      {
265        include_once($handler['include_path']);
266      }
267
268      call_user_func_array($handler['function'], $args);
269    }
270  }
271}
272
273/**
274 * Saves some data with the associated plugin id, data are only available
275 * during script lifetime.
276 * @depracted 2.6
277 *
278 * @param string $plugin_id
279 * @param mixed &$data
280 * @return bool
281 */
282function set_plugin_data($plugin_id, &$data)
283{
284  global $pwg_loaded_plugins;
285  if ( isset($pwg_loaded_plugins[$plugin_id]) )
286  {
287    $pwg_loaded_plugins[$plugin_id]['plugin_data'] = &$data;
288    return true;
289  }
290  return false;
291}
292
293/**
294 * Retrieves plugin data saved previously with set_plugin_data.
295 * @see set_plugin_data()
296 * @depracted 2.6
297 *
298 * @param string $plugin_id
299 * @return mixed
300 */
301function &get_plugin_data($plugin_id)
302{
303  global $pwg_loaded_plugins;
304  if ( isset($pwg_loaded_plugins[$plugin_id]['plugin_data']) )
305  {
306    return $pwg_loaded_plugins[$plugin_id]['plugin_data'];
307  }
308  return null;
309}
310
311/**
312 * Returns an array of plugins defined in the database.
313 *
314 * @param string $state optional filter
315 * @param string $id returns only data about given plugin
316 * @return array
317 */
318function get_db_plugins($state='', $id='')
319{
320  $query = '
321SELECT * FROM '.PLUGINS_TABLE;
322  $clauses = array();
323  if (!empty($state))
324  {
325    $clauses[] = 'state=\''.$state.'\'';
326  }
327  if (!empty($id))
328  {
329    $clauses[] = 'id="'.$id.'"';
330  }
331  if (count($clauses))
332  {
333    $query .= '
334  WHERE '. implode(' AND ', $clauses);
335  }
336
337  return query2array($query);
338}
339
340/**
341 * Loads a plugin in memory.
342 * It performs autoupdate, includes the main.inc.php file and updates *$pwg_loaded_plugins*.
343 *
344 * @param string $plugin
345 */
346function load_plugin($plugin)
347{
348  $file_name = PHPWG_PLUGINS_PATH.$plugin['id'].'/main.inc.php';
349  if (file_exists($file_name))
350  {
351    autoupdate_plugin($plugin);
352    global $pwg_loaded_plugins;
353    $pwg_loaded_plugins[ $plugin['id'] ] = $plugin;
354    include_once($file_name);
355  }
356}
357
358/**
359 * Performs update task of a plugin.
360 * Autoupdate is only performed if the plugin has a maintain.class.php file.
361 *
362 * @since 2.7
363 *
364 * @param array &$plugin (id, version, state) will be updated if version changes
365 */
366function autoupdate_plugin(&$plugin)
367{
368  $maintain_file = PHPWG_PLUGINS_PATH.$plugin['id'].'/maintain.class.php';
369
370  // autoupdate is applicable only to plugins with 2.7 architecture
371  if (file_exists($maintain_file))
372  {
373    // try to find the filesystem version in lines 2 to 10 of main.inc.php
374    $fh = fopen(PHPWG_PLUGINS_PATH.$plugin['id'].'/main.inc.php', 'r');
375    $fs_version = null;
376    $i = -1;
377
378    while (($line = fgets($fh))!==false && $fs_version==null && $i<10)
379    {
380      $i++;
381      if ($i < 2) continue; // first lines are typically "<?php" and "/*"
382
383      if (preg_match('#Version: ([\\w.-]+)#', $line, $matches))
384      {
385        $fs_version = $matches[1];
386      }
387    }
388
389    fclose($fh);
390
391    if ($fs_version != null)
392    {
393      global $pwg_loaded_plugins, $page;
394
395      // if version is auto (dev) or superior
396      if (
397          $fs_version == 'auto' or $plugin['version'] == 'auto'
398          or safe_version_compare($plugin['version'], $fs_version, '<')
399        )
400      {
401        // call update method
402        include_once($maintain_file);
403
404        $classname = $plugin['id'].'_maintain';
405        $plugin_maintain = new $classname($plugin['id']);
406        $plugin_maintain->update($plugin['version'], $fs_version, $page['errors']);
407
408        $plugin['version'] = $fs_version;
409
410        // update database (only on production)
411        if ($plugin['version'] != 'auto')
412        {
413          $query = '
414UPDATE '. PLUGINS_TABLE .'
415  SET version = "'. $plugin['version'] .'"
416  WHERE id = "'. $plugin['id'] .'"
417;';
418          pwg_query($query);
419        }
420      }
421    }
422  }
423}
424
425/**
426 * Loads all the registered plugins.
427 */
428function load_plugins()
429{
430  global $conf, $pwg_loaded_plugins;
431  $pwg_loaded_plugins = array();
432  if ($conf['enable_plugins'])
433  {
434    $plugins = get_db_plugins('active');
435    foreach( $plugins as $plugin)
436    {// include main from a function to avoid using same function context
437      load_plugin($plugin);
438    }
439    trigger_notify('plugins_loaded');
440  }
441}
442
443?>
Note: See TracBrowser for help on using the repository browser.