source: trunk/admin/include/updates.class.php @ 10511

Last change on this file since 10511 was 10511, checked in by patdenice, 13 years ago

feature:2271
Merge autoupdate plugin into piwigo core.

File size: 13.5 KB
Line 
1<?php
2
3if (!defined('PHPWG_ROOT_PATH')) die('Hacking attempt!');
4
5class updates
6{
7  var $types = array();
8  var $plugins;
9  var $themes;
10  var $languages;
11  var $missing = array();
12  var $default_plugins = array();
13  var $default_themes = array();
14  var $default_languages = array();
15  var $merged_extensions = array();
16  var $merged_extension_url = 'http://piwigo.org/download/merged_extensions.txt';
17
18  function __construct()
19  {
20    $this->types = array('plugins', 'themes', 'languages');
21    $this->default_themes = array('clear', 'dark', 'Sylvia');
22    $this->default_plugins = array('admin_multi_view', 'c13y_upgrade', 'language_switch', 'LocalFilesEditor');
23
24    foreach ($this->types as $type)
25    {
26      include_once(PHPWG_ROOT_PATH.'admin/include/'.$type.'.class.php');
27      $this->$type = new $type();
28    }
29  }
30
31  function check_piwigo_upgrade()
32  {
33    $_SESSION['need_update'] = null;
34
35    if (preg_match('/(\d+\.\d+)\.(\d+)/', PHPWG_VERSION, $matches)
36      and @fetchRemote(PHPWG_URL.'/download/all_versions.php', $result))
37    {
38      $all_versions = @explode("\n", $result);
39      $new_version = trim($all_versions[0]);
40      $_SESSION['need_update'] = version_compare(PHPWG_VERSION, $new_version, '<');
41    }
42  }
43
44  function get_server_extensions($version=PHPWG_VERSION)
45  {
46    global $user;
47
48    $get_data = array(
49      'format' => 'php',
50    );
51
52    // Retrieve PEM versions
53    $versions_to_check = array();
54    $url = PEM_URL . '/api/get_version_list.php';
55    if (fetchRemote($url, $result, $get_data) and $pem_versions = @unserialize($result))
56    {
57      if (!preg_match('/^\d+\.\d+\.\d+/', $version))
58      {
59        $version = $pem_versions[0]['name'];
60      }
61      $branch = substr($version, 0, strrpos($version, '.'));
62      foreach ($pem_versions as $pem_version)
63      {
64        if (strpos($pem_version['name'], $branch) === 0)
65        {
66          $versions_to_check[] = $pem_version['id'];
67        }
68      }
69    }
70    if (empty($versions_to_check))
71    {
72      return false;
73    }
74
75    // Extensions to check
76    $ext_to_check = array();
77    foreach ($this->types as $type)
78    {
79      $fs = 'fs_'.$type;
80      foreach ($this->$type->$fs as $ext)
81      {
82        if (isset($ext['extension']))
83        {
84          $ext_to_check[$ext['extension']] = $type;
85        }
86      }
87    }
88
89    // Retrieve PEM plugins infos
90    $url = PEM_URL . '/api/get_revision_list.php';
91    $get_data = array_merge($get_data, array(
92      'last_revision_only' => 'true',
93      'version' => implode(',', $versions_to_check),
94      'lang' => substr($user['language'], 0, 2),
95      'get_nb_downloads' => 'true',
96      )
97    );
98
99    $post_data = array();
100    if (!empty($ext_to_check))
101    {
102      $post_data['extension_include'] = implode(',', array_keys($ext_to_check));
103    }
104
105    if (fetchRemote($url, $result, $get_data, $post_data))
106    {
107      $pem_exts = @unserialize($result);
108      if (!is_array($pem_exts))
109      {
110        return false;
111      }
112      foreach ($pem_exts as $ext)
113      {
114        if (isset($ext_to_check[$ext['extension_id']]))
115        {
116          $server = 'server_'.$ext_to_check[$ext['extension_id']];
117          $this->$ext_to_check[$ext['extension_id']]->$server += array($ext['extension_id'] => $ext);
118          unset($ext_to_check[$ext['extension_id']]);
119        }
120      }
121      $this->check_missing_extensions($ext_to_check);
122      return true;
123    }
124    return false;
125  }
126
127  // Check all extensions upgrades
128  function check_extensions()
129  {
130    global $conf;
131
132    if (!$this->get_server_extensions())
133    {
134      autoupdate_error();
135    }
136
137    $_SESSION['extensions_need_update'] = array();
138
139    foreach ($this->types as $type)
140    {
141      $fs = 'fs_'.$type;
142      $server = 'server_'.$type;
143      $server_ext = $this->$type->$server;
144      $fs_ext = $this->$type->$fs;
145
146      $ignore_list = array();
147      $need_upgrade = array();
148
149      foreach($fs_ext as $ext_id => $fs_ext)
150      {
151        if (isset($fs_ext['extension']) and isset($server_ext[$fs_ext['extension']]))
152        {
153          $ext_info = $server_ext[$fs_ext['extension']];
154
155          if (!$this->version_compare($fs_ext['version'], $ext_info['revision_name'], $type))
156          {
157            if (in_array($ext_id, $conf['updates_ignored'][$type]))
158            {
159              array_push($ignore_list, $ext_id);
160            }
161            else
162            {
163              $_SESSION['extensions_need_update'][$type][$ext_id] = $ext_info['revision_name'];
164            }
165          }
166        }
167      }
168      $conf['updates_ignored'][$type] = $ignore_list;
169    }
170    conf_update_param('updates_ignored', pwg_db_real_escape_string(serialize($conf['updates_ignored'])));
171  }
172
173  // Check if extension have been upgraded since last check
174  function check_updated_extensions()
175  {
176    foreach ($this->types as $type)
177    {
178      if (!empty($_SESSION['extensions_need_update'][$type]))
179      {
180        $fs = 'fs_'.$type;
181        foreach($this->$type->$fs as $ext_id => $fs_ext)
182        {
183          if (isset($_SESSION['extensions_need_update'][$type][$ext_id])
184            and $this->version_compare($fs_ext['version'], $_SESSION['extensions_need_update'][$type][$ext_id], $type))
185          {
186            // Extension have been upgraded
187            $this->check_extensions();
188            break;
189          }
190        }
191      }
192    }
193  }
194
195  function check_missing_extensions($missing)
196  {
197    foreach ($missing as $id => $type)
198    {
199      $fs = 'fs_'.$type;
200      $default = 'default_'.$type;
201      foreach ($this->$type->$fs as $ext_id => $ext)
202      {
203        if (isset($ext['extension']) and $id == $ext['extension']
204          and !in_array($ext_id, $this->$default)
205          and !in_array($ext['extension'], $this->merged_extensions))
206        {
207          $this->missing[$type][] = $ext;
208          break;
209        }
210      }
211    }
212  }
213
214  function get_merged_extensions($version)
215  {
216    if (fetchRemote($this->merged_extension_url, $result))
217    {
218      $rows = explode("\n", $result);
219      foreach ($rows as $row)
220      {
221        if (preg_match('/^(\d+\.\d+): *(.*)$/', $row, $match))
222        {
223          if (version_compare($version, $match[1], '>='))
224          {
225            $extensions = explode(',', trim($match[2]));
226            $this->merged_extensions = array_merge($this->merged_extensions, $extensions);
227          }
228        }
229      }
230    }
231  }
232
233  function version_compare($a, $b, $type)
234  {
235    $version_compare = rtrim($type, 's').'_version_compare';
236
237    return $this->$type->$version_compare($a, $b);
238  }
239
240  function deltree($path, $move_to_trash=false)
241  {
242    if (is_dir($path))
243    {
244      $fh = opendir($path);
245      while ($file = readdir($fh))
246      {
247        if ($file != '.' and $file != '..')
248        {
249          $pathfile = $path . '/' . $file;
250          if (is_dir($pathfile))
251          {
252            self::deltree($pathfile, $move_to_trash);
253          }
254          else
255          {
256            @unlink($pathfile);
257          }
258        }
259      }
260      closedir($fh);
261      if (@rmdir($path))
262      {
263        return true;
264      }
265      elseif ($move_to_trash)
266      {
267        $trash = PHPWG_ROOT_PATH.'_trash';
268        if (!is_dir($trash))
269        {
270          @mkgetdir($trash);
271        }
272        return @rename($path, $trash . '/'.md5(uniqid(rand(), true)));
273      }
274      else
275      {
276        return false;
277      }
278    }
279  }
280
281  function process_obsolete_list($file)
282  {
283    if (file_exists(PHPWG_ROOT_PATH.$file)
284      and $old_files = file(PHPWG_ROOT_PATH.$file, FILE_IGNORE_NEW_LINES)
285      and !empty($old_files))
286    {
287      array_push($old_files, $file);
288      foreach($old_files as $old_file)
289      {
290        $path = PHPWG_ROOT_PATH.$old_file;
291        if (is_file($path))
292        {
293          @unlink($path);
294        }
295        elseif (is_dir($path))
296        {
297          self::deltree($path, true);
298        }
299      }
300    }
301  }
302
303  function dump_database($include_history=false)
304  {
305    global $page, $conf, $cfgBase;
306
307    if (version_compare(PHPWG_VERSION, '2.1', '<'))
308    {
309      $conf['db_base'] = $cfgBase;
310    }
311
312    include(PHPWG_ROOT_PATH.'admin/include/mysqldump.php');
313
314    $path = $conf['local_data_dir'].'/update';
315
316    if (@mkgetdir($path)
317      and ($backupFile = tempnam($path, 'sql'))
318      and ($dumper = new MySQLDump($conf['db_base'],$backupFile,false,false)))
319    {
320      foreach (get_defined_constants() as $constant => $value)
321      {
322        if (preg_match('/_TABLE$/', $constant))
323        {
324          $dumper->getTableStructure($value);
325          if ($constant == 'HISTORY_TABLE' and !$include_history)
326          {
327            continue;
328          }
329          $dumper->getTableData($value);
330        }
331      }
332    }
333
334    if (@filesize($backupFile))
335    {
336      $http_headers = array(
337        'Content-Length: '.@filesize($backupFile),
338        'Content-Type: text/x-sql',
339        'Content-Disposition: attachment; filename="database.sql";',
340        'Content-Transfer-Encoding: binary',
341        );
342
343      foreach ($http_headers as $header) {
344        header($header);
345      }
346
347      @readfile($backupFile);
348      self::deltree($conf['local_data_dir'].'/update');
349      exit();
350    }
351    else
352    {
353      array_push($page['errors'], l10n('Unable to dump database.'));
354    }
355  }
356
357  function upgrade_to($upgrade_to, &$step)
358  {
359    global $page, $conf, $template;
360
361    if (!version_compare($_POST['upgrade_to'], PHPWG_VERSION, '>'))
362    {
363      redirect(get_root_url().'admin.php?page=plugin-'.basename(dirname(__FILE__)));
364    }
365
366    if ($step == 2)
367    {
368      preg_match('/(\d+\.\d+)\.(\d+)/', PHPWG_VERSION, $matches);
369      $code =  $matches[1].'.x_to_'.$_POST['upgrade_to'];
370      $dl_code = str_replace(array('.', '_'), '', $code);
371      $remove_path = $code;
372      $obsolete_list = 'obsolete.list';
373    }
374    else
375    {
376      $code = $_POST['upgrade_to'];
377      $dl_code = $code;
378      $remove_path = version_compare($code, '2.0.8', '>=') ? 'piwigo' : 'piwigo-'.$code;
379      $obsolete_list = PHPWG_ROOT_PATH.'install/obsolete.list';
380    }
381
382    if (empty($page['errors']))
383    {
384      $path = $conf['local_data_dir'].'/update';
385      $filename = $path.'/'.$code.'.zip';
386      @mkgetdir($path);
387
388      $chunk_num = 0;
389      $end = false;
390      $zip = @fopen($filename, 'w');
391      while (!$end)
392      {
393        $chunk_num++;
394        if (@fetchRemote(PHPWG_URL.'/download/dlcounter.php?code='.$dl_code.'&chunk_num='.$chunk_num, $result)
395          and $input = @unserialize($result))
396        {
397          if (0 == $input['remaining'])
398          {
399            $end = true;
400          }
401          @fwrite($zip, base64_decode($input['data']));
402        }
403        else
404        {
405          $end = true;
406        }
407      }
408      @fclose($zip);
409
410      if (@filesize($filename))
411      {
412        $zip = new PclZip($filename);
413        if ($result = $zip->extract(PCLZIP_OPT_PATH, PHPWG_ROOT_PATH,
414                                    PCLZIP_OPT_REMOVE_PATH, $remove_path,
415                                    PCLZIP_OPT_SET_CHMOD, 0755,
416                                    PCLZIP_OPT_REPLACE_NEWER))
417        {
418          //Check if all files were extracted
419          $error = '';
420          foreach($result as $extract)
421          {
422            if (!in_array($extract['status'], array('ok', 'filtered', 'already_a_directory')))
423            {
424              // Try to change chmod and extract
425              if (@chmod(PHPWG_ROOT_PATH.$extract['filename'], 0777)
426                and ($res = $zip->extract(PCLZIP_OPT_BY_NAME, $remove_path.'/'.$extract['filename'],
427                                          PCLZIP_OPT_PATH, PHPWG_ROOT_PATH,
428                                          PCLZIP_OPT_REMOVE_PATH, $remove_path,
429                                          PCLZIP_OPT_SET_CHMOD, 0755,
430                                          PCLZIP_OPT_REPLACE_NEWER))
431                and isset($res[0]['status'])
432                and $res[0]['status'] == 'ok')
433              {
434                continue;
435              }
436              else
437              {
438                $error .= $extract['filename'].': '.$extract['status']."\n";
439              }
440            }
441          }
442
443          if (empty($error))
444          {
445            self::obsolete_list($obsolete_list);
446            self::deltree($conf['local_data_dir'].'/update');
447            invalidate_user_cache(true);
448            $template->delete_compiled_templates();
449            unset($_SESSION['need_update']);
450            if ($step == 2)
451            {
452              array_push($page['infos'], sprintf(l10n('autoupdate_success'), $upgrade_to));
453              $step = -1;
454            }
455            else
456            {
457              redirect(PHPWG_ROOT_PATH.'upgrade.php?now=');
458            }
459          }
460          else
461          {
462            file_put_contents($conf['local_data_dir'].'/update/log_error.txt', $error);
463            $relative_path = trim(str_replace(dirname(dirname(dirname(dirname(__FILE__)))), '', $conf['local_data_dir']), '/\\');
464            array_push($page['errors'], sprintf(l10n('autoupdate_extract_fail'), PHPWG_ROOT_PATH.$relative_path.'/update/log_error.txt'));
465          }
466        }
467        else
468        {
469          self::deltree($conf['local_data_dir'].'/update');
470          array_push($page['errors'], l10n('autoupdate_fail'));
471        }
472      }
473      else
474      {
475        array_push($page['errors'], l10n('Piwigo cannot retrieve upgrade file from server'));
476      }
477    }
478  }
479}
480
481?>
Note: See TracBrowser for help on using the repository browser.