2086 lines
67 KiB
PHP
Executable File
2086 lines
67 KiB
PHP
Executable File
<?php
|
|
/*
|
|
* 2007-2011 PrestaShop
|
|
*
|
|
* NOTICE OF LICENSE
|
|
*
|
|
* This source file is subject to the Open Software License (OSL 3.0)
|
|
* that is bundled with this package in the file LICENSE.txt.
|
|
* It is also available through the world-wide-web at this URL:
|
|
* http://opensource.org/licenses/osl-3.0.php
|
|
* If you did not receive a copy of the license and are unable to
|
|
* obtain it through the world-wide-web, please send an email
|
|
* to license@prestashop.com so we can send you a copy immediately.
|
|
*
|
|
* DISCLAIMER
|
|
*
|
|
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
|
|
* versions in the future. If you wish to customize PrestaShop for your
|
|
* needs please refer to http://www.prestashop.com for more information.
|
|
*
|
|
* @author PrestaShop SA <contact@prestashop.com>
|
|
* @copyright 2007-2011 PrestaShop SA
|
|
* @version Release: $Revision$
|
|
* @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
|
|
* International Registered Trademark & Property of PrestaShop SA
|
|
*/
|
|
|
|
require_once(_PS_ADMIN_DIR_.'/tabs/AdminPreferences.php');
|
|
|
|
class AdminUpgrade extends AdminPreferences
|
|
{
|
|
public $ajax = false;
|
|
public $nextResponseType = 'json'; // json, xml
|
|
public $next = 'N/A';
|
|
public $isModule = false;
|
|
|
|
/**
|
|
* 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',
|
|
'keepDefaultTheme',
|
|
'keepTrad',
|
|
'manualMode',
|
|
'desactivateCustomModule',
|
|
|
|
//
|
|
'backupDbFilename',
|
|
'backupFilesFilename',
|
|
|
|
|
|
);
|
|
public $autoupgradePath = null;
|
|
/**
|
|
* autoupgradeDir
|
|
*
|
|
* @var string directory relative to admin dir
|
|
*/
|
|
public $autoupgradeDir = 'autoupgrade';
|
|
public $latestRootDir = '';
|
|
public $prodRootDir = '';
|
|
public $adminDir = '';
|
|
public $rootWritable = false;
|
|
public $svnDir = 'svn';
|
|
public $destDownloadFilename = 'prestashop.zip';
|
|
public $toUpgradeFileList = array();
|
|
public $backupFileList = array();
|
|
public $sampleFileList = array();
|
|
private $backupIgnoreFiles = array();
|
|
private $backupIgnoreAbsoluteFiles = array();
|
|
private $excludeFilesFromUpgrade = array();
|
|
private $excludeAbsoluteFilesFromUpgrade = array();
|
|
|
|
private $backupFilesFilename = '';
|
|
private $backupDbFilename = '';
|
|
|
|
/**
|
|
* 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 = 1000;
|
|
/**
|
|
* int loopUpgradeFiles : if your server has a low memory size, lower this value
|
|
*/
|
|
public static $loopUpgradeFiles = 1000;
|
|
/**
|
|
* intloopRemoveSamples : if your server has a low memory size, lower this value
|
|
*/
|
|
public static $loopRemoveSamples = 1000;
|
|
|
|
// public static $skipAction = array('unzip'=>'listSampleFiles');
|
|
public static $skipAction;
|
|
public $useSvn;
|
|
|
|
protected $_includeContainer = false;
|
|
|
|
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;
|
|
|
|
$cookiePath = __PS_BASE_URI__.str_replace($this->prodRootDir,'',trim($this->adminDir,'/'));
|
|
setcookie('id_employee', $id_employee, time()+3600, $cookiePath);
|
|
setcookie('id_tab', $this->id, time()+3600, $cookiePath);
|
|
setcookie('autoupgrade', $this->encrypt($id_employee), time()+3600, $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()
|
|
{
|
|
@set_time_limit(0);
|
|
@ini_set('max_execution_time', '0');
|
|
|
|
$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
|
|
$this->id = $_COOKIE['id_tab'];
|
|
}
|
|
|
|
protected function l($string, $class = 'AdminTab', $addslashes = FALSE, $htmlentities = TRUE)
|
|
{
|
|
if($this->isModule)
|
|
{
|
|
$currentClass = get_class($this);
|
|
// need to be called in order to populate $classInModule
|
|
return SelfModule::findTranslation('autoupgrade', $string, 'AdminSelfUpgrade');
|
|
}
|
|
else
|
|
return parent::l($string, $class, $addslashes, $htmlentities);
|
|
}
|
|
|
|
/**
|
|
* _setFields function to set fields (only when we need it).
|
|
*
|
|
* @return void
|
|
*/
|
|
private function _setFields()
|
|
{
|
|
$this->_fieldsAutoUpgrade['PS_AUTOUP_DONT_SAVE_IMAGES'] = array(
|
|
'title' => $this->l('Don\'t 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 too yes, you will keep all your translations'),
|
|
);
|
|
|
|
$this->_fieldsAutoUpgrade['PS_AUTOUP_CUSTOM_MOD_DESACT'] = array(
|
|
'title' => $this->l('Desactivate custom modules'), 'cast' => 'intval', 'validation' => 'isBool',
|
|
'type' => 'bool', 'desc'=>$this->l('If you don\'t desactivate your modules, you can have some compatibility problem 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 = (ConfigurationTest::test_fopen() && $this->rootWritable);
|
|
$allowed &= !Configuration::get('PS_SHOP_ENABLE');
|
|
|
|
return $allowed;
|
|
}
|
|
/**
|
|
* isUpgradeAllowed checks if all server configuration is valid for upgrade
|
|
*
|
|
* @return void
|
|
*/
|
|
public function isUpgradeAllowed()
|
|
{
|
|
$allowed = (ConfigurationTest::test_fopen() && $this->rootWritable);
|
|
|
|
if (!defined('_PS_MODE_DEV_') OR !_PS_MODE_DEV_)
|
|
$allowed &= $this->upgrader->autoupgrade;
|
|
|
|
return $allowed;
|
|
}
|
|
|
|
/**
|
|
* 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_;
|
|
|
|
// test writable recursively
|
|
if ($this->isModule)
|
|
{
|
|
require_once('ConfigurationTest.php');
|
|
if(!class_exists('ConfigurationTest', false) AND class_exists('ConfigurationTestCore'))
|
|
eval('class ConfigurationTest extends ConfigurationTestCore{}');
|
|
}
|
|
if (ConfigurationTest::test_dir($this->prodRootDir,true))
|
|
$this->rootWritable = true;
|
|
|
|
// checkPSVersion will be not
|
|
$this->upgrader = new Upgrader(true);
|
|
$this->upgrader->checkPSVersion();
|
|
// 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 (!$this->isModule OR class_exists('Configuration',false))
|
|
$this->useSvn = Configuration::get('PS_AUTOUP_USE_SVN');
|
|
}
|
|
else
|
|
$this->useSvn = false;
|
|
|
|
// from $_POST or $_GET
|
|
$this->action = empty($_REQUEST['action'])?null:$_REQUEST['action'];
|
|
$this->currentParams = empty($_REQUEST['params'])?null:$_REQUEST['params'];
|
|
|
|
// 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'];
|
|
|
|
if (class_exists('Configuration',false))
|
|
{
|
|
$time = time();
|
|
$this->backupDbFilename = Configuration::get('UPGRADER_BACKUPDB_FILENAME');
|
|
if(!file_exists($this->backupDbFilename))
|
|
{
|
|
// If not exists, the filename is generated by Backup.php
|
|
$this->backupDbFilename = '';
|
|
Configuration::updateValue('UPGRADER_BACKUPDB_FILENAME', $this->backupDbFilename);
|
|
}
|
|
|
|
$this->backupFilesFilename = Configuration::get('UPGRADER_BACKUPFILES_FILENAME');
|
|
if(!file_exists($this->backupFilesFilename))
|
|
{
|
|
$this->backupFilesFilename = $this->autoupgradePath . DIRECTORY_SEPARATOR . 'backupfile-'.date('Y-m-d').'-'.$time.'.zip';
|
|
Configuration::updateValue('UPGRADER_BACKUPFILES_FILENAME', $this->backupFilesFilename);
|
|
}
|
|
}
|
|
else{
|
|
// backupDbFilename should never be empty
|
|
$this->backupDbFilename = $this->currentParams['backupDbFilename'];
|
|
// backupFilesFilename should never etc.
|
|
$this->backupFilesFilename = $this->currentParams['backupFilesFilename'];
|
|
}
|
|
$this->autoupgradePath = $this->adminDir.DIRECTORY_SEPARATOR.$this->autoupgradeDir;
|
|
|
|
if (!file_exists($this->autoupgradePath))
|
|
if (!@mkdir($this->autoupgradePath,0777))
|
|
$this->_errors[] = Tools::displayError(sprintf($this->l('unable to create directory %s'),$this->autoupgradePath));
|
|
|
|
$latest = $this->autoupgradePath.DIRECTORY_SEPARATOR.'latest';
|
|
if (!file_exists($latest))
|
|
if (!@mkdir($latest,0777))
|
|
$this->_errors[] = Tools::displayError(sprintf($this->l('unable to create directory %s'),$latest));
|
|
|
|
$this->latestRootDir = $latest.DIRECTORY_SEPARATOR.'prestashop';
|
|
$this->adminDir = str_replace($this->prodRootDir,'',$this->adminDir);
|
|
// @TODO future option
|
|
// $this->testRootDir = $this->autoupgradePath.DIRECTORY_SEPARATOR.'test';
|
|
|
|
/* option */
|
|
if (class_exists('Configuration',false))
|
|
{
|
|
$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->manualMode = Configuration::get('PS_AUTOUP_MANUAL_MODE');
|
|
$this->desactivateCustomModule = Configuration::get('PS_AUTOUP_CUSTOM_MOD_DESACT');
|
|
}
|
|
else
|
|
{
|
|
$this->dontBackupImages = $this->currentParams['dontBackupImages'];
|
|
$this->keepDefaultTheme = $this->currentParams['keepDefaultTheme'];
|
|
$this->keepTrad = $this->currentParams['keepTrad'];
|
|
$this->manualMode = $this->currentParams['manualMode'];
|
|
$this->desactivateCustomModule = $this->current['desactivateCustomModule'];
|
|
}
|
|
// 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";
|
|
|
|
$this->excludeFilesFromUpgrade[] = '.';
|
|
$this->excludeFilesFromUpgrade[] = '..';
|
|
$this->excludeFilesFromUpgrade[] = '.svn';
|
|
$this->excludeFilesFromUpgrade[] = 'install';
|
|
$this->excludeFilesFromUpgrade[] = 'settings.inc.php';
|
|
$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";
|
|
|
|
if ($this->keepTrad)
|
|
$this->excludeFilesFromUpgrade[] = "translations";
|
|
}
|
|
|
|
/**
|
|
* getFilePath return the path to the zipfile containing prestashop.
|
|
*
|
|
* @return void
|
|
*/
|
|
private function getFilePath()
|
|
{
|
|
return $this->autoupgradePath.DIRECTORY_SEPARATOR.$this->destDownloadFilename;
|
|
}
|
|
|
|
public function postProcess()
|
|
{
|
|
$this->_setFields();
|
|
|
|
if (sizeof($_POST)>0)
|
|
{
|
|
$this->_postConfig($this->_fieldsAutoUpgrade);
|
|
}
|
|
}
|
|
|
|
public function ajaxProcessUpgradeComplete()
|
|
{
|
|
$this->nextDesc = $this->l('Upgrade process done. Congratulations ! You can now reactive your shop.');
|
|
$this->next = '';
|
|
}
|
|
|
|
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 desactivated. Now downloading (this can takes some times )...');
|
|
}
|
|
}
|
|
|
|
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);
|
|
|
|
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 ');
|
|
}
|
|
}
|
|
}
|
|
|
|
public function ajaxProcessUnzip(){
|
|
if ($this->isModule 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);
|
|
|
|
if (Tools::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!='.' AND $file != '..' AND $file != '.svn')
|
|
{
|
|
|
|
if (preg_match('#'.preg_quote($fileext,'#').'$#i',$file))
|
|
{
|
|
$this->sampleFileList[] = $dir.$file;
|
|
}
|
|
else if (is_dir($dir.$file))
|
|
{
|
|
$res &= $this->_listSampleFiles($dir.$file);
|
|
}
|
|
}
|
|
}
|
|
return $res;
|
|
}
|
|
|
|
public function _listBackupFiles($dir)
|
|
{
|
|
$allFiles = scandir($dir);
|
|
foreach ($allFiles as $file)
|
|
{
|
|
$fullPath = $dir.DIRECTORY_SEPARATOR.$file;
|
|
|
|
if (!$this->_skipFile($file, $fullPath,'backup'))
|
|
{
|
|
if (is_dir($fullPath))
|
|
$this->_listBackupFiles($fullPath);
|
|
else
|
|
$this->backupFileList[] = $fullPath;
|
|
}
|
|
else
|
|
$this->backupIgnoreFiles[] = $fullPath;
|
|
|
|
}
|
|
}
|
|
|
|
public function _listFilesToUpgrade($dir)
|
|
{
|
|
$allFiles = scandir($dir);
|
|
foreach ($allFiles as $file)
|
|
{
|
|
$fullPath = $dir.DIRECTORY_SEPARATOR.$file;
|
|
|
|
if (!$this->_skipFile($file, $fullPath, "upgrade"))
|
|
{
|
|
if (is_dir($fullPath))
|
|
{
|
|
// if is_dir, we will create it :)e it :)
|
|
$this->toUpgradeFileList[] = $fullPath;
|
|
if (strpos($dir.DIRECTORY_SEPARATOR.$file, 'install') === false)
|
|
{
|
|
$this->_listFilesToUpgrade($fullPath);
|
|
}
|
|
}
|
|
else
|
|
$this->toUpgradeFileList[] = $fullPath;
|
|
}
|
|
}
|
|
|
|
$this->nextParams['filesToUpgrade'] = $this->toUpgradeFileList;
|
|
}
|
|
|
|
|
|
public function ajaxProcessUpgradeFiles(){
|
|
// @TODO :
|
|
$this->nextParams = $this->currentParams;
|
|
if (!isset($this->nextParams['filesToUpgrade']))
|
|
$this->_listFilesToUpgrade($this->latestRootDir);
|
|
|
|
// 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';
|
|
if (!is_array($this->nextParams['filesToUpgrade']))
|
|
{
|
|
error($this->nextParams);
|
|
$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($this->nextParams['filesToUpgrade'])<=0)
|
|
{
|
|
$this->next = 'upgradeDb';
|
|
$this->nextDesc = $this->l('All files upgraded. Now upgrading database');
|
|
$this->nextResponseType = 'xml';
|
|
break;
|
|
}
|
|
|
|
//$file = array_shift($this->nextParams['filesToUpgrade']);
|
|
$file = array_shift($this->nextParams['filesToUpgrade']);
|
|
if (!$this->upgradeThisFile($file))
|
|
{
|
|
// put the file back to the begin of the list
|
|
$totalFiles = array_unshift($this->nextParams['filesToUpgrade'],$file);
|
|
$this->next = 'error';
|
|
$this->nextQuickInfo[] = sprintf($this->l('error when trying to upgrade %s'),$file);
|
|
break;
|
|
}
|
|
else{
|
|
// @TODO : maybe put several files at the same times ?
|
|
$this->nextDesc = sprintf($this->l('%2$s files left to upgrade.'),$file,sizeof($this->nextParams['filesToUpgrade']));
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* _modelDoUpgrade prepare the call to doUpgrade.php file (like model.php)
|
|
*
|
|
* @return void
|
|
*/
|
|
public function _modelDoUpgrade()
|
|
{
|
|
// a. set logger
|
|
// it will be used later
|
|
global $logger;
|
|
$logger = new FileLogger();
|
|
if (function_exists('date_default_timezone_set'))
|
|
date_default_timezone_set('Europe/Paris');
|
|
// use autoupgrade as log dir
|
|
$logger->setFilename($this->latestRootDir.'/'.date('Ymd').'_autoupgrade.log');
|
|
|
|
// init env.
|
|
@set_time_limit(0);
|
|
@ini_set('max_execution_time', '0');
|
|
// 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']);
|
|
|
|
///////////////////////
|
|
// Copy from model.php
|
|
///////////////////////
|
|
$upgrader = $this->upgrader;
|
|
$upgrader->checkPSVersion(true);
|
|
|
|
define('INSTALL_VERSION', $upgrader->version_num);
|
|
// now the install dir to use is in a subdirectory of the admin dir
|
|
define('INSTALL_PATH', realpath($this->latestRootDir.DIRECTORY_SEPARATOR.'install'));
|
|
|
|
define('PS_INSTALLATION_IN_PROGRESS', true);
|
|
// Note : we don't need ToolsInstall.php
|
|
// include_once(INSTALL_PATH.'/classes/ToolsInstall.php');
|
|
|
|
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))));
|
|
|
|
// Note : INSTALLER__PS_BASE_URI_ABSOLUTE is not used for upgrade
|
|
// define('INSTALLER__PS_BASE_URI_ABSOLUTE', 'http://'.ToolsInstall::getHttpHost(false, true).INSTALLER__PS_BASE_URI);
|
|
|
|
// XML Header
|
|
header('Content-Type: text/xml');
|
|
require_once(INSTALL_PATH.DIRECTORY_SEPARATOR.'xml'.DIRECTORY_SEPARATOR.'doUpgrade.php');
|
|
|
|
//////////////////////////////
|
|
// End of copy from model.php
|
|
//////////////////////////////
|
|
|
|
}
|
|
|
|
public function ajaxProcessUpgradeDb(){
|
|
// @TODO : 1/2/3 have to be done at the beginning !!!!!!!!!!!!!!!!!!!!!!
|
|
|
|
// use something like actual in install-dev
|
|
// Notice : xml used here ...
|
|
|
|
// 1) confirm version is correct(DB)
|
|
// install/model.php?method=getVersionFromDb&language=0
|
|
// later
|
|
|
|
// 2) confirm config is correct (r/w rights)
|
|
// install/model.php?method=checkConfig&firsttime=0
|
|
// later
|
|
|
|
// 3) save current activated modules in nextParams, or don't desactivate them ?
|
|
// @TODO
|
|
// 4) upgrade
|
|
// install/model.php?_=1309193641470&method=doUpgrade&customModule=desactivate
|
|
if (!empty($this->currentParams['desactivateCustomModule']))
|
|
$_GET['customModule'] = 'desactivate';
|
|
|
|
if (!$this->_modelDoUpgrade())
|
|
{
|
|
$this->next = 'error';
|
|
$this->nextDesc = $this->l('error during upgrade Db');
|
|
}
|
|
|
|
// 5) compare activated modules and reactivate them
|
|
// @TODO
|
|
|
|
}
|
|
|
|
|
|
/**
|
|
* upgradeThisFile
|
|
*
|
|
* @param mixed $file
|
|
* @return void
|
|
*/
|
|
public function upgradeThisFile($file)
|
|
{
|
|
// @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()
|
|
if ($this->_skipFile('', $file,'upgrade'))
|
|
{
|
|
$this->nextQuickInfo[] = $this->l('%s ignored');
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
$dest = str_replace($this->latestRootDir, $this->destUpgradePath,$file);
|
|
|
|
if (is_dir($file))
|
|
{
|
|
if (!file_exists($dest))
|
|
{
|
|
if (@mkdir($dest))
|
|
{
|
|
$this->nextQuickInfo[] = sprintf($this->l('created dir %2$s. %3$s files left to upgrade.'),$file, $dest, sizeof($this->nextParams['filesToUpgrade']));
|
|
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
|
|
return true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (copy($file,$dest))
|
|
{
|
|
$this->nextQuickInfo[] = sprintf($this->l('copied %1$s in %2$s. %3$s files left to upgrade.'),$file, $dest, sizeof($this->nextParams['filesToUpgrade']));
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
$this->next = 'error';
|
|
$this->nextQuickInfo[] = sprintf($this->l('error for copy %1$s in %2$s'),$file,$dest);
|
|
$this->nextDesc = sprintf($this->l('error for copy %1$s in %2$s'),$file,$dest);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
public function ajaxProcessRollback()
|
|
{
|
|
// 1st, need to analyse what was wrong.
|
|
|
|
$this->nextParams = $this->currentParams;
|
|
if (!empty($this->backupFilesFilename) AND file_exists($this->backupFilesFilename))
|
|
{
|
|
$this->next = 'restoreFiles';
|
|
$this->status = 'ok';
|
|
$this->nextDesc = $this->l('Files restored, now restoring database.');
|
|
}
|
|
else
|
|
{
|
|
if (!empty($this->backupDbFilename) AND file_exists($this->backupDbFilename))
|
|
{
|
|
$this->next = 'restoreDb';
|
|
$this->status = 'ok';
|
|
$this->nextDesc = $this->l('Database restored');
|
|
}
|
|
else
|
|
{
|
|
// 2nd case if upgradeFiles made an error
|
|
// 3rd case if no upgrade has been done
|
|
// all theses cases are handled by the method ajaxRequestRollback()
|
|
$this->next = ''; // next is empty : nothing next :)
|
|
$this->status = 'ok';
|
|
$this->nextDesc = $this->l('All your site is restored... ');
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* ajaxProcessRestoreFiles restore the previously saved files.
|
|
*
|
|
* @return boolean true if succeed
|
|
*/
|
|
public function ajaxProcessRestoreFiles()
|
|
{
|
|
// @TODO : workaround max_execution_time / ajax batch unzip
|
|
if (!empty($this->backupFilesFilename) AND file_exists($this->backupFilesFilename))
|
|
{
|
|
// cleanup current PS tree
|
|
$list = $this->_listArchivedFiles();
|
|
if (count($list) > 0)
|
|
{
|
|
$this->_cleanUp($this->prodRootDir.'/');
|
|
$this->nextQuickInfo[] = $this->l('root directory cleaned.');
|
|
|
|
$filepath = $this->backupFilesFilename;
|
|
$destExtract = $this->prodRootDir;
|
|
|
|
if (self::ZipExtract($filepath, $destExtract))
|
|
{
|
|
// once it's restored, delete the file !
|
|
unlink($this->backupFilesFilename);
|
|
Configuration::updateValue('UPGRADER_BACKUPFILES_FILENAME', '');
|
|
if (!empty($this->backupDbFilename))
|
|
{
|
|
$this->nextDesc = $this->l('Files restored. No database backup found. Restoration done.');
|
|
$this->next = '';
|
|
}
|
|
else
|
|
{
|
|
$this->nextDesc = $this->l('Files restored.');
|
|
$this->next = 'rollback';
|
|
}
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
$this->next = "error";
|
|
$this->nextDesc = sprintf($this->l('unable to extract $1$s into %2$s .'), $filepath, $destExtract);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
$this->next = 'error';
|
|
$this->nextDesc = $this->l('no known backup. nothing to restore.');
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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()
|
|
{
|
|
$exts = explode('.', $this->backupDbFilename);
|
|
$fileext = $exts[count($exts)-1];
|
|
$requests = array();
|
|
$errors = array();
|
|
$content = '';
|
|
switch ($fileext)
|
|
{
|
|
case 'bz':
|
|
case 'bz2':
|
|
if ($fp = bzopen($this->backupDbFilename, 'r'))
|
|
{
|
|
while(!feof($fp))
|
|
$content .= bzread($fp, filesize($this->backupDbFilename));
|
|
bzclose($fp);
|
|
}
|
|
break;
|
|
case 'gz':
|
|
if ($fp = gzopen($this->backupDbFilename, 'r'))
|
|
{
|
|
while(!feof($fp))
|
|
$content = gzread($fp, filesize($this->backupDbFilename));
|
|
gzclose($fp);
|
|
}
|
|
break;
|
|
// default means sql ?
|
|
default :
|
|
if ($fp = fopen($this->backupDbFilename, 'r'))
|
|
{
|
|
while(!feof($fp))
|
|
$content = fread($fp, filesize($this->backupDbFilename));
|
|
fclose($fp);
|
|
}
|
|
}
|
|
|
|
if ($content=='')
|
|
return false;
|
|
|
|
// preg_match_all is better than preg_split (what is used in doUpgrade.php)
|
|
// This way we avoid extra blank lines
|
|
// option s (PCRE_DOTALL) added
|
|
// @TODO need to check if a ";" in description could block that (I suppose it can at the end of a line)
|
|
preg_match_all('/.*;[\n]\+/s', $content, $requests);
|
|
/* @TODO maybe improve regex pattern ... */
|
|
$db = Db::getInstance();
|
|
if (count($requests)>0)
|
|
{
|
|
foreach ($requests as $request)
|
|
if (!empty($request))
|
|
if (!$db->Execute($request))
|
|
$this->nextQuickInfo[] = $db->getMsgError();
|
|
|
|
// once it's restored, delete the file !
|
|
unlink($this->backupDbFilename);
|
|
Configuration::updateValue('UPGRADER_BACKUPDB_FILENAME','');
|
|
}
|
|
else
|
|
$this->nextQuickInfo[] = $this->l('Nothing to restore (no request found)');
|
|
|
|
$this->next = 'rollback';
|
|
$this->nextDesc = 'Database restore done.';
|
|
}
|
|
|
|
public function ajaxProcessBackupDb()
|
|
{
|
|
if(!class_exists('ObjectModel',false))
|
|
{
|
|
require_once(_PS_ROOT_DIR_.'/classes/ObjectModel.php');
|
|
if(!class_exists('ObjectModel',false))
|
|
eval('Class ObjectModel extends ObjectModelCore{}');
|
|
}
|
|
|
|
if(!class_exists('Language',false))
|
|
{
|
|
require_once(_PS_ROOT_DIR_.'/classes/Language.php');
|
|
if(!class_exists('Language',false))
|
|
eval('Class Language extends LanguageCore{}');
|
|
}
|
|
if(!class_exists('Validate',false))
|
|
{
|
|
require_once(_PS_ROOT_DIR_.'/classes/Validate.php');
|
|
if(!class_exists('Validate',false))
|
|
eval('Class Validate extends ValidateCore{}');
|
|
}
|
|
if(!class_exists('Db',false))
|
|
{
|
|
require_once(_PS_ROOT_DIR_.'/classes/Db.php');
|
|
if(!class_exists('Db',false))
|
|
eval('abstract Class Db extends DbCore{}');
|
|
}
|
|
if(!class_exists('MySQL',false))
|
|
{
|
|
require_once(_PS_ROOT_DIR_.'/classes/MySQL.php');
|
|
if(!class_exists('MySQL',false))
|
|
eval('Class MySQL extends MySQLCore{}');
|
|
}
|
|
if(!class_exists('Configuration',false))
|
|
{
|
|
require_once(_PS_ROOT_DIR_.'/classes/Configuration.php');
|
|
if(!class_exists('Configuration',false))
|
|
eval('Class Configuration extends ConfigurationCore{}');
|
|
}
|
|
if(!class_exists('Backup',false))
|
|
{
|
|
require_once('Backup.php');
|
|
if(!class_exists('ObjectModel',false))
|
|
eval('Class ObjectModel extends ObjectModelCore{}');
|
|
}
|
|
|
|
if(!defined('_PS_MAGIC_QUOTES_GPC_'))
|
|
define('_PS_MAGIC_QUOTES_GPC_', get_magic_quotes_gpc());
|
|
if(!defined('_PS_MYSQL_REAL_ESCAPE_STRING_'))
|
|
define('_PS_MYSQL_REAL_ESCAPE_STRING_', function_exists('mysql_real_escape_string'));
|
|
|
|
$backup = new Backup();
|
|
// for backup db, use autoupgrade directory
|
|
// @TODO : autoupgrade must not be static
|
|
$backup->setCustomBackupPath('autoupgrade');
|
|
// maybe for big tables we should save them in more than one file ?
|
|
$res = $backup->add();
|
|
if ($res)
|
|
{
|
|
$this->nextParams['backupDbFilename'] = $backup->id;
|
|
// We need to load configuration to use it ...
|
|
Configuration::loadConfiguration();
|
|
Configuration::updateValue('UPGRADER_BACKUPDB_FILENAME', $backup->id);
|
|
|
|
$this->next = 'upgradeFiles';
|
|
$this->nextDesc = sprintf($this->l('Database backup done in %s. Now updating files'),$backup->id);
|
|
}
|
|
// if an error occur, we assume the file is not saved
|
|
}
|
|
|
|
public function ajaxProcessBackupFiles()
|
|
{
|
|
$this->nextParams = $this->currentParams;
|
|
$this->stepDone = false;
|
|
/////////////////////
|
|
|
|
if (!isset($this->nextParams['filesForBackup']))
|
|
{
|
|
$list = $this->_listBackupFiles($this->prodRootDir);
|
|
$this->nextQuickInfo[] = sprintf($this->l('%s Files to backup.'), sizeof($this->backupFileList));
|
|
$this->nextParams['filesForBackup'] = $this->backupFileList;
|
|
|
|
// delete old backup, create new
|
|
if (file_exists($this->backupFilesFilename))
|
|
unlink($this->backupFilesFilename);
|
|
|
|
$this->nextQuickInfo[] = sprintf($this->l('backup files initialized in %s'), $this->backupFilesFilename);
|
|
}
|
|
|
|
/////////////////////
|
|
$this->next = 'backupFiles';
|
|
// @TODO : display % instead of this
|
|
$this->nextDesc = sprintf($this->l('Backup files in progress. %s files left'), sizeof($this->nextParams['filesForBackup']));
|
|
if (is_array($this->nextParams['filesForBackup']))
|
|
{
|
|
// @TODO later
|
|
// 1) calculate crc32 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 = '';
|
|
|
|
$zip = new ZipArchive();
|
|
if ($zip->open($this->backupFilesFilename, ZIPARCHIVE::CREATE))
|
|
{
|
|
$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->stepok = false;
|
|
// @TODO min(self::$loopBackupFiles, sizeof())
|
|
for($i=0;$i<self::$loopBackupFiles;$i++)
|
|
{
|
|
if (sizeof($this->nextParams['filesForBackup'])<=0)
|
|
{
|
|
$this->stepok = 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($this->nextParams['filesForBackup']);
|
|
$archiveFilename = str_replace($this->prodRootDir,'',$file);
|
|
// @TODO : maybe put several files at the same times ?
|
|
if ($zip->addFile($file,$archiveFilename))
|
|
$this->nextQuickInfo[] = sprintf($this->l('%1$s added to archive. %2$s left.'),$file, sizeof($this->nextParams['filesForBackup']));
|
|
else
|
|
{
|
|
// if an error occur, it's more safe to delete the corrupted backup
|
|
if (file_exists($this->backupFilesFilename))
|
|
unlink($this->backupFilesFilename);
|
|
$this->next = 'error';
|
|
$this->nextDesc = sprintf($this->l('error when trying to add %1$s to archive %2$s.'),$file, $backupFilePath);
|
|
break;
|
|
}
|
|
}
|
|
$zip->close();
|
|
return true;
|
|
}
|
|
else{
|
|
$this->next = 'error';
|
|
$this->nextDesc = $this->l('unable to open archive');
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
$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 = 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;
|
|
}
|
|
|
|
public function ajaxProcessRemoveSamples(){
|
|
$this->stepDone = false;
|
|
// @TODO : list exaustive list of files to remove :
|
|
// 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/modules/editorial/', 'homepage_logo.jpg');
|
|
// @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
|
|
// @unlink(_PS_ROOT_DIR_.'modules'.DIRECTORY_SEPARATOR.'editorial'.DIRECTORY_SEPARATOR.'editorial.xml');
|
|
// @unlink(_PS_ROOT_DIR_.'modules'.DIRECTORY_SEPARATOR.'editorial'.DIRECTORY_SEPARATOR.'homepage_logo.jpg'); // homepage custom ?
|
|
// @unlink(_PS_ROOT_DIR_.'img'.DIRECTORY_SEPARATOR.'logo.jpg');
|
|
// @unlink(_PS_ROOT_DIR_.'img'.DIRECTORY_SEPARATOR.'favicon.ico');
|
|
$resRemove = true;
|
|
for($i=0;$i<self::$loopRemoveSamples;$i++)
|
|
{
|
|
if (sizeof($this->nextParams['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){
|
|
$svnLink = 'http://svn.prestashop.com/trunk';
|
|
$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($svnLink, $dest))
|
|
{
|
|
$this->next = 'svnExport';
|
|
$this->nextDesc = sprintf($this->l('SVN Checkout done from %s . now exporting it into latest...'),$svnLink);
|
|
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'))
|
|
{
|
|
$res = $this->upgrader->downloadLast($this->autoupgradePath,$this->destDownloadFilename);
|
|
if ($res){
|
|
$this->next = 'unzip';
|
|
$this->nextDesc = $this->l('Download complete. Now extracting');
|
|
}
|
|
else
|
|
{
|
|
$this->next = 'error';
|
|
$this->nextDesc = $this->l('Error during download');
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// @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;
|
|
|
|
foreach($this->ajaxParams as $v)
|
|
if(property_exists($this,$v))
|
|
$this->nextParams[$v] = $this->$v;
|
|
|
|
$return['nextParams'] = $this->nextParams;
|
|
|
|
$return['nextParams']['typeResult'] = $this->nextResponseType;
|
|
|
|
$return['nextQuickInfo'] = $this->nextQuickInfo;
|
|
return Tools::jsonEncode($return);
|
|
}
|
|
|
|
/**
|
|
* displayConf
|
|
*
|
|
* @return void
|
|
*/
|
|
public function displayConf()
|
|
{
|
|
if(!$this->isModule)
|
|
{
|
|
if (version_compare(_PS_VERSION_,'1.4.4.0','<') AND false)
|
|
$this->_errors[] = Tools::displayError('This class depends of several files modified in 1.4.4.0 version and should not be used in an older version');
|
|
}
|
|
parent::displayConf();
|
|
|
|
}
|
|
|
|
public function ajaxPreProcess()
|
|
{
|
|
if (!empty($_POST['responseType']) AND $_POST['responseType'] == 'json')
|
|
header('Content-Type: application/json');
|
|
|
|
if(!empty($_POST['action']))
|
|
{
|
|
$action = $_POST['action'];
|
|
if (isset(self::$skipAction[strtolower($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 ($this->apacheModExists('mod_evasive'))
|
|
sleep(1);
|
|
}
|
|
/**
|
|
* apacheModExists return true if the apache module $name is loaded
|
|
* @TODO move this method in class Information (when it will exist)
|
|
*
|
|
* @param string $name module name
|
|
* @return boolean true if exists
|
|
*/
|
|
function apacheModExists($name)
|
|
{
|
|
if(function_exists('apache_get_modules'))
|
|
{
|
|
static $apacheModuleList = null;
|
|
|
|
if (!is_array($apacheModuleList))
|
|
$apacheModuleList = apache_get_modules();
|
|
|
|
// we need strpos (example can be evasive20
|
|
foreach($apacheModuleList as $module)
|
|
if (strpos($name, $module)!==false)
|
|
return true;
|
|
}
|
|
else{
|
|
// If apache_get_modules does not exists,
|
|
// one solution should be parsing httpd.conf,
|
|
// but we could simple parse phpinfo(INFO_MODULES) return string
|
|
ob_start();
|
|
phpinfo(INFO_MODULES);
|
|
$phpinfo = ob_get_contents();
|
|
ob_end_clean();
|
|
if (strpos($phpinfo, $module) !== false)
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
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('Can\'t 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('Can\'t 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 config/settings.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('Can\'t 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();
|
|
}
|
|
|
|
private function _displayRollbackForm()
|
|
{
|
|
echo '<fieldset><legend>'.$this->l('Rollback').'</legend>
|
|
<div id="rollbackForm">';
|
|
if (empty($this->backupFilesFilename) AND empty($this->backupDbFilename))
|
|
echo $this->l('No rollback available');
|
|
else if (!empty($this->backupFilesFilename) OR !empty($this->backupDbFilename))
|
|
{
|
|
echo '<div id="rollbackContainer"><a class="upgradestep button" href="" id="rollback">'.$this->l('rollback').'</a></div><br/>';
|
|
}
|
|
if (!empty($this->backupFilesFilename) AND file_exists($this->backupFilesFilename))
|
|
echo '<div id="restoreFilesContainer"><a href="" class="upgradestep button" id="restoreFiles">restoreFiles</a> '.sprintf($this->l('click to restore %s'),$this->backupFilesFilename).'</div><br/>';
|
|
if (!empty($this->backupDbFilename) AND file_exists($this->backupDbFilename))
|
|
echo '<div id="restoreDbContainer"><a href="" class="upgradestep button" id="restoreDb">restoreDb</a> '.sprintf($this->l('click to restore %s'), $this->backupDbFilename).'</div><br/>';
|
|
|
|
echo '</div></fieldset>';
|
|
}
|
|
|
|
private function _displayUpgraderForm()
|
|
{
|
|
global $cookie;
|
|
$pleaseUpdate = $this->upgrader->checkPSVersion();
|
|
|
|
echo '<fieldset class="width autoupgrade " >';
|
|
echo '<legend>'.$this->l('Your current configuration').'</legend>';
|
|
echo '<b>'.$this->l('Root directory').' : </b>'.$this->prodRootDir.'<br/><br/>';
|
|
|
|
if ($this->rootWritable)
|
|
$srcRootWritable = '../img/admin/enabled.gif';
|
|
else
|
|
$srcRootWritable = '../img/admin/disabled.gif';
|
|
echo '<b>'.$this->l('Root directory status').' : </b>'.'<img src="'.$srcRootWritable.'" /> '.($this->rootWritable?$this->l('fully writable'):$this->l('not writable recursively')).'<br/><br/>';
|
|
|
|
if ($this->upgrader->needUpgrade)
|
|
{
|
|
if ($this->upgrader->autoupgrade)
|
|
$srcAutoupgrade = '../img/admin/enabled.gif';
|
|
else
|
|
$srcAutoupgrade = '../img/admin/disabled.gif';
|
|
echo '<b>'.$this->l('Autoupgrade allowed').' : </b>'.'<img src="'.$srcAutoupgrade.'" /> '.($this->upgrader->autoupgrade?$this->l('This release allow autoupgrade.'):$this->l('This release does not allow autoupgrade')).'. <br/><br/>';
|
|
}
|
|
|
|
if (Configuration::get('PS_SHOP_ENABLE'))
|
|
{
|
|
$srcShopStatus = '../img/admin/disabled.gif';
|
|
$label = $this->l('No');
|
|
}
|
|
else
|
|
{
|
|
$srcShopStatus = '../img/admin/enabled.gif';
|
|
$label = $this->l('Yes');
|
|
}
|
|
echo '<b>'.$this->l('Shop desactivated').' : </b>'.'<img src="'.$srcShopStatus.'" />'.$label.'<br/><br/>';
|
|
|
|
$max_exec_time = ini_get('max_execution_time');
|
|
if ($max_exec_time == 0)
|
|
$srcExecTime = '../img/admin/enabled.gif';
|
|
else
|
|
$srcExecTime = '../img/admin/warning.gif';
|
|
echo '<b>'.$this->l('PHP time limit').' : </b>'.'<img src="'.$srcExecTime.'" />'.($max_exec_time == 0?$this->l('disabled'):$max_exec_time.' '.$this->l('seconds')).' <br/><br/>';
|
|
|
|
if ($this->rootWritable)
|
|
$srcRootWritable = '../img/admin/enabled.gif';
|
|
else
|
|
$srcRootWritable = '../img/admin/disabled.gif';
|
|
echo '<b>'.$this->l('Root directory').' : </b>'.'<img src="'.$srcRootWritable.'" /> '.($this->rootWritable?$this->l('writable recursively'):$this->l('not writable recursively')).'. <br/><br/>';
|
|
|
|
echo '<a class="button" id="scrollToOptions" href="#options">'.$this->l('Modify your options').'</a>';
|
|
echo '</fieldset>';
|
|
|
|
echo '<br/>';
|
|
|
|
echo '<fieldset class=""><legend>'.$this->l('Update').'</legend>';
|
|
|
|
|
|
echo '<div style="float:left">
|
|
<h1>'.sprintf($this->l('Your current prestashop version : %s '),_PS_VERSION_).'</h1>';
|
|
echo '<p>'.sprintf($this->l('Last version is %1$s (%2$s) '), $this->upgrader->version_name, $this->upgrader->version_num).'</p>';
|
|
|
|
// @TODO : this should be checked when init()
|
|
if ($this->isUpgradeAllowed()) {
|
|
if ($pleaseUpdate) {
|
|
echo '<li><img src="'._PS_ADMIN_IMG_.'information.png" alt="information"/> '.$this->l('Latest Prestashop version available is:').' <b>'.$pleaseUpdate['name'].'</b></li>';
|
|
}
|
|
// echo '<input class="button" type="submit" name="sumbitUpdateVersion" value="'.$this->l('Backup Database, backup files and update right now and in one click !').'"/>';
|
|
// echo '<input class="button" type="submit" id="refreshCurrent" value="'.$this->l("refresh update dir / current").'"/>';
|
|
echo '<br/>';
|
|
if ($this->upgrader->needUpgrade)
|
|
{
|
|
if($this->configOk())
|
|
echo '<a href="" id="upgradeNow" class="button-autoupgrade upgradestep">'.$this->l('Upgrade PrestaShop now !').'</a>';
|
|
else
|
|
echo $this->displayWarning('Your current configuration does not allow upgrade.');
|
|
}
|
|
else
|
|
{
|
|
echo '<span class="button-autoupgrade upgradestep" >'.$this->l('Your shop is already up to date.').'</span> ';
|
|
}
|
|
echo '<small>'.sprintf($this->l('last datetime check : %s '),date('Y-m-d H:i:s',Configuration::get('PS_LAST_VERSION_CHECK'))).'</span>
|
|
<a class="button" href="index.php?tab=AdminUpgrade&token='.Tools::getAdminToken(get_class($this).(int)(Tab::getIdFromClassName(get_class($this))).(int)$cookie->id_employee).'&refreshCurrentVersion=1">'.$this->l('Please click to refresh').'</a>
|
|
</small>';
|
|
|
|
echo'</div>
|
|
<div id="currentlyProcessing" style="display:none;float:right"><h4>Currently processing <img id="pleaseWait" src="'.__PS_BASE_URI__.'img/loader.gif"/></h4>
|
|
|
|
<div id="infoStep" class="processing" style=height:50px;width:400px;" >'.$this->l('I\'m waiting for your command, sir').'</div>';
|
|
echo '</div>';
|
|
|
|
echo '</fieldset>';
|
|
|
|
|
|
echo '<br class="clear"/>';
|
|
|
|
if (defined('_PS_MODE_DEV_') AND _PS_MODE_DEV_)
|
|
{
|
|
echo '<fieldset class="autoupgradeSteps"><legend>'.$this->l('Step').'</legend>';
|
|
echo '<h4>'.$this->l('Upgrade steps').'</h4>';
|
|
echo '<div>';
|
|
echo '<a href="" id="download" class="button upgradestep" >download</a>';
|
|
echo '<a href="" id="unzip" class="button upgradestep" >unzip</a>'; // unzip in autoupgrade/latest
|
|
echo '<a href="" id="removeSamples" class="button upgradestep" >removeSamples</a>'; // remove samples (iWheel images)
|
|
echo '<a href="" id="backupFiles" class="button upgradestep" >backupFiles</a>'; // backup files
|
|
echo '<a href="" id="backupDb" class="button upgradestep" >backupDb</a>';
|
|
echo '<a href="" id="upgradeFiles" class="button upgradestep" >upgradeFiles</a>';
|
|
echo '<a href="" id="upgradeDb" class="button upgradestep" >upgradeDb</a>';
|
|
echo '</div>';
|
|
|
|
if (defined('_PS_ALLOW_UPGRADE_UNSTABLE_') AND _PS_ALLOW_UPGRADE_UNSTABLE_ )
|
|
{
|
|
echo '<h4>Development tools </h4><div>
|
|
<a href="" name="action" id="svnCheckout" class="button upgradestep" type="submit" >svnCheckout</a>
|
|
<a href="" name="action" id="svnUpdate" class="button upgradestep" type="submit" >svnUpdate</a>
|
|
<a href="" name="action" id="svnExport" class="button upgradestep" type="submit" >svnExport</a>
|
|
</div>';
|
|
}
|
|
}
|
|
|
|
echo '<br class="clear"/>';
|
|
echo' <div id="quickInfo" class="processing" style="height:100px;">quick info</div>';
|
|
// for upgradeDb
|
|
echo '<p id="dbResultCheck"></p>';
|
|
echo '<p id="dbCreateResultCheck"></p>';
|
|
}
|
|
else
|
|
echo '<p>'.$this->l('Your current configuration does not allow upgrade.').'</p>';
|
|
|
|
echo '</fieldset>';
|
|
/* echo '<fieldset class="right">
|
|
<legend>Error</legend>
|
|
<div id="errorWindow" > no error yet</div>
|
|
</fieldset>';
|
|
*
|
|
*/
|
|
// information to keep will be in #infoStep
|
|
// temporary infoUpdate will be in #tmpInformation
|
|
echo '<script type="text/javascript">';
|
|
// _PS_MODE_DEV_ is available in js
|
|
if (defined('_PS_MODE_DEV_') AND _PS_MODE_DEV_)
|
|
echo 'var _PS_MODE_DEV_ = true;';
|
|
echo $this->_getJsErrorMsgs();
|
|
|
|
echo '</script>';
|
|
}
|
|
|
|
public function display()
|
|
{
|
|
global $cookie, $currentIndex;
|
|
echo '<fieldset><legend><img src="'.__PS_BASE_URI__.'modules/autoupgrade/logo.gif" />'.$this->l('Upgrade').'</legend>';
|
|
if(is_dir('autoupgrade'))
|
|
{
|
|
echo '<p>'.$this->l('To make an upgrade, you need to activate the autoupgrade module').'</p>';
|
|
$tokenModule = Tools::getAdminToken('AdminModules'.(int)(Tab::getIdFromClassName('AdminModules')).(int)$cookie->id_employee);
|
|
$tokenAdminTabs = Tools::getAdminToken('AdminTabs'.(int)(Tab::getIdFromClassName('AdminTabs')).(int)$cookie->id_employee);
|
|
$tokenAdminTools = Tools::getAdminToken('AdminTools'.(int)(Tab::getIdFromClassName('AdminTools')).(int)$cookie->id_employee);
|
|
if (!$idTab = Tab::getIdFromClassName('AdminSelfUpgrade'))
|
|
{
|
|
echo '<br/><p id="ContainerActivateButton">
|
|
<a class="button" id="activateAutoupgradeModule" href="index.php?tab=AdminModules&token='.$tokenModule.'&install=autoupgrade&module_name=autoupgrade">'
|
|
.$this->l('Activate the module').'</a></p>';
|
|
}
|
|
else{
|
|
$tokenSelfUpgrade = Tools::getAdminToken('AdminSelfUpgrade'.(int)(Tab::getIdFromClassName('AdminSelfUpgrade')).(int)$cookie->id_employee);
|
|
echo ' <a class="button" href="index.php?tab=AdminSelfUpgrade&token='.$tokenSelfUpgrade.'">'
|
|
.$this->l('Use the autoupgrade module').'</a></p>';
|
|
}
|
|
echo '<script type="text/javascript">
|
|
$("#activateAutoupgradeModule").click(function(e){
|
|
e.preventDefault();
|
|
console.warn($(this).attr("href"));
|
|
req = $.ajax({
|
|
type:"POST",
|
|
url : "index.php",
|
|
async: true,
|
|
data : {
|
|
ajaxMode : "1",
|
|
token : "'.$tokenModule.'",
|
|
tab : "AdminModules",
|
|
install: "autoupgrade",
|
|
module_name:"autoupgrade",
|
|
},
|
|
success : function(res,textStatus,jqXHR)
|
|
{
|
|
console.log(res);
|
|
$.get("index.php?token='.$tokenAdminTabs.'&tab=AdminTabs&id_tab='.Tab::getIdFromClassName('AdminUpgrade').'&deletetab",
|
|
function(e){
|
|
alert("'.$this->l('Previous UpgradeTab has been removed. Please go to the new Upgrade tab').'");
|
|
window.location = "'.'index.php?token='.$tokenAdminTools.'&tab=AdminTools&id_tab='.Tab::getIdFromClassName('AdminTools').'";
|
|
|
|
}
|
|
);
|
|
},
|
|
error: function(res,textStatus,jqXHR)
|
|
{
|
|
alert("'.$this->l("Unable to install the module automatically. Please manually remove the Upgrade AdminTab and install the autoupgrade module.").'");
|
|
}
|
|
});
|
|
});
|
|
|
|
</script>';
|
|
}
|
|
else{
|
|
echo "please download autoupgrade at this url";
|
|
}
|
|
echo '</fieldset>';
|
|
}
|
|
|
|
private function _getJsInit()
|
|
{
|
|
global $currentIndex;
|
|
$js = '';
|
|
$js .= '
|
|
function ucFirst(str) {
|
|
if (str.length > 0) {
|
|
return str[0].toUpperCase() + str.substring(1);
|
|
}
|
|
else {
|
|
return str;
|
|
}
|
|
}
|
|
|
|
function cleanInfo(){
|
|
$("#infoStep").html("reset<br/>");
|
|
}
|
|
|
|
function updateInfoStep(msg){
|
|
if (msg)
|
|
{
|
|
$("#infoStep").html(msg);
|
|
$("#infoStep").attr({ scrollTop: $("#infoStep").attr("scrollHeight") });
|
|
}
|
|
}
|
|
|
|
function addError(msg){
|
|
if (msg)
|
|
$("#errorWindow").html(msg);
|
|
}
|
|
|
|
function addQuickInfo(arrQuickInfo){
|
|
if (arrQuickInfo)
|
|
{
|
|
$("#quickInfo").show();
|
|
for(i=0;i<arrQuickInfo.length;i++)
|
|
$("#quickInfo").append(arrQuickInfo[i]+"<br/>");
|
|
|
|
$("#quickInfo").attr({ scrollTop: $("#quickInfo").attr("scrollHeight") });
|
|
}
|
|
}';
|
|
|
|
if ($this->manualMode)
|
|
$js .= 'var manualMode = true;';
|
|
else
|
|
$js .= 'var manualMode = false;';
|
|
|
|
$js .= '
|
|
var firstTimeParams = '.$this->buildAjaxResult().';
|
|
firstTimeParams = firstTimeParams.nextParams;
|
|
|
|
$(document).ready(function(){
|
|
$(".upgradestep").click(function(e)
|
|
{
|
|
e.preventDefault();
|
|
// $.scrollTo("#options")
|
|
});
|
|
|
|
// more convenient to have that param for handling success and error
|
|
var requestParams;
|
|
|
|
// set timeout to 5 minutes (download can be long)?
|
|
$.ajaxSetup({timeout:300000});
|
|
|
|
|
|
// prepare available button here, without params ?
|
|
prepareNextButton("#upgradeNow",firstTimeParams);
|
|
prepareNextButton("#rollback",firstTimeParams);
|
|
prepareNextButton("#restoreDb",firstTimeParams);
|
|
prepareNextButton("#restoreFiles",firstTimeParams);
|
|
|
|
});
|
|
|
|
/**
|
|
* parseXMLResult is used to handle the return value of the doUpgrade method
|
|
*
|
|
*/
|
|
function parseXMLResult(xmlRet)
|
|
{
|
|
ret = $(xmlRet);
|
|
ret = ret.find("action")[0];
|
|
if (ret.getAttribute("result") == "ok")
|
|
{
|
|
$("#dbResultCheck")
|
|
.addClass("ok")
|
|
.removeClass("fail")
|
|
.html("<p>'.$this->l('upgrade complete. Please check your front-office (try to make an order, check theme)').'</p>")
|
|
.show("slow");
|
|
$("#dbCreateResultCheck")
|
|
.hide("slow");
|
|
|
|
// difference with the original function
|
|
ret = {next:"upgradeComplete",nextParams:{typeResult:"json"},status:"ok"};
|
|
|
|
}
|
|
else
|
|
{
|
|
$("#dbResultCheck")
|
|
.addClass("fail")
|
|
.removeClass("ok")
|
|
.html(txtError[parseInt(ret.getAttribute("error"))])
|
|
.show("slow");
|
|
$("#dbCreateResultCheck")
|
|
.hide("slow");
|
|
|
|
// propose rollback if there is an error
|
|
if (confirm(txtError[parseInt(ret.getAttribute("error"))]+"\r\n\r\n'.$this->l('Do you want to rollback ?').'"))
|
|
ret = {next:"rollback",nextParams:{typeResult:"json"},status:"error"};
|
|
}
|
|
|
|
return ret
|
|
};
|
|
|
|
function afterUpgradeComplete()
|
|
{
|
|
$("#pleaseWait").hide();
|
|
$("#infoStep").html("<h3>'.$this->l('Upgrade Complete ! ').'</h3>");
|
|
}
|
|
/**
|
|
* afterBackupDb display the button
|
|
*
|
|
*/
|
|
function afterBackupDb()
|
|
{
|
|
$("#restoreDbContainer").html("<a href=\"\" class=\"upgradestep button\" id=\"restoreDb\">restoreDb</a> '.$this->l('click to restore database').'");
|
|
prepareNextButton("#restoreDb",{});
|
|
}
|
|
|
|
function afterRestoreDb()
|
|
{
|
|
$("#restoreDbContainer").html("");
|
|
}
|
|
|
|
function afterRestoreFiles()
|
|
{
|
|
$("#restoreFilesContainer").html("");
|
|
}
|
|
|
|
function afterBackupFiles()
|
|
{
|
|
$("#restoreFilesContainer").html("<div id=\"restoreFilesContainer\"><a href=\"\" class=\"upgradestep button\" id=\"restoreFiles\">restoreFiles</a> '.$this->l('click to restore files').'");
|
|
prepareNextButton("#restoreFiles",{});
|
|
|
|
}
|
|
|
|
function doAjaxRequest(action, nextParams){
|
|
req = $.ajax({
|
|
type:"POST",
|
|
url : "'.($this->isModule? __PS_BASE_URI__ . trim($this->adminDir,'/').'/autoupgrade/ajax-upgradetab.php' : str_replace('index','ajax-tab',$currentIndex)).'",
|
|
async: true,
|
|
data : {
|
|
dir:"'.trim($this->adminDir,'/').'",
|
|
ajaxMode : "1",
|
|
token : "'.$this->token.'",
|
|
tab : "'.get_class($this).'",
|
|
action : action,
|
|
params : nextParams
|
|
},
|
|
success : function(res,textStatus,jqXHR)
|
|
{
|
|
if(eval("typeof nextParams") == "undefined")
|
|
{
|
|
nextParams = {typeResult : "json"};
|
|
}
|
|
|
|
if (nextParams.typeResult == "xml")
|
|
{
|
|
xmlRes = parseXMLResult(res);
|
|
res = {};
|
|
res.next = xmlRes.next;
|
|
// if xml, we keep the next params
|
|
nextParams = myNext;
|
|
res.status = xmlRes.status;
|
|
}
|
|
else
|
|
{
|
|
try{
|
|
res = $.parseJSON(res);
|
|
nextParams = res.nextParams;
|
|
}
|
|
catch(e){
|
|
res = {status : "error"};
|
|
alert("error during "+action);
|
|
/*
|
|
nextParams = {
|
|
error:"0",
|
|
next:"cancelUpgrade",
|
|
nextDesc:"Error detected during ["+action+"] step, reverting...",
|
|
nextQuickInfo:[],
|
|
status:"ok",
|
|
"stepDone":true
|
|
}
|
|
*/
|
|
}
|
|
}
|
|
|
|
if (res.status == "ok")
|
|
{
|
|
$("#"+action).addClass("done");
|
|
if (res.stepDone)
|
|
$("#"+action).addClass("stepok");
|
|
|
|
// if a function "after[action name]" exists, it should be called.
|
|
// This is used for enabling restore buttons for example
|
|
funcName = "after"+ucFirst(action);
|
|
if (typeof funcName == "string" &&
|
|
eval("typeof " + funcName) == "function") {
|
|
|
|
eval(funcName+"()");
|
|
}
|
|
|
|
handleSuccess(res,nextParams.typeResult);
|
|
}
|
|
else
|
|
{
|
|
// display progression
|
|
$("#"+action).addClass("done");
|
|
$("#"+action).addClass("steperror");
|
|
handleError(res);
|
|
}
|
|
},
|
|
error: function(res,textStatus,jqXHR)
|
|
{
|
|
if (textStatus == "timeout" && action == "download")
|
|
{
|
|
updateInfoStep("'.$this->l('Your server can\'t download the file. Please upload it first by ftp in your admin/autoupgrade directory').'");
|
|
}
|
|
else
|
|
{
|
|
//console.log(res);
|
|
//console.log(jqXHR);
|
|
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)
|
|
{
|
|
// myNext;
|
|
myNext = nextParams;
|
|
$(button_selector).unbind();
|
|
$(button_selector).click(function(e){
|
|
e.preventDefault();
|
|
$("#currentlyProcessing").show();
|
|
';
|
|
if (defined('_PS_MODE_DEV_') AND _PS_MODE_DEV_)
|
|
$js .= 'addQuickInfo(["[DEV] request : "+$(this).attr("id")]);';
|
|
$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 != "")
|
|
{
|
|
addQuickInfo(res.nextQuickInfo);
|
|
|
|
$("#"+res.next).addClass("nextStep");
|
|
if (manualMode)
|
|
{
|
|
prepareNextButton("#"+res.next,res.nextParams);
|
|
alert("manually go to "+res.next+" button ");
|
|
}
|
|
else
|
|
{
|
|
// @TODO :
|
|
// 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
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Way To Go, end of upgrade process
|
|
addQuickInfo(["End of upgrade process"]);
|
|
}
|
|
}
|
|
|
|
// res = {nextParams, NextDesc}
|
|
function handleError(res)
|
|
{
|
|
// display error message in the main process thing
|
|
updateInfoStep(res.nextDesc);
|
|
addQuickInfo(res.nextQuickInfo);
|
|
// In case the rollback button has been desactivated, just re-enable it
|
|
prepareNextButton("#rollback",res.nextParams);
|
|
// ask if you want to rollback
|
|
// TODO : be sure there is useful infos
|
|
addQuickInfo(res);
|
|
if (confirm(res+"\r\r'.$this->l('Do you want to rollback ?').'"))
|
|
{
|
|
if (manualMode)
|
|
alert("'.$this->l('Please go manually go to rollback button').'");
|
|
else
|
|
{
|
|
$("#rollback").click();
|
|
}
|
|
|
|
}
|
|
}
|
|
';
|
|
return $js;
|
|
}
|
|
private function _cleanUp($path)
|
|
{
|
|
// as we need theses files for restore operation, we can't remove them.
|
|
// They will be overwritten
|
|
$skipDirs = array('backups', 'pclzip', 'autoupgrade', '.', '..', '.svn');
|
|
$skipFiles = array('autoload.php', 'init.php', 'settings.inc.php', 'config.inc.php', 'Tools.php', 'AdminUpgrade.php');
|
|
$skipFiles[] = $this->isModule?'ajax-upgradetab.php':'ajax-tab.php';
|
|
if (is_dir($path))
|
|
{
|
|
$fp = opendir($path);
|
|
while ($file = readdir($fp))
|
|
{
|
|
if (!in_array($file, $skipDirs) AND !$this->_skipFile('', $path.$file, 'backup'))
|
|
{
|
|
$fullpath = $path.$file;
|
|
if (is_dir($fullpath))
|
|
$this->_cleanUp($fullpath.'/');
|
|
else
|
|
{
|
|
if (!in_array($file, $skipFiles))
|
|
{
|
|
unlink($fullpath);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
closedir($fp);
|
|
/* fortunately not empty dir won't be removed by the following */
|
|
@rmdir($path);
|
|
}
|
|
else
|
|
if (!$this->_skipFile($file, '', 'backup')) unlink($path);
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* @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 static function ZipExtract($fromFile, $toDir)
|
|
{
|
|
if (!file_exists($toDir))
|
|
if (!@mkdir($toDir,0777))
|
|
{
|
|
$this->next = 'error';
|
|
$this->nextDesc = sprintf($this->l('unable to create directory %s'),$toDir);
|
|
return false;
|
|
}
|
|
|
|
if (class_exists('ZipArchive', false))
|
|
{
|
|
$zip = new ZipArchive();
|
|
if ($zip->open($fromFile) === true AND $zip->extractTo($toDir) AND $zip->close())
|
|
return true;
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
|
|
if (!class_exists('PclZip',false))
|
|
require_once($this->prodRootDir.'/tools/pclzip/pclzip.lib.php');
|
|
|
|
$zip = new PclZip($fromFile);
|
|
$list = $zip->extract(PCLZIP_OPT_PATH, $toDir);
|
|
foreach ($list as $extractedFile)
|
|
if ($extractedFile['status'] != 'ok')
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
private function _listArchivedFiles()
|
|
{
|
|
if (!empty($this->nextParams['backupFilesFilepath']))
|
|
{
|
|
if (class_exists('ZipArchive', false))
|
|
{
|
|
$files=array();
|
|
if ($zip = zip_open($this->currentParams['backupFilesFilepath']))
|
|
{
|
|
while ($entry=zip_read($zip))
|
|
$files[] = zip_entry_name($entry);
|
|
|
|
zip_close($zip);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
require_once($this->prodRootDir.'/tools/pclzip/pclzip.lib.php');
|
|
if ($zip = new PclZip($this->currentParams['backupFilesFilepath']))
|
|
return $zip->listContent();
|
|
}
|
|
}
|
|
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/img'
|
|
* @param type $way : 'backup' , 'upgrade'
|
|
*/
|
|
private function _skipFile($file,$fullpath,$way='backup')
|
|
{
|
|
$fullpath = str_replace('\\','/', $fullpath); // wamp compliant
|
|
$rootpath = str_replace('\\','/', $this->prodRootDir);
|
|
switch ($way)
|
|
{
|
|
case 'backup':
|
|
if (in_array($file, $this->backupIgnoreFiles))
|
|
return true;
|
|
|
|
foreach($this->backupIgnoreAbsoluteFiles as $path)
|
|
if ($file == 'img')
|
|
if (strpos($fullpath, $rootpath.$path) !== false)
|
|
return true;
|
|
break;
|
|
|
|
case 'upgrade':
|
|
if (in_array($file, $this->excludeFilesFromUpgrade))
|
|
return true;
|
|
|
|
foreach ($this->excludeAbsoluteFilesFromUpgrade as $path)
|
|
if (strpos($fullpath, $rootpath.$path) !== false)
|
|
return true;
|
|
break;
|
|
// default : if it's not a backup or an upgrade, juste skip the file
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|