From 491e8adc7467de9f9aa6dbbebfcb8b82c676b46a Mon Sep 17 00:00:00 2001 From: patdenice Date: Wed, 20 Apr 2011 14:52:52 +0000 Subject: feature:2271 Merge autoupdate plugin into piwigo core. git-svn-id: http://piwigo.org/svn/trunk@10511 68402e56-0260-453c-a942-63ccdbb3a9ee --- admin/include/functions.php | 1 + admin/include/mysqldump.php | 421 +++++++++++++++++++++ admin/include/updates.class.php | 481 ++++++++++++++++++++++++ admin/themes/default/images/ajax-loader-bar.gif | Bin 0 -> 10819 bytes admin/themes/default/template/admin.tpl | 1 + admin/themes/default/template/updates_ext.tpl | 272 ++++++++++++++ admin/themes/default/template/updates_pwg.tpl | 99 +++++ admin/updates.php | 44 +++ admin/updates_ext.php | 86 +++++ admin/updates_pwg.php | 166 ++++++++ 10 files changed, 1571 insertions(+) create mode 100644 admin/include/mysqldump.php create mode 100644 admin/include/updates.class.php create mode 100644 admin/themes/default/images/ajax-loader-bar.gif create mode 100644 admin/themes/default/template/updates_ext.tpl create mode 100644 admin/themes/default/template/updates_pwg.tpl create mode 100644 admin/updates.php create mode 100644 admin/updates_ext.php create mode 100644 admin/updates_pwg.php (limited to 'admin') diff --git a/admin/include/functions.php b/admin/include/functions.php index f5afa9633..3d32e7b0e 100644 --- a/admin/include/functions.php +++ b/admin/include/functions.php @@ -2085,6 +2085,7 @@ function get_active_menu($menu_page) case 'maintenance': case 'thumbnail': case 'comments': + case 'updates': return 4; case 'configuration': diff --git a/admin/include/mysqldump.php b/admin/include/mysqldump.php new file mode 100644 index 000000000..515d86ae9 --- /dev/null +++ b/admin/include/mysqldump.php @@ -0,0 +1,421 @@ + +* $connection = @mysql_connect($dbhost,$dbuser,$dbpsw); +* $dumper = new MySQLDump($dbname,'filename.sql',false,false); +* $dumper->doDump(); +* +* +* Special thanks to: +* - Andrea Ingaglio helping in development of all class code +* - Dylan Pugh for precious advices halfing the size of the output file and for helping in debug +* +* @name MySQLDump +* @author Daniele Vigaṇ - CreativeFactory.it +* @version 2.20 - 02/11/2007 +* @license http://opensource.org/licenses/gpl-license.php GNU Public License +*/ + +class MySQLDump { + /** + * @access private + */ + var $database = null; + + /** + * @access private + */ + var $compress = false; + + /** + * @access private + */ + var $hexValue = false; + + /** + * The output filename + * @access private + */ + var $filename = null; + + /** + * The pointer of the output file + * @access private + */ + var $file = null; + + /** + * @access private + */ + var $isWritten = false; + + /** + * Class constructor + * @param string $db The database name + * @param string $filepath The file where the dump will be written + * @param boolean $compress It defines if the output file is compress (gzip) or not + * @param boolean $hexValue It defines if the outup values are base-16 or not + */ + function MYSQLDump($db = null, $filepath = 'dump.sql', $compress = false, $hexValue = false){ + $this->compress = $compress; + if ( !$this->setOutputFile($filepath) ) + return false; + return $this->setDatabase($db); + } + + /** + * Sets the database to work on + * @param string $db The database name + */ + function setDatabase($db){ + $this->database = $db; + if ( !@mysql_select_db($this->database) ) + return false; + return true; + } + + /** + * Returns the database where the class is working on + * @return string + */ + function getDatabase(){ + return $this->database; + } + + /** + * Sets the output file type (It can be made only if the file hasn't been already written) + * @param boolean $compress If it's true, the output file will be compressed + */ + function setCompress($compress){ + if ( $this->isWritten ) + return false; + $this->compress = $compress; + $this->openFile($this->filename); + return true; + } + + /** + * Returns if the output file is or not compressed + * @return boolean + */ + function getCompress(){ + return $this->compress; + } + + /** + * Sets the output file + * @param string $filepath The file where the dump will be written + */ + function setOutputFile($filepath){ + if ( $this->isWritten ) + return false; + $this->filename = $filepath; + $this->file = $this->openFile($this->filename); + return $this->file; + } + + /** + * Returns the output filename + * @return string + */ + function getOutputFile(){ + return $this->filename; + } + + /** + * Writes to file the $table's structure + * @param string $table The table name + */ + function getTableStructure($table){ + if ( !$this->setDatabase($this->database) ) + return false; + // Structure Header + $structure = "-- \n"; + $structure .= "-- Table structure for table `{$table}` \n"; + $structure .= "-- \n\n"; + // Dump Structure + $structure .= 'DROP TABLE IF EXISTS `'.$table.'`;'."\n"; + $structure .= "CREATE TABLE `".$table."` (\n"; + $records = @mysql_query('SHOW FIELDS FROM `'.$table.'`'); + if ( @mysql_num_rows($records) == 0 ) + return false; + while ( $record = mysql_fetch_assoc($records) ) { + $structure .= '`'.$record['Field'].'` '.$record['Type']; + if ( isset($record['Default']) ) + $structure .= ' DEFAULT \''.$record['Default'].'\''; + if ( @strcmp($record['Null'],'YES') != 0 ) + $structure .= ' NOT NULL'; + elseif ( is_null($record['Default']) ) + $structure .= ' DEFAULT NULL'; + if ( !empty($record['Extra']) ) + $structure .= ' '.$record['Extra']; + $structure .= ",\n"; + } + $structure = @ereg_replace(",\n$", null, $structure); + + // Save all Column Indexes + $structure .= $this->getSqlKeysTable($table); + $structure .= "\n)"; + + //Save table engine + $records = @mysql_query("SHOW TABLE STATUS LIKE '".$table."'"); + + if ( $record = @mysql_fetch_assoc($records) ) { + if ( !empty($record['Engine']) ) + $structure .= ' ENGINE='.$record['Engine']; + if ( !empty($record['Auto_increment']) ) + $structure .= ' AUTO_INCREMENT='.$record['Auto_increment']; + } + + $structure .= ";\n\n-- --------------------------------------------------------\n\n"; + $this->saveToFile($this->file,$structure); + } + + /** + * Writes to file the $table's data + * @param string $table The table name + * @param boolean $hexValue It defines if the output is base 16 or not + */ + function getTableData($table,$hexValue = true) { + if ( !$this->setDatabase($this->database) ) + return false; + // Header + $data = "-- \n"; + $data .= "-- Dumping data for table `$table` \n"; + $data .= "-- \n\n"; + + $records = mysql_query('SHOW FIELDS FROM `'.$table.'`'); + $num_fields = @mysql_num_rows($records); + if ( $num_fields == 0 ) + return false; + // Field names + $selectStatement = "SELECT "; + $insertStatement = "INSERT INTO `$table` ("; + $hexField = array(); + for ($x = 0; $x < $num_fields; $x++) { + $record = @mysql_fetch_assoc($records); + if ( ($hexValue) && ($this->isTextValue($record['Type'])) ) { + $selectStatement .= 'HEX(`'.$record['Field'].'`)'; + $hexField [$x] = true; + } + else + $selectStatement .= '`'.$record['Field'].'`'; + $insertStatement .= '`'.$record['Field'].'`'; + $insertStatement .= ", "; + $selectStatement .= ", "; + } + $insertStatement = @substr($insertStatement,0,-2).') VALUES'."\n"; + $selectStatement = @substr($selectStatement,0,-2).' FROM `'.$table.'`'; + + $records = @mysql_query($selectStatement); + $num_rows = @mysql_num_rows($records); + $num_fields = @mysql_num_fields($records); + // Dump data + if ( $num_rows > 0 ) { + $data .= $insertStatement; + for ($i = 0; $i < $num_rows; $i++) { + $record = @mysql_fetch_assoc($records); + $data .= ' ('; + for ($j = 0; $j < $num_fields; $j++) { + $field_name = @mysql_field_name($records, $j); + if ( @$hexField[$j] && (@strlen($record[$field_name]) > 0) ) + $data .= "0x".$record[$field_name]; + elseif (is_null($record[$field_name])) + $data .= "NULL"; + else + $data .= "'".@str_replace('\"','"',@mysql_real_escape_string($record[$field_name]))."'"; + $data .= ','; + } + $data = @substr($data,0,-1).")"; + $data .= ( $i < ($num_rows-1) ) ? ',' : ';'; + $data .= "\n"; + //if data in greather than 1MB save + if (strlen($data) > 1048576) { + $this->saveToFile($this->file,$data); + $data = ''; + } + } + $data .= "\n-- --------------------------------------------------------\n\n"; + $this->saveToFile($this->file,$data); + } + } + + /** + * Writes to file all the selected database tables structure + * @return boolean + */ + function getDatabaseStructure(){ + $records = @mysql_query('SHOW TABLES'); + if ( @mysql_num_rows($records) == 0 ) + return false; + $structure = ''; + while ( $record = @mysql_fetch_row($records) ) { + $structure .= $this->getTableStructure($record[0]); + } + return true; + } + + /** + * Writes to file all the selected database tables data + * @param boolean $hexValue It defines if the output is base-16 or not + */ + function getDatabaseData($hexValue = true){ + $records = @mysql_query('SHOW TABLES'); + if ( @mysql_num_rows($records) == 0 ) + return false; + while ( $record = @mysql_fetch_row($records) ) { + $this->getTableData($record[0],$hexValue); + } + } + + /** + * Writes to file the selected database dump + */ + function doDump() { + $this->saveToFile($this->file,"SET FOREIGN_KEY_CHECKS = 0;\n\n"); + $this->getDatabaseStructure(); + $this->getDatabaseData($this->hexValue); + $this->saveToFile($this->file,"SET FOREIGN_KEY_CHECKS = 1;\n\n"); + $this->closeFile($this->file); + return true; + } + + /** + * @deprecated Look at the doDump() method + */ + function writeDump($filename) { + if ( !$this->setOutputFile($filename) ) + return false; + $this->doDump(); + $this->closeFile($this->file); + return true; + } + + /** + * @access private + */ + function getSqlKeysTable ($table) { + $primary = ""; + $unique = array(); + $index = array(); + $fulltext = array(); + $results = mysql_query("SHOW KEYS FROM `{$table}`"); + if ( @mysql_num_rows($results) == 0 ) + return false; + while($row = mysql_fetch_object($results)) { + if (($row->Key_name == 'PRIMARY') AND ($row->Index_type == 'BTREE')) { + if ( $primary == "" ) + $primary = " PRIMARY KEY (`{$row->Column_name}`"; + else + $primary .= ", `{$row->Column_name}`"; + } + if (($row->Key_name != 'PRIMARY') AND ($row->Non_unique == '0') AND ($row->Index_type == 'BTREE')) { + if ( (empty($unique)) OR (empty($unique[$row->Key_name])) ) + $unique[$row->Key_name] = " UNIQUE KEY `{$row->Key_name}` (`{$row->Column_name}`"; + else + $unique[$row->Key_name] .= ", `{$row->Column_name}`"; + } + if (($row->Key_name != 'PRIMARY') AND ($row->Non_unique == '1') AND ($row->Index_type == 'BTREE')) { + if ( (empty($index)) OR (empty($index[$row->Key_name])) ) + $index[$row->Key_name] = " KEY `{$row->Key_name}` (`{$row->Column_name}`"; + else + $index[$row->Key_name] .= ", `{$row->Column_name}`"; + } + if (($row->Key_name != 'PRIMARY') AND ($row->Non_unique == '1') AND ($row->Index_type == 'FULLTEXT')) { + if ( (empty($fulltext)) OR (empty($fulltext[$row->Key_name])) ) + $fulltext[$row->Key_name] = " FULLTEXT `{$row->Key_name}` (`{$row->Column_name}`"; + else + $fulltext[$row->Key_name] .= ", `{$row->Column_name}`"; + } + } + $sqlKeyStatement = ''; + // generate primary, unique, key and fulltext + if ( $primary != "" ) { + $sqlKeyStatement .= ",\n"; + $primary .= ")"; + $sqlKeyStatement .= $primary; + } + if (!empty($unique)) { + foreach ($unique as $keyName => $keyDef) { + $sqlKeyStatement .= ",\n"; + $keyDef .= ")"; + $sqlKeyStatement .= $keyDef; + + } + } + if (!empty($index)) { + foreach ($index as $keyName => $keyDef) { + $sqlKeyStatement .= ",\n"; + $keyDef .= ")"; + $sqlKeyStatement .= $keyDef; + } + } + if (!empty($fulltext)) { + foreach ($fulltext as $keyName => $keyDef) { + $sqlKeyStatement .= ",\n"; + $keyDef .= ")"; + $sqlKeyStatement .= $keyDef; + } + } + return $sqlKeyStatement; + } + + /** + * @access private + */ + function isTextValue($field_type) { + switch ($field_type) { + case "tinytext": + case "text": + case "mediumtext": + case "longtext": + case "binary": + case "varbinary": + case "tinyblob": + case "blob": + case "mediumblob": + case "longblob": + return True; + break; + default: + return False; + } + } + + /** + * @access private + */ + function openFile($filename) { + $file = false; + if ( $this->compress ) + $file = @gzopen($filename, "w9"); + else + $file = @fopen($filename, "w"); + return $file; + } + + /** + * @access private + */ + function saveToFile($file, $data) { + if ( $this->compress ) + @gzwrite($file, $data); + else + @fwrite($file, $data); + $this->isWritten = true; + } + + /** + * @access private + */ + function closeFile($file) { + if ( $this->compress ) + @gzclose($file); + else + @fclose($file); + } +} +?> \ No newline at end of file diff --git a/admin/include/updates.class.php b/admin/include/updates.class.php new file mode 100644 index 000000000..b53001488 --- /dev/null +++ b/admin/include/updates.class.php @@ -0,0 +1,481 @@ +types = array('plugins', 'themes', 'languages'); + $this->default_themes = array('clear', 'dark', 'Sylvia'); + $this->default_plugins = array('admin_multi_view', 'c13y_upgrade', 'language_switch', 'LocalFilesEditor'); + + foreach ($this->types as $type) + { + include_once(PHPWG_ROOT_PATH.'admin/include/'.$type.'.class.php'); + $this->$type = new $type(); + } + } + + function check_piwigo_upgrade() + { + $_SESSION['need_update'] = null; + + if (preg_match('/(\d+\.\d+)\.(\d+)/', PHPWG_VERSION, $matches) + and @fetchRemote(PHPWG_URL.'/download/all_versions.php', $result)) + { + $all_versions = @explode("\n", $result); + $new_version = trim($all_versions[0]); + $_SESSION['need_update'] = version_compare(PHPWG_VERSION, $new_version, '<'); + } + } + + function get_server_extensions($version=PHPWG_VERSION) + { + global $user; + + $get_data = array( + 'format' => 'php', + ); + + // Retrieve PEM versions + $versions_to_check = array(); + $url = PEM_URL . '/api/get_version_list.php'; + if (fetchRemote($url, $result, $get_data) and $pem_versions = @unserialize($result)) + { + if (!preg_match('/^\d+\.\d+\.\d+/', $version)) + { + $version = $pem_versions[0]['name']; + } + $branch = substr($version, 0, strrpos($version, '.')); + foreach ($pem_versions as $pem_version) + { + if (strpos($pem_version['name'], $branch) === 0) + { + $versions_to_check[] = $pem_version['id']; + } + } + } + if (empty($versions_to_check)) + { + return false; + } + + // Extensions to check + $ext_to_check = array(); + foreach ($this->types as $type) + { + $fs = 'fs_'.$type; + foreach ($this->$type->$fs as $ext) + { + if (isset($ext['extension'])) + { + $ext_to_check[$ext['extension']] = $type; + } + } + } + + // Retrieve PEM plugins infos + $url = PEM_URL . '/api/get_revision_list.php'; + $get_data = array_merge($get_data, array( + 'last_revision_only' => 'true', + 'version' => implode(',', $versions_to_check), + 'lang' => substr($user['language'], 0, 2), + 'get_nb_downloads' => 'true', + ) + ); + + $post_data = array(); + if (!empty($ext_to_check)) + { + $post_data['extension_include'] = implode(',', array_keys($ext_to_check)); + } + + if (fetchRemote($url, $result, $get_data, $post_data)) + { + $pem_exts = @unserialize($result); + if (!is_array($pem_exts)) + { + return false; + } + foreach ($pem_exts as $ext) + { + if (isset($ext_to_check[$ext['extension_id']])) + { + $server = 'server_'.$ext_to_check[$ext['extension_id']]; + $this->$ext_to_check[$ext['extension_id']]->$server += array($ext['extension_id'] => $ext); + unset($ext_to_check[$ext['extension_id']]); + } + } + $this->check_missing_extensions($ext_to_check); + return true; + } + return false; + } + + // Check all extensions upgrades + function check_extensions() + { + global $conf; + + if (!$this->get_server_extensions()) + { + autoupdate_error(); + } + + $_SESSION['extensions_need_update'] = array(); + + foreach ($this->types as $type) + { + $fs = 'fs_'.$type; + $server = 'server_'.$type; + $server_ext = $this->$type->$server; + $fs_ext = $this->$type->$fs; + + $ignore_list = array(); + $need_upgrade = array(); + + foreach($fs_ext as $ext_id => $fs_ext) + { + if (isset($fs_ext['extension']) and isset($server_ext[$fs_ext['extension']])) + { + $ext_info = $server_ext[$fs_ext['extension']]; + + if (!$this->version_compare($fs_ext['version'], $ext_info['revision_name'], $type)) + { + if (in_array($ext_id, $conf['updates_ignored'][$type])) + { + array_push($ignore_list, $ext_id); + } + else + { + $_SESSION['extensions_need_update'][$type][$ext_id] = $ext_info['revision_name']; + } + } + } + } + $conf['updates_ignored'][$type] = $ignore_list; + } + conf_update_param('updates_ignored', pwg_db_real_escape_string(serialize($conf['updates_ignored']))); + } + + // Check if extension have been upgraded since last check + function check_updated_extensions() + { + foreach ($this->types as $type) + { + if (!empty($_SESSION['extensions_need_update'][$type])) + { + $fs = 'fs_'.$type; + foreach($this->$type->$fs as $ext_id => $fs_ext) + { + if (isset($_SESSION['extensions_need_update'][$type][$ext_id]) + and $this->version_compare($fs_ext['version'], $_SESSION['extensions_need_update'][$type][$ext_id], $type)) + { + // Extension have been upgraded + $this->check_extensions(); + break; + } + } + } + } + } + + function check_missing_extensions($missing) + { + foreach ($missing as $id => $type) + { + $fs = 'fs_'.$type; + $default = 'default_'.$type; + foreach ($this->$type->$fs as $ext_id => $ext) + { + if (isset($ext['extension']) and $id == $ext['extension'] + and !in_array($ext_id, $this->$default) + and !in_array($ext['extension'], $this->merged_extensions)) + { + $this->missing[$type][] = $ext; + break; + } + } + } + } + + function get_merged_extensions($version) + { + if (fetchRemote($this->merged_extension_url, $result)) + { + $rows = explode("\n", $result); + foreach ($rows as $row) + { + if (preg_match('/^(\d+\.\d+): *(.*)$/', $row, $match)) + { + if (version_compare($version, $match[1], '>=')) + { + $extensions = explode(',', trim($match[2])); + $this->merged_extensions = array_merge($this->merged_extensions, $extensions); + } + } + } + } + } + + function version_compare($a, $b, $type) + { + $version_compare = rtrim($type, 's').'_version_compare'; + + return $this->$type->$version_compare($a, $b); + } + + function deltree($path, $move_to_trash=false) + { + if (is_dir($path)) + { + $fh = opendir($path); + while ($file = readdir($fh)) + { + if ($file != '.' and $file != '..') + { + $pathfile = $path . '/' . $file; + if (is_dir($pathfile)) + { + self::deltree($pathfile, $move_to_trash); + } + else + { + @unlink($pathfile); + } + } + } + closedir($fh); + if (@rmdir($path)) + { + return true; + } + elseif ($move_to_trash) + { + $trash = PHPWG_ROOT_PATH.'_trash'; + if (!is_dir($trash)) + { + @mkgetdir($trash); + } + return @rename($path, $trash . '/'.md5(uniqid(rand(), true))); + } + else + { + return false; + } + } + } + + function process_obsolete_list($file) + { + if (file_exists(PHPWG_ROOT_PATH.$file) + and $old_files = file(PHPWG_ROOT_PATH.$file, FILE_IGNORE_NEW_LINES) + and !empty($old_files)) + { + array_push($old_files, $file); + foreach($old_files as $old_file) + { + $path = PHPWG_ROOT_PATH.$old_file; + if (is_file($path)) + { + @unlink($path); + } + elseif (is_dir($path)) + { + self::deltree($path, true); + } + } + } + } + + function dump_database($include_history=false) + { + global $page, $conf, $cfgBase; + + if (version_compare(PHPWG_VERSION, '2.1', '<')) + { + $conf['db_base'] = $cfgBase; + } + + include(PHPWG_ROOT_PATH.'admin/include/mysqldump.php'); + + $path = $conf['local_data_dir'].'/update'; + + if (@mkgetdir($path) + and ($backupFile = tempnam($path, 'sql')) + and ($dumper = new MySQLDump($conf['db_base'],$backupFile,false,false))) + { + foreach (get_defined_constants() as $constant => $value) + { + if (preg_match('/_TABLE$/', $constant)) + { + $dumper->getTableStructure($value); + if ($constant == 'HISTORY_TABLE' and !$include_history) + { + continue; + } + $dumper->getTableData($value); + } + } + } + + if (@filesize($backupFile)) + { + $http_headers = array( + 'Content-Length: '.@filesize($backupFile), + 'Content-Type: text/x-sql', + 'Content-Disposition: attachment; filename="database.sql";', + 'Content-Transfer-Encoding: binary', + ); + + foreach ($http_headers as $header) { + header($header); + } + + @readfile($backupFile); + self::deltree($conf['local_data_dir'].'/update'); + exit(); + } + else + { + array_push($page['errors'], l10n('Unable to dump database.')); + } + } + + function upgrade_to($upgrade_to, &$step) + { + global $page, $conf, $template; + + if (!version_compare($_POST['upgrade_to'], PHPWG_VERSION, '>')) + { + redirect(get_root_url().'admin.php?page=plugin-'.basename(dirname(__FILE__))); + } + + if ($step == 2) + { + preg_match('/(\d+\.\d+)\.(\d+)/', PHPWG_VERSION, $matches); + $code = $matches[1].'.x_to_'.$_POST['upgrade_to']; + $dl_code = str_replace(array('.', '_'), '', $code); + $remove_path = $code; + $obsolete_list = 'obsolete.list'; + } + else + { + $code = $_POST['upgrade_to']; + $dl_code = $code; + $remove_path = version_compare($code, '2.0.8', '>=') ? 'piwigo' : 'piwigo-'.$code; + $obsolete_list = PHPWG_ROOT_PATH.'install/obsolete.list'; + } + + if (empty($page['errors'])) + { + $path = $conf['local_data_dir'].'/update'; + $filename = $path.'/'.$code.'.zip'; + @mkgetdir($path); + + $chunk_num = 0; + $end = false; + $zip = @fopen($filename, 'w'); + while (!$end) + { + $chunk_num++; + if (@fetchRemote(PHPWG_URL.'/download/dlcounter.php?code='.$dl_code.'&chunk_num='.$chunk_num, $result) + and $input = @unserialize($result)) + { + if (0 == $input['remaining']) + { + $end = true; + } + @fwrite($zip, base64_decode($input['data'])); + } + else + { + $end = true; + } + } + @fclose($zip); + + if (@filesize($filename)) + { + $zip = new PclZip($filename); + if ($result = $zip->extract(PCLZIP_OPT_PATH, PHPWG_ROOT_PATH, + PCLZIP_OPT_REMOVE_PATH, $remove_path, + PCLZIP_OPT_SET_CHMOD, 0755, + PCLZIP_OPT_REPLACE_NEWER)) + { + //Check if all files were extracted + $error = ''; + foreach($result as $extract) + { + if (!in_array($extract['status'], array('ok', 'filtered', 'already_a_directory'))) + { + // Try to change chmod and extract + if (@chmod(PHPWG_ROOT_PATH.$extract['filename'], 0777) + and ($res = $zip->extract(PCLZIP_OPT_BY_NAME, $remove_path.'/'.$extract['filename'], + PCLZIP_OPT_PATH, PHPWG_ROOT_PATH, + PCLZIP_OPT_REMOVE_PATH, $remove_path, + PCLZIP_OPT_SET_CHMOD, 0755, + PCLZIP_OPT_REPLACE_NEWER)) + and isset($res[0]['status']) + and $res[0]['status'] == 'ok') + { + continue; + } + else + { + $error .= $extract['filename'].': '.$extract['status']."\n"; + } + } + } + + if (empty($error)) + { + self::obsolete_list($obsolete_list); + self::deltree($conf['local_data_dir'].'/update'); + invalidate_user_cache(true); + $template->delete_compiled_templates(); + unset($_SESSION['need_update']); + if ($step == 2) + { + array_push($page['infos'], sprintf(l10n('autoupdate_success'), $upgrade_to)); + $step = -1; + } + else + { + redirect(PHPWG_ROOT_PATH.'upgrade.php?now='); + } + } + else + { + file_put_contents($conf['local_data_dir'].'/update/log_error.txt', $error); + $relative_path = trim(str_replace(dirname(dirname(dirname(dirname(__FILE__)))), '', $conf['local_data_dir']), '/\\'); + array_push($page['errors'], sprintf(l10n('autoupdate_extract_fail'), PHPWG_ROOT_PATH.$relative_path.'/update/log_error.txt')); + } + } + else + { + self::deltree($conf['local_data_dir'].'/update'); + array_push($page['errors'], l10n('autoupdate_fail')); + } + } + else + { + array_push($page['errors'], l10n('Piwigo cannot retrieve upgrade file from server')); + } + } + } +} + +?> \ No newline at end of file diff --git a/admin/themes/default/images/ajax-loader-bar.gif b/admin/themes/default/images/ajax-loader-bar.gif new file mode 100644 index 000000000..d84f65378 Binary files /dev/null and b/admin/themes/default/images/ajax-loader-bar.gif differ diff --git a/admin/themes/default/template/admin.tpl b/admin/themes/default/template/admin.tpl index 2daad8118..56a92a75e 100644 --- a/admin/themes/default/template/admin.tpl +++ b/admin/themes/default/template/admin.tpl @@ -75,6 +75,7 @@ jQuery(document).ready(function(){ldelim} {/if}
  • {'Maintenance'|@translate}
  • {'Pending Comments'|@translate}
  • +
  • {'Updates'|@translate}
  • diff --git a/admin/themes/default/template/updates_ext.tpl b/admin/themes/default/template/updates_ext.tpl new file mode 100644 index 000000000..d0cd7a2a7 --- /dev/null +++ b/admin/themes/default/template/updates_ext.tpl @@ -0,0 +1,272 @@ +{combine_script id='jquery.ajaxmanager' load='footer' require='jquery' path='themes/default/js/plugins/jquery.ajaxmanager.js'} +{combine_script id='jquery.jgrowl' load='footer' require='jquery' path='themes/default/js/plugins/jquery.jgrowl_minimized.js'} +{combine_css path="admin/themes/default/uploadify.jGrowl.css"} + +{footer_script require='jquery.effects.blind,jquery.ajaxmanager,jquery.jgrowl'} +var pwg_token = '{$PWG_TOKEN}'; +var extList = new Array(); +var confirmMsg = '{'Are you sure?'|@translate|@escape:'javascript'}'; +var errorHead = '{'ERROR'|@translate|@escape:'javascript'}'; +var successHead = '{'Update Complete'|@translate|@escape:'javascript'}'; +var errorMsg = '{'an error happened'|@translate|@escape:'javascript'}'; +var restoreMsg = '{'Reset ignored updates'|@translate|@escape:'javascript'}'; + +{literal} +var todo = 0; +var queuedManager = $.manageAjax.create('queued', { + queue: true, + maxRequests: 1, + beforeSend: function() { autoupdate_bar_toggle(1); }, + complete: function() { autoupdate_bar_toggle(-1); } +}); + +function updateAll() { + if (confirm(confirmMsg)) { + jQuery('.updateExtension').each( function() { + if (jQuery(this).parents('div').css('display') == 'block') + jQuery(this).click(); + }); + } +}; + +function resetIgnored() { + jQuery.ajax({ + type: 'GET', + url: 'ws.php', + dataType: 'json', + data: { method: 'pwg.extensions.ignoreUpdate', reset: true, pwg_token: pwg_token, format: 'json' }, + success: function(data) { + if (data['stat'] == 'ok') { + jQuery(".pluginBox, fieldset").show(); + jQuery("#update_all").show(); + jQuery("#up_to_date").hide(); + jQuery("#reset_ignore").hide(); + jQuery("#ignored").hide(); + checkFieldsets(); + } + } + }); +}; + +function checkFieldsets() { + var types = new Array('plugins', 'themes', 'languages'); + var total = 0; + var ignored = 0; + for (i=0;i<3;i++) { + nbExtensions = 0; + jQuery("div[id^='"+types[i]+"_']").each(function(index) { + if (jQuery(this).css('display') == 'block') + nbExtensions++; + else + ignored++; + }); + total = total + nbExtensions; + if (nbExtensions == 0) + jQuery("#"+types[i]).hide(); + } + + if (total == 0) { + jQuery("#update_all").hide(); + jQuery("#up_to_date").show(); + } + if (ignored > 0) { + jQuery("#reset_ignore").val(restoreMsg + ' (' + ignored + ')'); + } +}; + +function updateExtension(type, id, revision) { + queuedManager.add({ + type: 'GET', + dataType: 'json', + url: 'ws.php', + data: { method: 'pwg.extensions.update', type: type, id: id, revision: revision, pwg_token: pwg_token, format: 'json' }, + success: function(data) { + if (data['stat'] == 'ok') { + jQuery.jGrowl( data['result'], { theme: 'success', header: successHead, life: 4000, sticky: false }); + jQuery("#"+type+"_"+id).remove(); + checkFieldsets(); + } else { + jQuery.jGrowl( data['result'], { theme: 'error', header: errorHead, sticky: true }); + } + }, + error: function(data) { + jQuery.jGrowl( errorMsg, { theme: 'error', header: errorHead, sticky: true }); + } + }); +}; + +function ignoreExtension(type, id) { + jQuery.ajax({ + type: 'GET', + url: 'ws.php', + dataType: 'json', + data: { method: 'pwg.extensions.ignoreUpdate', type: type, id: id, pwg_token: pwg_token, format: 'json' }, + success: function(data) { + if (data['stat'] == 'ok') { + jQuery("#"+type+"_"+id).hide(); + jQuery("#reset_ignore").show(); + checkFieldsets(); + } + } + }); +}; + +function autoupdate_bar_toggle(i) { + todo = todo + i; + if ((i == 1 && todo == 1) || (i == -1 && todo == 0)) + jQuery('.autoupdate_bar').toggle(); +} + +jQuery(document).ready(function() { + jQuery("td[id^='desc_'], p[id^='revdesc_']").click(function() { + id = this.id.split('_'); + jQuery("#revdesc_"+id[1]).toggle('blind'); + jQuery(".button_"+id[1]).toggle(); + return false; + }); +}); + +checkFieldsets(); +{/literal} +{/footer_script} + +
    +

    {'Updates'|@translate}

    +
    + +
    +
    + + +
    + + + + +{if not empty($update_plugins)} +
    +
    +{'Plugins'|@translate} +{foreach from=$update_plugins item=plugin name=plugins_loop} +
    + + + + + + + + + + + + + +
    + {$plugin.EXT_NAME} + + {'Install'|@translate} + | {'Download'|@translate} + | {'Ignore this update'|@translate} +
    + {'Version'|@translate} {$plugin.CURRENT_VERSION} + + {'Downloads'|@translate}: {$plugin.DOWNLOADS} + + + {'New Version'|@translate} : {$plugin.NEW_VERSION} + | {'By %s'|@translate|@sprintf:$plugin.AUTHOR} +
    + +
    +
    +{/foreach} +
    +
    +{/if} + +{if not empty($update_themes)} +
    +
    +{'Themes'|@translate} +{foreach from=$update_themes item=theme name=themes_loop} +
    + + + + + + + + + + + + + +
    + {$theme.EXT_NAME} + + {'Install'|@translate} + | {'Download'|@translate} + | {'autoupdate_ignore'|@translate} +
    + {'Version'|@translate} {$theme.CURRENT_VERSION} + + {'Downloads'|@translate}: {$theme.DOWNLOADS} + + + {'New Version'|@translate} : {$theme.NEW_VERSION} + | {'By %s'|@translate|@sprintf:$theme.AUTHOR} +
    + +
    +
    +{/foreach} +
    +
    +{/if} + +{if not empty($update_languages)} +
    +
    +{'Languages'|@translate} +{foreach from=$update_languages item=language name=languages_loop} +
    + + + + + + + + + + + + + +
    + {$language.EXT_NAME} + + {'Install'|@translate} + | {'Download'|@translate} + | {'autoupdate_ignore'|@translate} +
    + {'Version'|@translate} {$language.CURRENT_VERSION} + + {'Downloads'|@translate}: {$language.DOWNLOADS} + + + {'New Version'|@translate} : {$language.NEW_VERSION} + | {'By %s'|@translate|@sprintf:$language.AUTHOR} +
    + +
    +
    +{/foreach} +
    +
    +{/if} diff --git a/admin/themes/default/template/updates_pwg.tpl b/admin/themes/default/template/updates_pwg.tpl new file mode 100644 index 000000000..c85e1fb6b --- /dev/null +++ b/admin/themes/default/template/updates_pwg.tpl @@ -0,0 +1,99 @@ +{footer_script} +jQuery(document).ready(function() {ldelim} + jQuery('input[name="submit"]').click(function() {ldelim} + if(!confirm('{'autoupdate_alert'|@translate}')) + return false; + jQuery(this).hide(); + jQuery('.autoupdate_bar').show(); + }); + jQuery('[name="understand"]').click(function() {ldelim} + jQuery('[name="submit"]').attr('disabled', !this.checked); + }); +}); +{/footer_script} + +{html_head} +{literal} + +{/literal} +{/html_head} + +
    +

    {'Updates'|@translate}

    +
    + +{if $STEP == 0} + {if $CHECK_VERSION} +

    {'You are running the latest version of Piwigo.'|@translate}

    + {elseif $DEV_VERSION} +

    {'You are running on development sources, no check possible.'|@translate}

    + {else} +

    {'Check for update failed for unknown reasons.'|@translate}

    + {/if} +{/if} + +{if $STEP == 1} +

    {'Two updates are available'|@translate}:

    +

    +

    +

    +

    {'You can update to Piwigo %s directly, without upgrading to Piwigo %s (recommended).'|@translate|@sprintf:$MAJOR_VERSION:$MINOR_VERSION}

    +{/if} + +{if $STEP == 2} +

    + {'A new version of Piwigo is available.'|@translate}
    + {'This is a minor update, with only bug corrections.'|@translate} +

    +
    +

    + +

    +
    +{/if} + +{if $STEP == 3} +

    + {'A new version of Piwigo is available.'|@translate}
    + {'This is a major update, with new exciting features.'|@translate|@sprintf:$RELEASE_URL} {'Some themes and plugins may be not available yet.'|@translate} +

    +
    + +{counter assign=i} +
    + {'Dump Database'|@translate} +

      {'Include history data (Warning: server memory limit may be exceeded)'|@translate}

    +

    +
    + +{counter assign=i} +
    + {'Update to Piwigo %s'|@translate|@sprintf:$UPGRADE_TO} + {if !empty($missing.plugins)} +

    {'Following plugins may not be compatible with the new version of Piwigo:'|@translate}

    +


    + {/if} + {if !empty($missing.themes)} +

    {'Following themes may not be compatible with the new version of Piwigo:'|@translate}

    +


    + {/if} +

    + {if !empty($missing.plugins) or !empty($missing.themes)} +

    + {/if} +

    +

    + +
    + +

    +
    +{/if} \ No newline at end of file diff --git a/admin/updates.php b/admin/updates.php new file mode 100644 index 000000000..97c56a2cd --- /dev/null +++ b/admin/updates.php @@ -0,0 +1,44 @@ +add('pwg', l10n('Piwigo Update'), $my_base_url); +$tabsheet->add('ext', l10n('Extensions Update'), $my_base_url.'&tab=ext'); +$tabsheet->select($page['tab']); +$tabsheet->assign(); + +include(PHPWG_ROOT_PATH.'admin/updates_'.$page['tab'].'.php'); + +?> \ No newline at end of file diff --git a/admin/updates_ext.php b/admin/updates_ext.php new file mode 100644 index 000000000..4ce47b83f --- /dev/null +++ b/admin/updates_ext.php @@ -0,0 +1,86 @@ +get_server_extensions()) +{ + array_push($page['errors'], l10n('Can\'t connect to server.')); + return; +} + +foreach ($autoupdate->types as $type) +{ + $fs = 'fs_'.$type; + $server = 'server_'.$type; + $server_ext = $autoupdate->$type->$server; + $fs_ext = $autoupdate->$type->$fs; + + if (empty($server_ext)) + { + continue; + } + + foreach($fs_ext as $ext_id => $fs_ext) + { + if (!isset($fs_ext['extension']) or !isset($server_ext[$fs_ext['extension']])) + { + continue; + } + + $ext_info = $server_ext[$fs_ext['extension']]; + + if (!$autoupdate->version_compare($fs_ext['version'], $ext_info['revision_name'], $type)) + { + $template->append('update_'.$type, array( + 'ID' => $ext_info['extension_id'], + 'REVISION_ID' => $ext_info['revision_id'], + 'EXT_ID' => $ext_id, + 'EXT_NAME' => $fs_ext['name'], + 'EXT_URL' => PEM_URL.'/extension_view.php?eid='.$ext_info['extension_id'], + 'EXT_DESC' => trim($ext_info['extension_description'], " \n\r"), + 'REV_DESC' => trim($ext_info['revision_description'], " \n\r"), + 'CURRENT_VERSION' => $fs_ext['version'], + 'NEW_VERSION' => $ext_info['revision_name'], + 'AUTHOR' => $ext_info['author_name'], + 'DOWNLOADS' => $ext_info['extension_nb_downloads'], + 'URL_DOWNLOAD' => $ext_info['download_url'] . '&origin=piwigo_download', + 'IGNORED' => in_array($ext_id, $conf['updates_ignored'][$type]), + ) + ); + } + } +} + +$template->assign('SHOW_RESET', (!empty($conf['updates_ignored']['plugins']) or !empty($conf['updates_ignored']['themes']) or !empty($conf['updates_ignored']['languages']))); +$template->assign('PWG_TOKEN', get_pwg_token()); +$template->set_filename('plugin_admin_content', 'updates_ext.tpl'); +$template->assign_var_from_handle('ADMIN_CONTENT', 'plugin_admin_content'); + +?> \ No newline at end of file diff --git a/admin/updates_pwg.php b/admin/updates_pwg.php new file mode 100644 index 000000000..016deeecd --- /dev/null +++ b/admin/updates_pwg.php @@ -0,0 +1,166 @@ + user may choose upgrade. +2 = upgrade on same branch +3 = upgrade on different branch +*/ +$step = isset($_GET['step']) ? $_GET['step'] : 0; +$upgrade_to = isset($_GET['to']) ? $_GET['to'] : ''; + +// +-----------------------------------------------------------------------+ +// | Step 0 | +// +-----------------------------------------------------------------------+ +if ($step == 0) +{ + $template->assign(array( + 'CHECK_VERSION' => false, + 'DEV_VERSION' => false, + ) + ); + + if (preg_match('/(\d+\.\d+)\.(\d+)/', PHPWG_VERSION, $matches)) + { + $url = PHPWG_URL.'/download/all_versions.php'; + $url .= '?rand='.md5(uniqid(rand(), true)); // Avoid server cache + + if (@fetchRemote($url, $result) + and $all_versions = @explode("\n", $result) + and is_array($all_versions)) + { + $template->assign('CHECK_VERSION', true); + + $last_version = trim($all_versions[0]); + $upgrade_to = $last_version; + + if (version_compare(PHPWG_VERSION, $last_version, '<')) + { + $new_branch = preg_replace('/(\d+\.\d+)\.\d+/', '$1', $last_version); + $actual_branch = $matches[1]; + + if ($new_branch == $actual_branch) + { + $step = 2; + } + else + { + $step = 3; + + // Check if new version exists in same branch + foreach ($all_versions as $version) + { + $new_branch = preg_replace('/(\d+\.\d+)\.\d+/', '$1', $version); + + if ($new_branch == $actual_branch) + { + if (version_compare(PHPWG_VERSION, $version, '<')) + { + $step = 1; + } + break; + } + } + } + } + } + } + else + { + $template->assign('DEV_VERSION', true); + } +} + +// +-----------------------------------------------------------------------+ +// | Step 1 | +// +-----------------------------------------------------------------------+ +if ($step == 1) +{ + $template->assign(array( + 'MINOR_VERSION' => $version, + 'MAJOR_VERSION' => $last_version, + ) + ); +} + +// +-----------------------------------------------------------------------+ +// | Step 2 | +// +-----------------------------------------------------------------------+ +if ($step == 2 and is_webmaster()) +{ + if (isset($_POST['submit']) and isset($_POST['upgrade_to'])) + { + updates::upgrade_to($_POST['upgrade_to'], $step); + } +} + +// +-----------------------------------------------------------------------+ +// | Step 3 | +// +-----------------------------------------------------------------------+ +if ($step == 3 and is_webmaster()) +{ + if (isset($_POST['dumpDatabase'])) + { + updates::dump_database(isset($_POST['includeHistory'])); + } + + if (isset($_POST['submit']) and isset($_POST['upgrade_to'])) + { + updates::upgrade_to($_POST['upgrade_to'], $step); + } + + $updates = new updates(); + $updates->get_merged_extensions($upgrade_to); + $updates->get_server_extensions($upgrade_to); + $template->assign('missing', $updates->missing); +} + +// +-----------------------------------------------------------------------+ +// | Process template | +// +-----------------------------------------------------------------------+ + +if (!is_webmaster()) +{ + array_push($page['errors'], l10n('Webmaster status is required.')); +} + +$template->assign(array( + 'STEP' => $step, + 'PHPWG_VERSION' => PHPWG_VERSION, + 'UPGRADE_TO' => $upgrade_to, + 'RELEASE_URL' => PHPWG_URL.'/releases/'.$upgrade_to, + ) +); + +$template->set_filename('plugin_admin_content', 'updates_pwg.tpl'); +$template->assign_var_from_handle('ADMIN_CONTENT', 'plugin_admin_content'); + +?> \ No newline at end of file -- cgit v1.2.3