* @copyright 2007-2012 PrestaShop SA * @version Release: $Revision: 14506 $ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) * International Registered Trademark & Property of PrestaShop SA */ // _PS_ADMIN_DIR_ is defined in ajax-upgradetab, but may be not defined in direct call if (!defined('_PS_ADMIN_DIR_') && defined('PS_ADMIN_DIR')) define('_PS_ADMIN_DIR_', PS_ADMIN_DIR); // Note : we cannot use the native AdminTab because // we don't know the current PrestaShop version number require_once(_PS_ROOT_DIR_.'/modules/autoupgrade/AdminSelfTab.php'); // require_once(_PS_ROOT_DIR_.'/modules/autoupgrade/Upgrader.php'); if (!class_exists('Upgrader',false)) { if(file_exists(_PS_ROOT_DIR_.'/override/classes/Upgrader.php')) require_once(_PS_ROOT_DIR_.'/override/classes/Upgrader.php'); else eval('class Upgrader extends UpgraderCore{}'); } require_once(_PS_ROOT_DIR_.'/modules/autoupgrade/Tools14.php'); if(!class_exists('Tools',false)) eval('class Tools extends Tools14{}'); class AdminSelfUpgrade extends AdminSelfTab { // used for translations public static $l_cache; // retrocompatibility public $noTabLink = array(); public $id = -1; public $svn_link = 'http://svn.prestashop.com/trunk'; public $ajax = false; public $nextResponseType = 'json'; // json, xml public $next = 'N/A'; public $upgrader = null; public $standalone = true; /** * set to false if the current step is a loop * * @var boolean */ public $stepDone = true; public $status = true; public $error ='0'; public $nextDesc = '.'; public $nextParams = array(); public $nextQuickInfo = array(); public $currentParams = array(); /** * @var array theses values will be automatically added in "nextParams" * if their properties exists */ public $ajaxParams = array( // autoupgrade options 'dontBackupImages', 'install_version', 'keepDefaultTheme', 'keepTrad', 'keepMails', 'manualMode', 'deactivateCustomModule', 'backupName', 'backupFilesFilename', 'backupDbFilename', 'restoreName', 'restoreFilesFilename', 'restoreDbFilenames', ); public $autoupgradePath = null; /** * autoupgradeDir * * @var string directory relative to admin dir */ public $autoupgradeDir = 'autoupgrade'; public $latestRootDir = ''; public $prodRootDir = ''; public $adminDir = ''; public $root_writable = null; public $module_version = null; public $lastAutoupgradeVersion = ''; public $svnDir = 'svn'; public $destDownloadFilename = 'prestashop.zip'; /** * during upgradeFiles process, * this files contains the list of queries left to upgrade in a serialized array. * (this file is deleted in init() method if you reload the page) * @var string */ public $toUpgradeQueriesList = 'queriesToUpgrade.list'; /** * during upgradeFiles process, * this files contains the list of files left to upgrade in a serialized array. * (this file is deleted in init() method if you reload the page) * @var string */ public $toUpgradeFileList = 'filesToUpgrade.list'; /** * during upgradeFiles process, * this files contains the list of files left to upgrade in a serialized array. * (this file is deleted in init() method if you reload the page) * @var string */ public $diffFileList = 'filesDiff.list'; /** * during backupFiles process, * this files contains the list of files left to save in a serialized array. * (this file is deleted in init() method if you reload the page) * @var string */ public $toBackupFileList = 'filesToBackup.list'; /** * during backupDb process, * this files contains the list of tables left to save in a serialized array. * (this file is deleted in init() method if you reload the page) * @var string */ public $toBackupDbList = 'tablesToBackup.list'; /** * during restoreDb process, * this file contains a serialized array of queries which left to execute for restoring database * (this file is deleted in init() method if you reload the page) * @var string */ public $toRestoreQueryList = 'queryToRestore.list'; /** * during restoreFiles process, * this file contains difference between files present in a backupFiles archive * and files currently in directories, in a serialized array. * (this file is deleted in init() method if you reload the page) * @var string */ public $toRemoveFileList = 'filesToRemove.list'; /** * during restoreFiles process, * contains list of files present in backupFiles archive * * @var string */ public $fromArchiveFileList = 'filesFromArchive.list'; /** * mailCustomList contains list of mails files which are customized, * relative to original files for the current PrestaShop version * * @var string */ public $mailCustomList = 'mails-custom.list'; /** * tradCustomList contains list of mails files which are customized, * relative to original files for the current PrestaShop version * * @var string */ public $tradCustomList = 'translations-custom.list'; /** * tmp_files contains an array of filename which will be removed * at the beginning of the upgrade process * * @var array */ public $tmp_files = array( 'toUpgradeFileList', 'toUpgradeQueriesList', 'diffFileList', 'toBackupFileList', 'toBackupDbList', 'toRestoreQueryList', 'toRemoveFileList', 'fromArchiveFileList', 'tradCustomList', 'mailCustomList', ); public $install_version; public $dontBackupImages = null; public $keepDefaultTheme = null; public $keepTrad = null; public $keepMails = null; public $manualMode = null; public $deactivateCustomModule = null; public $sampleFileList = array(); private $backupIgnoreFiles = array(); private $backupIgnoreAbsoluteFiles = array(); private $excludeFilesFromUpgrade = array(); private $excludeAbsoluteFilesFromUpgrade = array(); private $restoreName = null; private $backupName = null; private $backupFilesFilename = null; private $backupDbFilename = null; private $restoreFilesFilename = null; private $restoreDbFilenames = array(); /** * int loopBackupFiles : if your server has a low memory size, lower this value * @TODO remove the static, add a const, and use it like this : min(AdminUpgrade::DEFAULT_LOOP_ADD_FILE_TO_ZIP,Configuration::get('LOOP_ADD_FILE_TO_ZIP'); */ public static $loopBackupFiles = 500; /** * int loopBackupDbTime : if your server has a low memory size, lower this value * @TODO remove the static, add a const, and use it like this : min(AdminUpgrade::DEFAULT_LOOP_ADD_FILE_TO_ZIP,Configuration::get('LOOP_ADD_FILE_TO_ZIP'); */ public static $loopBackupDbTime = 6; /** * int loopUpgradeFiles : if your server has a low memory size, lower this value */ public static $loopUpgradeFiles = 1000; /** * int loopRestoreFiles : if your server has a low memory size, lower this value */ public static $loopRestoreFiles = 500; /** * int loopRestoreQueryTime : if your server has a low memory size, lower this value (in sec) */ public static $loopRestoreQueryTime = 6; /** * int loopRemoveSamples : if your server has a low memory size, lower this value */ public static $loopRemoveSamples = 1000; /** * int loopRemoveUpgraderFiles : remove files to keep only files that where in backup archive */ public static $loopRemoveUpgradedFiles = 1000; /* usage : key = the step you want to ski * value = the next step you want instead * example : public static $skipAction = array('download' => 'unzip'); * initial order upgrade: download, unzip, removeSamples, backupFiles, backupDb, upgradeFiles, upgradeDb, upgradeComplete * initial order rollback: rollback, restoreFiles, restoreDb, rollbackComplete */ public static $skipAction = array(); public $useSvn; /** * if set to true, will use pclZip library * even if ZipArchive is available */ public static $force_pclZip = false; protected $_includeContainer = false; /** * replace tools encrypt * * @param mixed $string * @return void */ public function encrypt($string) { return md5(_COOKIE_KEY_.$string); } public function checkToken() { // simple checkToken in ajax-mode, to be free of Cookie class (and no Tools::encrypt() too ) if ($this->ajax) return ($_COOKIE['autoupgrade'] == $this->encrypt($_COOKIE['id_employee'])); else return parent::checkToken(); } /** * create cookies id_employee, id_tab and autoupgrade (token) */ public function createCustomToken() { // ajax-mode for autoupgrade, we can't use the classic authentication // so, we'll create a cookie in admin dir, based on cookie key global $cookie; $id_employee = $cookie->id_employee; $adminDir = trim(str_replace($this->prodRootDir, '', $this->adminDir), DIRECTORY_SEPARATOR); $cookiePath = __PS_BASE_URI__.$adminDir; setcookie('id_employee', $id_employee, time()+7200, $cookiePath); setcookie('id_tab', $this->id, time()+7200, $cookiePath); setcookie('autoupgrade', $this->encrypt($id_employee), time()+7200, $cookiePath); return false; } public function viewAccess($disable = false) { if ($this->ajax) return true; else { // simple access : we'll allow only admin global $cookie; if ($cookie->profile == 1) return true; } return false; } public function __construct() { // @todo : do this only in ajax mode and if we are allowed to use theses functions @set_time_limit(0); @ini_set('max_execution_time', '0'); global $ajax; if (!empty($ajax)) $this->ajax = true; $this->init(); // retrocompatibility when used in module : Tab can't work, // but we saved the tab id in a cookie. if(class_exists('Tab',false)) parent::__construct(); else { if (isset($_COOKIE['id_tab'])) $this->id = $_COOKIE['id_tab']; } } protected function l($string, $class = 'AdminTab', $addslashes = FALSE, $htmlentities = TRUE) { if(version_compare(_PS_VERSION_,'1.4.3.0','<')) { // need to be called in order to populate $classInModule return self::findTranslation('autoupgrade', $string, 'AdminSelfUpgrade'); } else return parent::l($string, $class, $addslashes, $htmlentities); } /** * findTranslation (initially in Module class), to make translations works * * @param string $name module name * @param string $string string to translate * @param string $source current class * @return string translated string */ public static function findTranslation($name, $string, $source) { global $_MODULES; $cache_key = $name . '|' . $string . '|' . $source; if (!isset(self::$l_cache[$cache_key])) { if (!is_array($_MODULES)) return str_replace('"', '"', $string); // set array key to lowercase for 1.3 compatibility $_MODULES = array_change_key_case($_MODULES); $currentKey = '<{'.strtolower($name).'}'.strtolower(_THEME_NAME_).'>'.strtolower($source).'_'.md5($string); // note : we should use a variable to define the default theme (instead of "prestashop") $defaultKey = '<{'.strtolower($name).'}prestashop>'.strtolower($source).'_'.md5($string); if (isset($_MODULES[$currentKey])) $ret = stripslashes($_MODULES[$currentKey]); elseif (isset($_MODULES[strtolower($currentKey)])) $ret = stripslashes($_MODULES[strtolower($currentKey)]); elseif (isset($_MODULES[$defaultKey])) $ret = stripslashes($_MODULES[$defaultKey]); elseif (isset($_MODULES[strtolower($defaultKey)])) $ret = stripslashes($_MODULES[strtolower($defaultKey)]); else $ret = stripslashes($string); self::$l_cache[$cache_key] = str_replace('"', '"', $ret); } return self::$l_cache[$cache_key]; } /** * function to set configuration fields display * * @return void */ private function _setFields() { $this->_fieldsAutoUpgrade['PS_AUTOUP_DONT_SAVE_IMAGES'] = array( 'title' => $this->l('Also save images'), 'cast' => 'intval', 'validation' => 'isBool', 'type' => 'bool', 'desc'=>$this->l('You can exclude the image directory from backup if you already saved it by another method (not recommended)'), ); $this->_fieldsAutoUpgrade['PS_AUTOUP_KEEP_DEFAULT_THEME'] = array( 'title' => $this->l('Keep theme "prestashop"'), 'cast' => 'intval', 'validation' => 'isBool', 'type' => 'bool', 'desc'=>$this->l('If you have customized PrestaShop default theme, you can protect it from upgrade (not recommended)'), ); $this->_fieldsAutoUpgrade['PS_AUTOUP_KEEP_TRAD'] = array( 'title' => $this->l('Keep translations'), 'cast' => 'intval', 'validation' => 'isBool', 'type' => 'bool', 'desc'=>$this->l('If set to yes, you will keep all your translations'), ); $this->_fieldsAutoUpgrade['PS_AUTOUP_KEEP_MAILS'] = array( 'title' => $this->l('Keep default mails'), 'cast' => 'intval', 'validation' => 'isBool', 'type' => 'bool', 'desc'=>$this->l('If set to yes, new mailtemplate will be added but old will not be overwritten (not recommended)'), ); $this->_fieldsAutoUpgrade['PS_AUTOUP_CUSTOM_MOD_DESACT'] = array( 'title' => $this->l('Deactivate custom modules'), 'cast' => 'intval', 'validation' => 'isBool', 'type' => 'bool', 'desc'=>$this->l('If you don\'t deactivate your modules, you can have some compatibility problems and the Modules page might not load correctly.'), ); // allow manual mode only for dev if (defined('_PS_MODE_DEV_') AND _PS_MODE_DEV_) $this->_fieldsAutoUpgrade['PS_AUTOUP_MANUAL_MODE'] = array( 'title' => $this->l('Manual mode'), 'cast' => 'intval', 'validation' => 'isBool', 'type' => 'bool', 'desc'=>$this->l('Check this if you want to stop after each step'), ); if (defined('_PS_ALLOW_UPGRADE_UNSTABLE_') AND _PS_ALLOW_UPGRADE_UNSTABLE_ AND function_exists('svn_checkout')) { $this->_fieldsAutoUpgrade['PS_AUTOUP_USE_SVN'] = array( 'title' => $this->l('Use Subversion'), 'cast' => 'intval', 'validation' => 'isBool', 'type' => 'bool', 'desc' => $this->l('check this if you want to use unstable svn instead of official release'), ); } } public function configOk() { $allowed_array = $this->getCheckCurrentConfig(); $allowed = array_product($allowed_array); return $allowed; } public function getcheckCurrentConfig() { static $allowed_array; if(empty($allowed_array)) { $allowed_array = array(); $allowed_array['fopen'] = ConfigurationTest::test_fopen(); $allowed_array['root_writable'] = $this->getRootWritable(); $allowed_array['shop_deactivated'] = !Configuration::get('PS_SHOP_ENABLE'); // xml can enable / disable upgrade $allowed_array['autoupgrade_allowed'] = $this->upgrader->autoupgrade; $allowed_array['need_upgrade'] = $this->upgrader->need_upgrade; $allowed_array['module_version_ok'] = $this->checkAutoupgradeLastVersion(); // if one option has been defined, all options are. $allowed_array['module_configured'] = (Configuration::get('PS_AUTOUP_KEEP_MAILS') !== false); } return $allowed_array; } public function getRootWritable() { // test if prodRootDir is writable recursively if (ConfigurationTest::test_dir($this->prodRootDir, true)) $this->root_writable = true; return $this->root_writable; } public function getModuleVersion() { if (is_null($this->module_version)) { if (file_exists(_PS_ROOT_DIR_.'/modules/autoupgrade/config.xml') && $xml_module_version = simplexml_load_file(_PS_ROOT_DIR_.'/modules/autoupgrade/config.xml') ) $this->module_version = (string)$xml_module_version->version; else $this->module_version = false; } return $this->module_version; } public function checkAutoupgradeLastVersion() { if ($this->getModuleVersion()) $this->lastAutoupgradeVersion = version_compare($this->module_version, $this->upgrader->autoupgrade_last_version, '>='); else $this->lastAutoupgradeVersion = true; return $this->lastAutoupgradeVersion; } /** * init to build informations we need * * @return void */ public function init() { // For later use, let's set up prodRootDir and adminDir // This way it will be easier to upgrade a different path if needed $this->prodRootDir = _PS_ROOT_DIR_; $this->adminDir = _PS_ADMIN_DIR_; // from $_POST or $_GET $this->action = empty($_REQUEST['action'])?null:$_REQUEST['action']; $this->currentParams = empty($_REQUEST['params'])?null:$_REQUEST['params']; // test writable recursively if(version_compare(_PS_VERSION_,'1.4.6.0','<') || !class_exists('ConfigurationTest', false)) { require_once('ConfigurationTest.php'); if(!class_exists('ConfigurationTest', false) AND class_exists('ConfigurationTestCore')) eval('class ConfigurationTest extends ConfigurationTestCore{}'); } // checkPSVersion only if not ajax if (empty($this->action)) { $this->upgrader = new Upgrader(); $this->upgrader->checkPSVersion(); $this->install_version = $this->upgrader->version_num; } // If you have defined this somewhere, you know what you do if (defined('_PS_ALLOW_UPGRADE_UNSTABLE_') AND _PS_ALLOW_UPGRADE_UNSTABLE_ AND function_exists('svn_checkout')) { if(version_compare(_PS_VERSION_,'1.4.5.0','<') OR class_exists('Configuration',false)) $this->useSvn = Configuration::get('PS_AUTOUP_USE_SVN'); } else $this->useSvn = false; // If not exists in this sessions, "create" // session handling : from current to next params if (isset($this->currentParams['removeList'])) $this->nextParams['removeList'] = $this->currentParams['removeList']; if (isset($this->currentParams['filesToUpgrade'])) $this->nextParams['filesToUpgrade'] = $this->currentParams['filesToUpgrade']; // set autoupgradePath, to be used in backupFiles and backupDb config values $this->autoupgradePath = $this->adminDir.DIRECTORY_SEPARATOR.$this->autoupgradeDir; // directory missing // @todo move this in upgrade step if (!file_exists($this->autoupgradePath)) if (!@mkdir($this->autoupgradePath,0777)) $this->_errors[] = sprintf($this->l('unable to create directory %s'),$this->autoupgradePath); // directory missing // @todo move this in upgrade step $latest = $this->autoupgradePath.DIRECTORY_SEPARATOR.'latest'; if (!file_exists($latest)) if (!@mkdir($latest,0777)) $this->_errors[] = sprintf($this->l('unable to create directory %s'),$latest); $this->latestRootDir = $latest.DIRECTORY_SEPARATOR.'prestashop'; // @TODO future option "install in test dir" // $this->testRootDir = $this->autoupgradePath.DIRECTORY_SEPARATOR.'test'; /* load options from configuration if we're not in ajax mode */ if (false == $this->ajax) { $this->dontBackupImages = !Configuration::get('PS_AUTOUP_DONT_SAVE_IMAGES'); $this->keepDefaultTheme = Configuration::get('PS_AUTOUP_KEEP_DEFAULT_THEME'); $this->keepTrad = Configuration::get('PS_AUTOUP_KEEP_TRAD'); $this->keepMails = Configuration::get('PS_AUTOUP_KEEP_MAILS'); $this->manualMode = Configuration::get('PS_AUTOUP_MANUAL_MODE'); $this->deactivateCustomModule = Configuration::get('PS_AUTOUP_CUSTOM_MOD_DESACT'); $rand = dechex ( mt_rand(0, min(0xffffffff, mt_getrandmax() ) ) ); $date = date('Ymd-His'); $this->backupName = 'V'._PS_VERSION_.'_'.$date.'-'.$rand; $this->backupFilesFilename = 'auto-backupfiles_'.$this->backupName.'.zip'; $this->backupDbFilename = 'auto-backupdb_XXXXXX_'.$this->backupName.'.sql'; // removing temporary files foreach($this->tmp_files as $tmp_file) if (file_exists($this->autoupgradePath.DIRECTORY_SEPARATOR.$this->$tmp_file)) unlink($this->autoupgradePath.DIRECTORY_SEPARATOR.$this->$tmp_file); } else { foreach($this->ajaxParams as $prop) if(property_exists($this, $prop)) $this->{$prop} = isset($this->currentParams[$prop])?$this->currentParams[$prop]:''; } // We can add any file or directory in the exclude dir : theses files will be not removed or overwritten // @TODO cache should be ignored recursively, but we have to reconstruct it after upgrade // - compiled from smarty // - .svn $this->backupIgnoreAbsoluteFiles[] = "/tools/smarty_v2/compile"; $this->backupIgnoreAbsoluteFiles[] = "/tools/smarty_v2/cache"; $this->backupIgnoreAbsoluteFiles[] = "/tools/smarty/compile"; $this->backupIgnoreAbsoluteFiles[] = "/tools/smarty/cache"; // do not care about the two autoupgrade dir we use; $this->backupIgnoreAbsoluteFiles[] = "/modules/autoupgrade"; $this->backupIgnoreAbsoluteFiles[] = "/admin/autoupgrade"; $this->excludeFilesFromUpgrade[] = '.'; $this->excludeFilesFromUpgrade[] = '..'; $this->excludeFilesFromUpgrade[] = '.svn'; // do not copy install, neither settings.inc.php in case it would be present $this->excludeAbsoluteFilesFromUpgrade[] = "/install"; $this->excludeFilesFromUpgrade[] = 'settings.inc.php'; // this will exclude autoupgrade dir from admin, and autoupgrade from modules $this->excludeFilesFromUpgrade[] = 'autoupgrade'; $this->backupIgnoreFiles[] = '.'; $this->backupIgnoreFiles[] = '..'; $this->backupIgnoreFiles[] = '.svn'; $this->backupIgnoreFiles[] = 'autoupgrade'; if ($this->dontBackupImages) $this->backupIgnoreAbsoluteFiles[] = "/img"; if ($this->keepDefaultTheme) { $this->excludeAbsoluteFilesFromUpgrade[] = "/themes/prestashop"; $this->excludeAbsoluteFilesFromUpgrade[] = "/themes/default"; } } /** * getFilePath return the path to the zipfile containing prestashop. * * @return void */ private function getFilePath() { return $this->autoupgradePath.DIRECTORY_SEPARATOR.$this->destDownloadFilename; } public function postProcess() { global $currentIndex; $this->_setFields(); if (Tools::isSubmit('submitAutoUpgradeOptions')) return $this->_postConfig($this->_fieldsAutoUpgrade); if (Tools::isSubmit('deletebackup')) { $res = true; $name = Tools::getValue('name'); $filelist = scandir($this->autoupgradePath); foreach($filelist as $filename) if (preg_match('#^auto-backup(db|files)_'.preg_quote($name).'\..*$#', $filename, $matches)) { if (is_file($this->autoupgradePath.DIRECTORY_SEPARATOR.$filename)) $res &= unlink($this->autoupgradePath.DIRECTORY_SEPARATOR.$filename); if (!empty($name) && is_dir($this->autoupgradePath.DIRECTORY_SEPARATOR.$name)) Tools::deleteDirectory($this->autoupgradePath.DIRECTORY_SEPARATOR.$name); } if ($res) Tools::redirectAdmin($currentIndex.'&conf=1&token='.Tools::getValue('token')); else $this->_errors[] = sprintf($this->l('Error when trying to delete backups %s'), $name); } } /** * ends the rollback process * * @return void */ public function ajaxProcessRollbackComplete() { $this->nextDesc = $this->l('Restoration process done. Congratulations ! You can now reactive your shop.'); $this->next = ''; } /** * ends the upgrade process * * @return void */ public function ajaxProcessUpgradeComplete() { $this->nextDesc = $this->l('Upgrade process done. Congratulations ! You can now reactive your shop.'); $this->next = ''; } public function ajaxProcessCompareReleases() { $this->upgrader = new Upgrader(); $this->upgrader->checkPSVersion(); $diffFileList = $this->upgrader->getDiffFilesList(_PS_VERSION_, $this->upgrader->version_num); if (!is_array($diffFileList)) { $this->nextParams['status'] = 'error'; $this->nextParams['msg'] = '[TECHNICAL ERROR] Unable to generate diff file list'; } else { file_put_contents($this->autoupgradePath.DIRECTORY_SEPARATOR.$this->diffFileList, serialize($diffFileList)); if (count($diffFileList) > 0) $this->nextParams['msg'] = sprintf($this->l('%1$s files are diff and will be removed during this upgrade'), count($diffFileList['deleted'])); else $this->nextParams['msg'] = $this->l('No diff files found.'); $this->nextParams['result'] = $diffFileList; } } public function ajaxProcessCheckFilesVersion() { $this->upgrader = new Upgrader(); $changedFileList = $this->upgrader->getChangedFilesList(); if ($this->upgrader->isAuthenticPrestashopVersion() === true && !is_array($changedFileList) ) { $this->nextParams['status'] = 'error'; $this->nextParams['msg'] = '[TECHNICAL ERROR] Unable to check files'; $testOrigCore = false; } else { if ($this->upgrader->isAuthenticPrestashopVersion() === true) { $this->nextParams['status'] = 'ok'; $testOrigCore = true; } else { $testOrigCore = false; $this->nextParams['status'] = 'warn'; } if (!isset($changedFileList['core'])) $changedFileList['core'] = array(); if (!isset($changedFileList['translation'])) $changedFileList['translation'] = array(); file_put_contents($this->autoupgradePath.DIRECTORY_SEPARATOR.$this->tradCustomList,serialize($changedFileList['translation'])); if (!isset($changedFileList['mail'])) $changedFileList['mail'] = array(); file_put_contents($this->autoupgradePath.DIRECTORY_SEPARATOR.$this->mailCustomList,serialize($changedFileList['mail'])); if ($changedFileList === false) { $changedFileList = array(); $this->nextParams['msg'] = $this->l('Unable to check files'); $this->nextParams['status'] = 'error'; } else { $this->nextParams['msg'] = ($testOrigCore?$this->l('Core files are ok'):sprintf($this->l('%1$s core files have been modified (%2$s total)'), count($changedFileList['core']), count(array_merge($changedFileList['core'], $changedFileList['mail'], $changedFileList['translation'])))); } $this->nextParams['result'] = $changedFileList; } } public function ajaxProcessUpgradeNow() { $this->nextDesc = $this->l('Starting upgrade ...'); if ($this->useSvn) { $this->next = 'svnCheckout'; $this->nextDesc = $this->l('switching to svn checkout (useSvn set to true)'); } else { $this->next = 'download'; $this->nextDesc = $this->l('Shop deactivated. Now downloading (this can take some time )...'); } } public function ajaxProcessSvnExport() { if ($this->useSvn) { // first of all, delete the content of the latest root dir just in case if (is_dir($this->latestRootDir)) { Tools::deleteDirectory($this->latestRootDir, false); $this->nextQuickInfo[] = $this->l('latest directory has been emptied'); } if (!file_exists($this->latestRootDir)) @mkdir($this->latestRootDir); if (svn_export($this->autoupgradePath . DIRECTORY_SEPARATOR . $this->svnDir, $this->latestRootDir)) { // export means svn means install-dev and admin-dev. // let's rename admin to the correct admin dir // and rename install-dev to install $adminDir = str_replace($this->prodRootDir, '', $this->adminDir); rename($this->latestRootDir.DIRECTORY_SEPARATOR.'install-dev', $this->latestRootDir.DIRECTORY_SEPARATOR.'install'); rename($this->latestRootDir.DIRECTORY_SEPARATOR.'admin-dev', $this->latestRootDir.DIRECTORY_SEPARATOR.$adminDir); // Unsetting to force listing unset($this->nextParams['removeList']); $this->next = "removeSamples"; $this->nextDesc = $this->l('Export svn complete. removing sample files...'); return true; } else { $this->next = 'error'; $this->nextDesc = $this->l('error when svn export '); } } } /** * extract last version into admin/autoupgrade/latest directory * * @return void */ public function ajaxProcessUnzip(){ if(version_compare(_PS_VERSION_,'1.4.5.0','<') AND !class_exists('Tools',false) ) require_once('Tools.php'); $filepath = $this->getFilePath(); $destExtract = $this->autoupgradePath.DIRECTORY_SEPARATOR.'latest'; if (file_exists($destExtract)) { Tools::deleteDirectory($destExtract, false); $this->nextQuickInfo[] = $this->l('latest directory has been emptied'); } if ($this->ZipExtract($filepath, $destExtract)) { $adminDir = str_replace($this->prodRootDir, '', $this->adminDir); rename($this->latestRootDir.DIRECTORY_SEPARATOR.'admin', $this->latestRootDir.DIRECTORY_SEPARATOR.$adminDir); // Unsetting to force listing unset($this->nextParams['removeList']); $this->next = "removeSamples"; $this->nextDesc = $this->l('Extract complete. removing sample files...'); return true; } else{ $this->next = "error"; $this->nextDesc = sprintf($this->l('unable to extract %1$s into %2$s ...'), $filepath, $destExtract); return true; } } /** * _listSampleFiles will make a recursive call to scandir() function * and list all file which match to the $fileext suffixe (this can be an extension or whole filename) * * @TODO maybe $regex instead of $fileext ? * @param string $dir directory to look in * @param string $fileext suffixe filename * @return void */ private function _listSampleFiles($dir, $fileext = '.jpg'){ $res = true; $dir = rtrim($dir,'/').DIRECTORY_SEPARATOR; $toDel = scandir($dir); // copied (and kind of) adapted from AdminImages.php foreach ($toDel AS $file) { if ($file[0] != '.') { if (preg_match('#'.preg_quote($fileext,'#').'$#i',$file)) { $this->sampleFileList[] = $dir.$file; } else if (is_dir($dir.$file)) { $res &= $this->_listSampleFiles($dir.$file, $fileext); } } } return $res; } public function _listFilesInDir($dir, $way = 'backup') { $list = array(); $allFiles = scandir($dir); foreach ($allFiles as $file) { if ($file[0] != '.') { $fullPath = $dir.DIRECTORY_SEPARATOR.$file; if (!$this->_skipFile($file, $fullPath, $way)) { if (is_dir($fullPath)) $list = array_merge($list, $this->_listFilesInDir($fullPath, $way)); else $list[] = $fullPath; } // no else needed ! } } return $list; } public function _listFilesToRemove() { $prev_version = preg_match('#auto-backupfiles_V([0-9.]*)_#', $this->restoreFilesFilename, $matches); if ($prev_version) $prev_version = $matches[1]; if (!$this->upgrader) $this->upgrader = new Upgrader(); $toRemove = $this->upgrader->getDiffFilesList(_PS_VERSION_, $prev_version, false); // if we can't find the diff file list corresponding to _PS_VERSION_ and prev_version, // let's assume to remove every files ... if (!$toRemove) $toRemove = $this->_listFilesInDir($this->prodRootDir, 'restore'); $adminDir = str_replace($this->prodRootDir, '', $this->adminDir); foreach ($toRemove as $key => $file) { $filename = substr($file, strrpos($file, '/')+1); $toRemove[$key] = preg_replace('#^/admin#', $adminDir, $file); // additional checks : preserve everything that contains autoupgrade if ($this->_skipFile($filename, $file, 'backup') || strpos($file, 'autoupgrade')) unset($toRemove[$key]); } return $toRemove; } /** * list files to upgrade and save them in a serialized array in $this->toUpgradeFileList * * @param string $dir * @return number of files found */ public function _listFilesToUpgrade($dir) { static $list = array(); if (!is_dir($dir)) { $this->nextQuickInfo[] = sprintf('[ERROR] %s doesn\'t exists or is not a directory', $dir); $this->nextDesc = $this->l('Nothing has been extracted. It seems the unzip step has been skipped.'); $this->next = 'error'; return false; } $allFiles = scandir($dir); foreach ($allFiles as $file) { $fullPath = $dir.DIRECTORY_SEPARATOR.$file; if (!$this->_skipFile($file, $fullPath, "upgrade")) { $list[] = str_replace($this->latestRootDir, '', $fullPath); // if is_dir, we will create it :) if (is_dir($fullPath)) if (strpos($dir.DIRECTORY_SEPARATOR.$file, 'install') === false) $this->_listFilesToUpgrade($fullPath); } } file_put_contents($this->autoupgradePath.DIRECTORY_SEPARATOR.$this->toUpgradeFileList,serialize($list)); $this->nextParams['filesToUpgrade'] = $this->toUpgradeFileList; return sizeof($this->toUpgradeFileList); } public function ajaxProcessUpgradeFiles() { $this->nextParams = $this->currentParams; if (!isset($this->nextParams['filesToUpgrade'])) { // list saved in $this->toUpgradeFileList $total_files_to_upgrade = $this->_listFilesToUpgrade($this->latestRootDir); if ($total_files_to_upgrade == 0) { $this->nextQuickInfo[] = '[ERROR] Unable to find files to upgrade.'; $this->nextDesc = $this->l('Unable to list files to upgrade'); $this->next = 'error'; return false; } $this->nextQuickInfo[] = sprintf($this->l('%s files will be upgraded.'), $total_files_to_upgrade); $this->next = 'upgradeFiles'; return true; } // later we could choose between _PS_ROOT_DIR_ or _PS_TEST_DIR_ $this->destUpgradePath = $this->prodRootDir; // upgrade files one by one like for the backup // with a 1000 loop because it's funny // @TODO : // foreach files in latest, copy $this->next = 'upgradeFiles'; $filesToUpgrade = @unserialize(file_get_contents($this->autoupgradePath.DIRECTORY_SEPARATOR.$this->nextParams['filesToUpgrade'])); if (!is_array($filesToUpgrade)) { $this->next = 'error'; $this->nextDesc = $this->l('filesToUpgrade is not an array'); $this->nextQuickInfo[] = $this->l('filesToUpgrade is not an array'); return false; } // @TODO : does not upgrade files in modules, translations if they have not a correct md5 (or crc32, or whatever) from previous version for ($i=0;$i < self::$loopUpgradeFiles;$i++) { if (sizeof($filesToUpgrade) <= 0) { $this->next = 'upgradeDb'; unlink($this->nextParams['filesToUpgrade']); $this->nextDesc = $this->l('All files upgraded. Now upgrading database'); $this->nextResponseType = 'json'; break; } $file = array_shift($filesToUpgrade); if (!$this->upgradeThisFile($file)) { // put the file back to the begin of the list $totalFiles = array_unshift($filesToUpgrade, $file); $this->next = 'error'; $this->nextQuickInfo[] = sprintf($this->l('error when trying to upgrade %s'), $file); break; } } $this->nextDesc = sprintf($this->l('%1$s files left to upgrade.'), sizeof($filesToUpgrade)); $this->nextQuickInfo[] = sprintf($this->l('%2$s files left to upgrade.'), (isset($file)?$file:''), sizeof($filesToUpgrade)); file_put_contents($this->autoupgradePath.DIRECTORY_SEPARATOR.$this->nextParams['filesToUpgrade'],serialize($filesToUpgrade)); return true; } private function createCacheFsDirectories($level_depth, $directory = false) { if (!$directory) $directory = _PS_CACHEFS_DIRECTORY_; $chars = '0123456789abcdef'; for ($i = 0; $i < strlen($chars); $i++) { $new_dir = $directory.$chars[$i].'/'; if (mkdir($new_dir)) if (chmod($new_dir, 0777)) if ($level_depth - 1 > 0) self::createCacheFsDirectories($level_depth - 1, $new_dir); } } public function ajaxProcessUpgradeDb() { // @TODO : 1/2/3 have to be done at the beginning !!!!!!!!!!!!!!!!!!!!!! $this->nextParams = $this->currentParams; if (!$this->doUpgrade()) { $this->next = 'error'; $this->nextDesc = $this->l('error during upgrade Db. You may need to restore your database'); return false; } // @TODO // 5) compare activated modules and reactivate them return true; } /** * This function now replaces doUpgrade.php or upgrade.php * * @return void */ public function doUpgrade() { // Initialize // setting the memory limit to 128M only if current is lower $memory_limit = ini_get('memory_limit'); if (substr($memory_limit,-1) != 'G' AND ((substr($memory_limit,-1) == 'M' AND substr($memory_limit,0,-1) < 128) OR is_numeric($memory_limit) AND (intval($memory_limit) < 131072)) ){ @ini_set('memory_limit','128M'); } /* Redefine REQUEST_URI if empty (on some webservers...) */ if (!isset($_SERVER['REQUEST_URI']) || $_SERVER['REQUEST_URI'] == '') $_SERVER['REQUEST_URI'] = $_SERVER['SCRIPT_NAME']; if ($tmp = strpos($_SERVER['REQUEST_URI'], '?')) $_SERVER['REQUEST_URI'] = substr($_SERVER['REQUEST_URI'], 0, $tmp); $_SERVER['REQUEST_URI'] = str_replace('//', '/', $_SERVER['REQUEST_URI']); define('INSTALL_VERSION', $this->install_version); define('INSTALL_PATH', realpath($this->latestRootDir.DIRECTORY_SEPARATOR.'install')); define('PS_INSTALLATION_IN_PROGRESS', true); define('SETTINGS_FILE', $this->prodRootDir . '/config/settings.inc.php'); define('DEFINES_FILE', $this->prodRootDir .'/config/defines.inc.php'); define('INSTALLER__PS_BASE_URI', substr($_SERVER['REQUEST_URI'], 0, -1 * (strlen($_SERVER['REQUEST_URI']) - strrpos($_SERVER['REQUEST_URI'], '/')) - strlen(substr(dirname($_SERVER['REQUEST_URI']), strrpos(dirname($_SERVER['REQUEST_URI']), '/')+1)))); // define('INSTALLER__PS_BASE_URI_ABSOLUTE', 'http://'.ToolsInstall::getHttpHost(false, true).INSTALLER__PS_BASE_URI); // XML Header // header('Content-Type: text/xml'); $filePrefix = 'PREFIX_'; $engineType = 'ENGINE_TYPE'; $mysqlEngine = (defined('_MYSQL_ENGINE_') ? _MYSQL_ENGINE_ : 'MyISAM'); if (function_exists('date_default_timezone_set')) date_default_timezone_set('Europe/Paris'); // if _PS_ROOT_DIR_ is defined, use it instead of "guessing" the module dir. if (defined('_PS_ROOT_DIR_') AND !defined('_PS_MODULE_DIR_')) define('_PS_MODULE_DIR_', _PS_ROOT_DIR_.'/modules/'); else if (!defined('_PS_MODULE_DIR_')) define('_PS_MODULE_DIR_', INSTALL_PATH.'/../modules/'); // @todo upgrade_dir_php should be handled by Upgrader class $upgrade_dir_php = 'upgrade/php'; if (!file_exists(INSTALL_PATH.DIRECTORY_SEPARATOR.$upgrade_dir_php)) { $upgrade_dir_php = 'php'; if (!file_exists(INSTALL_PATH.DIRECTORY_SEPARATOR.$upgrade_dir_php)) { $this->next = 'error'; $this->nextDesc = $this->l('php upgrade dir is not found'); $this->nextQuickInfo[] = 'php upgrade dir is missing'; return false; } } define('_PS_INSTALLER_PHP_UPGRADE_DIR_', INSTALL_PATH.DIRECTORY_SEPARATOR.$upgrade_dir_php.DIRECTORY_SEPARATOR); //old version detection global $oldversion, $logger; $oldversion = false; if (file_exists(SETTINGS_FILE)) { include_once(SETTINGS_FILE); // include_once(DEFINES_FILE); $oldversion = _PS_VERSION_; } else { $this->next = 'error'; $this->nextQuickInfo[] = $this->l('The config/settings.inc.php file was not found.'); return false; die(''."\n"); } if (!defined('__PS_BASE_URI__')) define('__PS_BASE_URI__', realpath(dirname($_SERVER['SCRIPT_NAME'])).'/../../'); if (!defined('_THEMES_DIR_')) define('_THEMES_DIR_', __PS_BASE_URI__.'themes/'); $oldversion = _PS_VERSION_; $versionCompare = version_compare(INSTALL_VERSION, $oldversion); if ($versionCompare == '-1') { $this->next = 'error'; $this->nextQuickInfo[] = $this->l('This installer is too old'); return false; // die(''."\n"); } elseif ($versionCompare == 0) { $this->next = 'error'; $this->nextQuickInfo[] = $this->l(sprintf('You already have the %s version.',INSTALL_VERSION)); return false; die(''."\n"); } elseif ($versionCompare === false) { $this->next = 'error'; $this->nextQuickInfo[] = $this->l('There is no older version. Did you delete or rename the config/settings.inc.php file?'); return false; die(''."\n"); } //check DB access $this->db(); error_reporting(E_ALL); $resultDB = MySql::tryToConnect(_DB_SERVER_, _DB_USER_, _DB_PASSWD_, _DB_NAME_); if ($resultDB !== 0) { // $logger->logError('Invalid database configuration.'); $this->next = 'error'; $this->nextQuickInfo[] = $this->l('Invalid database configuration'); return false; die("\n"); } //custom sql file creation $upgradeFiles = array(); // @todo : upgrade/sql or sql/upgrade should be handled in the Upgrader class $upgrade_dir_sql = INSTALL_PATH.'/upgrade/sql'; // if 1.4; if (!file_exists($upgrade_dir_sql)) $upgrade_dir_sql = INSTALL_PATH.'/sql/upgrade'; if (!file_exists($upgrade_dir_sql)) { $this->next = 'error'; $this->nextDesc = $this->l('unable to find upgrade directory in the install path'); return false; } if ($handle = opendir($upgrade_dir_sql)) { while (false !== ($file = readdir($handle))) if ($file != '.' AND $file != '..') $upgradeFiles[] = str_replace(".sql", "", $file); closedir($handle); } if (empty($upgradeFiles)) { $this->next = 'error'; $this->nextQuickInfo[] = sprintf($this->l('Cannot find the sql upgrade files. Please verify that the %s folder is not empty'), $upgrade_dir_sql); // fail 31 return false; } natcasesort($upgradeFiles); $neededUpgradeFiles = array(); // fix : complete version number if there is not all 4 numbers // for example replace 1.4.3 by 1.4.3.0 // consequences : file 1.4.3.0.sql will be skipped if oldversion = 1.4.3 // @since 1.4.4.0 $arrayVersion = preg_split('#\.#', $oldversion); $versionNumbers = sizeof($arrayVersion); if ($versionNumbers != 4) $arrayVersion = array_pad($arrayVersion, 4, '0'); $oldversion = implode('.', $arrayVersion); // end of fix foreach ($upgradeFiles AS $version) { if (version_compare($version, $oldversion) == 1 AND version_compare(INSTALL_VERSION, $version) != -1) $neededUpgradeFiles[] = $version; } if (empty($neededUpgradeFiles)) { $this->next = 'error'; $this->nextQuickInfo[] = $this->l('No upgrade is possible.'); return false; $logger->logError('No upgrade is possible.'); die(''."\n"); } $sqlContentVersion = array(); if(isset($_GET['customModule']) AND $_GET['customModule'] == 'desactivate') { require_once(_PS_INSTALLER_PHP_UPGRADE_DIR_.'deactivate_custom_modules.php'); deactivate_custom_modules(); } foreach($neededUpgradeFiles AS $version) { $file = $upgrade_dir_sql.DIRECTORY_SEPARATOR.$version.'.sql'; if (!file_exists($file)) { $this->next = 'error'; $this->nextQuickInfo[] = sprintf($this->l('Error while loading sql upgrade file "%s.sql".'), $version); return false; $logger->logError('Error while loading sql upgrade file.'); die(''."\n"); } if (!$sqlContent = file_get_contents($file)."\n") { $this->next = 'error'; $this->nextQuickInfo[] = $this->l(sprintf('Error while loading sql upgrade file %s.', $version)); return false; $logger->logError(sprintf('Error while loading sql upgrade file %s.', $version)); die(''."\n"); } $sqlContent = str_replace(array($filePrefix, $engineType), array(_DB_PREFIX_, $mysqlEngine), $sqlContent); $sqlContent = preg_split("/;\s*[\r\n]+/",$sqlContent); $sqlContentVersion[$version] = $sqlContent; } //sql file execution global $requests, $warningExist; $requests = ''; $warningExist = false; // Configuration::loadConfiguration(); $request = ''; foreach ($sqlContentVersion as $upgrade_file => $sqlContent) foreach ($sqlContent as $query) { $query = trim($query); if(!empty($query)) { /* If php code have to be executed */ if (strpos($query, '/* PHP:') !== false) { /* Parsing php code */ $pos = strpos($query, '/* PHP:') + strlen('/* PHP:'); $phpString = substr($query, $pos, strlen($query) - $pos - strlen(' */;')); $php = explode('::', $phpString); preg_match('/\((.*)\)/', $phpString, $pattern); $paramsString = trim($pattern[0], '()'); preg_match_all('/([^,]+),? ?/', $paramsString, $parameters); if (isset($parameters[1])) $parameters = $parameters[1]; else $parameters = array(); if (is_array($parameters)) foreach ($parameters AS &$parameter) $parameter = str_replace('\'', '', $parameter); // reset phpRes to a null value $phpRes = null; /* Call a simple function */ if (strpos($phpString, '::') === false) { $func_name = str_replace($pattern[0], '', $php[0]); if (!file_exists(_PS_INSTALLER_PHP_UPGRADE_DIR_.strtolower($func_name).'.php')) { $this->nextQuickInfo[] = '
[ERROR] '.$upgrade_file.' PHP - missing file '.$query.'
'; } else { require_once(_PS_INSTALLER_PHP_UPGRADE_DIR_.strtolower($func_name).'.php'); $phpRes = call_user_func_array($func_name, $parameters); } } /* Or an object method */ else { $func_name = array($php[0], str_replace($pattern[0], '', $php[1])); $this->nextQuickInfo[] = '
[ERROR] '.$upgrade_file.' PHP - Object Method called '.$php[0].'::'.str_replace($pattern[0], '', $php[1]).'
'; } if (isset($phpRes) && (is_array($phpRes) && !empty($phpRes['error'])) || $phpRes === false ) { $this->next = 'error'; $this->nextQuickInfo[] = '
[ERROR] PHP '.$upgrade_file .' '.(empty($phpRes['error'])?$query:' '.$phpRes['error']).' '.(empty($phpRes['msg'])?'':' - '.$phpRes['msg']).'
'; } else $this->nextQuickInfo[] = '
[OK] PHP'.$upgrade_file.' '.$query.'
'; } elseif(!Db::getInstance()->execute($query)) { $this->next = 'error'; $this->nextQuickInfo[] = '
[ERROR] SQL '.$upgrade_file.' ' . Db::getInstance()->getNumberError().' in '.$query.': '.Db::getInstance()->getMsgError().'
'; $warningExist = true; } else $this->nextQuickInfo[] = '
[OK] SQL '.$upgrade_file.' '.$query.'
'; } } if ($this->next == 'error') { $this->nextDesc = $this->l('An error happen during database upgrade'); return false; } $this->nextQuickInfo[] = $this->l('Upgrade Db Ok'); // no error ! # At this point, database upgrade is over. # Now we need to add all previous missing settings items, and reset cache and compile directories $this->writeNewSettings(); // Settings updated, compile and cache directories must be emptied // @todo : the list of theses directory should be available elsewhere $arrayToClean[] = INSTALL_PATH.'/../tools/smarty/cache/'; $arrayToClean[] = INSTALL_PATH.'/../tools/smarty/compile/'; $arrayToClean[] = INSTALL_PATH.'/../tools/smarty_v2/cache/'; $arrayToClean[] = INSTALL_PATH.'/../tools/smarty_v2/compile/'; foreach ($arrayToClean as $dir) if (!file_exists($dir)) { $this->nextQuickInfo[] = sprintf($this->l('[SKIP] directory "%s" doesn\'t exist and cannot be emptied.'), $dir); continue; } else foreach (scandir($dir) as $file) if ($file[0] != '.' AND $file != 'index.php' AND $file != '.htaccess') { unlink($dir.$file); $this->nextQuickInfo[] = sprintf($this->l('[cleaning cache] %s removed'), $file); } // delete cache filesystem if activated $depth = (int)Db::getInstance()->getValue('SELECT value FROM '._DB_PREFIX_.'configuration WHERE name = "PS_CACHEFS_DIRECTORY_DEPTH"'); if($depth) { Tools::deleteDirectory(_PS_CACHEFS_DIRECTORY_, false); CacheFs::createCacheFsDirectories((int)$depth); } // we do not use class Configuration because it's not loaded; Db::getInstance()->execute('UPDATE `'._DB_PREFIX_.'configuration` SET value="0" WHERE name = "PS_HIDE_OPTIMIZATION_TIS"'); Db::getInstance()->execute('UPDATE `'._DB_PREFIX_.'configuration` SET value="1" WHERE name = "PS_NEED_REBUILD_INDEX"'); Db::getInstance()->execute('UPDATE `'._DB_PREFIX_.'configuration` SET value="'.INSTALL_VERSION.'" WHERE name = "PS_VERSION_DB"'); if ($warningExist) { $this->next = 'error'; $this->nextQuickInfo[] = $this->l('Warning detected during upgrade.'); $this->nextDesc = $this->l('Error while inserting content into the database'); return false; } else { $this->next = 'upgradeComplete'; $this->nextDesc = $this->l('Upgrade completed'); return true; } } public function writeNewSettings() { // note : duplicated line $mysqlEngine = (defined('_MYSQL_ENGINE_') ? _MYSQL_ENGINE_ : 'MyISAM'); $oldLevel = error_reporting(E_ALL); //refresh conf file require_once(_PS_ROOT_DIR_.'/modules/autoupgrade/AddConfToFile.php'); $confFile = new AddConfToFile(SETTINGS_FILE, 'w'); if ($confFile->error) { $this->next = 'error'; $this->nextDesc = $this->l('Error when opening settings.inc.php file in write mode'); $this->nextQuickInfo[] = $confFile->error; return false; } $datas = array( array('_DB_SERVER_', _DB_SERVER_), array('_DB_TYPE_', _DB_TYPE_), array('_DB_NAME_', _DB_NAME_), array('_DB_USER_', _DB_USER_), array('_DB_PASSWD_', _DB_PASSWD_), array('_DB_PREFIX_', _DB_PREFIX_), array('_MYSQL_ENGINE_', $mysqlEngine), array('_PS_CACHING_SYSTEM_', (defined('_PS_CACHING_SYSTEM_') AND _PS_CACHING_SYSTEM_ != 'CacheMemcache') ? _PS_CACHING_SYSTEM_ : 'CacheMemcache'), array('_PS_CACHE_ENABLED_', defined('_PS_CACHE_ENABLED_') ? _PS_CACHE_ENABLED_ : '0'), array('_MEDIA_SERVER_1_', defined('_MEDIA_SERVER_1_') ? _MEDIA_SERVER_1_ : ''), array('_MEDIA_SERVER_2_', defined('_MEDIA_SERVER_2_') ? _MEDIA_SERVER_2_ : ''), array('_MEDIA_SERVER_3_', defined('_MEDIA_SERVER_3_') ? _MEDIA_SERVER_3_ : ''), array('_COOKIE_KEY_', _COOKIE_KEY_), array('_COOKIE_IV_', _COOKIE_IV_), array('_PS_CREATION_DATE_', defined("_PS_CREATION_DATE_") ? _PS_CREATION_DATE_ : date('Y-m-d')), array('_PS_VERSION_', INSTALL_VERSION) ); if (defined('_RIJNDAEL_KEY_')) $datas[] = array('_RIJNDAEL_KEY_', _RIJNDAEL_KEY_); if (defined('_RIJNDAEL_IV_')) $datas[] = array('_RIJNDAEL_IV_', _RIJNDAEL_IV_); if(!defined('_PS_CACHE_ENABLED_')) define('_PS_CACHE_ENABLED_', '0'); if(!defined('_MYSQL_ENGINE_')) define('_MYSQL_ENGINE_', 'MyISAM'); // if 1.4.7 or above if (version_compare(INSTALL_VERSION, '1.5.0.0', '<=')) { $datas[] = array('__PS_BASE_URI__', __PS_BASE_URI__); $datas[] = array('_THEME_NAME_', _THEME_NAME_); } else $datas[] = array('_PS_DIRECTORY_', __PS_BASE_URI__); foreach ($datas AS $data){ $confFile->writeInFile($data[0], $data[1]); } if ($confFile->error != false) { $this->next = 'error'; $this->nextDesc = $this->l('Error when generating new settings.inc.php file.'); $this->nextQuickInfo[] = $confFile->error; return false; } else $this->nextQuickInfo[] = $this->l('settings file updated'); error_reporting($oldLevel); } /** * upgradeThisFile * * @param mixed $file * @return void */ public function upgradeThisFile($file) { if ($this->keepTrad && file_exists($this->autoupgradePath.DIRECTORY_SEPARATOR.$this->tradCustomList)) $translations_custom = unserialize(file_get_contents($this->autoupgradePath.DIRECTORY_SEPARATOR.$this->tradCustomList)); else $translations_custom = array(); if ($this->keepMails && file_exists($this->autoupgradePath.DIRECTORY_SEPARATOR.$this->mailCustomList)) $mails_custom = unserialize(file_get_contents($this->autoupgradePath.DIRECTORY_SEPARATOR.$this->mailCustomList)); else $mails_custom = array(); $excludeList = array_merge($mails_custom, $translations_custom); // relative to keep translations and keep default mails if (in_array(ltrim($file, '/'), $excludeList)) { $this->nextQuickInfo[] = sprintf($this->l('%s is preserved'), $file); return true; } // @TODO : later, we could handle customization with some kind of diff functions // for now, just copy $file in str_replace($this->latestRootDir,_PS_ROOT_DIR_) // $file comes from scandir function, no need to lost time and memory with file_exists() $orig = $this->latestRootDir.$file; $dest = $this->destUpgradePath . $file; if ($this->_skipFile($file, $dest, 'upgrade')) { $this->nextQuickInfo[] = sprintf($this->l('%s ignored'), $file); return true; } else { if (is_dir($orig)) { // if $dest is not a directory (that can happen), just remove that file if (!is_dir($dest) AND file_exists($dest)) { unlink($dest); $this->nextQuickInfo[] = sprintf('[WARNING] file %1$s has been deleted.', $file); } if (!file_exists($dest)) { if (@mkdir($dest, 0777)) { $this->nextQuickInfo[] = sprintf($this->l('directory %1$s created.'), $file); return true; } else { $this->next = 'error'; $this->nextQuickInfo[] = sprintf($this->l('error when creating directory %s'), $dest); $this->nextDesc = sprintf($this->l('error when creating directory %s'), $dest); return false; } } else // directory already exists { $this->nextQuickInfo[] = sprintf($this->l('directory %1$s already exists.'), $file); return true; } } else { if (copy($orig, $dest)) { $this->nextQuickInfo[] = sprintf($this->l('copied %1$s.'), $file); return true; } else { $this->next = 'error'; $this->nextQuickInfo[] = sprintf($this->l('error for copying %1$s'), $file); $this->nextDesc = sprintf($this->l('error for copying %1$s'), $file); return false; } } } } public function ajaxProcessRollback() { // 1st, need to analyse what was wrong. $this->nextParams = $this->currentParams; if (!empty($this->restoreName) && empty($this->restoreFilesFilename) && empty($this->restoreDbFilenames)) { $files = scandir($this->autoupgradePath); // find backup filenames, and be sure they exists foreach($files as $file) if (preg_match('#'.preg_quote('auto-backupfiles_'.$this->restoreName).'#', $file)) { $this->restoreFilesFilename = $file; break; } if (!is_file($this->autoupgradePath.DIRECTORY_SEPARATOR.$this->restoreFilesFilename)) { $this->next = 'error'; $this->nextQuickInfo[] = sprintf('[ERROR] file %s is missing : unable to restore files. Operation aborted.', $this->restoreFilesFilename); $this->nextDesc = sprintf($this->l('file %s does not exist. Files Restoration cannot be made.'), $this->restoreFilesFilename); return false; } $files = scandir($this->autoupgradePath.DIRECTORY_SEPARATOR.$this->restoreName); foreach($files as $file) if (preg_match('#auto-backupdb_[0-9]{6}_'.preg_quote($this->restoreName).'#', $file)) $this->restoreDbFilenames[] = $file; // order files is important ! sort($this->restoreDbFilenames); if (count($this->restoreDbFilenames) == 0) { $this->next = 'error'; $this->nextQuickInfo[] = sprintf('[ERROR] no backup db files found : it would be impossible to restore database. Operation aborted.'); $this->nextDesc = sprintf($this->l('no backup db files found. Database restoration cannot be made.'), count($this->restoreDbFilenames)); return false; } $this->next = 'restoreFiles'; $this->nextDesc = $this->l('Restoring files ...'); // remove tmp files related to restoreFiles if (file_exists($this->autoupgradePath.DIRECTORY_SEPARATOR.$this->fromArchiveFileList)) unlink($this->autoupgradePath.DIRECTORY_SEPARATOR.$this->fromArchiveFileList); if (file_exists($this->autoupgradePath.DIRECTORY_SEPARATOR.$this->toRemoveFileList)) unlink($this->autoupgradePath.DIRECTORY_SEPARATOR.$this->toRemoveFileList); } else $this->next = 'noRollbackFound'; } public function ajaxProcessNoRollbackFound() { $this->nextDesc = $this->l('Nothing to restore'); $this->next = 'rollbackComplete'; } /** * ajaxProcessRestoreFiles restore the previously saved files, * and delete files that weren't archived * * @return boolean true if succeed */ public function ajaxProcessRestoreFiles() { // loop $this->next = 'restoreFiles'; if ( !file_exists($this->autoupgradePath.DIRECTORY_SEPARATOR.$this->fromArchiveFileList) || !file_exists($this->autoupgradePath.DIRECTORY_SEPARATOR.$this->toRemoveFileList)) { // cleanup current PS tree $fromArchive = $this->_listArchivedFiles($this->autoupgradePath.DIRECTORY_SEPARATOR.$this->restoreFilesFilename); foreach($fromArchive as $k => $v) $fromArchive[$k] = '/'.$v; file_put_contents($this->autoupgradePath.DIRECTORY_SEPARATOR.$this->fromArchiveFileList, serialize($fromArchive)); // get list of files to remove $toRemove = $this->_listFilesToRemove(); $this->nextQuickInfo[] = sprintf($this->l('%s file(s) will be removed before restoring backup files'), count($toRemove)); file_put_contents($this->autoupgradePath.DIRECTORY_SEPARATOR.$this->toRemoveFileList, serialize($toRemove)); if ($fromArchive === false || $toRemove === false) { if (!$fromArchive) $this->nextQuickInfo[] = '[ERROR] '.sprintf($this->l('backup file %s does not exists'), $this->fromArchiveFileList); if (!$toRemove) $this->nextQuickInfo[] = '[ERROR] '.sprintf($this->l('file "%s" does not exists'), $this->toRemoveFileList); $this->nextDesc = $this->l('Unable to remove upgraded files.'); $this->next = 'error'; return false; } } // first restoreFiles step if (!isset($toRemove)) $toRemove = unserialize(file_get_contents($this->autoupgradePath.DIRECTORY_SEPARATOR.$this->toRemoveFileList)); if (count($toRemove) > 0) { for($i=0;$istepDone = true; $this->status = 'ok'; $this->next = 'restoreFiles'; $this->nextDesc = $this->l('Files from upgrade has been removed.'); $this->nextQuickInfo[] = $this->l('files from upgrade has been removed.'); file_put_contents($this->autoupgradePath.DIRECTORY_SEPARATOR.$this->toRemoveFileList, serialize($toRemove)); return true; } else { $filename = array_shift($toRemove); $file = rtrim($this->prodRootDir, DIRECTORY_SEPARATOR).$filename; if (file_exists($file)) { if (is_file($file) && @unlink($file)) $this->nextQuickInfo[] = sprintf('%s removed', $filename); else { if (!file_exists($file)) $this->nextQuickInfo[] = sprintf('[NOTICE] %s does not exists', $filename); elseif (is_dir($file)) { Tools::deleteDirectory($file, true); $this->nextQuickInfo[] = sprintf('[NOTICE] %s directory deleted', $filename); } else { $this->next = 'error'; $this->nextDesc = sprintf($this->l('error when removing %1$s'), $filename); $this->nextQuickInfo[] = sprintf($this->l('%s not removed'), $filename); return false; } } } } } file_put_contents($this->autoupgradePath.DIRECTORY_SEPARATOR.$this->toRemoveFileList, serialize($toRemove)); $this->nextDesc = sprintf($this->l('%s left to remove'), count($toRemove)); $this->next = 'restoreFiles'; return true; } // very second restoreFiles step : extract backup // if (!isset($fromArchive)) // $fromArchive = unserialize(file_get_contents($this->autoupgradePath.DIRECTORY_SEPARATOR.$this->fromArchiveFileList)); $filepath = $this->autoupgradePath.DIRECTORY_SEPARATOR.$this->restoreFilesFilename; $destExtract = $this->prodRootDir; if ($res = $this->ZipExtract($filepath, $destExtract)) { $this->next = 'restoreDb'; $this->nextDesc = $this->l('Files restored. Now restoring database ...'); // get new file list $this->nextQuickInfo[] = $this->l('Files restored.'); // once it's restored, do not delete the archive file. This has to be done manually // and we do not empty the var, to avoid infinite loop. return true; } else { $this->next = "error"; $this->nextDesc = sprintf($this->l('unable to extract %1$s into %2$s .'), $filepath, $destExtract); return false; } return true; } /** * try to restore db backup file * @return type : hey , what you expect ? well mysql errors array ..... * @TODO : maybe this could be in the Backup class */ public function ajaxProcessRestoreDb() { $this->nextParams['dbStep'] = $this->currentParams['dbStep']; $start_time = time(); $db = $this->db(); // deal with the next files stored in restoreDbFilenames if (is_array($this->restoreDbFilenames) && count($this->restoreDbFilenames) > 0) { $currentDbFilename = array_shift($this->restoreDbFilenames); if (!preg_match('#auto-backupdb_([0-9]{6})_#', $currentDbFilename, $match)) { $this->next = 'error'; $this->nextDesc = $this->l(sprintf('%s : File format does not match', $currentDbFilename)); return false; } $this->nextParams['dbStep'] = $match[1]; $backupdb_path = $this->autoupgradePath.DIRECTORY_SEPARATOR.$this->restoreName; $dot_pos = strrpos($currentDbFilename, '.'); $fileext = substr($currentDbFilename, $dot_pos+1); $requests = array(); $errors = array(); $content = ''; switch ($fileext) { case 'bz': case 'bz2': $this->nextQuickInfo[] = 'opening backup db in bz mode'; if ($fp = bzopen($backupdb_path.DIRECTORY_SEPARATOR.$currentDbFilename, 'r')) { while(!feof($fp)) $content .= bzread($fp, 4096); } else die("error when trying to open in bzmode"); break; case 'gz': $this->nextQuickInfo[] = 'opening backup db in gz mode'; if ($fp = gzopen($backupdb_path.DIRECTORY_SEPARATOR.$currentDbFilename, 'r')) { while(!feof($fp)) $content .= gzread($fp, 4096); gzclose($fp); } break; // default means sql ? default : $this->nextQuickInfo[] = 'opening backup db in txt mode'; if ($fp = fopen($backupdb_path.DIRECTORY_SEPARATOR.$currentDbFilename, 'r')) { while(!feof($fp)) $content .= fread($fp, 4096); fclose($fp); } } $currentDbFilename = ''; if ($content == '') { $this->nextQuickInfo[] = $this->l('database backup is empty'); $this->next = 'rollback'; return false; } // preg_match_all is better than preg_split (what is used in do Upgrade.php) // This way we avoid extra blank lines // option s (PCRE_DOTALL) added $listQuery = preg_split('/;[\n\r]+/Usm', $content); unset($content); // @TODO : drop all old tables (created in upgrade) $all_tables = $db->executeS('SHOW TABLES LIKE "'._DB_PREFIX_.'%"'); $ignore_stats_table = array(_DB_PREFIX_.'connections', _DB_PREFIX_.'connections_page', _DB_PREFIX_.'connections_source', _DB_PREFIX_.'guest', _DB_PREFIX_.'statssearch'); $drops = array(); foreach ($all_tables as $k => $v) { $table = array_shift($v); if (!in_array($table, $ignore_stats_table)) { $drops['drop table '.$k] = 'DROP TABLE IF EXISTS `'.bqSql($table).'`'; $drops['drop view '.$k] = 'DROP VIEW IF EXISTS `'.bqSql($table).'`'; } } unset($all_tables); $listQuery = array_merge($drops, $listQuery); file_put_contents($this->autoupgradePath.DIRECTORY_SEPARATOR.$this->toRestoreQueryList, serialize($listQuery)); } // handle current backup file if (!isset($listQuery)) if (file_exists($this->autoupgradePath.DIRECTORY_SEPARATOR.$this->toRestoreQueryList)) $listQuery = unserialize(file_get_contents($this->autoupgradePath.DIRECTORY_SEPARATOR.$this->toRestoreQueryList)); else $listQuery = array(); $time_elapsed = time() - $start_time; if (is_array($listQuery) && (sizeof($listQuery) > 0)) { do { if (sizeof($listQuery)<=0) { unlink($this->autoupgradePath.DIRECTORY_SEPARATOR.$this->toRestoreQueryList); $currentDbFilename = ''; if (count($this->restoreDbFilenames) > 0) { $this->stepDone = true; $this->status = 'ok'; $this->next = 'restoreDb'; $this->nextDesc = sprintf($this->l('Database restoration step %s done. %s left) ...'), $this->nextParams['dbStep'], count($this->restoreDbFilenames)); $this->nextQuickInfo[] = sprintf('Database restoration step %s done. %s left) ...', $this->nextParams['dbStep'], count($this->restoreDbFilenames)); return true; } else { $this->stepDone = true; $this->status = 'ok'; $this->next = 'rollbackComplete'; $this->nextDesc = $this->l('Database restoration done.'); $this->nextQuickInfo[] = $this->l('database has been restored.'); return true; } } // filesForBackup already contains all the correct files if (count($listQuery) == 0) continue; $query = array_shift($listQuery); if (!empty($query)) { if (!$db->execute($query)) { if (is_array($listQuery)) $listQuery = array_unshift($listQuery, $query); $this->nextQuickInfo[] = '[SQL ERROR] '.$query.' - '.$db->getMsgError(); $this->next = 'error'; $this->nextDesc = $this->l('error during database restoration'); return false; } else $this->nextQuickInfo[] = '[OK] '.$query; } $time_elapsed = time() - $start_time; } while($time_elapsed < self::$loopRestoreQueryTime); unset($query); $queries_left = count($listQuery); file_put_contents($this->autoupgradePath.DIRECTORY_SEPARATOR.$this->toRestoreQueryList, serialize($listQuery)); unset($listQuery); $this->next = 'restoreDb'; $this->nextDesc = sprintf($this->l('%s queries left for %s...'), $queries_left, $this->nextParams['dbStep']); } else { $this->stepDone = true; $this->status = 'ok'; $this->next = 'rollbackComplete'; $this->nextDesc = $this->l('Database restoration done.'); $this->nextQuickInfo[] = $this->l('database has been restored.'); return true; } return true; } protected function db() { require_once('db/Db.php'); eval('abstract class Db extends DbCore{}'); require_once('db/MySQL.php'); eval('class MySQL extends MySQLCore{}'); require_once('db/DbMySQLi.php'); eval('class DbMySQLi extends DbMySQLiCore{}'); require_once('db/DbPDO.php'); eval('class DbPDO extends DbPDOCore{}'); require_once('db/DbQuery.php'); eval('class DbQuery extends DbQueryCore{}'); require_once('alias.php'); return Db::getInstance(); } public function ajaxProcessBackupDb() { $this->next = 'backupDb'; $this->nextParams = $this->currentParams; $start_time = time(); $this->db(); $psBackupAll = false; $psBackupDropTable = true; if (!$psBackupAll) { $ignore_stats_table = array(_DB_PREFIX_.'connections', _DB_PREFIX_.'connections_page', _DB_PREFIX_.'connections_source', _DB_PREFIX_.'guest', _DB_PREFIX_.'statssearch'); } else $ignore_stats_table = array(); // INIT LOOP if (!file_exists($this->autoupgradePath.DIRECTORY_SEPARATOR.$this->toBackupDbList)) { if (!is_dir($this->autoupgradePath.DIRECTORY_SEPARATOR.$this->backupName)) mkdir($this->autoupgradePath.DIRECTORY_SEPARATOR.$this->backupName, 0755); $this->nextParams['dbStep'] = 0; $tablesToBackup = Db::getInstance()->executeS('SHOW TABLES LIKE "'._DB_PREFIX_.'%"'); file_put_contents($this->autoupgradePath.DIRECTORY_SEPARATOR.$this->toBackupDbList, serialize($tablesToBackup)); } if (!isset($tablesToBackup)) $tablesToBackup = unserialize(file_get_contents($this->autoupgradePath.DIRECTORY_SEPARATOR.$this->toBackupDbList)); $found = 0; $views = ''; // MAIN BACKUP LOOP // $written = 0; do { if (!empty($this->nextParams['backup_table'])) { // only insert (schema already done) $table = $this->nextParams['backup_table']; $lines = $this->nextParams['backup_lines']; } else { if (count($tablesToBackup) == 0) break; $table = current(array_shift($tablesToBackup)); $this->nextParams['backup_loop_limit'] = 0; } if ($written == 0 || $written > 4194304) { // new file, new step $written = 0; if (isset($fp)) fclose($fp); $backupfile = $this->autoupgradePath.DIRECTORY_SEPARATOR.$this->backupName.DIRECTORY_SEPARATOR.$this->backupDbFilename; $backupfile = preg_replace("#_XXXXXX_#", '_'.str_pad($this->nextParams['dbStep'], 6, '0', STR_PAD_LEFT).'_', $backupfile); // start init file // Figure out what compression is available and open the file if (function_exists('bzopen')) { $backupfile .= '.bz2'; $fp = bzopen($backupfile, 'w'); } else if (function_exists('gzopen')) { $backupfile .= '.gz'; $fp = @gzopen($backupfile, 'w'); } else $fp = @fopen($backupfile, 'w'); if ($fp === false) { $this->nextQuickInfo[] = sprintf($this->l('Unable to create backup db file %s'), addslashes($backupfile)); $this->next = 'error'; $this->nextDesc = $this->l('Error during database backup.'); return false; } $written += fwrite($fp, '/* Backup ' . $this->nextParams['dbStep'] . ' for ' . Tools::getHttpHost(false, false) . __PS_BASE_URI__ . "\n * at " . date('r') . "\n */\n"); $written += fwrite($fp, "\n".'SET NAMES \'utf8\';'."\n\n"); // end init file } // Skip tables which do not start with _DB_PREFIX_ if (strlen($table) < strlen(_DB_PREFIX_) || strncmp($table, _DB_PREFIX_, strlen(_DB_PREFIX_)) != 0) continue; // start schema : drop & create table only if (empty($this->currentParams['backup_table'])) { // Export the table schema $schema = Db::getInstance()->executeS('SHOW CREATE TABLE `' . $table . '`'); if (count($schema) != 1 || !((isset($schema[0]['Table']) && isset($schema[0]['Create Table'])) || (isset($schema[0]['View']) && isset($schema[0]['Create View'])))) { fclose($fp); unlink($backupfile); $this->nextQuickInfo[] = sprintf($this->l('An error occurred while backing up. Unable to obtain the schema of %s'), $table); $this->next = 'error'; $this->nextDesc = $this->l('Error during database backup.'); return false; } // case view if (isset($schema[0]['View'])) { $views .= '/* Scheme for view' . $schema[0]['View'] . " */\n"; if ($psBackupDropTable) { // If some *upgrade* transform a table in a view, drop both just in case $views .= 'DROP VIEW IF EXISTS `'.$schema[0]['View'].'`;'."\n"; $views .= 'DROP TABLE IF EXISTS `'.$schema[0]['View'].'`;'."\n"; } $views .= preg_replace('#DEFINER[^ ]* #', ' ', $schema[0]['Create View']).";\n\n"; $written += fwrite($fp, "\n".$views); } // case table elseif (isset($schema[0]['Table'])) { // Case common table $written += fwrite($fp, '/* Scheme for table ' . $schema[0]['Table'] . " */\n"); if ($psBackupDropTable && !in_array($schema[0]['Table'], $ignore_stats_table)) { // If some *upgrade* transform a table in a view, drop both just in case $written += fwrite($fp, 'DROP VIEW IF EXISTS `'.$schema[0]['Table'].'`;'."\n"); $written += fwrite($fp, 'DROP TABLE IF EXISTS `'.$schema[0]['Table'].'`;'."\n"); // CREATE TABLE $written += fwrite($fp, $schema[0]['Create Table'] . ";\n\n"); } // schema created, now we need to create the missing vars $this->nextParams['backup_table'] = $table; $lines = $this->nextParams['backup_lines'] = explode("\n", $schema[0]['Create Table']); } } // end of schema // POPULATE TABLE if (!in_array($table, $ignore_stats_table)) { do { $backup_loop_limit = $this->nextParams['backup_loop_limit']; $data = Db::getInstance()->executeS('SELECT * FROM `'.$table.'` LIMIT '.(int)$backup_loop_limit.',200', false); $this->nextParams['backup_loop_limit'] += 200; $sizeof = DB::getInstance()->numRows(); if ($data && ($sizeof > 0)) { // Export the table data $written += fwrite($fp, 'INSERT INTO `'.$table."` VALUES\n"); $i = 1; while ($row = DB::getInstance()->nextRow($data)) { // this starts a row $s = '('; foreach ($row AS $field => $value) { $tmp = "'" . Db::getInstance()->escape($value) . "',"; if ($tmp != "'',") $s .= $tmp; else { foreach($lines AS $line) if (strpos($line, '`'.$field.'`') !== false) { if (preg_match('/(.*NOT NULL.*)/Ui', $line)) $s .= "'',"; else $s .= 'NULL,'; break; } } } $s = rtrim($s, ','); if ($i < $sizeof) $s .= "),\n"; else $s .= ");\n"; $written += fwrite($fp, $s); ++$i; } $time_elapsed = time() - $start_time; } else break; } while(($time_elapsed < self::$loopBackupDbTime) || ($written < 4194304)); } $found++; unset($this->nextParams['backup_table']); $time_elapsed = time() - $start_time; $this->nextQuickInfo[] = sprintf($this->l('%1$s table has been saved.'), $table); } while(($time_elapsed < self::$loopBackupDbTime) || ($written < 4194304)); if (isset($fp)) { $this->nextParams['dbStep']++; fclose($fp); unset($fp); } file_put_contents($this->autoupgradePath.DIRECTORY_SEPARATOR.$this->toBackupDbList, serialize($tablesToBackup)); if (count($tablesToBackup) > 0){ $this->nextQuickInfo[] = sprintf($this->l('%1$s tables has been saved.'), $found); $this->next = 'backupDb'; $this->stepDone = false; $this->nextDesc = sprintf($this->l('database backup : %s table(s) left ...'), count($tablesToBackup)); $this->nextQuickInfo[] = sprintf('database backup : %s table(s) left ...', count($tablesToBackup)); return true; } if ($found == 0) { if (isset($backupfile)) unlink($backupfile); $this->nextQuickInfo[] = $this->l('No valid tables were found to backup. Backup cancelled.'); $this->next = 'error'; $this->nextDesc = $this->l('Error during database backup.'); return false; } else { $this->nextQuickInfo[] = sprintf($this->l('%1$s tables has been saved.'), $found); $this->next = 'upgradeFiles'; $this->stepDone = true; $this->nextDesc = sprintf($this->l('database backup done in %s. Now upgrading files ...'), $this->backupName); return true; } // for backup db, use autoupgrade/backup directory // @TODO : autoupgrade must not be static // maybe for big tables we should save them in more than one file ? // if an error occur, we assume the file is not saved } public function ajaxProcessBackupFiles() { $this->nextParams = $this->currentParams; $this->stepDone = false; if (empty($this->backupFilesFilename)) { $this->next = 'error'; $this->nextDesc = $this->l('error during backupFiles'); $this->nextQuickInfo[] = '[ERROR] backupFiles filename has not been set'; return false; } if (empty($this->nextParams['filesForBackup'])) { // @todo : only add files and dir listed in "originalPrestashopVersion" list $filesToBackup = $this->_listFilesInDir($this->prodRootDir); file_put_contents($this->autoupgradePath.DIRECTORY_SEPARATOR.$this->toBackupFileList, serialize($filesToBackup)); $this->nextQuickInfo[] = sprintf($this->l('%s Files to backup.'), sizeof($this->toBackupFileList)); $this->nextParams['filesForBackup'] = $this->toBackupFileList; // delete old backup, create new if (!empty($this->backupFilesFilename) && file_exists($this->autoupgradePath.DIRECTORY_SEPARATOR.$this->backupFilesFilename)) unlink($this->autoupgradePath.DIRECTORY_SEPARATOR.$this->backupFilesFilename); $this->nextQuickInfo[] = sprintf($this->l('backup files initialized in %s'), $this->backupFilesFilename); } $filesToBackup = unserialize(file_get_contents($this->autoupgradePath.DIRECTORY_SEPARATOR.$this->toBackupFileList)); ///////////////////// $this->next = 'backupFiles'; // @TODO : display percent instead of this $this->nextDesc = sprintf($this->l('Backup files in progress. %s files left'), sizeof($filesToBackup)); if (is_array($filesToBackup)) { // @TODO later // 1) calculate crc32 or md5 of next file // 2) use the provided xml with crc32 calculated from previous versions ? // or simply use the latest dir ? //$current = crc32(file_get_contents($file)); //$file = $this->nextParams['filesForBackup'][0]; //$latestFile = str_replace(_PS_ROOT_DIR_,$this->latestRootDir,$file); // if (file_exists($latestFile)) // $latest = crc32($latestFile); // else // $latest = ''; if (!self::$force_pclZip && class_exists('ZipArchive', false)) { $zip_archive = true; $zip = new ZipArchive(); $zip->open($this->autoupgradePath.DIRECTORY_SEPARATOR.$this->backupFilesFilename, ZIPARCHIVE::CREATE); } else { $zip_archive = false; // pclzip can be already loaded (server configuration) if (!class_exists('PclZip',false)) require_once(dirname(__FILE__).'/pclzip.lib.php'); $zip = new PclZip($this->autoupgradePath.DIRECTORY_SEPARATOR.$this->backupFilesFilename); } if ($zip) { $this->next = 'backupFiles'; // @TODO all in one time will be probably too long // 1000 ok during test, but 10 by 10 to be sure $this->stepDone = false; // @TODO min(self::$loopBackupFiles, sizeof()) for ($i=0;$istepDone = true; $this->status = 'ok'; $this->next = 'backupDb'; $this->nextDesc = $this->l('All files saved. Now backup Database'); $this->nextQuickInfo[] = $this->l('all files have been added to archive.'); break; } // filesForBackup already contains all the correct files $file = array_shift($filesToBackup); $archiveFilename = ltrim(str_replace($this->prodRootDir, '', $file), DIRECTORY_SEPARATOR); if ($zip_archive) { $added_to_zip = $zip->addFile($file, $archiveFilename); if ($added_to_zip) $this->nextQuickInfo[] = sprintf($this->l('%1$s added to archive. %2$s left.'), $archiveFilename, sizeof($filesToBackup)); else { // if an error occur, it's more safe to delete the corrupted backup $zip->close(); if (file_exists($this->autoupgradePath.DIRECTORY_SEPARATOR.$this->backupFilesFilename)) unlink($this->autoupgradePath.DIRECTORY_SEPARATOR.$this->backupFilesFilename); $this->next = 'error'; $this->nextDesc = sprintf($this->l('error when trying to add %1$s to archive %2$s.'),$archiveFilename, $backupFilePath); break; } } else { $files_to_add[] = $file; $this->nextQuickInfo[] = sprintf($this->l('%1$s added to archive. %2$s left.'), $archiveFilename, sizeof($filesToBackup)); } } if ($zip_archive) $zip->close(); else { $added_to_zip = $zip->add($files_to_add, PCLZIP_OPT_REMOVE_PATH, $this->prodRootDir); $zip->privCloseFd(); if (!$added_to_zip) { $this->nextQuickInfo[] = '[ERROR] error on backup using pclzip : '.$zip->errorInfo(true); $this->next = 'error'; } } file_put_contents($this->autoupgradePath.DIRECTORY_SEPARATOR.$this->toBackupFileList,serialize($filesToBackup)); return true; } else{ $this->next = 'error'; $this->nextDesc = $this->l('unable to open archive'); return false; } } else { $this->stepDone = true; $this->next = 'backupDb'; $this->nextDesc = 'All files saved. Now backup Database'; return true; } // 4) save for display. } private function _removeOneSample($removeList) { if (is_array($removeList) AND sizeof($removeList)>0) { if (file_exists($removeList[0]) AND unlink($removeList[0])) { $item = str_replace($this->prodRootDir, '', array_shift($removeList)); $this->next = 'removeSamples'; $this->nextParams['removeList'] = $removeList; $this->nextQuickInfo[] = sprintf($this->l('%1$s removed. %2$s items left'), $item, sizeof($removeList)); } else { $this->next = 'error'; $this->nextParams['removeList'] = $removeList; $this->nextQuickInfo[] = sprintf($this->l('error when removing %1$s, %2$s items left'), $removeList[0], sizeof($removeList)); return false; } } return true; } /** * Remove all sample files. * @TODO : this should be handled with the md5 xml files with a "is_sample" param * * @return boolean true if succeed */ public function ajaxProcessRemoveSamples() { $this->stepDone = false; // all images from img dir exept admin ? // all images like logo, favicon, ?. // all custom image from modules ? // all custom image from theme ? if (!isset($this->currentParams['removeList'])) { $this->_listSampleFiles($this->autoupgradePath.'/latest/prestashop/img', '.jpg'); $this->_listSampleFiles($this->autoupgradePath.'/latest/prestashop/img', '.gif'); $this->_listSampleFiles($this->autoupgradePath.'/latest/prestashop/img', 'favicon.ico'); $this->_listSampleFiles($this->autoupgradePath.'/latest/prestashop/modules/editorial', 'homepage_logo.jpg'); // remove all override present in the archive $this->_listSampleFiles($this->autoupgradePath.'/latest/prestashop/override', '.php'); // @TODO handle this bad thing $this->nextQuickInfo[] = sprintf($this->l('Starting to remove %1$s sample files'), sizeof($this->sampleFileList)); $this->nextParams['removeList'] = $this->sampleFileList; } // @TODO : removing @, adding if file_exists $resRemove = true; for($i=0;$inextParams['removeList']) <= 0 ) { $this->stepDone = true; $this->next = 'backupFiles'; $this->nextDesc = $this->l('All sample files removed. Now backup files.'); // break the loop, all sample already removed return true; } $resRemove &= $this->_removeOneSample($this->nextParams['removeList']); if (!$resRemove) break; } return $resRemove; } public function ajaxProcessSvnCheckout() { $this->nextParams = $this->currentParams; if ($this->useSvn){ $dest = $this->autoupgradePath . DIRECTORY_SEPARATOR . $this->svnDir; $svnStatus = svn_status($dest); if (is_array($svnStatus)) { if (sizeof($svnStatus) == 0) { $this->next = 'svnExport'; $this->nextDesc = sprintf($this->l('working copy already %s up-to-date. now exporting it into latest dir'),$dest); } else { // we assume no modification has been done // @TODO a svn revert ? if ($svnUpdate = svn_update($dest)) { $this->next = 'svnExport'; $this->nextDesc = sprintf($this->l('SVN Update done for working copy %s . now exporting it into latest...'),$dest); } } } else { // no valid status found // @TODO : is 0777 good idea ? if (!file_exists($dest)) if (!@mkdir($dest,0777)) { $this->next = 'error'; $this->nextDesc = sprintf($this->l('unable to create directory %s'),$dest); return false; } if (svn_checkout($this->svn_link, $dest)) { $this->next = 'svnExport'; $this->nextDesc = sprintf($this->l('SVN Checkout done from %s . now exporting it into latest...'),$this->svn_link); return true; } else { $this->next = 'error'; $this->nextDesc = $this->l('SVN Checkout error...'); } } } else { $this->next = 'error'; $this->nextDesc = $this->l('not allowed to use svn'); } } public function ajaxProcessDownload() { if (@ini_get('allow_url_fopen')) { if (!is_object($this->upgrader)) $this->upgrader = new Upgrader(); $res = $this->upgrader->downloadLast($this->autoupgradePath,$this->destDownloadFilename); if ($res){ if (md5_file(realpath($this->autoupgradePath).DIRECTORY_SEPARATOR.$this->destDownloadFilename) == $this->upgrader->md5 ) { $this->nextQuickInfo[] = 'Download complete.'; $this->next = 'unzip'; $this->nextDesc = $this->l('Download complete. Now extracting'); } else { $this->nextQuickInfo[] = 'Download complete but md5sum does not match.'; $this->next = 'error'; $this->nextDesc = $this->l('Download complete but md5sum does not match. Operation aborted.'); } } else { $this->nextQuickInfo[] = 'Error during download'; $this->next = 'error'; $this->nextDesc = $this->l('Error during download'); } } else { $this->nextQuickInfo[] = 'you need allow_url_fopen for automatic download.'; // @TODO : ftp mode $this->next = 'error'; $this->nextDesc = sprintf($this->l('you need allow_url_fopen for automatic download. You can also manually upload it in %s'),$this->autoupgradePath.$this->destDownloadFilename); } } public function buildAjaxResult() { $return['error'] = $this->error; $return['stepDone'] = $this->stepDone; $return['next'] = $this->next; $return['status'] = $this->next == 'error' ? 'error' : 'ok'; $return['nextDesc'] = $this->nextDesc; $return['nextParams']['dbStep'] = 0; foreach($this->ajaxParams as $v) if(property_exists($this,$v)) $this->nextParams[$v] = $this->$v; else $this->nextQuickInfo[] = sprintf('[WARNING] property %s is missing', $v); $return['nextParams'] = $this->nextParams; if (!isset($return['nextParams']['dbStep'])) $return['nextParams']['dbStep'] = 0; $return['nextParams']['typeResult'] = $this->nextResponseType; $return['nextQuickInfo'] = $this->nextQuickInfo; return Tools14::jsonEncode($return); } public function ajaxPreProcess() { /* PrestaShop demo mode */ if (defined('_PS_MODE_DEMO_') && _PS_MODE_DEMO_) return; /* PrestaShop demo mode*/ if (!empty($_POST['responseType']) AND $_POST['responseType'] == 'json') header('Content-Type: application/json'); if(!empty($_POST['action'])) { $action = $_POST['action']; if (isset(self::$skipAction[$action])) { $this->next = self::$skipAction[$action]; $this->nextDesc = sprintf($this->l('action %s skipped'),$action); $this->nextQuickInfo[] = sprintf($this->l('action %s skipped'),$action); unset($_POST['action']); } else if (!method_exists(get_class($this), 'ajaxProcess'.$action)) { $this->nextDesc = sprintf($this->l('action "%1$s" not found'), $action); $this->next = 'error'; $this->error = '1'; } } if (!method_exists('Tools', 'apacheModExists') || Tools::apacheModExists('evasive')) sleep(1); } private function _getJsErrorMsgs() { $INSTALL_VERSION = $this->upgrader->version_num; $ret = ' var txtError = new Array(); txtError[0] = "'.$this->l('Required field').'"; txtError[1] = "'.$this->l('Too long!').'"; txtError[2] = "'.$this->l('Fields are different!').'"; txtError[3] = "'.$this->l('This email adress is wrong!').'"; txtError[4] = "'.$this->l('Impossible to send the email!').'"; txtError[5] = "'.$this->l('Cannot create settings file, if /config/settings.inc.php exists, please give the public write permissions to this file, else please create a file named settings.inc.php in config directory.').'"; txtError[6] = "'.$this->l('Cannot write settings file, please create a file named settings.inc.php in config directory.').'"; txtError[7] = "'.$this->l('Impossible to upload the file!').'"; txtError[8] = "'.$this->l('Data integrity is not valided. Hack attempt?').'"; txtError[9] = "'.$this->l('Impossible to read the content of a MySQL content file.').'"; txtError[10] = "'.$this->l('Impossible the access the a MySQL content file.').'"; txtError[11] = "'.$this->l('Error while inserting data in the database:').'"; txtError[12] = "'.$this->l('The password is incorrect (alphanumeric string at least 8 characters).').'"; txtError[14] = "'.$this->l('A Prestashop database already exists, please drop it or change the prefix.').'"; txtError[15] = "'.$this->l('This is not a valid file name.').'"; txtError[16] = "'.$this->l('This is not a valid image file.').'"; txtError[17] = "'.$this->l('Error while creating the /config/settings.inc.php file.').'"; txtError[18] = "'.$this->l('Error:').'"; txtError[19] = "'.$this->l('This PrestaShop database already exists. Please revalidate your authentication informations to the database.').'"; txtError[22] = "'.$this->l('An error occurred while resizing the picture.').'"; txtError[23] = "'.$this->l('Database connection is available!').'"; txtError[24] = "'.$this->l('Database Server is available but database is not found').'"; txtError[25] = "'.$this->l('Database Server is not found. Please verify the login, password and server fields.').'"; txtError[26] = "'.$this->l('An error occurred while sending email, please verify your parameters.').'"; txtError[37] = "'.$this->l('Impossible to write the image /img/logo.jpg. If this image already exists, please delete it.').'"; txtError[38] = "'.$this->l('The uploaded file exceeds the upload_max_filesize directive in php.ini').'"; txtError[39] = "'.$this->l('The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form').'"; txtError[40] = "'.$this->l('The uploaded file was only partially uploaded').'"; txtError[41] = "'.$this->l('No file was uploaded.').'"; txtError[42] = "'.$this->l('Missing a temporary folder').'"; txtError[43] = "'.$this->l('Failed to write file to disk').'"; txtError[44] = "'.$this->l('File upload stopped by extension').'"; txtError[45] = "'.$this->l('Cannot convert your database\'s data to utf-8.').'"; txtError[46] = "'.$this->l('Invalid shop name').'"; txtError[47] = "'.$this->l('Your firstname contains some invalid characters').'"; txtError[48] = "'.$this->l('Your lastname contains some invalid characters').'"; txtError[49] = "'.$this->l('Your database server does not support the utf-8 charset.').'"; txtError[50] = "'.$this->l('Your MySQL server doesn\'t support this engine, please use another one like MyISAM').'"; txtError[51] = "'.$this->l('The file /img/logo.jpg is not writable, please CHMOD 755 this file or CHMOD 777').'"; txtError[52] = "'.$this->l('Invalid catalog mode').'"; txtError[999] = "'.$this->l('No error code available').'"; //upgrader txtError[27] = "'.$this->l('This installer is too old.').'"; txtError[28] = "'.sprintf($this->l('You already have the %s version.'),$INSTALL_VERSION).'"; txtError[29] = "'.$this->l('There is no older version. Did you delete or rename the configsettings.inc.php file?').'"; txtError[30] = "'.$this->l('The config/settings.inc.php file was not found. Did you delete or rename this file?').'"; txtError[31] = "'.$this->l('Cannot find the sql upgrade files. Please verify that the /install/sql/upgrade folder is not empty)').'"; txtError[32] = "'.$this->l('No upgrade is possible.').'"; txtError[33] = "'.$this->l('Error while loading sql upgrade file.').'"; txtError[34] = "'.$this->l('Error while inserting content into the database').'"; txtError[35] = "'.$this->l('Unfortunately,').'"; txtError[36] = "'.$this->l('SQL errors have occurred.').'"; txtError[37] = "'.$this->l('The config/defines.inc.php file was not found. Where did you move it?').'";'; return $ret; } public function displayAjax(){ echo $this->buildAjaxResult(); } protected function getBackupFilesAvailable() { $array = array(); $files = scandir($this->autoupgradePath); foreach($files as $file) if ($file[0] != '.') { if (substr($file, 0, 16) == 'auto-backupfiles') $array[] = preg_replace('#^auto-backupfiles_(.*-[0-9a-f]{1,8})\..*$#', '$1', $file); } return $array; } protected function getBackupDbAvailable() { $array = array(); $files = scandir($this->autoupgradePath); foreach($files as $file) if ($file[0] == 'V' && is_dir($this->autoupgradePath.DIRECTORY_SEPARATOR.$file)) { $array[] = $file; } return $array; } protected function _displayRollbackForm() { $content = ''; $content .= '
'.$this->l('Rollback').'
'; $content .= '

' .$this->l('After upgrading your shop, you can rollback to the previously database and files. Use this function if your theme or an essential module is not working correctly.') .'


'; if (empty($this->backupFilesFilename) && empty($this->backupDbFilename)) $content .= $this->l('No rollback available'); else if (!empty($this->backupFilesFilename) || !empty($this->backupDbFilename)) { $content .= '
'; } $backup_files_list = $this->getBackupFilesAvailable(); $backup_db_list = $this->getBackupDbAvailable(); $backup_available = array_intersect($backup_db_list, $backup_files_list); $content .= '
' .$this->l('backup to use :').''; $content .'
 
'; $content .= '
'; echo $content; } /** this returns fieldset containing the configuration points you need to use autoupgrade * @return string */ private function getCurrentConfiguration() { $current_config = $this->getcheckCurrentConfig(); $content = '
'; $content .= ''.$this->l('Your current configuration').''; $content .= '

'.$this->l('All the following points must be ok in order to allow the upgrade.').'

'.$this->l('Root directory').' : '.$this->prodRootDir.'

'; // module version : checkAutoupgradeLastVersion if ($current_config['module_version_ok']) $srcModuleVersion = '../img/admin/enabled.gif'; else $srcModuleVersion = '../img/admin/disabled.gif'; $content .= ''.$this->l('Module version').' : ' .' '; if($this->lastAutoupgradeVersion) $content .= sprintf($this->l('Your version is up-to-date (%s)'), $this->getModuleVersion(), $this->upgrader->autoupgrade_last_version).'

'; else { $token_modules = Tools14::getAdminTokenLite('AdminModules'); $content .= sprintf($this->l('Module version is outdated ( %1$s ). Please install the last version (%2$s)'), $this->getModuleVersion(), $this->upgrader->autoupgrade_last_version); $content .= '

' .$this->l('Install the latest by clicking "Add from my computer"').'

' ; } // root : getRootWritable() if ($current_config['root_writable']) $srcRootWritable = '../img/admin/enabled.gif'; else $srcRootWritable = '../img/admin/disabled.gif'; $content .= ''.$this->l('Root directory status').' : ' .' ' .($current_config['root_writable']?$this->l('fully writable'):$this->l('not writable recursively')).'

'; // upgrade available : // - upgrader->autoupgrade // - upgrader->need_upgrade // - checkAutoupgradeLastVersion() $content .= ''.$this->l('Upgrade available').' : '; if ($current_config['fopen']) { if ($current_config['need_upgrade']) { if ($current_config['autoupgrade_allowed']) $srcAutoupgrade = '../img/admin/enabled.gif'; else $srcAutoupgrade = '../img/admin/disabled.gif'; $content .= ' ' .($this->upgrader->autoupgrade ?$this->l('This release allows autoupgrade.') :$this->l('This release does not allow autoupgrade')).'

'; } else { $content .= '' .$this->l('You already have the last version.').'

'; } } else $content .= '' .$this->l('please contact your server administrator to enable fopen.').'

'; // shop enabled if ($current_config['shop_deactivated']) { $srcShopStatus = '../img/admin/enabled.gif'; $label = $this->l('Yes'); } else { $srcShopStatus = '../img/admin/disabled.gif'; $label = $this->l('No'); } if (method_exists('Tools','getAdminTokenLite')) $token_preferences = Tools::getAdminTokenLite('AdminPreferences'); else $token_preferences = Tools14::getAdminTokenLite('AdminPreferences'); $content .= ''.$this->l('Shop deactivated').' : '.''.$label.'

'; // for informaiton, display time limit $max_exec_time = ini_get('max_execution_time'); if ($max_exec_time == 0) $srcExecTime = '../img/admin/enabled.gif'; else $srcExecTime = '../img/admin/warning.gif'; $content .= ''.$this->l('PHP time limit').' : '.''.($max_exec_time == 0?$this->l('disabled'):$max_exec_time.' '.$this->l('seconds')).'

'; // configuration done ? if ($current_config['module_configured']) $configurationDone = '../img/admin/enabled.gif'; else $configurationDone = '../img/admin/disabled.gif'; $content .= ''.$this->l('Options chosen').' : ' .' ' .($current_config['module_configured'] ?$this->l('autoupgrade configuration ok') .' - '.$this->l('Modify your options') :$this->l('Please configure autoupgrade options') ).'

'; $content .= '
'; return $content; } public function displayDevTools() { $content = ''; $content .= '
'; $content .= '
'.$this->l('Step').''; $content .= '

'.$this->l('Upgrade steps').'

'; $content .= '
'; $content .= 'download'; $content .= 'unzip'; // unzip in autoupgrade/latest $content .= 'removeSamples'; // remove samples (iWheel images) $content .= 'backupFiles'; // backup files $content .= 'backupDb'; $content .= 'upgradeFiles'; $content .= 'upgradeDb'; $content .= '
'; if (defined('_PS_ALLOW_UPGRADE_UNSTABLE_') AND _PS_ALLOW_UPGRADE_UNSTABLE_ ) { $content .= '

Development tools

'; $content .= 'svnCheckout'; $content .= 'svnUpdate'; $content .= 'svnExport'; $content .= '
'; $content .= '
'; } return $content; } private function _displayUpgraderForm() { global $cookie; $content = ''; $pleaseUpdate = $this->upgrader->checkPSVersion(); $content .= $this->getCurrentConfiguration(); $content .= '
'; $content .= '
'.$this->l('Update').''; $content .= ''.$this->l('PrestaShop Original version').' : '.'
'; $content .= ''.$this->l('Version modifications').' : '.' '; $content .= ''; // smarty2 uses is a warning only; $use_smarty3 = !(Configuration::get('PS_FORCE_SMARTY_2') === '1' || Configuration::get('PS_FORCE_SMARTY_2') === false); if ($use_smarty3) { $srcShopStatus = '../img/admin/enabled.gif'; $label = $this->l('You use Smarty 3'); } else { $srcShopStatus = '../img/admin/warning.gif'; $label = $this->l('Smarty 2 is deprecated in 1.4 and removed maintained in 1.5. You may need to upgrade your current theme or use a new one.'); } // if current version is 1.4, we propose to edit now the configuration if (version_compare(_PS_VERSION_, '1.4.0.0', '>=')) { if (method_exists('Tools','getAdminTokenLite')) $token_preferences = Tools::getAdminTokenLite('AdminPreferences'); else $token_preferences = Tools14::getAdminTokenLite('AdminPreferences'); $content .= '
 
'.$this->l('Smarty 3 Usage').' : '.''.$label; if (version_compare(_PS_VERSION_, '1.4.0.0', '<')) $content .= '
 
'.$this->l('Edit your Smarty configuration').'

'; } $content .= '
 

'.sprintf($this->l('Your current prestashop version : %s '),_PS_VERSION_).'

'; // @TODO : this should be checked when init() $content .= 'information ' .$this->l('Latest Prestashop version available is:') .' '.$this->upgrader->version_name.' ('. $this->upgrader->version_num.')

'; if ($this->upgrader->need_upgrade) { if($this->configOk()) { $content .= '

'.$this->l('Upgrade PrestaShop now !').'

'; $content .= ''.sprintf($this->l('PrestaShop will be downloaded from %s'), $this->upgrader->link).'
'; $content .= ''.$this->l('see CHANGELOG').''; } else $content .= '

'.$this->displayWarning($this->l('Your current configuration does not allow upgrade.')).'

'; } else $content .= ''.$this->l('Your shop is already up to date.').' '; $content .= '
'; $content .= '


'.sprintf($this->l('last datetime check : %s '),date('Y-m-d H:i:s',Configuration::get('PS_LAST_VERSION_CHECK'))).' '.$this->l('Please click to refresh').'
'; $content .= ''; $content .= '
'; if (defined('_PS_MODE_DEV_') AND _PS_MODE_DEV_ AND $this->manualMode) $content .= $this->displayDevTools(); $content .='
 
'; // for upgradeDb $content .= '

'; $content .= '

'; $content .= '
'; // information to keep will be in #infoStep // temporary infoUpdate will be in #tmpInformation $content .= ''; echo $content; } public function display() { // We need jquery 1.6 for json // do we ? echo ' '; $this->createCustomToken(); /* PrestaShop demo mode */ if (defined('_PS_MODE_DEMO_') && _PS_MODE_DEMO_) { echo '
'.$this->l('This functionnality has been disabled.').'
'; return; } if (!file_exists($this->autoupgradePath.DIRECTORY_SEPARATOR.'ajax-upgradetab.php')) { echo '
'.' [TECHNICAL ERROR] '.$this->l('ajax-upgradetab.php is missing. please reinstall or reset the module').'
'; return false; } /* PrestaShop demo mode*/ // in order to not use Tools class if(isset($_GET['refreshCurrentVersion'])) { $upgrader = new Upgrader(); $upgrader->checkPSVersion(true); // delete the potential xml files we saved in config/xml (from last release and from current) $upgrader->clearXmlMd5File(_PS_VERSION_); $upgrader->clearXmlMd5File($upgrader->version_num); $this->upgrader = $upgrader; } echo ''; $this->displayWarning($this->l('This function is experimental. It\'s highly recommended to make a backup of your files and database before starting the upgrade process.')); global $currentIndex; // update['name'] = version name // update['num'] = only the version // update['link'] = download link // @TODO if ($this->useSvn) echo '

'.$this->l('Unstable upgrade').'

'.$this->l('Your current configuration indicate you want to upgrade your system from the unstable development branch, with no version number. If you upgrade, you will not be able to follow the official release process anymore').'.

'; $this->_displayUpgraderForm(); echo '
'; $this->_displayRollbackForm(); echo '
'; $this->_displayForm('autoUpgradeOptions',$this->_fieldsAutoUpgrade,''.$this->l('Options').'', '','prefs'); // @todo manual upload with a form echo ''; echo ''; } private function _getJsInit() { global $currentIndex, $cookie; $js = ''; if (method_exists('Tools','getAdminTokenLite')) $token_preferences = Tools::getAdminTokenLite('AdminPreferences'); else $token_preferences = Tools14::getAdminTokenLite('AdminPreferences'); $js .= ' function ucFirst(str) { if (str.length > 0) { return str[0].toUpperCase() + str.substring(1); } else { return str; } } function cleanInfo(){ $("#infoStep").html("reset
"); } function updateInfoStep(msg){ if (msg) { $("#infoStep").html(msg); $("#infoStep").attr({ scrollTop: $("#infoStep").attr("scrollHeight") }); } } function addQuickInfo(arrQuickInfo){ if (arrQuickInfo) { $("#quickInfo").show(); for(i=0;i"); // Note : jquery 1.6 make uses of prop() instead of attr() $("#quickInfo").prop({ scrollTop: $("#quickInfo").prop("scrollHeight") },1); } }'; if ($this->manualMode) $js .= 'var manualMode = true;'; else $js .= 'var manualMode = false;'; // relative admin dir $adminDir = trim(str_replace($this->prodRootDir, '', $this->adminDir), DIRECTORY_SEPARATOR); $js .= ' var firstTimeParams = '.$this->buildAjaxResult().'; firstTimeParams = firstTimeParams.nextParams; firstTimeParams.firstTime = "1"; // js initialization : prepare upgrade and rollback buttons $(document).ready(function(){ $(".upgradestep").click(function(e) { e.preventDefault(); // $.scrollTo("#options") }); // set timeout to 5 minutes (download can be long) $.ajaxSetup({timeout:300000}); // prepare available button here, without params ? prepareNextButton("#upgradeNow",firstTimeParams); /** * reset rollbackParams js array (used to init rollback button) */ $("select[name=restoreName]").change(function(){ $(this).next().remove(); // show delete button if the value is not 0 if($(this).val() != 0) { $(this).after("id_employee) .'&deletebackup&name="+$(this).val()+"\">' .''.$this->l('Delete').'"); $(this).next().click(function(e){ if (!confirm("'.$this->l('Are you sure you want to delete this backup ?').'")) e.preventDefault(); }); } if ($("select[name=restoreName]").val() != 0) { $("#rollback").removeAttr("disabled"); rollbackParams = jQuery.extend(true, {}, firstTimeParams); delete rollbackParams.backupName; delete rollbackParams.backupFilesFilename; delete rollbackParams.backupDbFilename; delete rollbackParams.restoreFilesFilename; delete rollbackParams.restoreDbFilenames; // init new name to backup rollbackParams.restoreName = $("select[name=restoreName]").val(); prepareNextButton("#rollback", rollbackParams); // Note : theses buttons have been removed. // they will be available in a future release (when DEV_MODE and MANUAL_MODE enabled) // prepareNextButton("#restoreDb", rollbackParams); // prepareNextButton("#restoreFiles", rollbackParams); } else $("#rollback").attr("disabled", "disabled"); }); $("select[name=restoreName]").change(); }); // reuse previousParams, and handle xml returns to calculate next step // (and the correct next param array) // a case has to be defined for each requests that returns xml function afterUpgradeNow(params) { $("#upgradeNow").unbind(); $("#upgradeNow").replaceWith("'.$this->l('Upgrading PrestaShop').' ..."); } function afterUpgradeComplete(params) { $("#pleaseWait").hide(); $("#dbResultCheck") .addClass("ok") .removeClass("fail") .html("

'.$this->l('upgrade complete. Please check your front-office theme is functionnal (try to make an order, check theme)').'

") .show("slow") .append("'.$this->l('activate your shop here').'"); $("#dbCreateResultCheck") .hide("slow"); $("#infoStep").html("

'.$this->l('Upgrade Complete !').'

"); } function afterRollbackComplete(params) { $("#rollback").attr("disabled", "disabled"); $($("select[name=restoreName]").children()[0]) .attr("selected", "selected"); $(".button-autoupgrade").html("'.$this->l('Restoration complete.').'"); } function afterRollbackComplete(params) { $("#pleaseWait").hide(); $("#dbResultCheck") .addClass("ok") .removeClass("fail") .html("

'.$this->l('restoration complete.').'

") .show("slow") .append("'.$this->l('activate your shop here').'"); $("#dbCreateResultCheck") .hide("slow"); $("#infoStep").html("

'.$this->l('Restoration Complete.').'

"); } function afterRestoreDb(params) { // $("#restoreBackupContainer").hide(); } function afterRestoreFiles(params) { // $("#restoreFilesContainer").hide(); } function afterBackupFiles(params) { if (params.stepDone) { } } /** * afterBackupDb display the button * */ function afterBackupDb(params) { if (params.stepDone) { $("#restoreBackupContainer").show(); $("select[name=restoreName]").children().removeAttr("selected"); $("select[name=restoreName]") .append("") } } function call_function(func){ this[func].apply(this, Array.prototype.slice.call(arguments, 1)); } function doAjaxRequest(action, nextParams){ var _PS_MODE_DEV_; if (_PS_MODE_DEV_) addQuickInfo(["[DEV] ajax request : "+action]); $("#pleaseWait").show(); req = $.ajax({ type:"POST", url : "'. __PS_BASE_URI__.$adminDir.'/autoupgrade/ajax-upgradetab.php'.'", async: true, data : { dir:"'.$adminDir.'", ajaxMode : "1", token : "'.$this->token.'", tab : "AdminSelfUpgrade", action : action, params : nextParams }, success : function(res,textStatus,jqXHR) { $("#pleaseWait").hide(); try{ res = $.parseJSON(res); addQuickInfo(res.nextQuickInfo); currentParams = res.nextParams; if (res.status == "ok") { $("#"+action).addClass("done"); if (res.stepDone) $("#"+action).addClass("stepok"); // if a function "after[action name]" exists, it should be called now. // This is used for enabling restore buttons for example funcName = "after"+ucFirst(action); if (typeof funcName == "string" && eval("typeof " + funcName) == "function") call_function(funcName, currentParams); handleSuccess(res); } else { // display progression $("#"+action).addClass("done"); $("#"+action).addClass("steperror"); if (action != "rollback" && action != "rollbackComplete" && action != "restoreFiles" && action != "restoreDb" && action != "rollback" && action != "noRollbackFound" ) handleError(res); else alert("[TECHNICAL ERROR] Error detected during ["+action+"]."); } } catch(e){ res = {status : "error"}; alert("[TECHNICAL ERROR] Error detected during ["+action+"]."); } }, error: function(res, textStatus, jqXHR) { $("#pleaseWait").hide(); if (textStatus == "timeout" && action == "download") { updateInfoStep("'.$this->l('Your server cannot download the file. Please upload it first by ftp in your admin/autoupgrade directory').'"); } else if (textStatus == "timeout") updateInfoStep("[Server Error] Timeout:'.$this->l('The request excessed the max_time_limit. Please change your server configuration.').'"); { updateInfoStep("[Server Error] Status message : " + textStatus); } } }); }; /** * prepareNextButton make the button button_selector available, and update the nextParams values * * @param button_selector $button_selector * @param nextParams $nextParams * @return void */ function prepareNextButton(button_selector, nextParams) { $(button_selector).unbind(); $(button_selector).click(function(e){ e.preventDefault(); $("#currentlyProcessing").show(); '; $js .= ' action = button_selector.substr(1); res = doAjaxRequest(action, nextParams); }); } /** * handleSuccess * res = {error:, next:, nextDesc:, nextParams:, nextQuickInfo:,status:"ok"} * @param res $res * @return void */ function handleSuccess(res) { updateInfoStep(res.nextDesc); if (res.next != "") { $("#"+res.next).addClass("nextStep"); if (manualMode) { prepareNextButton("#"+res.next,res.nextParams); alert("manually go to "+res.next+" button "); } else { // 1) instead of click(), call a function. doAjaxRequest(res.next,res.nextParams); // 2) remove all step link (or show them only in dev mode) // 3) when steps link displayed, they should change color when passed if they are visible } } else { // Way To Go, end of upgrade process addQuickInfo(["End of process"]); } } // res = {nextParams, NextDesc} function handleError(res) { // display error message in the main process thing updateInfoStep(res.nextDesc); // In case the rollback button has been deactivated, just re-enable it $("#rollback").removeAttr("disabled"); $(".button-autoupgrade").html("'.$this->l('Operation cancelled. Restoration in progress ...').'"); doAjaxRequest("rollback",res.nextParams); } '; // ajax to check md5 files $js .= 'function addModifiedFileList(title, fileList, css_class, container) { subList = $("
    "); $(fileList).each(function(k,v){ $(subList).append("
  • "+v+"
  • "); }); $(container).append("

    "+title+" (" + fileList.length + ")

    "); $(container).append(subList); $(container).append("
    "); }'; if(!file_exists($this->autoupgradePath.DIRECTORY_SEPARATOR.'ajax-upgradetab.php')) $js .= '$(document).ready(function(){ $("#checkPrestaShopFilesVersion").html(" [TECHNICAL ERROR] ajax-upgradetab.php '.$this->l('is missing. please reinstall the module').'"); })'; else $js .= ' function isJsonString(str) { try { JSON.parse(str); } catch (e) { return false; } return true; } $(document).ready(function(){ $.ajax({ type:"POST", url : "'. __PS_BASE_URI__ . $adminDir.'/autoupgrade/ajax-upgradetab.php", async: true, data : { dir:"'.$adminDir.'", token : "'.$this->token.'", tab : "'.get_class($this).'", action : "checkFilesVersion", ajaxMode : "1", params : {} }, success : function(res,textStatus,jqXHR) { if (isJsonString(res)) res = $.parseJSON(res); else { res = {nextParams:{status:"error"}}; } answer = res.nextParams; $("#checkPrestaShopFilesVersion").html(" "+answer.msg+" "); if ((answer.status == "error") || (typeof(answer.result) == "undefined")) $("#checkPrestaShopFilesVersion").prepend(" "); else { $("#checkPrestaShopFilesVersion").prepend(" "); $("#checkPrestaShopFilesVersion").append("'.$this->l('See or hide the list').'
    "); $("#checkPrestaShopFilesVersion").append("

    "); if(answer.result.core.length) addModifiedFileList("'.$this->l('Core file(s)').'", answer.result.core, "changedImportant", "#changedList"); if(answer.result.mail.length) addModifiedFileList("'.$this->l('Mail file(s)').'", answer.result.mail, "changedNotice", "#changedList"); if(answer.result.translation.length) addModifiedFileList("'.$this->l('Translation file(s)').'", answer.result.translation, "changedNotice", "#changedList"); $("#toggleChangedList").bind("click",function(e){e.preventDefault();$("#changedList").toggle();}); $(".toggleSublist").live("click",function(e){e.preventDefault();$(this).parent().next().toggle();}); } } , error: function(res, textStatus, jqXHR) { if (textStatus == "timeout" && action == "download") { updateInfoStep("'.$this->l('Your server cannot download the file. Please upload it first by ftp in your admin/autoupgrade directory').'"); } else { // technical error : no translation needed $("#checkPrestaShopFilesVersion").html(" [TECHNICAL ERROR] Unable to check md5 files"); } } }) $.ajax({ type:"POST", url : "'. __PS_BASE_URI__ . $adminDir.'/autoupgrade/ajax-upgradetab.php", async: true, data : { dir:"'.$adminDir.'", token : "'.$this->token.'", tab : "'.get_class($this).'", action : "compareReleases", ajaxMode : "1", params : {} }, success : function(res,textStatus,jqXHR) { if (isJsonString(res)) res = $.parseJSON(res); else { res = {nextParams:{status:"error"}}; } answer = res.nextParams; $("#checkPrestaShopModifiedFiles").html(" "+answer.msg+" "); if ((answer.status == "error") || (typeof(answer.result) == "undefined")) $("#checkPrestaShopModifiedFiles").prepend(" "); else { $("#checkPrestaShopModifiedFiles").prepend(" "); $("#checkPrestaShopModifiedFiles").append("'.$this->l('See or hide the list').'
    "); $("#checkPrestaShopModifiedFiles").append("

    "); if(answer.result.deleted.length) addModifiedFileList("'.$this->l('Theses files will be deleted').'", answer.result.deleted, "diffImportant", "#diffList"); if(answer.result.modified.length) addModifiedFileList("'.$this->l('Theses files will be modified').'", answer.result.modified, "diffImportant", "#diffList"); $("#toggleDiffList").bind("click",function(e){e.preventDefault();$("#diffList").toggle();}); $(".toggleSublist").die().live("click",function(e){ e.preventDefault(); // this=a, parent=h3, next=ul $(this).parent().next().toggle(); }); } }, error: function(res, textStatus, jqXHR) { if (textStatus == "timeout" && action == "download") { updateInfoStep("'.$this->l('Your server cannot download the file. Please upload it first by ftp in your admin/autoupgrade directory').'"); } else { // technical error : no translation needed $("#checkPrestaShopFilesVersion").html(" [TECHNICAL ERROR] Unable to check md5 files"); } } }) });'; return $js; } /** * @desc extract a zip file to the given directory * @return bool success * we need a copy of it to be able to restore without keeping Tools and Autoload stuff */ private function ZipExtract($fromFile, $toDir) { if (!is_file($fromFile)) { $this->next = 'error'; $this->nextQuickInfo[] = sprintf($this->l('%s is not a file'), $fromFile); return false; } if (!file_exists($toDir)) if (!@mkdir($toDir, 0777)) { $this->next = 'error'; $this->nextQuickInfo[] = sprintf($this->l('unable to create directory %s'), $toDir); return false; } if (!self::$force_pclZip && class_exists('ZipArchive', false)) { $this->nextQuickInfo[] = $this->l('using class ZipArchive ...'); $zip = new ZipArchive(); if (@$zip->open($fromFile) === true) { if ($resExtract = $zip->extractTo($toDir)) { $this->nextQuickInfo[] = $this->l('backup extracted'); return true; } else { $this->nextQuickInfo[] = sprintf($this->l('zip->extractTo() : unable to use %s as extract destination.'), $toDir); return false; } } else { $this->nextQuickInfo[] = sprintf($this->l('Unable to open zipFile %s'), $fromFile); return false; } } else { // todo : no relative path if (!class_exists('PclZip',false)) require_once(_PS_ROOT_DIR_.'/modules/autoupgrade/pclzip.lib.php'); $this->nextQuickInfo[] = $this->l('using class pclZip.lib.php'); $zip = new PclZip($fromFile); // replace also modified files $extract_result = $zip->extract(PCLZIP_OPT_PATH, $toDir, PCLZIP_OPT_REPLACE_NEWER); if (is_array($extract_result)) { foreach ($extract_result as $extractedFile) { $file = str_replace($this->prodRootDir, '', $extractedFile['filename']); if ($extractedFile['status'] != 'ok') $this->nextQuickInfo[] = sprintf('[ERROR] %s has not been unzipped', $file); else $this->nextQuickInfo[] = sprintf('%1$s unzipped into %2$s', $file, str_replace(_PS_ROOT_DIR_, '', $toDir.'/')); } return true; } else { $this->next = 'error'; $this->nextQuickInfo[] = '[ERROR] error on extract using pclzip : '.$zip->errorInfo(true); return false; } } } private function _listArchivedFiles($zipfile) { if (file_exists($zipfile)) { if (!self::$force_pclZip && class_exists('ZipArchive', false)) { $files=array(); $zip = new ZipArchive(); $zip->open($zipfile); if ($zip){ for ($i = 0; $i < $zip->numFiles; $i++) $files[] = $zip->getNameIndex($i); return $files; } else { $this->nextQuickInfo[] = '[ERROR] Unable to list archived files'; return false; } // @todo : else throw new Exception() } else { require_once(dirname(__FILE__).'/pclzip.lib.php'); if ($zip = new PclZip($zipfile)); return $zip->listContent(); // @todo : else throw new Exception() } } return false; } /** * bool _skipFile : check whether a file is in backup or restore skip list * * @param type $file : current file or directory name eg:'.svn' , 'settings.inc.php' * @param type $fullpath : current file or directory fullpath eg:'/home/web/www/prestashop/config/settings.inc.php' * @param type $way : 'backup' , 'upgrade' */ protected function _skipFile($file, $fullpath, $way='backup') { $fullpath = str_replace('\\','/', $fullpath); // wamp compliant $rootpath = str_replace('\\','/', $this->prodRootDir); $adminDir = str_replace($this->prodRootDir, '', $this->adminDir); switch ($way) { case 'backup': if (in_array($file, $this->backupIgnoreFiles)) return true; foreach($this->backupIgnoreAbsoluteFiles as $path) { $path = str_replace('/admin', '/'.$adminDir, $path); if ($fullpath == $rootpath.$path) return true; } break; // restore or upgrade way : ignore the same files // note the restore process use skipFiles only if xml md5 files // are unavailable case 'restore': case 'upgrade': if (in_array($file, $this->excludeFilesFromUpgrade)) return true; foreach ($this->excludeAbsoluteFilesFromUpgrade as $path) { $path = str_replace('/admin', '/'.$adminDir, $path); if (strpos($fullpath, $rootpath.$path) !== false) return true; } break; // default : if it's not a backup or an upgrade, do not skip the file default: return false; } // by default, don't skip return false; } public function displayInvalidToken() { die("wrong token"); } }