source: branches/2.1/admin/include/plugins.class.php @ 6724

Last change on this file since 6724 was 6724, checked in by patdenice, 14 years ago

Plugin version compare must return false if plugin version is "auto" (developpement version).
Change language switch version number.

  • Property svn:eol-style set to LF
File size: 16.9 KB
Line 
1<?php
2// +-----------------------------------------------------------------------+
3// | Piwigo - a PHP based picture gallery                                  |
4// +-----------------------------------------------------------------------+
5// | Copyright(C) 2008-2010 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
24class plugins
25{
26  var $fs_plugins = array();
27  var $db_plugins_by_id = array();
28  var $server_plugins = array();
29
30  /**
31   * Initialize $fs_plugins and $db_plugins_by_id
32  */
33  function plugins()
34  {
35    $this->get_fs_plugins();
36
37    foreach (get_db_plugins() as $db_plugin)
38    {
39      $this->db_plugins_by_id[$db_plugin['id']] = $db_plugin;
40    }
41  }
42
43  /**
44   * Set tabsheet for plugins pages.
45   * @param string selected page.
46   */
47  function set_tabsheet($selected)
48  {
49    include_once(PHPWG_ROOT_PATH.'admin/include/tabsheet.class.php');
50
51    $link = get_root_url().'admin.php?page=';
52
53    $tabsheet = new tabsheet();
54    $tabsheet->add('plugins_list', l10n('Plugin list'), $link.'plugins_list');
55    $tabsheet->add('plugins_update', l10n('Check for updates'), $link.'plugins_update');
56    $tabsheet->add('plugins_new', l10n('Other plugins'), $link.'plugins_new');
57    $tabsheet->select($selected);
58    $tabsheet->assign();
59  }
60
61 /**
62   * Perform requested actions
63  *  @param string - action
64  * @param string - plugin id
65  * @param array - errors
66  */
67  function perform_action($action, $plugin_id)
68  {
69    if (isset($this->db_plugins_by_id[$plugin_id]))
70    {
71      $crt_db_plugin = $this->db_plugins_by_id[$plugin_id];
72    }
73    $file_to_include = PHPWG_PLUGINS_PATH . $plugin_id . '/maintain.inc.php';
74
75    $errors = array();
76
77    switch ($action)
78    {
79      case 'install':
80        if (!empty($crt_db_plugin))
81        {
82          array_push($errors, 'CANNOT INSTALL - ALREADY INSTALLED');
83          break;
84        }
85        if (!isset($this->fs_plugins[$plugin_id]))
86        {
87          array_push($errors, 'CANNOT INSTALL - NO SUCH PLUGIN');
88          break;
89        }
90        if (file_exists($file_to_include))
91        {
92          include_once($file_to_include);
93          if (function_exists('plugin_install'))
94          {
95            plugin_install($plugin_id, $this->fs_plugins[$plugin_id]['version'], $errors);
96          }
97        }
98        if (empty($errors))
99        {
100          $query = '
101INSERT INTO ' . PLUGINS_TABLE . ' (id,version) VALUES (\''
102. $plugin_id . '\',\'' . $this->fs_plugins[$plugin_id]['version'] . '\'
103)';
104          pwg_query($query);
105        }
106        break;
107
108      case 'activate':
109        if (!isset($crt_db_plugin))
110        {
111          array_push($errors, 'CANNOT ACTIVATE - NOT INSTALLED');
112          break;
113        }
114        if ($crt_db_plugin['state'] != 'inactive')
115        {
116          array_push($errors, 'invalid current state ' . $crt_db_plugin['state']);
117          break;
118        }
119        if (file_exists($file_to_include))
120        {
121          include_once($file_to_include);
122          if (function_exists('plugin_activate'))
123          {
124            plugin_activate($plugin_id, $crt_db_plugin['version'], $errors);
125          }
126        }
127        if (empty($errors))
128        {
129          $query = '
130UPDATE ' . PLUGINS_TABLE . '
131SET state=\'active\', version=\''.$this->fs_plugins[$plugin_id]['version'].'\'
132WHERE id=\'' . $plugin_id . '\'';
133          pwg_query($query);
134        }
135        break;
136
137      case 'deactivate':
138        if (!isset($crt_db_plugin))
139        {
140          die ('CANNOT DEACTIVATE - NOT INSTALLED');
141        }
142        if ($crt_db_plugin['state'] != 'active')
143        {
144          die('invalid current state ' . $crt_db_plugin['state']);
145        }
146        $query = '
147UPDATE ' . PLUGINS_TABLE . ' SET state=\'inactive\' WHERE id=\'' . $plugin_id . '\'';
148        pwg_query($query);
149        if (file_exists($file_to_include))
150        {
151          include_once($file_to_include);
152          if (function_exists('plugin_deactivate'))
153          {
154            plugin_deactivate($plugin_id);
155          }
156        }
157        break;
158
159      case 'uninstall':
160        if (!isset($crt_db_plugin))
161        {
162          die ('CANNOT UNINSTALL - NOT INSTALLED');
163        }
164        $query = '
165DELETE FROM ' . PLUGINS_TABLE . ' WHERE id=\'' . $plugin_id . '\'';
166        pwg_query($query);
167        if (file_exists($file_to_include))
168        {
169          include_once($file_to_include);
170          if (function_exists('plugin_uninstall'))
171          {
172            plugin_uninstall($plugin_id);
173          }
174        }
175        break;
176
177      case 'delete':
178        if (!empty($crt_db_plugin))
179        {
180          array_push($errors, 'CANNOT DELETE - PLUGIN IS INSTALLED');
181          break;
182        }
183        if (!isset($this->fs_plugins[$plugin_id]))
184        {
185          array_push($errors, 'CANNOT DELETE - NO SUCH PLUGIN');
186          break;
187        }
188        if (!$this->deltree(PHPWG_PLUGINS_PATH . $plugin_id))
189        {
190          $this->send_to_trash(PHPWG_PLUGINS_PATH . $plugin_id);
191        }
192        break;
193    }
194    return $errors;
195  }
196
197  /**
198  *  Get plugins defined in the plugin directory
199  */ 
200  function get_fs_plugins()
201  {
202    $dir = opendir(PHPWG_PLUGINS_PATH);
203    while ($file = readdir($dir))
204    {
205      if ($file!='.' and $file!='..')
206      {
207        $path = PHPWG_PLUGINS_PATH.$file;
208        if (is_dir($path) and !is_link($path)
209            and preg_match('/^[a-zA-Z0-9-_]+$/', $file )
210            and file_exists($path.'/main.inc.php')
211            )
212        {
213          $plugin = array(
214              'name'=>$file,
215              'version'=>'0',
216              'uri'=>'',
217              'description'=>'',
218              'author'=>'',
219            );
220          $plg_data = implode( '', file($path.'/main.inc.php') );
221
222          if ( preg_match("|Plugin Name: (.*)|", $plg_data, $val) )
223          {
224            $plugin['name'] = trim( $val[1] );
225          }
226          if (preg_match("|Version: (.*)|", $plg_data, $val))
227          {
228            $plugin['version'] = trim($val[1]);
229          }
230          if ( preg_match("|Plugin URI: (.*)|", $plg_data, $val) )
231          {
232            $plugin['uri'] = trim($val[1]);
233          }
234          if ($desc = load_language('description.txt', $path.'/', array('return' => true)))
235          {
236            $plugin['description'] = trim($desc);
237          }
238          elseif ( preg_match("|Description: (.*)|", $plg_data, $val) )
239          {
240            $plugin['description'] = trim($val[1]);
241          }
242          if ( preg_match("|Author: (.*)|", $plg_data, $val) )
243          {
244            $plugin['author'] = trim($val[1]);
245          }
246          if ( preg_match("|Author URI: (.*)|", $plg_data, $val) )
247          {
248            $plugin['author uri'] = trim($val[1]);
249          }
250          if (!empty($plugin['uri']) and strpos($plugin['uri'] , 'extension_view.php?eid='))
251          {
252            list( , $extension) = explode('extension_view.php?eid=', $plugin['uri']);
253            if (is_numeric($extension)) $plugin['extension'] = $extension;
254          }
255          // IMPORTANT SECURITY !
256          $plugin = array_map('htmlspecialchars', $plugin);
257          $this->fs_plugins[$file] = $plugin;
258        }
259      }
260    }
261    closedir($dir);
262  }
263
264  /**
265   * Sort fs_plugins
266   */
267  function sort_fs_plugins($order='name')
268  {
269    switch ($order)
270    {
271      case 'name':
272        uasort($this->fs_plugins, 'name_compare');
273        break;
274      case 'status':
275        $this->sort_plugins_by_state();
276        break;
277      case 'author':
278        uasort($this->fs_plugins, array($this, 'plugin_author_compare'));
279        break;
280      case 'id':
281        uksort($this->fs_plugins, 'strcasecmp');
282        break;
283    }
284  }
285
286  /**
287   * Retrieve PEM server datas to $server_plugins
288   */
289  function get_server_plugins($new=false)
290  {
291    global $user;
292
293    // Retrieve PEM versions
294    $version = PHPWG_VERSION;
295    $versions_to_check = array();
296    $url = PEM_URL . '/api/get_version_list.php?category_id=12&format=php';
297    if (fetchRemote($url, $result) and $pem_versions = @unserialize($result))
298    {
299      if (!preg_match('/^\d+\.\d+\.\d+/', $version))
300      {
301        $version = $pem_versions[0]['name'];
302      }
303      $branch = substr($version, 0, strrpos($version, '.'));
304      foreach ($pem_versions as $pem_version)
305      {
306        if (strpos($pem_version['name'], $branch) === 0)
307        {
308          $versions_to_check[] = $pem_version['id'];
309        }
310      }
311    }
312    if (empty($versions_to_check))
313    {
314      return false;
315    }
316
317    // Plugins to check
318    $plugins_to_check = array();
319    foreach($this->fs_plugins as $fs_plugin)
320    {
321      if (isset($fs_plugin['extension']))
322      {
323        $plugins_to_check[] = $fs_plugin['extension'];
324      }
325    }
326
327    // Retrieve PEM plugins infos
328    $url = PEM_URL . '/api/get_revision_list.php?category_id=12&format=php&last_revision_only=true';
329    $url .= '&version=' . implode(',', $versions_to_check);
330    $url .= '&lang=' . substr($user['language'], 0, 2);
331    $url .= '&get_nb_downloads=true';
332
333    if (!empty($plugins_to_check))
334    {
335      $url .= $new ? '&extension_exclude=' : '&extension_include=';
336      $url .= implode(',', $plugins_to_check);
337    }
338    if (fetchRemote($url, $result))
339    {
340      $pem_plugins = @unserialize($result);
341      if (!is_array($pem_plugins))
342      {
343        return false;
344      }
345      foreach ($pem_plugins as $plugin)
346      {
347        $this->server_plugins[$plugin['extension_id']] = $plugin;
348      }
349      return true;
350    }
351    return false;
352  }
353 
354  /**
355   * Sort $server_plugins
356   */
357  function sort_server_plugins($order='date')
358  {
359    switch ($order)
360    {
361      case 'date':
362        krsort($this->server_plugins);
363        break;
364      case 'revision':
365        usort($this->server_plugins, array($this, 'extension_revision_compare'));
366        break;
367      case 'name':
368        uasort($this->server_plugins, array($this, 'extension_name_compare'));
369        break;
370      case 'author':
371        uasort($this->server_plugins, array($this, 'extension_author_compare'));
372        break;
373      case 'downloads':
374        usort($this->server_plugins, array($this, 'extension_downloads_compare'));
375        break;
376    }
377  }
378
379  /**
380   * Extract plugin files from archive
381   * @param string - install or upgrade
382   *  @param string - archive URL
383    * @param string - plugin id or extension id
384   */
385  function extract_plugin_files($action, $revision, $dest)
386  {
387    if ($archive = tempnam( PHPWG_PLUGINS_PATH, 'zip'))
388    {
389      $url = PEM_URL . '/download.php?rid=' . $revision;
390      $url .= '&origin=piwigo_' . $action;
391
392      if ($handle = @fopen($archive, 'wb') and fetchRemote($url, $handle))
393      {
394        fclose($handle);
395        include(PHPWG_ROOT_PATH.'admin/include/pclzip.lib.php');
396        $zip = new PclZip($archive);
397        if ($list = $zip->listContent())
398        {
399          foreach ($list as $file)
400          {
401            // we search main.inc.php in archive
402            if (basename($file['filename']) == 'main.inc.php'
403              and (!isset($main_filepath)
404              or strlen($file['filename']) < strlen($main_filepath)))
405            {
406              $main_filepath = $file['filename'];
407            }
408          }
409          if (isset($main_filepath))
410          {
411            $root = dirname($main_filepath); // main.inc.php path in archive
412            if ($action == 'upgrade')
413            {
414              $extract_path = PHPWG_PLUGINS_PATH . $dest;
415            }
416            else
417            {
418              $extract_path = PHPWG_PLUGINS_PATH
419                  . ($root == '.' ? 'extension_' . $dest : basename($root));
420            }
421            if($result = $zip->extract(PCLZIP_OPT_PATH, $extract_path,
422                                       PCLZIP_OPT_REMOVE_PATH, $root,
423                                       PCLZIP_OPT_REPLACE_NEWER))
424            {
425              foreach ($result as $file)
426              {
427                if ($file['stored_filename'] == $main_filepath)
428                {
429                  $status = $file['status'];
430                  break;
431                }
432              }
433              if (file_exists($extract_path.'/obsolete.list')
434                and $old_files = file($extract_path.'/obsolete.list', FILE_IGNORE_NEW_LINES)
435                and !empty($old_files))
436              {
437                array_push($old_files, 'obsolete.list');
438                foreach($old_files as $old_file)
439                {
440                  $path = $extract_path.'/'.$old_file;
441                  if (is_file($path))
442                  {
443                    @unlink($path);
444                  }
445                  elseif (is_dir($path))
446                  {
447                    if (!$this->deltree($path))
448                    {
449                      $this->send_to_trash($path);
450                    }
451                  }
452                }
453              }
454            }
455            else $status = 'extract_error';
456          }
457          else $status = 'archive_error';
458        }
459        else $status = 'archive_error';
460      }
461      else $status = 'dl_archive_error';
462    }
463    else $status = 'temp_path_error';
464
465    @unlink($archive);
466    return $status;
467  }
468 
469  /**
470   * delete $path directory
471   * @param string - path
472   */
473  function deltree($path)
474  {
475    if (is_dir($path))
476    {
477      $fh = opendir($path);
478      while ($file = readdir($fh))
479      {
480        if ($file != '.' and $file != '..')
481        {
482          $pathfile = $path . '/' . $file;
483          if (is_dir($pathfile))
484          {
485            $this->deltree($pathfile);
486          }
487          else
488          {
489            @unlink($pathfile);
490          }
491        }
492      }
493      closedir($fh);
494      return @rmdir($path);
495    }
496  }
497
498  /**
499   * send $path to trash directory
500   * @param string - path
501   */
502  function send_to_trash($path)
503  {
504    $trash_path = PHPWG_PLUGINS_PATH . 'trash';
505    if (!is_dir($trash_path))
506    {
507      @mkdir($trash_path);
508      $file = @fopen($trash_path . '/.htaccess', 'w');
509      @fwrite($file, 'deny from all');
510      @fclose($file);
511    }
512    while ($r = $trash_path . '/' . md5(uniqid(rand(), true)))
513    {
514      if (!is_dir($r))
515      {
516        @rename($path, $r);
517        break;
518      }
519    }
520  }
521
522  /**
523   * Sort functions
524   */
525  function plugin_version_compare($a, $b)
526  {
527    if (strtolower($a) == 'auto') return false;
528
529    $pattern = array('/([a-z])/ei', '/\.+/', '/\.\Z|\A\./');
530    $replacement = array( "'.'.intval('\\1', 36).'.'", '.', '');
531
532    $array = preg_replace($pattern, $replacement, array($a, $b));
533
534    return version_compare($array[0], $array[1], '>=');
535  }
536
537  function extension_revision_compare($a, $b)
538  {
539    if ($a['revision_date'] < $b['revision_date']) return 1;
540    else return -1;
541  }
542
543  function extension_name_compare($a, $b)
544  {
545    return strcmp(strtolower($a['extension_name']), strtolower($b['extension_name']));
546  }
547
548  function extension_author_compare($a, $b)
549  {
550    $r = strcasecmp($a['author_name'], $b['author_name']);
551    if ($r == 0) return $this->extension_name_compare($a, $b);
552    else return $r;
553  }
554
555  function plugin_author_compare($a, $b)
556  {
557    $r = strcasecmp($a['author'], $b['author']);
558    if ($r == 0) return name_compare($a, $b);
559    else return $r;
560  }
561
562  function extension_downloads_compare($a, $b)
563  {
564    if ($a['extension_nb_downloads'] < $b['extension_nb_downloads']) return 1;
565    else return -1;
566  }
567
568  function sort_plugins_by_state()
569  {
570    uasort($this->fs_plugins, 'name_compare');
571
572    $active_plugins = array();
573    $inactive_plugins = array();
574    $not_installed = array();
575
576    foreach($this->fs_plugins as $plugin_id => $plugin)
577    {
578      if (isset($this->db_plugins_by_id[$plugin_id]))
579      {
580        $this->db_plugins_by_id[$plugin_id]['state'] == 'active' ?
581          $active_plugins[$plugin_id] = $plugin : $inactive_plugins[$plugin_id] = $plugin;
582      }
583      else
584      {
585        $not_installed[$plugin_id] = $plugin;
586      }
587    }
588    $this->fs_plugins = $active_plugins + $inactive_plugins + $not_installed;
589  }
590}
591?>
Note: See TracBrowser for help on using the repository browser.