privilegedemarque/modules/autoupgrade/AdminSelfUpgrade.php
Serveur preprod f0c0c48223 first push
2016-04-14 16:14:31 +02:00

5795 lines
215 KiB
PHP

<?php
/*
* 2007-2014 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-2014 PrestaShop SA
* @version Release: $Revision: 11834 $
* @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/classes/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/classes/Tools14.php');
if (!class_exists('Tools', false))
eval('class Tools extends Tools14{}');
class AdminSelfUpgrade extends AdminSelfTab
{
public $multishop_context;
public $multishop_context_group = false;
public $_html = '';
// used for translations
public static $l_cache;
// retrocompatibility
public $noTabLink = array();
public $id = -1;
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 $warning_exists = false;
public $error = '0';
public $next_desc = '';
public $nextParams = array();
public $nextQuickInfo = array();
public $nextErrors = array();
public $currentParams = array();
/**
* @var array theses values will be automatically added in "nextParams"
* if their properties exists
*/
public $ajaxParams = array(
// autoupgrade options
'install_version',
'backupName',
'backupFilesFilename',
'backupDbFilename',
'restoreName',
'restoreFilesFilename',
'restoreDbFilenames',
'installedLanguagesIso',
'modules_addons',
'warning_exists',
);
/**
* installedLanguagesIso is an array of iso_code of each installed languages
*
* @var array
* @access public
*/
public $installedLanguagesIso = array();
/**
* modules_addons is an array of array(id_addons => name_module).
*
* @var array
* @access public
*/
public $modules_addons = array();
public $autoupgradePath = null;
public $downloadPath = null;
public $backupPath = null;
public $latestPath = null;
public $tmpPath = 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 $destDownloadFilename = 'prestashop.zip';
/**
* configFilename contains all configuration specific to the autoupgrade module
*
* @var string
* @access public
*/
public $configFilename = 'config.var';
/**
* 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 upgradeModules process,
* this files contains the list of modules left to upgrade in a serialized array.
* (this file is deleted in init() method if you reload the page)
* @var string
*/
public $toUpgradeModuleList = 'modulesToUpgrade.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 queryToRestore and queries present in a backupFiles archive
* (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 $keepImages = null;
public $updateDefaultTheme = null;
public $keepMails = null;
public $manualMode = null;
public $deactivateCustomModule = null;
public $sampleFileList = array();
private $restoreIgnoreFiles = array();
private $restoreIgnoreAbsoluteFiles = array();
private $backupIgnoreFiles = array();
private $backupIgnoreAbsoluteFiles = array();
private $excludeFilesFromUpgrade = array();
private $excludeAbsoluteFilesFromUpgrade = array();
public static $classes14 = array('Cache', 'CacheFS', 'CarrierModule', 'Db', 'FrontController', 'Helper','ImportModule',
'MCached', 'Module', 'ModuleGraph', 'ModuleGraphEngine', 'ModuleGrid', 'ModuleGridEngine',
'MySQL', 'Order', 'OrderDetail', 'OrderDiscount', 'OrderHistory', 'OrderMessage', 'OrderReturn',
'OrderReturnState', 'OrderSlip', 'OrderState', 'PDF', 'RangePrice', 'RangeWeight', 'StockMvt',
'StockMvtReason', 'SubDomain', 'Shop', 'Tax', 'TaxRule', 'TaxRulesGroup', 'WebserviceKey', 'WebserviceRequest', '');
private $restoreName = null;
private $backupName = null;
private $backupFilesFilename = null;
private $backupDbFilename = null;
private $restoreFilesFilename = null;
private $restoreDbFilenames = array();
public static $loopBackupFiles = 400;
public static $maxBackupFileSize = 15728640; // 15 Mo
public static $loopBackupDbTime = 6;
public static $max_written_allowed = 4194304; // 4096 ko
public static $loopUpgradeFiles = 600;
public static $loopRestoreFiles = 400;
public static $loopRestoreQueryTime = 6;
public static $loopUpgradeModulesTime = 6;
public static $loopRemoveSamples = 400;
/* usage : key = the step you want to ski
* value = the next step you want instead
* example : public static $skipAction = array();
* initial order upgrade:
* download, unzip, removeSamples, backupFiles, backupDb, upgradeFiles, upgradeDb, upgradeModules, cleanDatabase, upgradeComplete
* initial order rollback: rollback, restoreFiles, restoreDb, rollbackComplete
*/
public static $skipAction = array();
/**
* if set to true, will use pclZip library
* even if ZipArchive is available
*/
public static $force_pclZip = false;
protected $_includeContainer = true;
public $_fieldsUpgradeOptions = array();
public $_fieldsBackupOptions = array();
/**
* 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 && isset($_COOKIE['id_employee']))
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;
if ($cookie->id_lang)
$iso_code = $_COOKIE['iso_code'] = Language::getIsoById((int)$cookie->id_lang);
else
$iso_code = 'en';
$admin_dir = trim(str_replace($this->prodRootDir, '', $this->adminDir), DIRECTORY_SEPARATOR);
$cookiePath = __PS_BASE_URI__.$admin_dir;
setcookie('id_employee', $id_employee, time() + 7200, $cookiePath);
setcookie('id_tab', $this->id, time() + 7200, $cookiePath);
setcookie('iso_code', $iso_code, 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()
{
@set_time_limit(0);
@ini_set('max_execution_time', '0');
@ini_set('magic_quotes_runtime', '0');
global $ajax, $currentIndex;
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();
elseif (isset($_COOKIE['id_tab']))
$this->id = $_COOKIE['id_tab'];
// Database instanciation (need to be cached because there will be at least 100k calls in the upgrade process
if (!class_exists('Db', false))
{
require_once(_PS_ROOT_DIR_.'/modules/autoupgrade/db/Db.php');
eval('abstract class Db extends DbCore{}');
require_once(_PS_ROOT_DIR_.'/modules/autoupgrade/db/MySQL.php');
eval('class MySQL extends MySQLCore{}');
require_once(_PS_ROOT_DIR_.'/modules/autoupgrade/db/DbMySQLi.php');
eval('class DbMySQLi extends DbMySQLiCore{}');
require_once(_PS_ROOT_DIR_.'/modules/autoupgrade/db/DbPDO.php');
eval('class DbPDO extends DbPDOCore{}');
require_once(_PS_ROOT_DIR_.'/modules/autoupgrade/db/DbQuery.php');
eval('class DbQuery extends DbQueryCore{}');
require_once(_PS_ROOT_DIR_.'/modules/autoupgrade/alias.php');
}
$this->db = Db::getInstance();
// Performance settings, if your server has a low memory size, lower these values
$perf_array = array(
'loopBackupFiles' => array(400, 800, 1600),
'maxBackupFileSize' => array(15728640, 31457280, 62914560),
'loopBackupDbTime' => array(6, 12, 25),
'max_written_allowed' => array(4194304, 8388608, 16777216),
'loopUpgradeFiles' => array(600, 1200, 2400),
'loopRestoreFiles' => array(400, 800, 1600),
'loopRestoreQueryTime' => array(6, 12, 25),
'loopUpgradeModulesTime' => array(6, 12, 25),
'loopRemoveSamples' => array(400, 800, 1600)
);
switch ($this->getConfig('PS_AUTOUP_PERFORMANCE'))
{
case 3:
foreach ($perf_array as $property => $values)
self::$$property = $values[2];
break;
case 2:
foreach ($perf_array as $property => $values)
self::$$property = $values[1];
break;
case 1:
default:
foreach ($perf_array as $property => $values)
self::$$property = $values[0];
}
/* Bug with backwardcompatibility overrinding currentIndex */
if (version_compare(_PS_VERSION_,'1.5.0.0','>'))
$this->currentIndex = $_SERVER['SCRIPT_NAME'].(($controller = Tools14::getValue('controller')) ? '?controller='.$controller: '');
else
$this->currentIndex = $currentIndex;
if (defined('_PS_ADMIN_DIR_'))
{
$file_tab = @filemtime($this->autoupgradePath.DIRECTORY_SEPARATOR.'ajax-upgradetab.php');
$file = @filemtime(_PS_ROOT_DIR_.DIRECTORY_SEPARATOR.'modules'.DIRECTORY_SEPARATOR.$this->autoupgradeDir.DIRECTORY_SEPARATOR.'ajax-upgradetab.php');
if ($file_tab < $file)
@copy(_PS_ROOT_DIR_.DIRECTORY_SEPARATOR.'modules'.DIRECTORY_SEPARATOR.$this->autoupgradeDir.DIRECTORY_SEPARATOR.'ajax-upgradetab.php',
$this->autoupgradePath.DIRECTORY_SEPARATOR.'ajax-upgradetab.php');
}
}
protected function l($string, $class = 'AdminTab', $addslashes = FALSE, $htmlentities = TRUE)
{
// need to be called in order to populate $classInModule
$str = self::findTranslation('autoupgrade', $string, 'AdminSelfUpgrade');
$str = $htmlentities ? str_replace('"', '&quot;', htmlentities($str, ENT_QUOTES, 'utf-8')) : $str;
$str = $addslashes ? addslashes($str) : stripslashes($str);
return $str;
}
/**
* 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)
{
static $_MODULES;
if (!is_array($_MODULES))
{
// note: $_COOKIE[iso_code] is set in createCustomToken();
$files_to_try = array(
_PS_MODULE_DIR_.'autoupgrade'.DIRECTORY_SEPARATOR.'translations'.DIRECTORY_SEPARATOR.$_COOKIE['iso_code'].'.php', // 1.5
_PS_MODULE_DIR_.'autoupgrade'.DIRECTORY_SEPARATOR.$_COOKIE['iso_code'].'.php' // 1.4
);
// translations may be in "autoupgrade/translations/iso_code.php" or "autoupgrade/iso_code.php",
// try both locations.
foreach ($files_to_try as $file)
if (file_exists($file) && include($file))
{
$_MODULES = !empty($_MODULES) ? array_merge($_MODULES, $_MODULE) : $_MODULE;
break;
}
}
$cache_key = $name.'|'.$string.'|'.$source;
if (!isset(self::$l_cache[$cache_key]))
{
if (!is_array($_MODULES))
return $string;
// set array key to lowercase for 1.3 compatibility
$_MODULES = array_change_key_case($_MODULES);
if (defined('_THEME_NAME_'))
$currentKey = '<{'.strtolower($name).'}'.strtolower(_THEME_NAME_).'>'.strtolower($source).'_'.md5($string);
else
$currentKey = '<{'.strtolower($name).'}default>'.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);
$currentKey = $defaultKey;
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] = $ret;
}
return self::$l_cache[$cache_key];
}
/**
* function to set configuration fields display
*
* @return void
*/
private function _setFields()
{
$this->_fieldsBackupOptions['PS_AUTOUP_BACKUP'] = array(
'title' => $this->l('Back up my files and database'), 'cast' => 'intval', 'validation' => 'isBool', 'defaultValue' => '1',
'type' => 'bool', 'desc' => $this->l('Automatically back up your database and files in order to restore your shop if needed. This is experimental: you should still perform your own manual backup for safety.'),
);
$this->_fieldsBackupOptions['PS_AUTOUP_KEEP_IMAGES'] = array(
'title' => $this->l('Back up my images'), 'cast' => 'intval', 'validation' => 'isBool', 'defaultValue' => '1',
'type' => 'bool', 'desc' => $this->l('To save time, you can decide not to back your images up. In any case, always make sure you did back them up manually.'),
);
$this->_fieldsUpgradeOptions['PS_AUTOUP_PERFORMANCE'] = array(
'title' => $this->l('Server performance'), 'cast' => 'intval', 'validation' => 'isInt', 'defaultValue' => '1',
'type' => 'select', 'desc' => $this->l('Unless you are using a dedicated server, select "Low".').'<br />'.
$this->l('A high value can cause the upgrade to fail if your server is not powerful enough to process the upgrade tasks in a short amount of time.'),
'choices' => array(1 => $this->l('Low (recommended)'), 2 => $this->l('Medium'), 3 => $this->l('High'))
);
$this->_fieldsUpgradeOptions['PS_AUTOUP_CUSTOM_MOD_DESACT'] = array(
'title' => $this->l('Disable non-native modules'), 'cast' => 'intval', 'validation' => 'isBool',
'type' => 'bool', 'desc' => $this->l('As non-native modules can experience some compatibility issues, we recommend to disable them by default.').'<br />'.
$this->l('Keeping them enabled might prevent you from loading the "Modules" page properly after the upgrade.'),
);
$this->_fieldsUpgradeOptions['PS_AUTOUP_UPDATE_DEFAULT_THEME'] = array(
'title' => $this->l('Upgrade and switch to the default theme of the new version'), 'cast' => 'intval', 'validation' => 'isBool', 'defaultValue' => '1',
'type' => 'bool', 'desc' => $this->l('This will change your theme: your shop will then use the default theme of the version of PrestaShop you are upgrading to.').'<br />'
.$this->l('If you customized the default PrestaShop theme in its folder (folder name "prestashop" in 1.4, "default" in 1.5, "bootstrap-default" in 1.6), enabling this option will lose your modifications.').'<br />'
.$this->l('If you are using your own theme, enabling this option will switch your shop to the updated default theme, and your own theme will be safe.'),
);
$this->_fieldsUpgradeOptions['PS_AUTOUP_KEEP_MAILS'] = array(
'title' => $this->l('Upgrade the default e-mails'), 'cast' => 'intval', 'validation' => 'isBool',
'type' => 'bool', 'desc' => $this->l('This will upgrade the default PrestaShop e-mails.').'<br />'
.$this->l('If you customized the default PrestaShop e-mail templates, enabling this option will lose your modifications.'),
);
/* Developers only options */
if (defined('_PS_MODE_DEV_') && _PS_MODE_DEV_)
{
$this->_fieldsUpgradeOptions['PS_AUTOUP_MANUAL_MODE'] = array(
'title' => $this->l('Step by step mode'), 'cast' => 'intval', 'validation' => 'isBool',
'type' => 'bool', 'desc' => $this->l('Allows to perform the upgrade step by step (debug mode).'),
);
$this->_fieldsUpgradeOptions['PS_DISPLAY_ERRORS'] = array(
'title' => $this->l('Display PHP errors'), 'cast' => 'intval', 'validation' => 'isBool', 'defaultValue' => '0',
'type' => 'bool', 'desc' => $this->l('This option will keep PHP\'s "display_errors" setting to On (or force it).').'<br />'
.$this->l('This is not recommended as the upgrade will immediately fail if a PHP error occurs during an Ajax call.'),
);
}
elseif ($this->getConfig('PS_DISPLAY_ERRORS'))
$this->writeConfig(array('PS_DISPLAY_ERRORS' => '0'));
}
public function configOk()
{
$allowed_array = $this->getCheckCurrentPsConfig();
$allowed = array_product($allowed_array);
return $allowed;
}
public function getCheckCurrentPsConfig()
{
static $allowed_array;
if(empty($allowed_array))
{
$allowed_array = array();
$allowed_array['fopen'] = ConfigurationTest::test_fopen() || ConfigurationTest::test_curl();
$allowed_array['root_writable'] = $this->getRootWritable();
$admin_dir = trim(str_replace($this->prodRootDir, '', $this->adminDir), DIRECTORY_SEPARATOR);
$allowed_array['admin_au_writable'] = ConfigurationTest::test_dir($admin_dir.DIRECTORY_SEPARATOR.$this->autoupgradeDir, false, $report);
$allowed_array['shop_deactivated'] = (!Configuration::get('PS_SHOP_ENABLE') || (isset($_SERVER['HTTP_HOST']) && in_array($_SERVER['HTTP_HOST'], array('127.0.0.1', 'localhost'))));
$allowed_array['cache_deactivated'] = !(defined('_PS_CACHE_ENABLED_') && _PS_CACHE_ENABLED_);
$allowed_array['module_version_ok'] = $this->checkAutoupgradeLastVersion();
if (version_compare(_PS_VERSION_,'1.5.0.0','<'))
$allowed_array['test_mobile'] = ConfigurationTest::test_mobile();
}
return $allowed_array;
}
public function getRootWritable()
{
// Root directory permissions cannot be checked recursively anymore, it takes too much time
$this->root_writable = ConfigurationTest::test_dir('/', false, $report);
$this->root_writable_report = $report;
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;
}
public function cleanTmpFiles()
{
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);
}
/**
* 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_;
if (!defined('__PS_BASE_URI__'))
{
// _PS_DIRECTORY_ replaces __PS_BASE_URI__ in 1.5
if (defined('_PS_DIRECTORY_'))
define('__PS_BASE_URI__', _PS_DIRECTORY_);
else
define('__PS_BASE_URI__', realpath(dirname($_SERVER['SCRIPT_NAME'])).'/../../');
}
// 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(dirname(__FILE__).'/classes/ConfigurationTest.php');
if(!class_exists('ConfigurationTest', false) AND class_exists('ConfigurationTestCore'))
eval('class ConfigurationTest extends ConfigurationTestCore{}');
}
$this->initPath();
$upgrader = new Upgrader();
preg_match('#([0-9]+\.[0-9]+)(?:\.[0-9]+){1,2}#', _PS_VERSION_, $matches);
if (isset($matches[1]))
$upgrader->branch = $matches[1];
$channel = $this->getConfig('channel');
switch ($channel)
{
case 'archive':
$this->install_version = $this->getConfig('archive.version_num');
$this->destDownloadFilename = $this->getConfig('archive.filename');
$upgrader->checkPSVersion(true, array('archive'));
break;
case 'directory';
$this->install_version = $this->getConfig('directory.version_num');
$upgrader->checkPSVersion(true, array('directory'));
break;
default:
$upgrader->channel = $channel;
if ($this->getConfig('channel') == 'private' && !$this->getConfig('private_allow_major'))
$upgrader->checkPSVersion(true, array('private', 'minor'));
else
$upgrader->checkPSVersion(true, array('minor'));
$this->install_version = $upgrader->version_num;
}
if (is_null($this->install_version) || empty($this->install_version))
$this->install_version = _PS_VERSION_;
// If you have defined this somewhere, you know what you do
/* load options from configuration if we're not in ajax mode */
if (!$this->ajax)
{
$this->createCustomToken();
$postData = 'version='._PS_VERSION_.'&method=listing&action=native&iso_code=all';
$xml_local = $this->prodRootDir.DIRECTORY_SEPARATOR.'config'.DIRECTORY_SEPARATOR.'xml'.DIRECTORY_SEPARATOR.'modules_native_addons.xml';
$xml = $upgrader->getApiAddons($xml_local, $postData, true);
if (is_object($xml))
foreach ($xml as $mod)
$this->modules_addons[(string)$mod->id] = (string)$mod->name;
// installedLanguagesIso is used to merge translations files
$iso_ids = Language::getIsoIds(false);
foreach($iso_ids as $v)
$this->installedLanguagesIso[] = $v['iso_code'];
$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
$this->cleanTmpFiles();
}
else
{
foreach($this->ajaxParams as $prop)
if(property_exists($this, $prop))
$this->{$prop} = isset($this->currentParams[$prop])?$this->currentParams[$prop]:'';
}
$this->keepImages = $this->getConfig('PS_AUTOUP_KEEP_IMAGES');
$this->updateDefaultTheme = $this->getConfig('PS_AUTOUP_UPDATE_DEFAULT_THEME');
$this->keepMails = $this->getConfig('PS_AUTOUP_KEEP_MAILS');
$this->manualMode = (defined('_PS_MODE_DEV_') && _PS_MODE_DEV_)? (bool)$this->getConfig('PS_AUTOUP_MANUAL_MODE') : false;
$this->deactivateCustomModule = $this->getConfig('PS_AUTOUP_CUSTOM_MOD_DESACT');
// during restoration, do not remove :
$this->restoreIgnoreAbsoluteFiles[] = '/config/settings.inc.php';
$this->restoreIgnoreAbsoluteFiles[] = '/modules/autoupgrade';
$this->restoreIgnoreAbsoluteFiles[] = '/admin/autoupgrade';
$this->restoreIgnoreAbsoluteFiles[] = '.';
$this->restoreIgnoreAbsoluteFiles[] = '..';
// during backup, do not save
$this->backupIgnoreAbsoluteFiles[] = '/tools/smarty_v2/compile';
$this->backupIgnoreAbsoluteFiles[] = '/tools/smarty_v2/cache';
$this->backupIgnoreAbsoluteFiles[] = '/tools/smarty/compile';
$this->backupIgnoreAbsoluteFiles[] = '/tools/smarty/cache';
$this->backupIgnoreAbsoluteFiles[] = '/cache/smarty/compile';
$this->backupIgnoreAbsoluteFiles[] = '/cache/smarty/cache';
$this->backupIgnoreAbsoluteFiles[] = '/cache/tcpdf';
$this->backupIgnoreAbsoluteFiles[] = '/cache/cachefs';
// do not care about the two autoupgrade dir we use;
$this->backupIgnoreAbsoluteFiles[] = '/modules/autoupgrade';
$this->backupIgnoreAbsoluteFiles[] = '/admin/autoupgrade';
$this->backupIgnoreFiles[] = '.';
$this->backupIgnoreFiles[] = '..';
$this->backupIgnoreFiles[] = '.svn';
$this->backupIgnoreFiles[] = '.git';
$this->backupIgnoreFiles[] = $this->autoupgradeDir;
$this->excludeFilesFromUpgrade[] = '.';
$this->excludeFilesFromUpgrade[] = '..';
$this->excludeFilesFromUpgrade[] = '.svn';
$this->excludeFilesFromUpgrade[] = '.git';
// do not copy install, neither settings.inc.php in case it would be present
$this->excludeAbsoluteFilesFromUpgrade[] = '/config/settings.inc.php';
$this->excludeAbsoluteFilesFromUpgrade[] = '/install';
$this->excludeAbsoluteFilesFromUpgrade[] = '/install-dev';
$this->excludeAbsoluteFilesFromUpgrade[] = '/config/modules_list.xml';
$this->excludeAbsoluteFilesFromUpgrade[] = '/config/xml/modules_list.xml';
// this will exclude autoupgrade dir from admin, and autoupgrade from modules
$this->excludeFilesFromUpgrade[] = $this->autoupgradeDir;
if ($this->keepImages === '0')
{
$this->backupIgnoreAbsoluteFiles[] = '/img';
$this->restoreIgnoreAbsoluteFiles[] = '/img';
}
else
{
$this->backupIgnoreAbsoluteFiles[] = '/img/tmp';
$this->restoreIgnoreAbsoluteFiles[] = '/img/tmp';
}
if (!$this->updateDefaultTheme) /* If set to false, we need to preserve the default themes */
{
if (version_compare(_PS_VERSION_, '1.6.0.0', '>'))
$this->excludeAbsoluteFilesFromUpgrade[] = '/themes/default-bootstrap';
elseif (version_compare(_PS_VERSION_, '1.5.0.0', '>'))
$this->excludeAbsoluteFilesFromUpgrade[] = '/themes/default';
else
$this->excludeAbsoluteFilesFromUpgrade[] = '/themes/prestashop';
}
}
/**
* create some required directories if they does not exists
*
* Also set nextParams (removeList and filesToUpgrade) if they
* exists in currentParams
*
*/
public function initPath()
{
// 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 (isset($this->currentParams['modulesToUpgrade']))
$this->nextParams['modulesToUpgrade'] = $this->currentParams['modulesToUpgrade'];
// set autoupgradePath, to be used in backupFiles and backupDb config values
$this->autoupgradePath = $this->adminDir.DIRECTORY_SEPARATOR.$this->autoupgradeDir;
// directory missing
if (!file_exists($this->autoupgradePath))
if (!mkdir($this->autoupgradePath))
$this->_errors[] = sprintf($this->l('unable to create directory %s'), $this->autoupgradePath);
if (!is_writable($this->autoupgradePath))
$this->_errors[] = sprintf($this->l('Unable to write in the directory "%s"'), $this->autoupgradePath);
$this->downloadPath = $this->autoupgradePath.DIRECTORY_SEPARATOR.'download';
if (!file_exists($this->downloadPath))
if (!mkdir($this->downloadPath))
$this->_errors[] = sprintf($this->l('unable to create directory %s'),$this->downloadPath);
$this->backupPath = $this->autoupgradePath.DIRECTORY_SEPARATOR.'backup';
$tmp = "order deny,allow\ndeny from all";
if (!file_exists($this->backupPath))
if (!mkdir($this->backupPath))
$this->_errors[] = sprintf($this->l('unable to create directory %s'),$this->backupPath);
if (!file_exists($this->backupPath.DIRECTORY_SEPARATOR.'index.php'))
if (!copy(_PS_ROOT_DIR_.DIRECTORY_SEPARATOR.'config'.DIRECTORY_SEPARATOR.'index.php', $this->backupPath.DIRECTORY_SEPARATOR.'index.php'))
$this->_errors[] = sprintf($this->l('unable to create file %s'), $this->backupPath.DIRECTORY_SEPARATOR.'index.php');
if (!file_exists($this->backupPath.DIRECTORY_SEPARATOR.'.htaccess'))
if (!file_put_contents($this->backupPath.DIRECTORY_SEPARATOR.'.htaccess', $tmp))
$this->_errors[] = sprintf($this->l('unable to create file %s'), $this->backupPath.DIRECTORY_SEPARATOR.'.htaccess');
// directory missing
$this->latestPath = $this->autoupgradePath.DIRECTORY_SEPARATOR.'latest';
if (!file_exists($this->latestPath))
if (!mkdir($this->latestPath))
$this->_errors[] = sprintf($this->l('unable to create directory %s'),$this->latestPath);
$this->tmpPath = $this->autoupgradePath.DIRECTORY_SEPARATOR.'tmp';
if (!file_exists($this->tmpPath))
if (!mkdir($this->tmpPath))
$this->_errors[] = sprintf($this->l('unable to create directory %s'),$this->tmpPath);
$this->latestRootDir = $this->latestPath.DIRECTORY_SEPARATOR.'prestashop';
}
/**
* getFilePath return the path to the zipfile containing prestashop.
*
* @return void
*/
private function getFilePath()
{
return $this->downloadPath.DIRECTORY_SEPARATOR.$this->destDownloadFilename;
}
public function postProcess()
{
$this->_setFields();
// set default configuration to default channel & dafault configuration for backup and upgrade
// (can be modified in expert mode)
$config = $this->getConfig('channel');
if ($config === false)
{
$config = array();
$config['channel'] = Upgrader::DEFAULT_CHANNEL;
$this->writeConfig($config);
if (class_exists('Configuration', false))
Configuration::updateValue('PS_UPGRADE_CHANNEL', $config['channel']);
$this->writeConfig(array (
'PS_AUTOUP_PERFORMANCE' => '1',
'PS_AUTOUP_CUSTOM_MOD_DESACT' => '1',
'PS_AUTOUP_UPDATE_DEFAULT_THEME' => '1',
'PS_AUTOUP_KEEP_MAILS' => '1',
'PS_AUTOUP_BACKUP' => '1',
'PS_AUTOUP_KEEP_IMAGES' => '0'
));
}
if (Tools14::isSubmit('putUnderMaintenance'))
Configuration::updateValue('PS_SHOP_ENABLE', 0);
if (Tools14::isSubmit('customSubmitAutoUpgrade'))
{
$config_keys = array_keys(array_merge($this->_fieldsUpgradeOptions, $this->_fieldsBackupOptions));
$config = array();
foreach ($config_keys as $key)
if (isset($_POST[$key]))
$config[$key] = $_POST[$key];
$res = $this->writeConfig($config);
if ($res)
Tools14::redirectAdmin($this->currentIndex.'&conf=6&token='.Tools14::getValue('token'));
}
if (Tools14::isSubmit('deletebackup'))
{
$res = false;
$name = Tools14::getValue('name');
$filelist = scandir($this->backupPath);
foreach($filelist as $filename)
// the following will match file or dir related to the selected backup
if (!empty($filename) && $filename[0] != '.' && $filename != 'index.php' && $filename != '.htaccess'
&& preg_match('#^(auto-backupfiles_|)'.preg_quote($name).'(\.zip|)$#', $filename, $matches))
{
if (is_file($this->backupPath.DIRECTORY_SEPARATOR.$filename))
$res &= unlink($this->backupPath.DIRECTORY_SEPARATOR.$filename);
elseif (!empty($name) && is_dir($this->backupPath.DIRECTORY_SEPARATOR.$name.DIRECTORY_SEPARATOR))
$res = self::deleteDirectory($this->backupPath.DIRECTORY_SEPARATOR.$name.DIRECTORY_SEPARATOR);
}
if ($res)
Tools14::redirectAdmin($this->currentIndex.'&conf=1&token='.Tools14::getValue('token'));
else
$this->_errors[] = sprintf($this->l('Error when trying to delete backups %s'), $name);
}
parent::postProcess();
}
/**
* ends the rollback process
*
* @return void
*/
public function ajaxProcessRollbackComplete()
{
$this->next_desc = $this->l('Restoration process done. Congratulations! You can now reactivate your shop.');
$this->next = '';
}
/**
* ends the upgrade process
*
* @return void
*/
public function ajaxProcessUpgradeComplete()
{
if (!$this->warning_exists)
$this->next_desc = $this->l('Upgrade process done. Congratulations! You can now reactivate your shop.');
else
$this->next_desc = $this->l('Upgrade process done, but some warnings have been found.');
$this->next = '';
if ($this->getConfig('channel') != 'archive' && file_exists($this->getFilePath()) && unlink($this->getFilePath()))
$this->nextQuickInfo[] = sprintf('%s removed', $this->getFilePath());
elseif (is_file($this->getFilePath()))
$this->nextQuickInfo[] = '<strong>'.sprintf('Please remove %s by ftp', $this->getFilePath()).'</strong>';
if ($this->getConfig('channel') != 'directory' && file_exists($this->latestRootDir) && self::deleteDirectory($this->latestRootDir))
$this->nextQuickInfo[] = sprintf('%s removed', $this->latestRootDir);
elseif(is_dir($this->latestRootDir))
$this->nextQuickInfo[] = '<strong>'.sprintf('Please remove %s by ftp', $this->latestRootDir).'</strong>';
}
// Simplification of _displayForm original function
protected function _displayForm($name, $fields, $tabname, $size, $icon)
{
$confValues = $this->getConfig();
$required = false;
$this->_html .= '
<fieldset id="'.$name.'Block"><legend><img src="../img/admin/'.strval($icon).'.gif" />'.$tabname.'</legend>';
foreach ($fields as $key => $field)
{
if (isset($field['required']) && $field['required'])
$required = true;
if (isset($field['disabled']) && $field['disabled'])
$disabled = true;
else
$disabled = false;
if (isset($confValues[$key]))
$val = $confValues[$key];
else
$val = isset($field['defaultValue'])?$field['defaultValue']:false;
if (!in_array($field['type'], array('image', 'radio', 'container', 'container_end')) || isset($field['show']))
$this->_html .= '<div style="clear: both; padding-top:15px;">'.($field['title'] ? '<label >'.$field['title'].'</label>' : '').'<div class="margin-form" style="padding-top:5px;">';
/* Display the appropriate input type for each field */
switch ($field['type'])
{
case 'disabled':
$this->_html .= $field['disabled'];
break;
case 'bool':
$this->_html .= '<label class="t" for="'.$key.'_on">
<img src="../img/admin/enabled.gif" alt="'.$this->l('Yes').'" title="'.$this->l('Yes').'" /></label>
<input type="radio" '.($disabled?'disabled="disabled"':'').' name="'.$key.'" id="'.$key.'_on" value="1"'.($val ? ' checked="checked"' : '').(isset($field['js']['on']) ? $field['js']['on'] : '').' />
<label class="t" for="'.$key.'_on"> '.$this->l('Yes').'</label>
<label class="t" for="'.$key.'_off"><img src="../img/admin/disabled.gif" alt="'.$this->l('No').'" title="'.$this->l('No').'" style="margin-left: 10px;" /></label>
<input type="radio" '.($disabled?'disabled="disabled"':'').' name="'.$key.'" id="'.$key.'_off" value="0" '.(!$val ? 'checked="checked"' : '').(isset($field['js']['off']) ? $field['js']['off'] : '').'/>
<label class="t" for="'.$key.'_off"> '.$this->l('No').'</label>';
break;
case 'radio':
foreach ($field['choices'] as $cValue => $cKey)
$this->_html .= '<input '.($disabled?'disabled="disabled"':'').' type="radio" name="'.$key.'" id="'.$key.$cValue.'_on" value="'.(int)($cValue).'"'.(($cValue == $val) ? ' checked="checked"' : '').(isset($field['js'][$cValue]) ? ' '.$field['js'][$cValue] : '').' /><label class="t" for="'.$key.$cValue.'_on"> '.$cKey.'</label><br />';
$this->_html .= '<br />';
break;
case 'select':
$this->_html .= '<select name='.$key.'>';
foreach ($field['choices'] as $cValue => $cKey)
$this->_html .= '<option value="'.(int)$cValue.'"'.(($cValue == $val) ? ' selected="selected"' : '').'>'.$cKey.'</option>';
$this->_html .= '</select>';
break;
case 'textarea':
$this->_html .= '<textarea '.($disabled?'disabled="disabled"':'').' name='.$key.' cols="'.$field['cols'].'" rows="'.$field['rows'].'">'.htmlentities($val, ENT_COMPAT, 'UTF-8').'</textarea>';
break;
case 'container':
$this->_html .= '<div id="'.$key.'">';
break;
case 'container_end':
$this->_html .= (isset($field['content']) === true ? $field['content'] : '').'</div>';
break;
case 'text':
default:
$this->_html .= '<input '.($disabled?'disabled="disabled"':'').' type="'.$field['type'].'"'.(isset($field['id']) === true ? ' id="'.$field['id'].'"' : '').' size="'.(isset($field['size']) ? (int)($field['size']) : 5).'" name="'.$key.'" value="'.($field['type'] == 'password' ? '' : htmlentities($val, ENT_COMPAT, 'UTF-8')).'" />'.(isset($field['next']) ? '&nbsp;'.strval($field['next']) : '');
}
$this->_html .= ((isset($field['required']) && $field['required'] && !in_array($field['type'], array('image', 'radio'))) ? ' <sup>*</sup>' : '');
$this->_html .= (isset($field['desc']) ? '<p style="clear:both">'.((isset($field['thumb']) && $field['thumb'] && $field['thumb']['pos'] == 'after') ? '<img src="'.$field['thumb']['file'].'" alt="'.$field['title'].'" title="'.$field['title'].'" style="float:left;" />' : '' ).$field['desc'].'</p>' : '');
if (!in_array($field['type'], array('image', 'radio', 'container', 'container_end')) || isset($field['show']))
$this->_html .= '</div></div>';
}
$this->_html .= ' <div align="center" style="margin-top: 20px;">
<input type="submit" value="'.$this->l(' Save ', 'AdminPreferences').'" name="customSubmitAutoUpgrade" class="button" />
</div>
'.($required ? '<div class="small"><sup>*</sup> '.$this->l('Required field', 'AdminPreferences').'</div>' : '').'
</fieldset>
<br/>';
}
/**
* return the value of $key, configuration saved in $this->configFilename.
* if $key is empty, will return an array with all configuration;
*
* @param string $key
* @access public
* @return array or string
*/
public function getConfig($key = '')
{
static $config = array();
if (count($config) == 0)
{
if (file_exists($this->autoupgradePath.DIRECTORY_SEPARATOR.$this->configFilename))
{
$config_content = Tools14::file_get_contents($this->autoupgradePath.DIRECTORY_SEPARATOR.$this->configFilename);
$config = @unserialize(base64_decode($config_content));
}
else
$config = array();
}
if (empty($key))
return $config;
elseif (isset($config[$key]))
return trim($config[$key]);
return false;
}
/**
* reset module configuration with $new_config values (previous config will be totally lost)
*
* @param array $new_config
* @return boolean true if success
*/
public function resetConfig($new_config)
{
return (bool)file_put_contents($this->autoupgradePath.DIRECTORY_SEPARATOR.$this->configFilename, base64_encode(serialize($new_config)));
}
/**
* update module configuration (saved in file $this->configFilename) with $new_config
*
* @param array $new_config
* @return boolean true if success
*/
public function writeConfig($new_config)
{
if (!file_exists($this->autoupgradePath.DIRECTORY_SEPARATOR.$this->configFilename))
return $this->resetConfig($new_config);
$config = file_get_contents($this->autoupgradePath.DIRECTORY_SEPARATOR.$this->configFilename);
$config_unserialized = @unserialize(base64_decode($config));
if (!is_array($config_unserialized))
$config_unserialized = @unserialize($config); // retrocompat, before base64_decode implemented
$config = $config_unserialized;
foreach($new_config as $key => $val)
$config[$key] = $val;
$this->next_desc = $this->l('Configuration successfully updated.').' <strong>'.$this->l('This page will now be reloaded and the module will check if a new version is available.').'</strong>';
return (bool)file_put_contents($this->autoupgradePath.DIRECTORY_SEPARATOR.$this->configFilename, base64_encode(serialize($config)));
}
/**
* update configuration after validating the new values
*
* @access public
*/
public function ajaxProcessUpdateConfig()
{
$config = array();
// nothing next
$this->next = '';
// update channel
if (isset($this->currentParams['channel']))
$config['channel'] = $this->currentParams['channel'];
if (isset($this->currentParams['private_release_link']) && isset($this->currentParams['private_release_md5']))
{
$config['channel'] = 'private';
$config['private_release_link'] = $this->currentParams['private_release_link'];
$config['private_release_md5'] = $this->currentParams['private_release_md5'];
$config['private_allow_major'] = $this->currentParams['private_allow_major'];
}
// if (!empty($this->currentParams['archive_name']) && !empty($this->currentParams['archive_num']))
if (!empty($this->currentParams['archive_prestashop']))
{
$file = $this->currentParams['archive_prestashop'];
if (!file_exists($this->downloadPath.DIRECTORY_SEPARATOR.$file))
{
$this->error = 1;
$this->next_desc = sprintf($this->l('File %s does not exist. Unable to select that channel.'), $file);
return false;
}
if (empty($this->currentParams['archive_num']))
{
$this->error = 1;
$this->next_desc = sprintf($this->l('Version number is missing. Unable to select that channel.'), $file);
return false;
}
$config['channel'] = 'archive';
$config['archive.filename'] = $this->currentParams['archive_prestashop'];
$config['archive.version_num'] = $this->currentParams['archive_num'];
// $config['archive_name'] = $this->currentParams['archive_name'];
$this->next_desc = $this->l('Upgrade process will use archive.');
}
if (isset($this->currentParams['directory_num']))
{
$config['channel'] = 'directory';
if (empty($this->currentParams['directory_num']) || strpos($this->currentParams['directory_num'], '.') === false)
{
$this->error = 1;
$this->next_desc = sprintf($this->l('Version number is missing. Unable to select that channel.'));
return false;
}
$config['directory.version_num'] = $this->currentParams['directory_num'];
}
if (isset($this->currentParams['skip_backup']))
$config['skip_backup'] = $this->currentParams['skip_backup'];
if (!$this->writeConfig($config))
{
$this->error = 1;
$this->next_desc = $this->l('Error on saving configuration');
}
}
/** returns an array containing information related to the channel $channel
*
* @param string $channel name of the channel
* @return <array> available, version_num, version_name, link, md5, changelog
*/
public function getInfoForChannel($channel)
{
$upgrade_info = array();
$public_channel = array('minor', 'major', 'rc', 'beta', 'alpha');
$upgrader = new Upgrader();
preg_match('#([0-9]+\.[0-9]+)(?:\.[0-9]+){1,2}#', _PS_VERSION_, $matches);
$upgrader->branch = $matches[1];
$upgrader->channel = $channel;
if (in_array($channel, $public_channel))
{
if ($this->getConfig('channel') == 'private' && !$this->getConfig('private_allow_major'))
$upgrader->checkPSVersion(false, array('private', 'minor'));
else
$upgrader->checkPSVersion(false, array('minor'));
$upgrade_info = array();
$upgrade_info['branch'] = $upgrader->branch;
$upgrade_info['available'] = $upgrader->available;
$upgrade_info['version_num'] = $upgrader->version_num;
$upgrade_info['version_name'] = $upgrader->version_name;
$upgrade_info['link'] = $upgrader->link;
$upgrade_info['md5'] = $upgrader->md5;
$upgrade_info['changelog'] = $upgrader->changelog;
}
else
{
switch ($channel)
{
case 'private':
if (!$this->getConfig('private_allow_major'))
$upgrader->checkPSVersion(false, array('private', 'minor'));
else
$upgrader->checkPSVersion(false, array('minor'));
$upgrade_info['available'] = $upgrader->available;
$upgrade_info['branch'] = $upgrader->branch;
$upgrade_info['version_num'] = $upgrader->version_num;
$upgrade_info['version_name'] = $upgrader->version_name;
$upgrade_info['link'] = $this->getConfig('private_release_link');
$upgrade_info['md5'] = $this->getConfig('private_release_md5');
$upgrade_info['changelog'] = $upgrader->changelog;
break;
case 'archive':
$upgrade_info['available'] = true;
break;
case 'directory':
$upgrade_info['available'] = true;
break;
}
}
return $upgrade_info;
}
/**
* display informations related to the selected channel : link/changelog for remote channel,
* or configuration values for special channels
*
* @access public
*/
public function ajaxProcessGetChannelInfo()
{
// do nothing after this request (see javascript function doAjaxRequest )
$this->next = '';
$channel = $this->currentParams['channel'];
$upgrade_info = $this->getInfoForChannel($channel);
$this->nextParams['result']['available'] = $upgrade_info['available'];
$this->nextParams['result']['div'] = $this->divChannelInfos($upgrade_info);
}
/**
* get the list of all modified and deleted files between current version
* and target version (according to channel configuration)
*
* @access public
*/
public function ajaxProcessCompareReleases()
{
// do nothing after this request (see javascript function doAjaxRequest )
$this->next = '';
$channel = $this->getConfig('channel');
$this->upgrader = new Upgrader();
switch ($channel)
{
case 'archive':
$version = $this->getConfig('archive.version_num');
break;
case 'directory':
$version = $this->getConfig('directory.version_num');
break;
default:
preg_match('#([0-9]+\.[0-9]+)(?:\.[0-9]+){1,2}#', _PS_VERSION_, $matches);
$this->upgrader->branch = $matches[1];
$this->upgrader->channel = $channel;
if ($this->getConfig('channel') == 'private' && !$this->getConfig('private_allow_major'))
$this->upgrader->checkPSVersion(false, array('private', 'minor'));
else
$this->upgrader->checkPSVersion(false, array('minor'));
$version = $this->upgrader->version_num;
}
$diffFileList = $this->upgrader->getDiffFilesList(_PS_VERSION_, $version);
if (!is_array($diffFileList))
{
$this->nextParams['status'] = 'error';
$this->nextParams['msg'] = sprintf('Unable to generate diff file list between %1$s and %2$s.', _PS_VERSION_, $version);
}
else
{
file_put_contents($this->autoupgradePath.DIRECTORY_SEPARATOR.$this->diffFileList, base64_encode(serialize($diffFileList)));
if (count($diffFileList) > 0)
$this->nextParams['msg'] = sprintf($this->l('%1$s files will be modified, %2$s files will be deleted (if they are found).'),
count($diffFileList['modified']), count($diffFileList['deleted']));
else
$this->nextParams['msg'] = $this->l('No diff files found.');
$this->nextParams['result'] = $diffFileList;
}
}
/**
* list the files modified in the current installation regards to the original version
*
* @access public
*/
public function ajaxProcessCheckFilesVersion()
{
// do nothing after this request (see javascript function doAjaxRequest )
$this->next = '';
$this->upgrader = new Upgrader();
$changedFileList = $this->upgrader->getChangedFilesList();
if ($this->upgrader->isAuthenticPrestashopVersion() === true
&& !is_array($changedFileList) )
{
$this->nextParams['status'] = 'error';
$this->nextParams['msg'] = $this->l('Unable to check files for the installed version of PrestaShop.');
$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, base64_encode(serialize($changedFileList['translation'])));
if (!isset($changedFileList['mail']))
$changedFileList['mail'] = array();
file_put_contents($this->autoupgradePath.DIRECTORY_SEPARATOR.$this->mailCustomList, base64_encode(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 file modifications have been detected, including %2$s from core and native modules:'), count(array_merge($changedFileList['core'], $changedFileList['mail'], $changedFileList['translation'])), count($changedFileList['core'])));
}
$this->nextParams['result'] = $changedFileList;
}
}
/**
* very first step of the upgrade process. The only thing done is the selection
* of the next step
*
* @access public
* @return void
*/
public function ajaxProcessUpgradeNow()
{
$this->next_desc = $this->l('Starting upgrade...');
$channel = $this->getConfig('channel');
$this->next = 'download';
if (!is_object($this->upgrader))
$this->upgrader = new Upgrader();
preg_match('#([0-9]+\.[0-9]+)(?:\.[0-9]+){1,2}#', _PS_VERSION_, $matches);
$this->upgrader->branch = $matches[1];
$this->upgrader->channel = $channel;
if ($this->getConfig('channel') == 'private' && !$this->getConfig('private_allow_major'))
$this->upgrader->checkPSVersion(false, array('private', 'minor'));
else
$this->upgrader->checkPSVersion(false, array('minor'));
switch ($channel)
{
case 'directory' :
// if channel directory is chosen, we assume it's "ready for use" (samples already removed for example)
$this->next = 'removeSamples';
$this->nextQuickInfo[] = $this->l('Skip downloading and unziping steps, upgrade process will now remove sample data.');
$this->next_desc = $this->l('Shop deactivated. Removing sample files...');
break;
case 'archive' :
$this->next = 'unzip';
$this->nextQuickInfo[] = $this->l('Skip downloading step, upgrade process will now unzip the local archive.');
$this->next_desc = $this->l('Shop deactivated. Extracting files...');
break;
default :
$this->next = 'download';
$this->next_desc = $this->l('Shop deactivated. Now downloading... (this can take a while)');
if ($this->upgrader->channel == 'private')
{
$this->upgrader->link = $this->getConfig('private_release_link');
$this->upgrader->md5 = $this->getConfig('private_release_md5');
}
$this->nextQuickInfo[] = sprintf($this->l('Downloaded archive will come from %s'), $this->upgrader->link);
$this->nextQuickInfo[] = sprintf($this->l('MD5 hash will be checked against %s'), $this->upgrader->md5);
}
}
/**
* extract chosen version into $this->latestPath directory
*
* @return void
*/
public function ajaxProcessUnzip()
{
$filepath = $this->getFilePath();
$destExtract = $this->latestPath;
if (file_exists($destExtract))
{
self::deleteDirectory($destExtract, false);
$this->nextQuickInfo[] = $this->l('"/latest" directory has been emptied');
}
$relative_extract_path = str_replace(_PS_ROOT_DIR_, '', $destExtract);
$report = '';
if (ConfigurationTest::test_dir($relative_extract_path, false, $report))
{
if ($this->ZipExtract($filepath, $destExtract))
{
// Unsetting to force listing
unset($this->nextParams['removeList']);
$this->next = 'removeSamples';
$this->next_desc = $this->l('File extraction complete. Removing sample files...');
return true;
}
else
{
$this->next = 'error';
$this->next_desc = sprintf($this->l('Unable to extract %1$s file into %2$s folder...'), $filepath, $destExtract);
return true;
}
}
else
{
$this->next_desc = $this->l('Extraction directory is not writable.');
$this->nextQuickInfo[] = $this->l('Extraction directory is not writable.');
$this->nextErrors[] = sprintf($this->l('Extraction directory %s is not writable.'), $destExtract);
$this->next = 'error';
}
}
/**
* _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)
*
* @param string $dir directory to look in
* @param string $fileext suffixe filename
* @return void
*/
private function _listSampleFiles($dir, $fileext = '.jpg'){
$res = false;
$dir = rtrim($dir,'/').DIRECTORY_SEPARATOR;
$toDel = false;
if (is_dir($dir) && is_readable($dir))
$toDel = scandir($dir);
// copied (and kind of) adapted from AdminImages.php
if (is_array($toDel))
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_directories = false)
{
$list = array();
$dir = rtrim($dir,'/').DIRECTORY_SEPARATOR;
$allFiles = false;
if (is_dir($dir) && is_readable($dir))
$allFiles = scandir($dir);
if (is_array($allFiles))
foreach ($allFiles as $file)
if ($file[0] != '.')
{
$fullPath = $dir.$file;
if (!$this->_skipFile($file, $fullPath, $way))
{
if (is_dir($fullPath))
{
$list = array_merge($list, $this->_listFilesInDir($fullPath, $way, $list_directories));
if ($list_directories)
$list[] = $fullPath;
}
else
$list[] = $fullPath;
}
}
return $list;
}
/**
* this function list all files that will be remove to retrieve the filesystem states before the upgrade
*
* @access public
* @return void
*/
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 = false;
// note : getDiffFilesList does not include files moved by upgrade scripts,
// so this method can't be trusted to fully restore directory
// $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', true);
$admin_dir = str_replace($this->prodRootDir, '', $this->adminDir);
// if a file in "ToRemove" has been skipped during backup,
// just keep it
foreach ($toRemove as $key => $file)
{
$filename = substr($file, strrpos($file, '/')+1);
$toRemove[$key] = preg_replace('#^/admin#', $admin_dir, $file);
// this is a really sensitive part, so we add an extra checks: preserve everything that contains "autoupgrade"
if ($this->_skipFile($filename, $file, 'backup') || strpos($file, $this->autoupgradeDir))
unset($toRemove[$key]);
}
return $toRemove;
}
/**
* list files to upgrade and return it as array
*
* @param string $dir
* @return number of files found
*/
public function _listFilesToUpgrade($dir)
{
static $list = array();
if (!is_dir($dir))
{
$this->nextQuickInfo[] = sprintf($this->l('[ERROR] %s does not exist or is not a directory.'), $dir);
$this->nextErrors[] = sprintf($this->l('[ERROR] %s does not exist or is not a directory.'), $dir);
$this->next_desc = $this->l('Nothing has been extracted. It seems the unzipping 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);
}
}
return $list;
}
public function ajaxProcessUpgradeFiles()
{
$this->nextParams = $this->currentParams;
$admin_dir = str_replace($this->prodRootDir.DIRECTORY_SEPARATOR, '', $this->adminDir);
if (file_exists($this->latestRootDir.DIRECTORY_SEPARATOR.'admin'))
rename($this->latestRootDir.DIRECTORY_SEPARATOR.'admin', $this->latestRootDir.DIRECTORY_SEPARATOR.$admin_dir);
elseif (file_exists($this->latestRootDir.DIRECTORY_SEPARATOR.'admin-dev'))
rename($this->latestRootDir.DIRECTORY_SEPARATOR.'admin-dev', $this->latestRootDir.DIRECTORY_SEPARATOR.$admin_dir);
if (file_exists($this->latestRootDir.DIRECTORY_SEPARATOR.'install-dev'))
rename($this->latestRootDir.DIRECTORY_SEPARATOR.'install-dev', $this->latestRootDir.DIRECTORY_SEPARATOR.'install');
if (!isset($this->nextParams['filesToUpgrade']))
{
// list saved in $this->toUpgradeFileList
// get files differences (previously generated)
$admin_dir = trim(str_replace($this->prodRootDir, '', $this->adminDir), DIRECTORY_SEPARATOR);
$filepath_list_diff = $this->autoupgradePath.DIRECTORY_SEPARATOR.$this->diffFileList;
if (file_exists($filepath_list_diff))
{
$list_files_diff = unserialize(base64_decode(file_get_contents($filepath_list_diff)));
// only keep list of files to delete. The modified files will be listed with _listFilesToUpgrade
$list_files_diff = $list_files_diff['deleted'];
foreach ($list_files_diff as $k => $path)
if (preg_match("#autoupgrade#", $path))
unset($list_files_diff[$k]);
else
$list_files_diff[$k] = str_replace('/'.'admin', '/'.$admin_dir, $path); // do not replace by DIRECTORY_SEPARATOR
}
else
$list_files_diff = array();
if (!($list_files_to_upgrade = $this->_listFilesToUpgrade($this->latestRootDir)))
return false;
// also add files to remove
$list_files_to_upgrade = array_merge($list_files_diff, $list_files_to_upgrade);
// save in a serialized array in $this->toUpgradeFileList
file_put_contents($this->autoupgradePath.DIRECTORY_SEPARATOR.$this->toUpgradeFileList, base64_encode(serialize($list_files_to_upgrade)));
$this->nextParams['filesToUpgrade'] = $this->toUpgradeFileList;
$total_files_to_upgrade = count($list_files_to_upgrade);
if ($total_files_to_upgrade == 0)
{
$this->nextQuickInfo[] = $this->l('[ERROR] Unable to find files to upgrade.');
$this->nextErrors[] = $this->l('[ERROR] Unable to find files to upgrade.');
$this->next_desc = $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_desc = sprintf($this->l('%s files will be upgraded.'), $total_files_to_upgrade);
$this->next = 'upgradeFiles';
$this->stepDone = false;
return true;
}
// later we could choose between _PS_ROOT_DIR_ or _PS_TEST_DIR_
$this->destUpgradePath = $this->prodRootDir;
$this->next = 'upgradeFiles';
$filesToUpgrade = @unserialize(base64_decode(file_get_contents($this->autoupgradePath.DIRECTORY_SEPARATOR.$this->nextParams['filesToUpgrade'])));
if (!is_array($filesToUpgrade))
{
$this->next = 'error';
$this->next_desc = $this->l('filesToUpgrade is not an array');
$this->nextQuickInfo[] = $this->l('filesToUpgrade is not an array');
$this->nextErrors[] = $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 (count($filesToUpgrade) <= 0)
{
$this->next = 'upgradeDb';
if (file_exists(($this->nextParams['filesToUpgrade'])))
unlink($this->nextParams['filesToUpgrade']);
$this->next_desc = $this->l('All files upgraded. Now upgrading database...');
$this->nextResponseType = 'json';
$this->stepDone = true;
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 file %s.'), $file);
$this->nextErrors[] = sprintf($this->l('Error when trying to upgrade file %s.'), $file);
break;
}
}
file_put_contents($this->autoupgradePath.DIRECTORY_SEPARATOR.$this->nextParams['filesToUpgrade'], base64_encode(serialize($filesToUpgrade)));
if (count($filesToUpgrade) > 0)
{
if (count($filesToUpgrade))
{
$this->next_desc = sprintf($this->l('%1$s files left to upgrade.'), count($filesToUpgrade));
$this->nextQuickInfo[] = sprintf($this->l('%2$s files left to upgrade.'), (isset($file)?$file:''), count($filesToUpgrade));
$this->stepDone = false;
}
}
return true;
}
private function createCacheFsDirectories($level_depth, $directory = false)
{
if (!$directory)
{
if (!defined('_PS_CACHEFS_DIRECTORY_'))
define('_PS_CACHEFS_DIRECTORY_', $this->prodRootDir.'/cache/cachefs/');
$directory = _PS_CACHEFS_DIRECTORY_;
}
$chars = '0123456789abcdef';
for ($i = 0; $i < strlen($chars); $i++)
{
$new_dir = $directory.$chars[$i].'/';
if (mkdir($new_dir, 0775) && chmod($new_dir, 0775) && $level_depth - 1 > 0)
self::createCacheFsDirectories($level_depth - 1, $new_dir);
}
}
/**
* list modules to upgrade and save them in a serialized array in $this->toUpgradeModuleList
*
* @param string $dir
* @return number of files found
*/
public function _listModulesToUpgrade()
{
static $list = array();
$dir = $this->prodRootDir.DIRECTORY_SEPARATOR.'modules';
if (!is_dir($dir))
{
$this->nextQuickInfo[] = sprintf($this->l('[ERROR] %s does not exist or is not a directory.'), $dir);
$this->nextErrors[] = sprintf($this->l('[ERROR] %s does not exist or is not a directory.'), $dir);
$this->next_desc = $this->l('Nothing has been extracted. It seems the unzip step has been skipped.');
$this->next = 'error';
return false;
}
$allModules = scandir($dir);
foreach ($allModules as $module_name)
{
if (is_file($dir.DIRECTORY_SEPARATOR.$module_name))
continue;
elseif (is_dir($dir.DIRECTORY_SEPARATOR.$module_name.DIRECTORY_SEPARATOR))
{
if(is_array($this->modules_addons))
$id_addons = array_search($module_name, $this->modules_addons);
if (isset($id_addons) && $id_addons)
if ($module_name != $this->autoupgradeDir)
$list[] = array('id' => $id_addons, 'name' => $module_name);
}
}
file_put_contents($this->autoupgradePath.DIRECTORY_SEPARATOR.$this->toUpgradeModuleList, base64_encode(serialize($list)));
$this->nextParams['modulesToUpgrade'] = $this->toUpgradeModuleList;
return count($list);
}
/**
* upgrade all partners modules according to the installed prestashop version
*
* @access public
* @return void
*/
public function ajaxProcessUpgradeModules()
{
$start_time = time();
if (!isset($this->nextParams['modulesToUpgrade']))
{
// list saved in $this->toUpgradeFileList
$total_modules_to_upgrade = $this->_listModulesToUpgrade();
if ($total_modules_to_upgrade)
{
$this->nextQuickInfo[] = sprintf($this->l('%s modules will be upgraded.'), $total_modules_to_upgrade);
$this->next_desc = sprintf($this->l('%s modules will be upgraded.'), $total_modules_to_upgrade);
}
$this->stepDone = false;
$this->next = 'upgradeModules';
return true;
}
$this->next = 'upgradeModules';
if (file_exists($this->autoupgradePath.DIRECTORY_SEPARATOR.$this->nextParams['modulesToUpgrade']))
$listModules = @unserialize(base64_decode(file_get_contents($this->autoupgradePath.DIRECTORY_SEPARATOR.$this->nextParams['modulesToUpgrade'])));
else
$listModules = array();
if (!is_array($listModules))
{
$this->next = 'upgradeComplete';
$this->warning_exists = true;
$this->next_desc = $this->l('upgradeModule step has not ended correctly.');
$this->nextQuickInfo[] = $this->l('listModules is not an array. No module has been updated.');
$this->nextErrors[] = $this->l('listModules is not an array. No module has been updated.');
return true;
}
$time_elapsed = time() - $start_time;
// module list
if (count($listModules) > 0)
{
do
{
$module_info = array_shift($listModules);
$this->upgradeThisModule($module_info['id'], $module_info['name']);
$time_elapsed = time() - $start_time;
}
while (($time_elapsed < self::$loopUpgradeModulesTime) && count($listModules) > 0);
$modules_left = count($listModules);
file_put_contents($this->autoupgradePath.DIRECTORY_SEPARATOR.$this->toUpgradeModuleList, base64_encode(serialize($listModules)));
unset($listModules);
$this->next = 'upgradeModules';
if ($modules_left)
$this->next_desc = sprintf($this->l('%s modules left to upgrade.'), $modules_left);
$this->stepDone = false;
}
else
{
if (version_compare($this->install_version, '1.5.0.0', '>='))
{
$modules_to_delete['backwardcompatibility'] = 'Backward Compatibility';
$modules_to_delete['dibs'] = 'Dibs';
$modules_to_delete['cloudcache'] = 'Cloudcache';
$modules_to_delete['mobile_theme'] = 'The 1.4 mobile_theme';
$modules_to_delete['trustedshops'] = 'Trustedshops';
$modules_to_delete['dejala'] = 'Dejala';
$modules_to_delete['stripejs'] = 'Stripejs';
$modules_to_delete['blockvariouslinks'] = 'Block Various Links';
foreach($modules_to_delete as $key => $module)
{
Db::getInstance()->execute('DELETE ms.*, hm.*
FROM `'._DB_PREFIX_.'module_shop` ms
INNER JOIN `'._DB_PREFIX_.'hook_module` hm USING (`id_module`)
INNER JOIN `'._DB_PREFIX_.'module` m USING (`id_module`)
WHERE m.`name` LIKE \''.pSQL($key).'\'');
Db::getInstance()->execute('UPDATE `'._DB_PREFIX_.'module` SET `active` = 0 WHERE `name` LIKE \''.pSQL($key).'\'');
$path = $this->prodRootDir.DIRECTORY_SEPARATOR.'modules'.DIRECTORY_SEPARATOR.$key.DIRECTORY_SEPARATOR;
if (file_exists($path.$key.'.php'))
{
if (self::deleteDirectory($path))
$this->nextQuickInfo[] = sprintf($this->l('%1$s module is not compatible with 1.5.X, it will be removed from your ftp.'), $module);
else
$this->nextErrors[] = sprintf($this->l('%1$s module is not compatible with 1.5.X, please remove it on your ftp.'), $module);
}
}
}
$this->stepDone = true;
$this->status = 'ok';
$this->next = 'cleanDatabase';
$this->next_desc = $this->l('Addons modules files have been upgraded.');
$this->nextQuickInfo[] = $this->l('Addons modules files have been upgraded.');
if ($this->manualMode)
$this->writeConfig(array('PS_AUTOUP_MANUAL_MODE' => '0'));
return true;
}
return true;
}
/**
* upgrade module $name (identified by $id_module on addons server)
*
* @param mixed $id_module
* @param mixed $name
* @access public
* @return void
*/
public function upgradeThisModule($id_module, $name)
{
$zip_fullpath = $this->tmpPath.DIRECTORY_SEPARATOR.$name.'.zip';
$dest_extract = $this->prodRootDir.DIRECTORY_SEPARATOR.'modules'.DIRECTORY_SEPARATOR;
$addons_url = 'api.addons.prestashop.com';
$protocolsList = array('https://' => 443, 'http://' => 80);
if (!extension_loaded('openssl'))
unset($protocolsList['https://']);
else
unset($protocolsList['http://']);
$postData = 'version='.$this->install_version.'&method=module&id_module='.(int)$id_module;
// Make the request
$opts = array(
'http'=>array(
'method'=> 'POST',
'content' => $postData,
'header' => 'Content-type: application/x-www-form-urlencoded',
'timeout' => 10,
)
);
$context = stream_context_create($opts);
foreach ($protocolsList as $protocol => $port)
{
// file_get_contents can return false if https is not supported (or warning)
$content = Tools14::file_get_contents($protocol.$addons_url, false, $context);
if ($content == false || substr($content, 5) == '<?xml')
continue;
if ($content !== null)
{
if ((bool)file_put_contents($zip_fullpath, $content))
{
if (filesize($zip_fullpath) <= 300)
unlink($zip_fullpath);
// unzip in modules/[mod name] old files will be conserved
elseif ($this->ZipExtract($zip_fullpath, $dest_extract))
{
$this->nextQuickInfo[] = sprintf($this->l('The files of module %s have been upgraded.'), $name);
if (file_exists($zip_fullpath))
unlink($zip_fullpath);
}
else
{
$this->nextQuickInfo[] = sprintf($this->l('[WARNING] Error when trying to upgrade module %s.'), $name);
$this->warning_exists = 1;
}
}
else
{
$this->nextQuickInfo[] = sprintf($this->l('[ERROR] Unable to write module %s\'s zip file in temporary directory.'), $name);
$this->nextErrors[] = sprintf($this->l('[ERROR] Unable to write module %s\'s zip file in temporary directory.'), $name);
$this->warning_exists = 1;
}
}
else
{
$this->nextQuickInfo[] = sprintf($this->l('[ERROR] No response from Addons server.'));
$this->nextErrors[] = sprintf($this->l('[ERROR] No response from Addons server.'));
$this->warning_exists = 1;
}
}
return true;
}
public function ajaxProcessUpgradeDb()
{
$this->nextParams = $this->currentParams;
if (!$this->doUpgrade())
{
$this->next = 'error';
$this->next_desc = $this->l('Error during database upgrade. You may need to restore your database.');
return false;
}
$this->next = 'upgradeModules';
$this->next_desc = $this->l('Database upgraded. Now upgrading your Addons modules...');
return true;
}
/**
* Clean the database from unwanted entires
*
* @return void
*/
public function ajaxProcessCleanDatabase()
{
global $warningExists;
/* Clean tabs order */
foreach ($this->db->ExecuteS('SELECT DISTINCT id_parent FROM '._DB_PREFIX_.'tab') as $parent)
{
$i = 1;
foreach ($this->db->ExecuteS('SELECT id_tab FROM '._DB_PREFIX_.'tab WHERE id_parent = '.(int)$parent['id_parent'].' ORDER BY IF(class_name IN ("AdminHome", "AdminDashboard"), 1, 2), position ASC') as $child)
$this->db->Execute('UPDATE '._DB_PREFIX_.'tab SET position = '.(int)($i++).' WHERE id_tab = '.(int)$child['id_tab'].' AND id_parent = '.(int)$parent['id_parent']);
}
/* Clean configuration integrity */
$this->db->Execute('DELETE FROM `'._DB_PREFIX_.'configuration_lang` WHERE (`value` IS NULL AND `date_upd` IS NULL) OR `value` LIKE ""', false);
$this->status = 'ok';
$this->next = 'upgradeComplete';
$this->next_desc = $this->l('The database has been cleaned.');
$this->nextQuickInfo[] = $this->l('The database has been cleaned.');
}
/**
* 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')
&& ((substr($memory_limit,-1) == 'M' AND substr($memory_limit,0,-1) < 128)
|| 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']) || empty($_SERVER['REQUEST_URI']))
{
if (!isset($_SERVER['SCRIPT_NAME']) && isset($_SERVER['SCRIPT_FILENAME']))
$_SERVER['SCRIPT_NAME'] = $_SERVER['SCRIPT_FILENAME'];
if (isset($_SERVER['SCRIPT_NAME']))
{
if (basename($_SERVER['SCRIPT_NAME']) == 'index.php' && empty($_SERVER['QUERY_STRING']))
$_SERVER['REQUEST_URI'] = dirname($_SERVER['SCRIPT_NAME']).'/';
else
{
$_SERVER['REQUEST_URI'] = $_SERVER['SCRIPT_NAME'];
if (isset($_SERVER['QUERY_STRING']) && !empty($_SERVER['QUERY_STRING']))
$_SERVER['REQUEST_URI'] .= '?'.$_SERVER['QUERY_STRING'];
}
}
}
$_SERVER['REQUEST_URI'] = str_replace('//', '/', $_SERVER['REQUEST_URI']);
define('INSTALL_VERSION', $this->install_version);
// 1.4
define('INSTALL_PATH', realpath($this->latestRootDir.DIRECTORY_SEPARATOR.'install'));
// 1.5 ...
define('_PS_INSTALL_PATH_', INSTALL_PATH.DIRECTORY_SEPARATOR);
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/');
$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->next_desc = $this->l('/install/upgrade/php directory is missing in archive or directory');
$this->nextQuickInfo[] = '/install/upgrade/php directory is missing in archive or directory';
$this->nextErrors[] = '/install/upgrade/php directory is missing in archive or directory.';
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.');
$this->nextErrors[] = $this->l('The config/settings.inc.php file was not found.');
return false;
}
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[] = sprintf($this->l('Current version: %1$s. Version to install: %2$s.'), $oldversion, INSTALL_VERSION);
$this->nextErrors[] = sprintf($this->l('Current version: %1$s. Version to install: %2$s'), $oldversion, INSTALL_VERSION);
$this->nextQuickInfo[] = $this->l('[ERROR] Version to install is too old.');
$this->nextErrors[] = $this->l('[ERROR] Version to install is too old.');
return false;
}
elseif ($versionCompare == 0)
{
$this->next = 'error';
$this->nextQuickInfo[] = $this->l(sprintf('You already have the %s version.',INSTALL_VERSION));
$this->nextErrors[] = $this->l(sprintf('You already have the %s version.',INSTALL_VERSION));
return false;
}
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?');
$this->nextErrors[] = $this->l('There is no older version. Did you delete or rename the config/settings.inc.php file?');
return false;
}
//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');
$this->nextErrors[] = $this->l('Invalid database configuration');
return false;
}
//custom sql file creation
$upgradeFiles = array();
$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->next_desc = $this->l('Unable to find upgrade directory in the installation 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 check that the %s folder is not empty.'), $upgrade_dir_sql);
$this->nextErrors[] = sprintf($this->l('Cannot find the SQL upgrade files. Please check that the %s folder is not empty.'), $upgrade_dir_sql);
// fail 31
return false;
}
natcasesort($upgradeFiles);
$neededUpgradeFiles = array();
$arrayVersion = explode('.', $oldversion);
$versionNumbers = count($arrayVersion);
if ($versionNumbers != 4)
$arrayVersion = array_pad($arrayVersion, 4, '0');
$oldversion = implode('.', $arrayVersion);
foreach ($upgradeFiles as $version)
if (version_compare($version, $oldversion) == 1 && version_compare(INSTALL_VERSION, $version) != -1)
$neededUpgradeFiles[] = $version;
if (empty($neededUpgradeFiles) || count($neededUpgradeFiles) === 0)
{
$this->next = 'error';
$this->nextQuickInfo[] = $this->l('No upgrade is possible.');
$this->nextErrors[] = $this->l('No upgrade is possible.');
if (strpos(INSTALL_VERSION, '.') === false)
{
$this->nextQuickInfo[] = sprintf($this->l('%s is not a valid version number.'), INSTALL_VERSION);
$this->nextErrors[] = sprintf($this->l('%s is not a valid version number.'), INSTALL_VERSION);
}
return false;
}
$sqlContentVersion = array();
if ($this->deactivateCustomModule)
{
require_once(_PS_INSTALLER_PHP_UPGRADE_DIR_.'deactivate_custom_modules.php');
deactivate_custom_modules();
}
if (version_compare(INSTALL_VERSION, '1.5.6.1', '='))
{
$filename = _PS_INSTALLER_PHP_UPGRADE_DIR_.'migrate_orders.php';
$content = file_get_contents($filename);
$str_old[] = '$values_order_detail = array();';
$str_old[] = '$values_order = array();';
$str_old[] = '$col_order_detail = array();';
$content = str_replace($str_old, '', $content);
file_put_contents($filename, $content);
}
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);
$this->nextErrors[] = sprintf($this->l('Error while loading SQL upgrade file "%s.sql".'), $version);
return false;
$logger->logError('Error while loading SQL upgrade file.');
}
if (!$sqlContent = file_get_contents($file)."\n")
{
$this->next = 'error';
$this->nextQuickInfo[] = $this->l(sprintf('Error while loading SQL upgrade file %s.', $version));
$this->nextErrors[] = $this->l(sprintf('Error while loading sql SQL file %s.', $version));
return false;
$logger->logError(sprintf('Error while loading sql upgrade file %s.', $version));
}
$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 (version_compare(INSTALL_VERSION, '1.5.5.0', '=') && $func_name == 'fix_download_product_feature_active')
continue;
if (!file_exists(_PS_INSTALLER_PHP_UPGRADE_DIR_.strtolower($func_name).'.php'))
{
$this->nextQuickInfo[] = '<div class="upgradeDbError">[ERROR] '.$upgrade_file.' PHP - missing file '.$query.'</div>';
$this->nextErrors[] = '[ERROR] '.$upgrade_file.' PHP - missing file '.$query;
$warningExist = true;
}
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[] = '<div class="upgradeDbError">[ERROR] '.$upgrade_file.' PHP - Object Method call is forbidden ( '.$php[0].'::'.str_replace($pattern[0], '', $php[1]).')</div>';
$this->nextErrors[] = '[ERROR] '.$upgrade_file.' PHP - Object Method call is forbidden ('.$php[0].'::'.str_replace($pattern[0], '', $php[1]).')';
$warningExist = true;
}
if (isset($phpRes) && (is_array($phpRes) && !empty($phpRes['error'])) || $phpRes === false)
{
// $this->next = 'error';
$this->nextQuickInfo[] = '
<div class="upgradeDbError">
[ERROR] PHP '.$upgrade_file.' '.$query."\n".'
'.(empty($phpRes['error']) ? '' : $phpRes['error']."\n").'
'.(empty($phpRes['msg']) ? '' : ' - '.$phpRes['msg']."\n").'
</div>';
$this->nextErrors[] = '
[ERROR] PHP '.$upgrade_file.' '.$query."\n".'
'.(empty($phpRes['error']) ? '' : $phpRes['error']."\n").'
'.(empty($phpRes['msg']) ? '' : ' - '.$phpRes['msg']."\n");
$warningExist = true;
}
else
$this->nextQuickInfo[] = '<div class="upgradeDbOk">[OK] PHP '.$upgrade_file.' : '.$query.'</div>';
if (isset($phpRes))
unset($phpRes);
}
else
{
if (strstr($query, 'CREATE TABLE') !== false)
{
$pattern = '/CREATE TABLE.*[`]*'._DB_PREFIX_.'([^`]*)[`]*\s\(/';
preg_match($pattern, $query, $matches);;
if (isset($matches[1]) && $matches[1])
{
$drop = 'DROP TABLE IF EXISTS `'._DB_PREFIX_.$matches[1].'`;';
$result = $this->db->execute($drop, false);
if ($result)
$this->nextQuickInfo[] = '<div class="upgradeDbOk">'.sprintf($this->l('[DROP] SQL %s table has been dropped.'), '`'._DB_PREFIX_.$matches[1].'`').'</div>';
}
}
$result = $this->db->execute($query, false);
if (!$result)
{
$error = $this->db->getMsgError();
$error_number = $this->db->getNumberError();
$this->nextQuickInfo[] = '
<div class="upgradeDbError">
[WARNING] SQL '.$upgrade_file.'
'.$error_number.' in '.$query.': '.$error.'</div>';
$duplicates = array('1050', '1054', '1060', '1061', '1062', '1091');
if (!in_array($error_number, $duplicates))
{
$this->nextErrors[] = 'SQL '.$upgrade_file.' '.$error_number.' in '.$query.': '.$error;
$warningExist = true;
}
}
else
$this->nextQuickInfo[] = '<div class="upgradeDbOk">[OK] SQL '.$upgrade_file.' '.$query.'</div>';
}
if (isset($query))
unset($query);
}
}
if ($this->next == 'error')
{
$this->next_desc = $this->l('An error happened during the database upgrade.');
return false;
}
$this->nextQuickInfo[] = $this->l('Database upgrade 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
$arrayToClean[] = $this->prodRootDir.'/tools/smarty/cache/';
$arrayToClean[] = $this->prodRootDir.'/tools/smarty/compile/';
$arrayToClean[] = $this->prodRootDir.'/tools/smarty_v2/cache/';
$arrayToClean[] = $this->prodRootDir.'/tools/smarty_v2/compile/';
if (version_compare(INSTALL_VERSION, '1.5.0.0', '>'))
{
$arrayToClean[] = $this->prodRootDir.'/cache/smarty/cache/';
$arrayToClean[] = $this->prodRootDir.'/cache/smarty/compile/';
}
foreach ($arrayToClean as $dir)
if (!file_exists($dir))
{
$this->nextQuickInfo[] = sprintf($this->l('[SKIP] directory "%s" doesn\'t exist and cannot be emptied.'), str_replace($this->prodRootDir, '', $dir));
continue;
}
else
foreach (scandir($dir) as $file)
if ($file[0] != '.' && $file != 'index.php' && $file != '.htaccess')
{
if (is_file($dir.$file))
unlink($dir.$file);
elseif(is_dir($dir.$file.DIRECTORY_SEPARATOR))
self::deleteDirectory($dir.$file.DIRECTORY_SEPARATOR);
$this->nextQuickInfo[] = sprintf($this->l('[CLEANING CACHE] File %s removed'), $file);
}
if (version_compare(INSTALL_VERSION, '1.5.0.0', '>'))
{
Db::getInstance()->execute('UPDATE `'._DB_PREFIX_.'configuration` SET `name` = \'PS_LEGACY_IMAGES\' WHERE name LIKE \'0\' AND `value` = 1');
Db::getInstance()->execute('UPDATE `'._DB_PREFIX_.'configuration` SET `value` = 0 WHERE `name` LIKE \'PS_LEGACY_IMAGES\'');
if (Db::getInstance()->getValue('SELECT COUNT(id_product_download) FROM `'._DB_PREFIX_.'product_download` WHERE `active` = 1') > 0)
Db::getInstance()->execute('UPDATE `'._DB_PREFIX_.'configuration` SET `value` = 1 WHERE `name` LIKE \'PS_VIRTUAL_PROD_FEATURE_ACTIVE\'');
if (defined('_THEME_NAME_') && $this->updateDefaultTheme && preg_match('#(default|prestashop|default-boostrap)$#', _THEME_NAME_))
{
$separator = addslashes(DIRECTORY_SEPARATOR);
$file = _PS_ROOT_DIR_.$separator.'themes'.$separator._THEME_NAME_.$separator.'cache'.$separator;
if (file_exists($file))
foreach (scandir($file) as $cache)
if ($cache[0] != '.' && $cache != 'index.php' && $cache != '.htaccess' && file_exists($file.$cache) && !is_dir($file.$cache))
if (file_exists($dir.$cache))
unlink($file.$cache);
}
if (version_compare(_PS_VERSION_, '1.5.0.0', '<='))
{
$dir = _PS_ROOT_DIR_.'/controllers/';
if (file_exists($dir))
foreach (scandir($dir) as $file)
if (!is_dir($file) && $file[0] != '.' && $file != 'index.php' && $file != '.htaccess')
if (file_exists($dir.basename(str_replace('.php', '', $file).'.php')))
unlink($dir.basename($file));
$dir = _PS_ROOT_DIR_.'/classes/';
foreach(self::$classes14 as $class)
if (file_exists($dir.basename($class).'.php'))
unlink($dir.basename($class).'.php');
$dir = _PS_ADMIN_DIR_.'/tabs/';
if (file_exists($dir))
foreach (scandir($dir) as $file)
if (!is_dir($file) && $file[0] != '.' && $file != 'index.php' && $file != '.htaccess')
if (file_exists($dir.basename(str_replace('.php', '', $file).'.php')))
unlink($dir.basename($file));
}
if (version_compare($this->install_version, '1.5.4.0', '>='))
{
// Upgrade languages
if (!defined('_PS_TOOL_DIR_'))
define('_PS_TOOL_DIR_', _PS_ROOT_DIR_.'/tools/');
if (!defined('_PS_TRANSLATIONS_DIR_'))
define('_PS_TRANSLATIONS_DIR_', _PS_ROOT_DIR_.'/translations/');
if (!defined('_PS_MODULES_DIR_'))
define('_PS_MODULES_DIR_', _PS_ROOT_DIR_.'/modules/');
if (!defined('_PS_MAILS_DIR_'))
define('_PS_MAILS_DIR_', _PS_ROOT_DIR_.'/mails/');
$langs = Db::getInstance()->executeS('SELECT * FROM `'._DB_PREFIX_.'lang` WHERE `active` = 1');
require_once(_PS_TOOL_DIR_.'tar/Archive_Tar.php');
if (is_array($langs))
foreach ($langs as $lang)
{
$lang_pack = Tools14::jsonDecode(Tools14::file_get_contents('http'.(extension_loaded('openssl')? 's' : '').'://www.prestashop.com/download/lang_packs/get_language_pack.php?version='.$this->install_version.'&iso_lang='.$lang['iso_code']));
if (!$lang_pack)
continue;
elseif ($content = Tools14::file_get_contents('http'.(extension_loaded('openssl')? 's' : '').'://translations.prestashop.com/download/lang_packs/gzip/'.$lang_pack->version.'/'.$lang['iso_code'].'.gzip'))
{
$file = _PS_TRANSLATIONS_DIR_.$lang['iso_code'].'.gzip';
if ((bool)file_put_contents($file, $content))
{
$gz = new Archive_Tar($file, true);
$files_list = $gz->listContent();
if (!$this->keepMails)
{
$files_listing = array();
foreach($files_list as $i => $file)
if (preg_match('/^mails\/'.$lang['iso_code'].'\/.*/', $file['filename']))
unset($files_list[$i]);
foreach($files_list as $file)
if (isset($file['filename']) && is_string($file['filename']))
$files_listing[] = $file['filename'];
if (is_array($files_listing))
$gz->extractList($files_listing, _PS_TRANSLATIONS_DIR_.'../', '');
}
else
$gz->extract(_PS_TRANSLATIONS_DIR_.'../', false);
}
}
}
}
if (version_compare(INSTALL_VERSION, '1.6.0.0', '>'))
{
if (file_exists(_PS_ROOT_DIR_.'/classes/Tools.php'))
require_once(_PS_ROOT_DIR_.'/classes/Tools.php');
if (!class_exists('Tools2', false) AND class_exists('ToolsCore'))
eval('class Tools2 extends ToolsCore{}');
if (class_exists('Tools2') && method_exists('Tools2', 'generateHtaccess'))
{
$url_rewrite = (bool)Db::getInstance()->getvalue('SELECT `value` FROM `'._DB_PREFIX_.'configuration` WHERE name=\'PS_REWRITING_SETTINGS\'');
if (!defined('_MEDIA_SERVER_1_'))
define('_MEDIA_SERVER_1_', '');
if (!defined('_MEDIA_SERVER_2_'))
define('_MEDIA_SERVER_2_', '');
if (!defined('_MEDIA_SERVER_3_'))
define('_MEDIA_SERVER_3_', '');
if (!defined('_PS_USE_SQL_SLAVE_'))
define('_PS_USE_SQL_SLAVE_', false);
if (file_exists(_PS_ROOT_DIR_.'/classes/ObjectModel.php'))
require_once(_PS_ROOT_DIR_.'/classes/ObjectModel.php');
if (!class_exists('ObjectModel', false) AND class_exists('ObjectModelCore'))
eval('abstract class ObjectModel extends ObjectModelCore{}');
if (file_exists(_PS_ROOT_DIR_.'/classes/Configuration.php'))
require_once(_PS_ROOT_DIR_.'/classes/Configuration.php');
if (!class_exists('Configuration', false) AND class_exists('ConfigurationCore'))
eval('class Configuration extends ConfigurationCore{}');
if (file_exists(_PS_ROOT_DIR_.'/classes/cache/Cache.php'))
require_once(_PS_ROOT_DIR_.'/classes/cache/Cache.php');
if (!class_exists('Cache', false) AND class_exists('CacheCore'))
eval('abstract class Cache extends CacheCore{}');
if (file_exists(_PS_ROOT_DIR_.'/classes/PrestaShopCollection.php'))
require_once(_PS_ROOT_DIR_.'/classes/PrestaShopCollection.php');
if(!class_exists('PrestaShopCollection', false) AND class_exists('PrestaShopCollectionCore'))
eval('class PrestaShopCollection extends PrestaShopCollectionCore{}');
if (file_exists(_PS_ROOT_DIR_.'/classes/shop/ShopUrl.php'))
require_once(_PS_ROOT_DIR_.'/classes/shop/ShopUrl.php');
if (!class_exists('ShopUrl', false) AND class_exists('ShopUrlCore'))
eval('class ShopUrl extends ShopUrlCore{}');
if (file_exists(_PS_ROOT_DIR_.'/classes/shop/Shop.php'))
require_once(_PS_ROOT_DIR_.'/classes/shop/Shop.php');
if (!class_exists('Shop', false) AND class_exists('ShopCore'))
eval('class Shop extends ShopCore{}');
if (file_exists(_PS_ROOT_DIR_.'/classes/Translate.php'))
require_once(_PS_ROOT_DIR_.'/classes/Translate.php');
if (!class_exists('Translate', false) AND class_exists('TranslateCore'))
eval('class Translate extends TranslateCore{}');
if (file_exists(_PS_ROOT_DIR_.'/classes/module/Module.php'))
require_once(_PS_ROOT_DIR_.'/classes/module/Module.php');
if (!class_exists('Module', false) AND class_exists('ModuleCore'))
eval('class Module extends ModuleCore{}');
if (file_exists(_PS_ROOT_DIR_.'/classes/Validate.php'))
require_once(_PS_ROOT_DIR_.'/classes/Validate.php');
if (!class_exists('Validate', false) AND class_exists('ValidateCore'))
eval('class Validate extends ValidateCore{}');
if (file_exists(_PS_ROOT_DIR_.'/classes/Language.php'))
require_once(_PS_ROOT_DIR_.'/classes/Language.php');
if (!class_exists('Language', false) AND class_exists('LanguageCore'))
eval('class Language extends LanguageCore{}');
if (file_exists(_PS_ROOT_DIR_.'/classes/Tab.php'))
require_once(_PS_ROOT_DIR_.'/classes/Tab.php');
if (!class_exists('Tab', false) AND class_exists('TabCore'))
eval('class Tab extends TabCore{}');
if (file_exists(_PS_ROOT_DIR_.'/classes/Dispatcher.php'))
require_once(_PS_ROOT_DIR_.'/classes/Dispatcher.php');
if (!class_exists('Dispatcher', false) AND class_exists('DispatcherCore'))
eval('class Dispatcher extends DispatcherCore{}');
if (file_exists(_PS_ROOT_DIR_.'/classes/Hook.php'))
require_once(_PS_ROOT_DIR_.'/classes/Hook.php');
if (!class_exists('Hook', false) AND class_exists('HookCore'))
eval('class Hook extends HookCore{}');
if (file_exists(_PS_ROOT_DIR_.'/classes/Context.php'))
require_once(_PS_ROOT_DIR_.'/classes/Context.php');
if (!class_exists('Context', false) AND class_exists('ContextCore'))
eval('class Context extends ContextCore{}');
if (file_exists(_PS_ROOT_DIR_.'/classes/Group.php'))
require_once(_PS_ROOT_DIR_.'/classes/Group.php');
if (!class_exists('Group', false) AND class_exists('GroupCore'))
eval('class Group extends GroupCore{}');
Tools2::generateHtaccess(null, $url_rewrite);
}
}
if (version_compare($this->install_version, '1.6.0.2', '>'))
{
$path = $this->adminDir.DIRECTORY_SEPARATOR.'themes'.DIRECTORY_SEPARATOR.'default'.DIRECTORY_SEPARATOR.'template'.DIRECTORY_SEPARATOR.'controllers'.DIRECTORY_SEPARATOR.'modules'.DIRECTORY_SEPARATOR.'header.tpl';
if (file_exists($path))
unlink($path);
}
}
if (file_exists(_PS_ROOT_DIR_.'/cache/class_index.php'))
unlink(_PS_ROOT_DIR_.'/cache/class_index.php');
// Clear XML files
if (file_exists(_PS_ROOT_DIR_.'/config/xml/blog-fr.xml'))
unlink(_PS_ROOT_DIR_.'/config/xml/blog-fr.xml');
if (file_exists(_PS_ROOT_DIR_.'/config/xml/default_country_modules_list.xml'))
unlink(_PS_ROOT_DIR_.'/config/xml/default_country_modules_list.xml');
if (file_exists(_PS_ROOT_DIR_.'/config/xml/modules_list.xml'))
unlink(_PS_ROOT_DIR_.'/config/xml/modules_list.xml');
if (file_exists(_PS_ROOT_DIR_.'/config/xml/modules_native_addons.xml'))
unlink(_PS_ROOT_DIR_.'/config/xml/modules_native_addons.xml');
if (file_exists(_PS_ROOT_DIR_.'/config/xml/must_have_modules_list.xml'))
unlink(_PS_ROOT_DIR_.'/config/xml/must_have_modules_list.xml');
if (file_exists(_PS_ROOT_DIR_.'/config/xml/tab_modules_list.xml'))
unlink(_PS_ROOT_DIR_.'/config/xml/tab_modules_list.xml');
if (file_exists(_PS_ROOT_DIR_.'/config/xml/trusted_modules_list.xml'))
unlink(_PS_ROOT_DIR_.'/config/xml/trusted_modules_list.xml');
if (file_exists(_PS_ROOT_DIR_.'/config/xml/untrusted_modules_list.xml'))
unlink(_PS_ROOT_DIR_.'/config/xml/untrusted_modules_list.xml');
if ($this->deactivateCustomModule)
{
$exist = Db::getInstance()->getValue('SELECT `id_configuration` FROM `'._DB_PREFIX_.'configuration` WHERE `name` LIKE \'PS_DISABLE_OVERRIDES\'');
if ($exist)
Db::getInstance()->execute('UPDATE `'._DB_PREFIX_.'configuration` SET value = 1 WHERE `name` LIKE \'PS_DISABLE_OVERRIDES\'');
else
Db::getInstance()->execute('INSERT INTO `'._DB_PREFIX_.'configuration` (name, value, date_add, date_upd) VALUES ("PS_DISABLE_OVERRIDES", 1, NOW(), NOW())');
if (file_exists(_PS_ROOT_DIR_.'/classes/PrestaShopAutoload.php'))
require_once(_PS_ROOT_DIR_.'/classes/PrestaShopAutoload.php');
if (version_compare($this->install_version, '1.6.0.0', '>') && class_exists('PrestaShopAutoload') && method_exists('PrestaShopAutoload', 'generateIndex'))
{
if (!defined('_PS_CORE_DIR_'))
define('_PS_CORE_DIR_', _PS_ROOT_DIR_);
PrestaShopAutoload::getInstance()->_include_override_path = false;
PrestaShopAutoload::getInstance()->generateIndex();
}
}
if ($this->updateDefaultTheme)
{
if (version_compare(INSTALL_VERSION, '1.6.0.0', '>'))
{
Db::getInstance()->execute('UPDATE `'._DB_PREFIX_.'shop`
SET id_theme = (SELECT id_theme FROM `'._DB_PREFIX_.'theme` WHERE name LIKE \'default-bootstrap\')');
Db::getInstance()->execute('DELETE FROM `'._DB_PREFIX_.'theme` WHERE name LIKE \'default\' OR name LIKE \'prestashop\'');
}
elseif (version_compare(INSTALL_VERSION, '1.5.0.0', '>'))
{
Db::getInstance()->execute('UPDATE `'._DB_PREFIX_.'shop`
SET id_theme = (SELECT id_theme FROM `'._DB_PREFIX_.'theme` WHERE name LIKE \'default\')');
Db::getInstance()->execute('DELETE FROM `'._DB_PREFIX_.'theme` WHERE name LIKE \'prestashop\'');
}
}
// delete cache filesystem if activated
if (defined('_PS_CACHE_ENABLED_') && _PS_CACHE_ENABLED_)
{
$depth = (int)$this->db->getValue('SELECT value
FROM '._DB_PREFIX_.'configuration
WHERE name = "PS_CACHEFS_DIRECTORY_DEPTH"');
if($depth)
{
if (!defined('_PS_CACHEFS_DIRECTORY_'))
define('_PS_CACHEFS_DIRECTORY_', $this->prodRootDir.'/cache/cachefs/');
self::deleteDirectory(_PS_CACHEFS_DIRECTORY_, false);
if (class_exists('CacheFs', false))
self::createCacheFsDirectories((int)$depth);
}
}
$this->db->execute('UPDATE `'._DB_PREFIX_.'configuration` SET value="0" WHERE name = "PS_HIDE_OPTIMIZATION_TIS"', false);
$this->db->execute('UPDATE `'._DB_PREFIX_.'configuration` SET value="1" WHERE name = "PS_NEED_REBUILD_INDEX"', false);
$this->db->execute('UPDATE `'._DB_PREFIX_.'configuration` SET value="'.INSTALL_VERSION.'" WHERE name = "PS_VERSION_DB"', false);
if ($warningExist)
{
$this->warning_exists = true;
$this->nextQuickInfo[] = $this->l('Warning detected during upgrade.');
$this->nextErrors[] = $this->l('Warning detected during upgrade.');
$this->next_desc = $this->l('Warning detected during upgrade.');
}
else
$this->next_desc = $this->l('Database 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/classes/AddConfToFile.php');
copy(SETTINGS_FILE, str_replace('.php', '.old.php', SETTINGS_FILE));
$confFile = new AddConfToFile(SETTINGS_FILE, 'w');
if ($confFile->error)
{
$this->next = 'error';
$this->next_desc = $this->l('Error when opening settings.inc.php file in write mode');
$this->nextQuickInfo[] = $confFile->error;
$this->nextErrors[] = $this->l('Error when opening settings.inc.php file in write mode').': '.$confFile->error;
return false;
}
$datas = array(
array('_DB_SERVER_', _DB_SERVER_),
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 install version is before 1.5
if (version_compare(INSTALL_VERSION, '1.5.0.0', '<='))
{
$datas[] = array('_DB_TYPE_', _DB_TYPE_);
$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->next_desc = $this->l('Error when generating new settings.inc.php file.');
$this->nextQuickInfo[] = $confFile->error;
$this->nextErrors[] = $this->l('Error when generating new settings.inc.php file.').' '.$confFile->error;
return false;
}
else
$this->nextQuickInfo[] = $this->l('Settings file updated');
error_reporting($oldLevel);
}
/**
* getTranslationFileType
*
* @param string $file filepath to check
* @access public
* @return string type of translation item
*/
public function getTranslationFileType($file)
{
$type = false;
// line shorter
$separator = addslashes(DIRECTORY_SEPARATOR);
$translation_dir = $separator.'translations'.$separator;
if (version_compare(_PS_VERSION_, '1.5.0.5', '<'))
$regex_module = '#'.$separator.'modules'.$separator.'.*'.$separator.'('.implode('|', $this->installedLanguagesIso).')\.php#';
else
$regex_module = '#'.$separator.'modules'.$separator.'.*'.$translation_dir.'('.implode('|', $this->installedLanguagesIso).')\.php#';
if (preg_match($regex_module, $file))
$type = 'module';
elseif (preg_match('#'.$translation_dir.'('.implode('|', $this->installedLanguagesIso).')'.$separator.'admin\.php#', $file))
$type = 'back office';
elseif (preg_match('#'.$translation_dir.'('.implode('|', $this->installedLanguagesIso).')'.$separator.'errors\.php#', $file))
$type = 'error message';
elseif (preg_match('#'.$translation_dir.'('.implode('|', $this->installedLanguagesIso).')'.$separator.'fields\.php#', $file))
$type = 'field';
elseif (preg_match('#'.$translation_dir.'('.implode('|', $this->installedLanguagesIso).')'.$separator.'pdf\.php#', $file))
$type = 'pdf';
elseif (preg_match('#'.$separator.'themes'.$separator.'(default|prestashop)'.$separator.'lang'.$separator.'('.implode('|', $this->installedLanguagesIso).')\.php#', $file))
$type = 'front office';
return $type;
}
/**
* return true if $file is a translation file
*
* @param string $file filepath (from prestashop root)
* @access public
* @return boolean
*/
public function isTranslationFile($file)
{
if ($this->getTranslationFileType($file) !== false)
return true;
return false;
}
/**
* merge the translations of $orig into $dest, according to the $type of translation file
*
* @param string $orig file from upgrade package
* @param string $dest filepath of destination
* @param string $type type of translation file (module, bo, fo, field, pdf, error)
* @access public
* @return boolean
*/
public function mergeTranslationFile($orig, $dest, $type)
{
switch ($type)
{
case 'front office':
$var_name = '_LANG';
break;
case 'back office':
$var_name = '_LANGADM';
break;
case 'error message':
$var_name = '_ERRORS';
break;
case 'field':
$var_name = '_FIELDS';
break;
case 'module':
$var_name = '_MODULE';
// if current version is before 1.5.0.5, module has no translations dir
if (version_compare(_PS_VERSION_, '1.5.0.5', '<') && (version_compare($this->install_version, '1.5.0.5', '>')))
$dest = str_replace(DIRECTORY_SEPARATOR.'translations', '', $dest);
break;
case 'pdf':
$var_name = '_LANGPDF';
break;
case 'mail':
$var_name = '_LANGMAIL';
break;
default:
return false;
}
if (!file_exists($orig))
{
$this->nextQuickInfo[] = sprintf($this->l('[NOTICE] File %s does not exist, merge skipped.'), $orig);
return true;
}
include($orig);
if (!isset($$var_name))
{
$this->nextQuickInfo[] = sprintf($this->l('[WARNING] %1$s variable missing in file %2$s. Merge skipped.'), $var_name, $orig);
return true;
}
$var_orig = $$var_name;
if (!file_exists($dest))
{
$this->nextQuickInfo[] = sprintf($this->l('[NOTICE] File %s does not exist, merge skipped.'), $dest);
return false;
}
include($dest);
if (!isset($$var_name))
{
// in that particular case : file exists, but variable missing, we need to delete that file
// (if not, this invalid file will be copied in /translations during upgradeDb process)
if ('module' == $type && file_exists($dest))
unlink($dest);
$this->nextQuickInfo[] = sprintf($this->l('[WARNING] %1$s variable missing in file %2$s. File %2$s deleted and merge skipped.'), $var_name, $dest);
return false;
}
$var_dest = $$var_name;
$merge = array_merge($var_orig, $var_dest);
if ($fd = fopen($dest, 'w'))
{
fwrite($fd, "<?php\n\nglobal \$".$var_name.";\n\$".$var_name." = array();\n");
foreach ($merge as $k => $v)
{
if (get_magic_quotes_gpc())
$v = stripslashes($v);
if ('mail' == $type)
fwrite($fd, '$'.$var_name.'[\''.$this->db->escape($k).'\'] = \''.$this->db->escape($v).'\';'."\n");
else
fwrite($fd, '$'.$var_name.'[\''.$this->db->escape($k, true).'\'] = \''.$this->db->escape($v, true).'\';'."\n");
}
fwrite($fd, "\n?>");
fclose($fd);
}
else
return false;
return true;
}
/**
* upgradeThisFile
*
* @param mixed $file
* @return void
*/
public function upgradeThisFile($file)
{
// note : keepMails is handled in skipFiles
// translations_custom and mails_custom list are currently not used
// later, we could handle customization with some kind of diff functions
// for now, just copy $file in str_replace($this->latestRootDir,_PS_ROOT_DIR_)
$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($this->l('[WARNING] File %1$s has been deleted.'), $file);
}
if (!file_exists($dest))
{
if (mkdir($dest))
{
$this->nextQuickInfo[] = sprintf($this->l('Directory %1$s created.'), $file);
return true;
}
else
{
$this->next = 'error';
$this->nextQuickInfo[] = sprintf($this->l('Error while creating directory %s.'), $dest);
$this->nextErrors[] = $this->next_desc = sprintf($this->l('Error while creating directory %s.'), $dest);
return false;
}
}
else // directory already exists
{
$this->nextQuickInfo[] = sprintf($this->l('Directory %1$s already exists.'), $file);
return true;
}
}
elseif (is_file($orig))
{
if ($this->isTranslationFile($file) && file_exists($dest))
{
$type_trad = $this->getTranslationFileType($file);
$res = $this->mergeTranslationFile($orig, $dest, $type_trad);
if ($res)
{
$this->nextQuickInfo[] = sprintf($this->l('[TRANSLATION] The translation files have been merged into file %1$s.'), $dest);
return true;
}
else
{
$this->nextQuickInfo[] = sprintf($this->l('[TRANSLATION] The translation files have not been merged into file %1$s. Switch to copy %2$s.'), $dest, $dest);
$this->nextErrors[] = sprintf($this->l('[TRANSLATION] The translation files have not been merged into file %1$s. Switch to copy %2$s.'), $dest, $dest);
}
}
// upgrade exception were above. This part now process all files that have to be upgraded (means to modify or to remove)
// delete before updating (and this will also remove deprecated files)
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->nextErrors[] = $this->next_desc = sprintf($this->l('Error while copying file %1$s'), $file);
return false;
}
}
elseif (is_file($dest))
{
if (file_exists($dest))
unlink($dest);
$this->nextQuickInfo[] = sprintf('removed file %1$s.', $file);
return true;
}
elseif (is_dir($dest))
{
if (strpos($dest, DIRECTORY_SEPARATOR.'modules'.DIRECTORY_SEPARATOR) === false)
self::deleteDirectory($dest, true);
$this->nextQuickInfo[] = sprintf('removed dir %1$s.', $file);
return true;
}
else
return true;
}
}
public function ajaxProcessRollback()
{
// 1st, need to analyse what was wrong.
$this->nextParams = $this->currentParams;
$this->restoreFilesFilename = $this->restoreName;
if (!empty($this->restoreName))
{
$files = scandir($this->backupPath);
// 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->backupPath.DIRECTORY_SEPARATOR.$this->restoreFilesFilename))
{
$this->next = 'error';
$this->nextQuickInfo[] = sprintf('[ERROR] file %s is missing : unable to restore files. Operation aborted.', $this->restoreFilesFilename);
$this->nextErrors[] = $this->next_desc = sprintf($this->l('[ERROR] File %s is missing: unable to restore files. Operation aborted.'), $this->restoreFilesFilename);
return false;
}
$files = scandir($this->backupPath.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 !
if (is_array($this->restoreDbFilenames))
sort($this->restoreDbFilenames);
if (count($this->restoreDbFilenames) == 0)
{
$this->next = 'error';
$this->nextQuickInfo[] = $this->l('[ERROR] No backup database files found: it would be impossible to restore the database. Operation aborted.');
$this->nextErrors[] = $this->next_desc = $this->l('[ERROR] No backup database files found: it would be impossible to restore the database. Operation aborted.');
return false;
}
$this->next = 'restoreFiles';
$this->next_desc = $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->next_desc = $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->backupPath.DIRECTORY_SEPARATOR.$this->restoreFilesFilename);
foreach($fromArchive as $k => $v)
$fromArchive[$k] = '/'.$v;
file_put_contents($this->autoupgradePath.DIRECTORY_SEPARATOR.$this->fromArchiveFileList, base64_encode(serialize($fromArchive)));
// get list of files to remove
$toRemove = $this->_listFilesToRemove();
// let's reverse the array in order to make possible to rmdir
// remove fullpath. This will be added later in the loop.
// we do that for avoiding fullpath to be revealed in a text file
foreach ($toRemove as $k => $v)
$toRemove[$k] = str_replace($this->prodRootDir, '', $v);
$this->nextQuickInfo[] = sprintf($this->l('%s file(s) will be removed before restoring the backup files.'), count($toRemove));
file_put_contents($this->autoupgradePath.DIRECTORY_SEPARATOR.$this->toRemoveFileList, base64_encode(serialize($toRemove)));
if ($fromArchive === false || $toRemove === false)
{
if (!$fromArchive)
{
$this->nextQuickInfo[] = sprintf($this->l('[ERROR] Backup file %s does not exist.'), $this->fromArchiveFileList);
$this->nextErrors[] = sprintf($this->l('[ERROR] Backup file %s does not exist.'), $this->fromArchiveFileList);
}
if (!$toRemove)
{
$this->nextQuickInfo[] = sprintf($this->l('[ERROR] File "%s" does not exist.'), $this->toRemoveFileList);
$this->nextErrors[] = sprintf($this->l('[ERROR] File "%s" does not exist.'), $this->toRemoveFileList);
}
$this->next_desc = $this->l('Unable to remove upgraded files.');
$this->next = 'error';
return false;
}
}
// first restoreFiles step
if (!isset($toRemove))
$toRemove = unserialize(base64_decode(file_get_contents($this->autoupgradePath.DIRECTORY_SEPARATOR.$this->toRemoveFileList)));
if (count($toRemove) > 0)
{
for($i=0;$i<self::$loopRestoreFiles ;$i++)
{
if (count($toRemove) <= 0)
{
$this->stepDone = true;
$this->status = 'ok';
$this->next = 'restoreFiles';
$this->next_desc = $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, base64_encode(serialize($toRemove)));
return true;
}
else
{
$filename = array_shift($toRemove);
$file = rtrim($this->prodRootDir, DIRECTORY_SEPARATOR).$filename;
if (file_exists($file))
{
if (is_file($file))
{
@chmod($file, 0777); // NT ?
if (@unlink($file))
$this->nextQuickInfo[] = sprintf($this->l('%s files removed'), $filename);
else
{
$this->next = 'error';
$this->next_desc = sprintf($this->l('Error when removing %1$s.'), $filename);
$this->nextQuickInfo[] = sprintf($this->l('File %s not removed.'), $filename);
$this->nextErrors[] = sprintf($this->l('File %s not removed.'), $filename);
return false;
}
}
elseif (is_dir($file))
{
if ($this->isDirEmpty($file))
{
self::deleteDirectory($file, true);
$this->nextQuickInfo[] = sprintf($this->l('[NOTICE] %s deleted.'), $filename);
}
else
$this->nextQuickInfo[] = sprintf($this->l('[NOTICE] Directory %s skipped (directory not empty).'), $filename);
}
}
else
$this->nextQuickInfo[] = sprintf($this->l('[NOTICE] %s does not exist'), $filename);
}
}
file_put_contents($this->autoupgradePath.DIRECTORY_SEPARATOR.$this->toRemoveFileList, base64_encode(serialize($toRemove)));
if (count($toRemove))
$this->next_desc = sprintf($this->l('%s file(s) left to remove.'), count($toRemove));
$this->next = 'restoreFiles';
return true;
}
// very second restoreFiles step : extract backup
// if (!isset($fromArchive))
// $fromArchive = unserialize(base64_decode(file_get_contents($this->autoupgradePath.DIRECTORY_SEPARATOR.$this->fromArchiveFileList)));
$filepath = $this->backupPath.DIRECTORY_SEPARATOR.$this->restoreFilesFilename;
$destExtract = $this->prodRootDir;
if ($this->ZipExtract($filepath, $destExtract))
{
$this->next = 'restoreDb';
$this->next_desc = $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->next_desc = sprintf($this->l('Unable to extract file %1$s into directory %2$s .'), $filepath, $destExtract);
return false;
}
return true;
}
public function isDirEmpty($dir, $ignore = array('.svn', '.git'))
{
$array_ignore = array_merge(array('.', '..'), $ignore);
$content = scandir($dir);
foreach($content as $filename)
if (!in_array($filename, $array_ignore))
return false;
return true;
}
/**
* Delete directory and subdirectories
*
* @param string $dirname Directory name
*/
public static function deleteDirectory($dirname, $delete_self = true)
{
return Tools14::deleteDirectory($dirname, $delete_self);
}
/**
* try to restore db backup file
*/
public function ajaxProcessRestoreDb()
{
$skip_ignore_tables = false;
$ignore_stats_table = array(
_DB_PREFIX_.'connections',
_DB_PREFIX_.'connections_page',
_DB_PREFIX_.'connections_source',
_DB_PREFIX_.'guest',
_DB_PREFIX_.'statssearch'
);
$this->nextParams['dbStep'] = $this->currentParams['dbStep'];
$start_time = time();
$db = $this->db;
$listQuery = array();
$errors = array();
// deal with running backup rest if exist
if (file_exists($this->autoupgradePath.DIRECTORY_SEPARATOR.$this->toRestoreQueryList))
$listQuery = unserialize(base64_decode(file_get_contents($this->autoupgradePath.DIRECTORY_SEPARATOR.$this->toRestoreQueryList)));
// deal with the next files stored in restoreDbFilenames
if (empty($listQuery) && 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->error = 1;
$this->nextQuickInfo[] = $this->next_desc = $this->l(sprintf('%s: File format does not match.', $currentDbFilename));
return false;
}
$this->nextParams['dbStep'] = $match[1];
$backupdb_path = $this->backupPath.DIRECTORY_SEPARATOR.$this->restoreName;
$dot_pos = strrpos($currentDbFilename, '.');
$fileext = substr($currentDbFilename, $dot_pos+1);
$requests = array();
$content = '';
$this->nextQuickInfo[] = $this->l(sprintf('Opening backup database file %1s in %2s mode', $currentDbFilename, $fileext));
switch ($fileext)
{
case 'bz':
case 'bz2':
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"); // @todo : handle error
break;
case 'gz':
if ($fp = gzopen($backupdb_path.DIRECTORY_SEPARATOR.$currentDbFilename, 'r'))
while(!feof($fp))
$content .= gzread($fp, 4096);
gzclose($fp);
break;
default :
if ($fp = fopen($backupdb_path.DIRECTORY_SEPARATOR.$currentDbFilename, 'r'))
while(!feof($fp))
$content .= fread($fp, 4096);
fclose($fp);
}
$currentDbFilename = '';
if (empty($content))
{
$this->nextErrors[] = $this->l('Database backup is empty.');
$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)
// This part has to be executed only onces (if dbStep=0)
if ($this->nextParams['dbStep'] == '1')
{
$all_tables = $this->db->executeS('SHOW TABLES LIKE "'._DB_PREFIX_.'%"', true, false);
$drops = array();
foreach ($all_tables as $k => $v)
{
$table = array_shift($v);
$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, base64_encode(serialize($listQuery)));
}
// @todo : error if listQuery is not an array (that can happen if toRestoreQueryList is empty for example)
$time_elapsed = time() - $start_time;
if (is_array($listQuery) && (count($listQuery) > 0))
{
do
{
if (count($listQuery) == 0)
{
if (file_exists($this->autoupgradePath.DIRECTORY_SEPARATOR.$this->toRestoreQueryList))
unlink($this->autoupgradePath.DIRECTORY_SEPARATOR.$this->toRestoreQueryList);
if (count($this->restoreDbFilenames))
$this->next_desc = sprintf($this->l('Database restoration file %1$s done. %2$s file(s) left...'), $this->nextParams['dbStep'], count($this->restoreDbFilenames));
else
$this->next_desc = sprintf($this->l('Database restoration file %1$s done.'), $this->nextParams['dbStep']);
$this->nextQuickInfo[] = $this->next_desc;
$this->stepDone = true;
$this->status = 'ok';
$this->next = 'restoreDb';
if (count($this->restoreDbFilenames) == 0)
{
$this->next = 'rollbackComplete';
$this->nextQuickInfo[] = $this->next_desc = $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 (!$this->db->execute($query, false))
{
if (is_array($listQuery))
$listQuery = array_unshift($listQuery, $query);
$this->nextErrors[] = $this->l('[SQL ERROR] ').$query.' - '.$this->db->getMsgError();
$this->nextQuickInfo[] = $this->l('[SQL ERROR] ').$query.' - '.$this->db->getMsgError();
$this->next = 'error';
$this->error = 1;
$this->next_desc = $this->l('Error during database restoration');
unlink($this->autoupgradePath.DIRECTORY_SEPARATOR.$this->toRestoreQueryList);
return false;
}
// note : theses queries can be too big and can cause issues for display
// else
// $this->nextQuickInfo[] = '[OK] '.$query;
$time_elapsed = time() - $start_time;
}
while ($time_elapsed < self::$loopRestoreQueryTime);
$queries_left = count($listQuery);
if ($queries_left > 0)
file_put_contents($this->autoupgradePath.DIRECTORY_SEPARATOR.$this->toRestoreQueryList, base64_encode(serialize($listQuery)));
elseif (file_exists($this->autoupgradePath.DIRECTORY_SEPARATOR.$this->toRestoreQueryList))
unlink($this->autoupgradePath.DIRECTORY_SEPARATOR.$this->toRestoreQueryList);
$this->stepDone = false;
$this->next = 'restoreDb';
$this->nextQuickInfo[] = $this->next_desc = sprintf($this->l('%1$s queries left for file %2$s...'), $queries_left, $this->nextParams['dbStep']);
unset($query);
unset($listQuery);
}
else
{
$this->stepDone = true;
$this->status = 'ok';
$this->next = 'rollbackComplete';
$this->nextQuickInfo[] = $this->next_desc = $this->l('Database restoration done.');
}
return true;
}
public function ajaxProcessMergeTranslations()
{
}
public function ajaxProcessBackupDb()
{
if (!$this->getConfig('PS_AUTOUP_BACKUP'))
{
$this->stepDone = true;
$this->nextParams['dbStep'] = 0;
$this->next_desc = sprintf($this->l('Database backup skipped. Now upgrading files...'), $this->backupName);
$this->next = 'upgradeFiles';
return true;
}
$relative_backup_path = str_replace(_PS_ROOT_DIR_, '', $this->backupPath);
$report = '';
if (!ConfigurationTest::test_dir($relative_backup_path, false, $report))
{
$this->next_desc = $this->l('Backup directory is not writable ');
$this->nextQuickInfo[] = 'Backup directory is not writable ';
$this->nextErrors[] = 'Backup directory is not writable "'.$this->backupPath.'"';
$this->next = 'error';
$this->error = 1;
return false;
}
$this->stepDone = false;
$this->next = 'backupDb';
$this->nextParams = $this->currentParams;
$start_time = time();
$psBackupAll = true;
$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->backupPath.DIRECTORY_SEPARATOR.$this->backupName))
{
mkdir($this->backupPath.DIRECTORY_SEPARATOR.$this->backupName);
}
$this->nextParams['dbStep'] = 0;
$tablesToBackup = $this->db->executeS('SHOW TABLES LIKE "'._DB_PREFIX_.'%"', true, false);
file_put_contents($this->autoupgradePath.DIRECTORY_SEPARATOR.$this->toBackupDbList, base64_encode(serialize($tablesToBackup)));
}
if (!isset($tablesToBackup))
$tablesToBackup = unserialize(base64_decode(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 > self::$max_written_allowed)
{
// increment dbStep will increment filename each time here
$this->nextParams['dbStep']++;
// new file, new step
$written = 0;
if (isset($fp))
fclose($fp);
$backupfile = $this->backupPath.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 (file_exists($backupfile))
{
$this->next = 'error';
$this->error = 1;
$this->nextErrors[] = sprintf($this->l('Backup file %s already exists. Operation aborted.'), $backupfile);
$this->nextQuickInfo[] = sprintf($this->l('Backup file %s already exists. Operation aborted.'), $backupfile);
}
if (function_exists('bzopen'))
{
$backupfile .= '.bz2';
$fp = bzopen($backupfile, 'w');
}
elseif (function_exists('gzopen'))
{
$backupfile .= '.gz';
$fp = gzopen($backupfile, 'w');
}
else
$fp = fopen($backupfile, 'w');
if ($fp === false)
{
$this->nextErrors[] = sprintf($this->l('Unable to create backup database file %s.'), addslashes($backupfile));
$this->nextQuickInfo[] = sprintf($this->l('Unable to create backup database file %s.'), addslashes($backupfile));
$this->next = 'error';
$this->error = 1;
$this->next_desc = $this->l('Error during database backup.');
return false;
}
$written += fwrite($fp, '/* Backup ' . $this->nextParams['dbStep'] . ' for ' . Tools14::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 = $this->db->executeS('SHOW CREATE TABLE `' . $table . '`', true, false);
if (count($schema) != 1 ||
!((isset($schema[0]['Table']) && isset($schema[0]['Create Table']))
|| (isset($schema[0]['View']) && isset($schema[0]['Create View']))))
{
fclose($fp);
if (file_exists($backupfile))
unlink($backupfile);
$this->nextErrors[] = sprintf($this->l('An error occurred while backing up. Unable to obtain the schema of %s'), $table);
$this->nextQuickInfo[] = sprintf($this->l('An error occurred while backing up. Unable to obtain the schema of %s'), $table);
$this->next = 'error';
$this->error = 1;
$this->next_desc = $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=[^\s]+\s#', 'DEFINER=CURRENT_USER ', $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 = $this->db->executeS('SELECT * FROM `'.$table.'` LIMIT '.(int)$backup_loop_limit.',200', false, false);
$this->nextParams['backup_loop_limit'] += 200;
$sizeof = $this->db->numRows();
if ($data && ($sizeof > 0))
{
// Export the table data
$written += fwrite($fp, 'INSERT INTO `'.$table."` VALUES\n");
$i = 1;
while ($row = $this->db->nextRow($data))
{
// this starts a row
$s = '(';
foreach ($row AS $field => $value)
{
$tmp = "'" . $this->db->escape($value, true) . "',";
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
{
unset($this->nextParams['backup_table']);
unset($this->currentParams['backup_table']);
break;
}
}
while(($time_elapsed < self::$loopBackupDbTime) && ($written < self::$max_written_allowed));
}
$found++;
$time_elapsed = time() - $start_time;
$this->nextQuickInfo[] = sprintf($this->l('%1$s table has been saved.'), $table);
}
while(($time_elapsed < self::$loopBackupDbTime) && ($written < self::$max_written_allowed));
// end of loop
if (isset($fp))
{
fclose($fp);
unset($fp);
}
file_put_contents($this->autoupgradePath.DIRECTORY_SEPARATOR.$this->toBackupDbList, base64_encode(serialize($tablesToBackup)));
if (count($tablesToBackup) > 0)
{
$this->nextQuickInfo[] = sprintf($this->l('%1$s tables have been saved.'), $found);
$this->next = 'backupDb';
$this->stepDone = false;
if (count($tablesToBackup))
{
$this->next_desc = sprintf($this->l('Database backup: %s table(s) left...'), count($tablesToBackup));
$this->nextQuickInfo[] = sprintf($this->l('Database backup: %s table(s) left...'), count($tablesToBackup));
}
return true;
}
if ($found == 0 && !empty($backupfile))
{
if (file_exists($backupfile))
unlink($backupfile);
$this->nextErrors[] = sprintf($this->l('No valid tables were found to back up. Backup of file %s canceled.'), $backupfile);
$this->nextQuickInfo[] = sprintf($this->l('No valid tables were found to back up. Backup of file %s canceled.'), $backupfile);
$this->error = 1;
$this->next_desc = sprintf($this->l('Error during database backup for file %s.'), $backupfile);
return false;
}
else
{
unset($this->nextParams['backup_loop_limit']);
unset($this->nextParams['backup_lines']);
unset($this->nextParams['backup_table']);
if ($found)
$this->nextQuickInfo[] = sprintf($this->l('%1$s tables have been saved.'), $found);
$this->stepDone = true;
// reset dbStep at the end of this step
$this->nextParams['dbStep'] = 0;
$this->next_desc = sprintf($this->l('Database backup done in filename %s. Now upgrading files...'), $this->backupName);
$this->next = 'upgradeFiles';
return true;
}
}
public function ajaxProcessBackupFiles()
{
if (!$this->getConfig('PS_AUTOUP_BACKUP'))
{
$this->stepDone = true;
$this->next = 'backupDb';
$this->next_desc = 'File backup skipped.';
return true;
}
$this->nextParams = $this->currentParams;
$this->stepDone = false;
if (empty($this->backupFilesFilename))
{
$this->next = 'error';
$this->error = 1;
$this->next_desc = $this->l('Error during backupFiles');
$this->nextErrors[] = $this->l('[ERROR] backupFiles filename has not been set');
$this->nextQuickInfo[] = $this->l('[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, 'backup', false);
file_put_contents($this->autoupgradePath.DIRECTORY_SEPARATOR.$this->toBackupFileList, base64_encode(serialize($filesToBackup)));
if (count($this->toBackupFileList))
$this->nextQuickInfo[] = sprintf($this->l('%s Files to backup.'), count($this->toBackupFileList));
$this->nextParams['filesForBackup'] = $this->toBackupFileList;
// delete old backup, create new
if (!empty($this->backupFilesFilename) && file_exists($this->backupPath.DIRECTORY_SEPARATOR.$this->backupFilesFilename))
unlink($this->backupPath.DIRECTORY_SEPARATOR.$this->backupFilesFilename);
$this->nextQuickInfo[] = sprintf($this->l('backup files initialized in %s'), $this->backupFilesFilename);
}
$filesToBackup = unserialize(base64_decode(file_get_contents($this->autoupgradePath.DIRECTORY_SEPARATOR.$this->toBackupFileList)));
$this->next = 'backupFiles';
if (count($this->toBackupFileList))
$this->next_desc = sprintf($this->l('Backup files in progress. %d files left'), count($filesToBackup));
if (is_array($filesToBackup))
{
$res = false;
if (!self::$force_pclZip && class_exists('ZipArchive', false))
{
$this->nextQuickInfo[] = $this->l('Using class ZipArchive...');
$zip_archive = true;
$zip = new ZipArchive();
$res = $zip->open($this->backupPath.DIRECTORY_SEPARATOR.$this->backupFilesFilename, ZIPARCHIVE::CREATE);
if ($res)
$res = (isset($zip->filename) && $zip->filename) ? true : false;
}
if(!$res)
{
$zip_archive = false;
$this->nextQuickInfo[] = $this->l('Using class PclZip...');
// pclzip can be already loaded (server configuration)
if (!class_exists('PclZip',false))
require_once(dirname(__FILE__).'/classes/pclzip.lib.php');
$zip = new PclZip($this->backupPath.DIRECTORY_SEPARATOR.$this->backupFilesFilename);
$res = true;
}
if($zip && $res)
{
$this->next = 'backupFiles';
$this->stepDone = false;
$files_to_add = array();
$close_flag = true;
for ($i = 0; $i < self::$loopBackupFiles; $i++)
{
if (count($filesToBackup) <= 0)
{
$this->stepDone = true;
$this->status = 'ok';
$this->next = 'backupDb';
$this->next_desc = $this->l('All files saved. Now backing up database');
$this->nextQuickInfo[] = $this->l('All files have been added to archive.', 'AdminSelfUpgrade', true);
break;
}
// filesForBackup already contains all the correct files
$file = array_shift($filesToBackup);
$archiveFilename = ltrim(str_replace($this->prodRootDir, '', $file), DIRECTORY_SEPARATOR);
$size = filesize($file);
if ($size < self::$maxBackupFileSize)
{
if ($zip_archive)
{
$added_to_zip = $zip->addFile($file, $archiveFilename);
if ($added_to_zip)
{
if ($filesToBackup)
$this->nextQuickInfo[] = sprintf($this->l('%1$s added to archive. %2$s files left.', 'AdminSelfUpgrade', true), $archiveFilename, count($filesToBackup));
}
else
{
// if an error occur, it's more safe to delete the corrupted backup
$zip->close();
if (file_exists($this->backupPath.DIRECTORY_SEPARATOR.$this->backupFilesFilename))
unlink($this->backupPath.DIRECTORY_SEPARATOR.$this->backupFilesFilename);
$this->next = 'error';
$this->error = 1;
$this->next_desc = sprintf($this->l('Error when trying to add %1$s to archive %2$s.', 'AdminSelfUpgrade', true), $file, $archiveFilename);
$close_flag = false;
break;
}
}
else
{
$files_to_add[] = $file;
if (count($filesToBackup))
$this->nextQuickInfo[] = sprintf($this->l('File %1$s (size: %3$s) added to archive. %2$s files left.' , 'AdminSelfUpgrade', true), $archiveFilename, count($filesToBackup), $size);
else
$this->nextQuickInfo[] = sprintf($this->l('File %1$s (size: %2$s) added to archive.', 'AdminSelfUpgrade', true), $archiveFilename, $size);
}
}
else
{
$this->nextQuickInfo[] = sprintf($this->l('File %1$s (size: %2$s) has been skipped during backup.', 'AdminSelfUpgrade', true), $archiveFilename, $size);
$this->nextErrors[] = sprintf($this->l('File %1$s (size: %2$s) has been skipped during backup.', 'AdminSelfUpgrade', true), $archiveFilename, $size);
}
}
if ($zip_archive && $close_flag && is_object($zip))
$zip->close();
elseif(!$zip_archive)
{
$added_to_zip = $zip->add($files_to_add, PCLZIP_OPT_REMOVE_PATH, $this->prodRootDir);
if($added_to_zip)
$zip->privCloseFd();
if (!$added_to_zip)
{
if (file_exists($this->backupPath.DIRECTORY_SEPARATOR.$this->backupFilesFilename))
unlink($this->backupPath.DIRECTORY_SEPARATOR.$this->backupFilesFilename);
$this->nextQuickInfo[] = sprintf($this->l('[ERROR] Error on backup using PclZip: %s.'), $zip->errorInfo(true));
$this->nextErrors[] = sprintf($this->l('[ERROR] Error on backup using PclZip: %s.'), $zip->errorInfo(true));
$this->next = 'error';
}
}
file_put_contents($this->autoupgradePath.DIRECTORY_SEPARATOR.$this->toBackupFileList, base64_encode(serialize($filesToBackup)));
return true;
}
else{
$this->next = 'error';
$this->next_desc = $this->l('unable to open archive');
return false;
}
}
else
{
$this->stepDone = true;
$this->next = 'backupDb';
$this->next_desc = $this->l('All files saved. Now backing up database.');
return true;
}
// 4) save for display.
}
private function _removeOneSample($removeList)
{
if (is_array($removeList) AND count($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;
if(count($removeList) > 0)
$this->nextQuickInfo[] = sprintf($this->l('%1$s items removed. %2$s items left.'), $item, count($removeList));
}
else
{
$this->next = 'error';
$this->nextParams['removeList'] = $removeList;
$this->nextQuickInfo[] = sprintf($this->l('Error while removing item %1$s, %2$s items left.'), $removeList[0], count($removeList));
$this->nextErrors[] = sprintf($this->l('Error while removing item %1$s, %2$s items left.'), $removeList[0], count($removeList));
return false;
}
}
return true;
}
/**
* Remove all sample files.
*
* @return boolean true if succeed
*/
public function ajaxProcessRemoveSamples()
{
$this->stepDone = false;
// remove all sample pics in img subdir
if (!isset($this->currentParams['removeList']))
{
$this->_listSampleFiles($this->latestPath.'/prestashop/img/c', '.jpg');
$this->_listSampleFiles($this->latestPath.'/prestashop/img/cms', '.jpg');
$this->_listSampleFiles($this->latestPath.'/prestashop/img/l', '.jpg');
$this->_listSampleFiles($this->latestPath.'/prestashop/img/m', '.jpg');
$this->_listSampleFiles($this->latestPath.'/prestashop/img/os', '.jpg');
$this->_listSampleFiles($this->latestPath.'/prestashop/img/p', '.jpg');
$this->_listSampleFiles($this->latestPath.'/prestashop/img/s', '.jpg');
$this->_listSampleFiles($this->latestPath.'/prestashop/img/scenes', '.jpg');
$this->_listSampleFiles($this->latestPath.'/prestashop/img/st', '.jpg');
$this->_listSampleFiles($this->latestPath.'/prestashop/img/su', '.jpg');
$this->_listSampleFiles($this->latestPath.'/prestashop/img', '404.gif');
$this->_listSampleFiles($this->latestPath.'/prestashop/img', 'favicon.ico');
$this->_listSampleFiles($this->latestPath.'/prestashop/img', 'logo.jpg');
$this->_listSampleFiles($this->latestPath.'/prestashop/img', 'logo_stores.gif');
$this->_listSampleFiles($this->latestPath.'/prestashop/modules/editorial', 'homepage_logo.jpg');
// remove all override present in the archive
$this->_listSampleFiles($this->latestPath.'/prestashop/override', '.php');
if (count($this->sampleFileList) > 0)
$this->nextQuickInfo[] = sprintf($this->l('Starting to remove %1$s sample files'), count($this->sampleFileList));
$this->nextParams['removeList'] = $this->sampleFileList;
}
$resRemove = true;
for($i = 0; $i < self::$loopRemoveSamples; $i++)
{
if (count($this->nextParams['removeList']) <= 0 )
{
$this->stepDone = true;
if ($this->getConfig('skip_backup'))
{
$this->next = 'upgradeFiles';
$this->next_desc = $this->l('All sample files removed. Backup process skipped. Now upgrading files.');
}
else
{
$this->next = 'backupFiles';
$this->next_desc = $this->l('All sample files removed. Now backing up files.');
}
// break the loop, all sample already removed
return true;
}
$resRemove &= $this->_removeOneSample($this->nextParams['removeList']);
if (!$resRemove)
break;
}
return $resRemove;
}
/**
* download PrestaShop archive according to the chosen channel
*
* @access public
*/
public function ajaxProcessDownload()
{
if (ConfigurationTest::test_fopen() || ConfigurationTest::test_curl())
{
if (!is_object($this->upgrader))
$this->upgrader = new Upgrader();
// regex optimization
preg_match('#([0-9]+\.[0-9]+)(?:\.[0-9]+){1,2}#', _PS_VERSION_, $matches);
$this->upgrader->channel = $this->getConfig('channel');
$this->upgrader->branch = $matches[1];
if ($this->getConfig('channel') == 'private' && !$this->getConfig('private_allow_major'))
$this->upgrader->checkPSVersion(false, array('private', 'minor'));
else
$this->upgrader->checkPSVersion(false, array('minor'));
if ($this->upgrader->channel == 'private')
{
$this->upgrader->link = $this->getConfig('private_release_link');
$this->upgrader->md5 = $this->getConfig('private_release_md5');
}
$this->nextQuickInfo[] = sprintf($this->l('downloading from %s'), $this->upgrader->link);
$this->nextQuickInfo[] = sprintf($this->l('file will be saved in %s'), $this->getFilePath());
if (file_exists($this->downloadPath))
{
self::deleteDirectory($this->downloadPath, false);
$this->nextQuickInfo[] = $this->l('download directory has been emptied');
}
$report = '';
$relative_download_path = str_replace(_PS_ROOT_DIR_, '', $this->downloadPath);
if (ConfigurationTest::test_dir($relative_download_path, false, $report))
{
$res = $this->upgrader->downloadLast($this->downloadPath, $this->destDownloadFilename);
if ($res)
{
$md5file = md5_file(realpath($this->downloadPath).DIRECTORY_SEPARATOR.$this->destDownloadFilename);
if ($md5file == $this->upgrader->md5)
{
$this->nextQuickInfo[] = $this->l('Download complete.');
$this->next = 'unzip';
$this->next_desc = $this->l('Download complete. Now extracting...');
}
else
{
$this->nextQuickInfo[] = sprintf($this->l('Download complete but MD5 sum does not match (%s).'), $md5file);
$this->nextErrors[] = sprintf($this->l('Download complete but MD5 sum does not match (%s).'), $md5file);
$this->next = 'error';
$this->next_desc = $this->l('Download complete but MD5 sum does not match (%s). Operation aborted.');
}
}
else
{
if ($this->upgrader->channel == 'private')
{
$this->next_desc = $this->l('Error during download. The private key may be incorrect.');
$this->nextQuickInfo[] = $this->l('Error during download. The private key may be incorrect.');
$this->nextErrors[] = $this->l('Error during download. The private key may be incorrect.');
}
else
{
$this->next_desc = $this->l('Error during download');
$this->nextQuickInfo[] = $this->l('Error during download');
$this->nextErrors[] = $this->l('Error during download');
}
$this->next = 'error';
}
}
else
{
$this->next_desc = $this->l('Download directory is not writable.');
$this->nextQuickInfo[] = $this->l('Download directory is not writable.');
$this->nextErrors[] = sprintf($this->l('Download directory %s is not writable.'), $this->downloadPath);
$this->next = 'error';
}
}
else
{
$this->nextQuickInfo[] = $this->l('You need allow_url_fopen or cURL enabled for automatic download to work.');
$this->nextErrors[] = $this->l('You need allow_url_fopen or cURL enabled for automatic download to work.');
$this->next = 'error';
$this->next_desc = sprintf($this->l('You need allow_url_fopen or cURL enabled for automatic download to work. You can also manually upload it in filepath %s.'), $this->getFilePath());
}
}
public function buildAjaxResult()
{
$return = array();
$return['error'] = $this->error;
$return['stepDone'] = $this->stepDone;
$return['next'] = $this->next;
$return['status'] = $this->next == 'error' ? 'error' : 'ok';
$return['next_desc'] = $this->next_desc;
$this->nextParams['config'] = $this->getConfig();
foreach($this->ajaxParams as $v)
if(property_exists($this,$v))
$this->nextParams[$v] = $this->$v;
else
$this->nextQuickInfo[] = sprintf($this->l('[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['nextErrors'] = $this->nextErrors;
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']) && $_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->nextQuickInfo[] = $this->next_desc = sprintf($this->l('action %s skipped'),$action);
unset($_POST['action']);
}
else if (!method_exists(get_class($this), 'ajaxProcess'.$action))
{
$this->next_desc = sprintf($this->l('action "%1$s" not found'), $action);
$this->next = 'error';
$this->error = '1';
}
}
if (!method_exists('Tools', 'apacheModExists') || Tools14::apacheModExists('evasive'))
sleep(1);
}
private function _getJsErrorMsgs()
{
$INSTALL_VERSION = $this->install_version;
$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 the "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('Cannot access 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('Cannot find the SQL upgrade files. Please verify that the /install/upgrade/sql 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->backupPath);
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->backupPath);
foreach($files as $file)
if ($file[0] == 'V' && is_dir($this->backupPath.DIRECTORY_SEPARATOR.$file))
{
$array[] = $file;
}
return $array;
}
protected function _displayRollbackForm()
{
$backup_available = array_intersect($this->getBackupDbAvailable(), $this->getBackupFilesAvailable());
if (!$this->getConfig('PS_AUTOUP_BACKUP') && is_array($backup_available) && count($backup_available) && !in_array($this->backupName, $backup_available))
$this->backupName = end($backup_available);
$this->_html .= '
<fieldset style="margin-top:10px">
<legend><img src="../img/admin/previous.gif"/>'.$this->l('Rollback').'</legend>
<div id="rollbackForm">
<p>
'.$this->l('After upgrading your shop, you can rollback to the previous database and files. Use this function if your theme or an essential module is not working correctly.').'
</p>
<br/>
<div id="rollbackContainer">
<a disabled="disabled" class="upgradestep button" href="" id="rollback">'.$this->l('Rollback').'</a>
</div>
<br/>
<div id="restoreBackupContainer">
'.$this->l('Choose your backup:').'
<select name="restoreName">
<option value="0">'.$this->l('-- Choose a backup to restore --').'</option>';
if (is_array($backup_available) && count($backup_available))
foreach ($backup_available as $backup_name)
$this->_html .= '<option value="'.$backup_name.'">'.$backup_name.'</option>';
$this->_html .= '</select>
</div>
<div class="clear">&nbsp;</div>
</div>
</fieldset>';
}
/** this returns fieldset containing the configuration points you need to use autoupgrade
* @return string
*/
private function _displayCurrentConfiguration()
{
$current_ps_config = $this->getcheckCurrentPsConfig();
$this->_html .= '
<fieldset id="currentConfigurationBlock" class="width autoupgrade" style="float: left; width: 60%; margin-left: 30px;">
<legend>'.$this->l('The pre-Upgrade checklist').'</legend>';
if (!$this->configOk())
$this->_html .= '<div class="clear"><br></div><p class="warn">'.$this->l('The checklist is not OK. You can only upgrade your shop once all indicators are green.').'</p>';
$this->_html .= '<div id="currentConfiguration">
<p>'.$this->l('Before starting the upgrade process, please make sure this checklist is all green.').'</p>
<table class="table" cellpadding="0" cellspacing="0">';
$pic_ok = '<img src="../img/admin/enabled.gif" alt="ok"/>';
$pic_nok = '<img src="../img/admin/disabled.gif" alt="nok"/>';
$pic_warn = '<img src="../img/admin/warning.gif" alt="warn"/>';
// module version : checkAutoupgradeLastVersion
$this->_html .= '
<tr>
<td>'.sprintf($this->l('The 1-click upgrade module is up-to-date (your current version is v%s)'), $this->getModuleVersion()).'
'.($current_ps_config['module_version_ok'] ? '' : '&nbsp;&nbsp;'.(version_compare(_PS_VERSION_,'1.5.3.0','>') ? '<strong><a href="index.php?controller=AdminModules&amp;token='.Tools14::getAdminTokenLite('AdminModules').'&update=autoupgrade">'.$this->l('Update').'</a></strong> - ' : '').'<strong><a class="_blank" href="http://addons.prestashop.com/en/administration-tools-prestashop-modules/5496-1-click-upgrade-autoupgrade.html">'.$this->l('Download').'</a><strong> ').'
</td>
<td>'.($current_ps_config['module_version_ok'] ? $pic_ok : $pic_nok).'</td>
</tr>';
// root : getRootWritable()
$this->_html .= '
<tr>
<td>'.$this->l('Your store\'s root directory is writable (with appropriate CHMOD permissions)').'</td>
<td>'.($current_ps_config['root_writable'] ? $pic_ok : $pic_nok.' '.$this->root_writable_report).'</td>
</tr>';
$admin_dir = trim(str_replace($this->prodRootDir, '', $this->adminDir), DIRECTORY_SEPARATOR);
$report = '';
ConfigurationTest::test_dir($admin_dir.DIRECTORY_SEPARATOR.$this->autoupgradeDir, true, $report);
if ($report)
$this->_html .= '
<tr>
<td>'.$this->l('The "/admin/autoupgrade" directory is writable (appropriate CHMOD permissions)').'</td>
<td>'.($current_ps_config['admin_au_writable'] ? $pic_ok : $pic_nok.' '.$report).'</td>
</tr>';
//check safe_mod
if (!$safe_mode = @ini_get('safe_mode'))
$safe_mode = '';
$safe_mode = in_array(Tools14::strtolower($safe_mode), array(1, 'on'));
$this->_html .= '
<tr><td>'.$this->l('PHP\'s "Safe mode" option is turned off').'</td>
<td>'.(!$safe_mode ? $pic_ok : $pic_warn).'</td></tr>';
$this->_html .= '
<tr><td>'.$this->l('PHP\'s "allow_url_fopen" option is turned on, or cURL is installed').'</td>
<td>'.((ConfigurationTest::test_fopen() || ConfigurationTest::test_curl()) ? $pic_ok : $pic_nok).'</td></tr>';
// shop enabled
$this->_html .= '
<tr><td>'.$this->l('Your store is in maintenance mode').' '.(!$current_ps_config['shop_deactivated'] ? '<br><form method="post" action="'.$this->currentIndex.'&token='.$this->token.'"><input type="submit" class="button" name="putUnderMaintenance" value="'.$this->l('Click here to put your shop under maintenance').'"></form>' : '').'</td>
<td>'.($current_ps_config['shop_deactivated'] ? $pic_ok : $pic_nok).'</td></tr>';
$this->_html .= '
<tr><td>'.$this->l('PrestaShop\'s caching features are disabled').'</td>
<td>'.($current_ps_config['cache_deactivated'] ? $pic_ok : $pic_nok).'</td></tr>';
if (version_compare(_PS_VERSION_,'1.5.0.0','<'))
$this->_html .= '
<tr><td>'.$this->l('The mobile theme is disabled').'</td>
<td>'.($current_ps_config['test_mobile'] ? $pic_ok : $pic_nok).'</td></tr>';
// for informaiton, display time limit
$max_exec_time = ini_get('max_execution_time');
$this->_html .= '
<tr><td>'.sprintf($this->l('PHP\'s max_execution_time setting has a high value or is disabled entirely (current value: %s)'), ($max_exec_time == 0 ? $this->l('unlimited') : sprintf($this->l('%s seconds'), $max_exec_time))).'</td>
<td>'.($max_exec_time == 0 ? $pic_ok : $pic_warn).'</td></tr>
</table>
<p>'.$this->l('Please also make sure you make a full manual backup of your files and database.').'</p>
</div>
</fieldset>';
}
public function divChannelInfos($upgrade_info)
{
if ($this->getConfig('channel') == 'private')
{
$upgrade_info['link'] = $this->getConfig('private_release_link');
$upgrade_info['md5'] = $this->getConfig('private_release_md5');
}
$content = '<div id="channel-infos" ><br/>';
if (isset($upgrade_info['branch']))
{
$content .= '<div style="clear:both">
<label class="label-small">'.$this->l('branch:').'</label>
<div class="margin-form margin-form-small" style="padding-top:5px">
<span class="available">
<img src="../img/admin/'.(!empty($upgrade_info['available'])?'enabled':'disabled').'.gif" />'
.' '.(!empty($upgrade_info['available'])?$this->l('available'):$this->l('unavailable')).'
</span>
</div></div>';
}
$content .= '<div class="all-infos">';
if (isset($upgrade_info['version_name']))
$content .= '<div style="clear:both;">
<label class="label-small">'.$this->l('Name:').'</label>
<div class="margin-form margin-form-small" style="padding-top:5px">
<span class="name">'.$upgrade_info['version_name'].'&nbsp;</span></div>
</div>';
if (isset($upgrade_info['version_number']))
$content .= '<div style="clear:both;">
<label class="label-small">'.$this->l('Version number:').'</label>
<div class="margin-form margin-form-small" style="padding-top:5px">
<span class="version">'.$upgrade_info['version_num'].'&nbsp;</span></div>
</div>';
if (!empty($upgrade_info['link']))
{
$content .= '<div style="clear:both;">
<label class="label-small">'.$this->l('URL:').'</label>
<div class="margin-form margin-form-small" style="padding-top:5px">
<a class="url" href="'.$upgrade_info['link'].'">'.$upgrade_info['link'].'</a>
</div>
</div>';
}
if (!empty($upgrade_info['md5']))
$content .= '<div style="clear:both;">
<label class="label-small">'.$this->l('MD5 hash:').'</label>
<div class="margin-form margin-form-small" style="padding-top:5px">
<span class="md5">'.$upgrade_info['md5'].'&nbsp;</span></div></div>';
if (!empty($upgrade_info['changelog']))
$content .= '<div style="clear:both;">
<label class="label-small">'.$this->l('Changelog:').'</label>
<div class="margin-form margin-form-small" style="padding-top:5px">
<a class="changelog" href="'.$upgrade_info['changelog'].'">'.$this->l('see changelog').'</a>
</div></div>';
$content .= '</div></div>';
return $content;
}
public function getBlockSelectChannel($channel = 'minor')
{
$admin_dir = trim(str_replace($this->prodRootDir, '', $this->adminDir), DIRECTORY_SEPARATOR);
$content = '';
$opt_channels = array();
// Hey ! I'm really using a fieldset element to regroup fields ?! !
$opt_channels[] = '<option id="useMajor" value="major" '.($channel == 'major'?'class="current" selected="selected">* ':'>')
.$this->l('Major release').'</option>';
$opt_channels[] = '<option id="useMinor" value="minor" '.($channel == 'minor'?'class="current" selected="selected">* ':'>')
.$this->l('Minor release (recommended)').'</option>';
$opt_channels[] = '<option id="useRC" value="rc" '.($channel == 'rc'?'class="current" selected="selected">* ':'>')
.$this->l('Release candidates').'</option>';
$opt_channels[] = '<option id="useBeta" value="beta" '.($channel == 'beta'?'class="current" selected="selected">* ':'>')
.$this->l('Beta releases').'</option>';
$opt_channels[] = '<option id="useAlpha" value="alpha" '.($channel == 'alpha'?'class="current" selected="selected">* ':'>')
.$this->l('Alpha releases').'</option>';
$opt_channels[] = '<option id="usePrivate" value="private" '.($channel == 'private'?'class="current" selected="selected">* ':'>')
.$this->l('Private release (require link and MD5 hash)').'</option>';
$opt_channels[] = '<option id="useArchive" value="archive" '.($channel == 'archive'?'class="current" selected="selected">* ':'>')
.$this->l('Local archive').'</option>';
$opt_channels[] = '<option id="useDirectory" value="directory" '.($channel == 'directory'?'class="current" selected="selected">* ':'>')
.$this->l('Local directory').'</option>';
$content .= '<label class="label-small">'.$this->l('Channel:').'</label><select name="channel" >';
$content .= implode('', $opt_channels);
$content .= '</select>';
$upgrade_info = $this->getInfoForChannel($channel);
$content .= $this->divChannelInfos($upgrade_info);
$content .= '<div id="for-useMinor" ><div class="margin-form margin-form-small">'.$this->l('This option regroup all stable versions.').'</div></div>';
$content .= '<div id="for-usePrivate">
<p><label class="label-small">'.$this->l('Link:').'</label>
<input size="50" type="text" name="private_release_link" value="'.$this->getConfig('private_release_link').'"/> *
</p>
<p><label class="label-small">'.$this->l('Hash key:').'</label>
<input size="32" type="text" name="private_release_md5" value="'.$this->getConfig('private_release_md5').'"/> *
</p>
<p><label class="label-small">'.$this->l('Allow major upgrade:').'</label>
<input type="checkbox" name="private_allow_major" value="1"'.($this->getConfig('private_allow_major')?' checked="checked"':'').'/>
</p>
</div>';
$download = $this->downloadPath.DIRECTORY_SEPARATOR;
$dir = glob($download.'*.zip');
$content .= '<div id="for-useArchive">';
if ($dir !== false && count($dir) > 0)
{
$archive_filename = $this->getConfig('archive.filename');
$content .= '<label class="label-small">'.$this->l('Archive to use:').'</label><div><select name="archive_prestashop" >
<option value="">'.$this->l('choose an archive').'</option>';
foreach ($dir as $file)
$content .= '<option '.($archive_filename ? 'selected="selected"' : '').' value="'.str_replace($download, '', $file).'">'.str_replace($download, '', $file).'</option>';
$content .= '</select> '
.$this->l('to upgrade for version').' <input type="text" size="10" name="archive_num"
value="'.($this->getConfig('archive.version_num')?$this->getConfig('archive.version_num'):'').'" /> *
</div>';
}
else
$content .= '<div class="warn">'.$this->l('No archive found in your admin/autoupgrade/download directory').'</div>';
$content .= '<div class="margin-form">'.$this->l('This option will skip the download step.').'</div></div>';
// $directory_dirname = $this->getConfig('directory.dirname');
$content .= '<div id="for-useDirectory">
<p> '.
sprintf($this->l('The directory %1$s will be used for upgrading to version '),
'<b>/admin/autoupgrade/latest/prestashop/</b>' ).
' <input type="text" size="10" name="directory_num"
value="'.($this->getConfig('directory.version_num')?$this->getConfig('directory.version_num'):'').'" /> <small>(1.6.0.6 for instance)</small> *
<br/>
<p>* '
.$this->l('This option will skip both download and unzip steps and will use admin/autoupgrade/download/prestashop/ as the source directory.').'</div>
</p>';
// backupFiles
// backupDb
$content .= '<div style="clear:both;">
<div class="margin-form" style="">
<input type="button" class="button" value="'.$this->l('Save').'" name="submitConf-channel" />
</div>
</div>';
return $content;
}
public function getBlockConfigurationAdvanced()
{
$content = '
<div>
<input type="button" class="button" style="float:right" name="btn_adv" value="'.$this->l('More options (Expert mode)').'"/>
</div>
<div style="float: left; margin-top: 13px; display:none;" id="configResult">&nbsp;</div>
<div class="clear" id="advanced">
<h3>'.$this->l('Expert mode').'</h3>
<h4 style="margin-top: 0px;">'.$this->l('Please select your channel:').'</h4>
<p>'.$this->l('Channels are offering you different ways to perform an upgrade. You can either upload the new version manually or let the 1-Click Upgrade module download it for you.').'<br />'.
$this->l('The Alpha, Beta and Private channels give you the ability to upgrade to a non-official or unstable release (for testing purposes only).').'<br />'.
$this->l('By default, you should use the "Minor release" channel which is offering the latest stable version available.').'</p><br />';
$config = $this->getConfig();
$channel = $config['channel'];
if (empty($channel))
$channel = Upgrader::DEFAULT_CHANNEL;
$content .= $this->getBlockSelectChannel($channel).'
</div>';
return $content;
}
public function displayDevTools()
{
$content = '';
$content .= '<br class="clear"/>';
$content .= '<fieldset class="autoupgradeSteps"><legend>'.$this->l('Step').'</legend>';
$content .= '<h4>'.$this->l('Upgrade steps').' : </h4>';
$content .= '<div>';
$content .= '<a id="download" class="upgradestep">download</a>';
$content .= '<a id="unzip" class="upgradestep">unzip</a>'; // unzip in autoupgrade/latest
$content .= '<a id="removeSamples" class="upgradestep">removeSamples</a>'; // remove samples (iWheel images)
$content .= '<a id="backupFiles" class="upgradestep">backupFiles</a>'; // backup files
$content .= '<a id="backupDb" class="upgradestep">backupDb</a>';
$content .= '<a id="upgradeFiles" class="upgradestep">upgradeFiles</a>';
$content .= '<a id="upgradeDb" class="upgradestep">upgradeDb</a>';
$content .= '<a id="upgradeModules" class="upgradestep">upgradeModules</a>';
$content .= '<a id="cleanDatabase" class="upgradestep">cleanDb</a>';
$content .= '<a id="upgradeComplete" class="upgradestep">upgradeComplete</a>';
$content .= '</div></fieldset>';
return $content;
}
private function _displayComparisonBlock()
{
$this->_html .= '
<fieldset id="comparisonBlock">
<legend>'.$this->l('Version comparison').'</legend>
<b>'.$this->l('PrestaShop Original version').':</b><br/>
<span id="checkPrestaShopFilesVersion">
<img id="pleaseWait" src="'.__PS_BASE_URI__.'img/loader.gif"/>
</span><br/>
<b>'.$this->l('Differences between versions').':</b><br/>
<span id="checkPrestaShopModifiedFiles">
<img id="pleaseWait" src="'.__PS_BASE_URI__.'img/loader.gif"/>
</span>
</fieldset>';
}
private function _displayBlockActivityLog()
{
$this->_html .= '
<fieldset id="activityLogBlock" style="display:none">
<legend><img src="../img/admin/slip.gif" /> '.$this->l('Activity Log').'</legend>
<p id="upgradeResultCheck"></p>
<div id="upgradeResultToDoList" style="width:890px!important;"></div>
<div id="currentlyProcessing" style="display:none;float:left">
<h4 id="pleaseWait">'.$this->l('Currently processing').' <img class="pleaseWait" src="'.__PS_BASE_URI__.'img/loader.gif"/></h4>
<div id="infoStep" class="processing" >'.$this->l('Analyzing the situation...').'</div>
</div>';
// this block will show errors and important warnings that happens during upgrade
$this->_html .= '
<div id="errorDuringUpgrade" style="display:none;float:right">
<h4>'.$this->l('Errors').'</h4>
<div id="infoError" class="processing" ></div>';
$this->_html .= '
</div>
<div class="clear">&nbsp;</div>
<div id="quickInfo" class="processing"></div>
</fieldset>';
}
/**
* _displayBlockUpgradeButton
* display the summary current version / target vesrion + "Upgrade Now" button with a "more options" button
*
* @access private
* @return void
*/
private function _displayBlockUpgradeButton()
{
global $cookie;
$admin_dir = trim(str_replace($this->prodRootDir, '', $this->adminDir), DIRECTORY_SEPARATOR);
$this->_html .= '
<fieldset id="upgradeButtonBlock">
<legend>'.$this->l('Start your Upgrade').'</legend>
<div class="blocOneClickUpgrade">';
if (version_compare(_PS_VERSION_, $this->upgrader->version_num, '=='))
$this->_html .= '<p>'.$this->l('Congratulations, you are already using the latest version available!').'</p>';
elseif (version_compare(_PS_VERSION_, $this->upgrader->version_num, '>'))
$this->_html .= '<p>'.$this->l('You come from the future! You are using a more recent version than the latest available!').'</p>';
$this->_html .= '<table class="table" cellpadding="0" cellspacing="0"><tr><th>'.$this->l('Your current prestashop version').'</th><td>'._PS_VERSION_.'</td></tr>';
$channel = $this->getConfig('channel');
$this->_html .= '<tr><th>'.sprintf($this->l('Latest official version for %1$s channel.'), $channel).'</th>';
if (!in_array($channel, array('archive', 'directory')))
{
if (!empty($this->upgrader->version_num))
$this->_html .= '<td><b>'.$this->upgrader->version_name.'</b> '.'('. $this->upgrader->version_num.')</td>';
}
else
$this->_html .= '<td>'.$this->l('N/A').'</td>';
$this->_html .= '</tr></table>
</div>';
/////////////////////////////////////////////////////////////////////////////////////////////////////////
// decide to display "Start Upgrade" or not
if ($this->configOk())
{
if (version_compare(_PS_VERSION_, $this->upgrader->version_num, '<'))
{
$show_big_button_new_version = false;
$this->_html .= '<p class="clear"><a href="" id="upgradeNow" class="button-autoupgrade upgradestep">'.$this->l('Upgrade PrestaShop now !').'</a></p>';
// smarty2 uses is a warning only, and will be displayed only if current version is 1.3 or 1.4 and target is <1.5;
$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 was removed 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', '<='))
{
$token_preferences = Tools14::getAdminTokenLite('AdminPreferences');
$this->_html .= '<div class="clear">&nbsp;</div><b>'.$this->l('Smarty 3 Usage:').'</b> <img src="'.$srcShopStatus.'" />'.$label;
if (version_compare(_PS_VERSION_, '1.4.0.0', '>') && version_compare(_PS_VERSION_, '1.5.0.0', '<'))
$this->_html .= '<div class="clear">&nbsp;</div>
<a href="index.php?tab=AdminPreferences&token='.$token_preferences.'#PS_FORCE_SMARTY_2" class="button">'
.$this->l('Edit your Smarty configuration').'</a>';
$this->_html .= '<div class="clear">&nbsp;</div>';
}
if (!in_array($channel, array('archive', 'directory')))
{
if ($this->getConfig('channel') == 'private')
$this->upgrader->link = $this->getConfig('private_release_link');
$this->_html .= '<small><a href="'.$this->upgrader->link.'">'.sprintf($this->l('PrestaShop will be downloaded from %s'), $this->upgrader->link).'</a></small><br/>';
$this->_html .= '<div class="clear">&nbsp;</div>';
$this->_html .= '<small><a href="'.$this->upgrader->changelog.'" target="_blank" >'.$this->l('open changelog in a new window').'</a></small>';
$this->_html .= '<div class="clear">&nbsp;</div>';
}
else
$this->_html .= sprintf($this->l('No file will be downloaded (channel %s is used)'), $channel);
// if skipActions property is used, we will handle that in the display :)
if (count(AdminSelfUpgrade::$skipAction) > 0)
{
$this->_html .= '<div id="skipAction-list" class="warn" style="display:block;font-weight:normal">
<img src="../img/admin/warning.gif"/>'
.$this->l('The following action are automatically replaced')
.'<ul>';
foreach(AdminSelfUpgrade::$skipAction as $k => $v)
$this->_html .= '<li>'
.sprintf($this->l('%1$s will be replaced by %2$s'), '<b>'.$k.'</b>', '<b>'.$v.'</b>').'</li>';
$this->_html .= '</ul><p>'.$this->l('To change this behavior, you need to manually edit your php files').'</p>
</div>';
}
}
else
$show_big_button_new_version = true;
}
else
$show_big_button_new_version = true;
if ($show_big_button_new_version)
{
$this->_html .=
'<div class="clear"></div>
<a class="button button-autoupgrade"
href="index.php?tab=AdminSelfUpgrade&amp;token='.$this->token.'&amp;refreshCurrentVersion=1">'.$this->l('Check if a new version is available').'</a>';
$this->_html .= '<div><span style="font-style: italic; font-size: 11px;">'.sprintf($this->l('Last check: %s'), Configuration::get('PS_LAST_VERSION_CHECK') ? date('Y-m-d H:i:s', Configuration::get('PS_LAST_VERSION_CHECK')) : $this->l('never')).'</span></div>';
}
else
{
$this->_html .= '<div class="clear"></div><a class="button button-autoupgrade" href="index.php?tab=AdminSelfUpgrade&token='
.$this->token
.'&refreshCurrentVersion=1">'.$this->l('refresh the page').'</a>';
$this->_html .= '<div>
<span>'.sprintf($this->l('Last datetime check: %s '), date('Y-m-d H:i:s',Configuration::get('PS_LAST_VERSION_CHECK')))
.'</span></div>';
}
$this->_html .= $this->getBlockConfigurationAdvanced();
$this->_html .= '</fieldset>';
if ($this->manualMode)
$this->_html .= $this->displayDevTools();
// information to keep will be in #infoStep
// temporary infoUpdate will be in #tmpInformation
}
public function display()
{
$this->_html .= '<script type="text/javascript">var jQueryVersionPS = parseInt($().jquery.replace(/\./g, ""));</script>
<script type="text/javascript" src="'.__PS_BASE_URI__.'modules/autoupgrade/js/jquery-1.6.2.min.js"></script>
<script type="text/javascript">if (jQueryVersionPS >= 162) jq162 = jQuery.noConflict(true);</script>';
/* PrestaShop demo mode */
if (defined('_PS_MODE_DEMO_') && _PS_MODE_DEMO_)
{
echo '<div class="error">'.$this->l('This functionality has been disabled.').'</div>';
return;
}
if (!file_exists($this->autoupgradePath.DIRECTORY_SEPARATOR.'ajax-upgradetab.php'))
{
echo '<div class="error">'.'<img src="../img/admin/warning.gif" /> '.$this->l('[TECHNICAL ERROR] ajax-upgradetab.php is missing. Please reinstall or reset the module.').'</div>';
return false;
}
/* PrestaShop demo mode*/
// in order to not use Tools class
$upgrader = new Upgrader();
preg_match('#([0-9]+\.[0-9]+)(?:\.[0-9]+){1,2}#', _PS_VERSION_, $matches);
$upgrader->branch = $matches[1];
$channel = $this->getConfig('channel');
switch ($channel)
{
case 'archive':
$upgrader->channel = 'archive';
$upgrader->version_num = $this->getConfig('archive.version_num');
break;
case 'directory':
$upgrader->channel = 'directory';
$upgrader->version_num = $this->getConfig('directory.version_num');
break;
default:
$upgrader->channel = $channel;
if (isset($_GET['refreshCurrentVersion']))
{
// 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);
if ($this->getConfig('channel') == 'private' && !$this->getConfig('private_allow_major'))
$upgrader->checkPSVersion(true, array('private', 'minor'));
else
$upgrader->checkPSVersion(true, array('minor'));
Tools14::redirectAdmin($this->currentIndex.'&conf=5&token='.Tools14::getValue('token'));
}
else
{
if ($this->getConfig('channel') == 'private' && !$this->getConfig('private_allow_major'))
$upgrader->checkPSVersion(false, array('private', 'minor'));
else
$upgrader->checkPSVersion(false, array('minor'));
}
}
$this->upgrader = $upgrader;
$this->_html .= '<link type="text/css" rel="stylesheet" href="'.__PS_BASE_URI__.'modules/autoupgrade/css/styles.css" />';
$this->_html .= '
<h1>'.$this->l('1-click Upgrade').'</h1>
<fieldset id="informationBlock" class="information" style="float: left; width: 30%;">
<legend>'.$this->l('Welcome!').'</legend>
<p>
'.$this->l('With the PrestaShop 1-Click Upgrade module, upgrading your store to the latest version available has never been easier!').'<br /><br />
<span style="color:#CC0000;font-weight:bold">'.$this->l('Please always perform a full manual backup of your files and database before starting any upgrade.').'</span><br />
'.$this->l('Double-check the integrity of your backup and that you can easily manually roll-back if necessary.').'<br />
'.$this->l('If you do not know how to proceed, ask your hosting provider.').'
</p>
</fieldset>';
/* Make sure the user has configured the upgrade options, or set default values */
$configuration_keys = array('PS_AUTOUP_UPDATE_DEFAULT_THEME' => 1, 'PS_AUTOUP_KEEP_MAILS' => 1, 'PS_AUTOUP_CUSTOM_MOD_DESACT' => 1,
'PS_AUTOUP_MANUAL_MODE' => 0, 'PS_AUTOUP_PERFORMANCE' => 1, 'PS_DISPLAY_ERRORS' => 0);
foreach ($configuration_keys as $k => $default_value)
if (Configuration::get($k) == '')
Configuration::updateValue($k, $default_value);
/* Checks/requirements and "Upgrade PrestaShop now" blocks */
$this->_displayCurrentConfiguration();
$this->_html .= '<div class="clear"></div>';
$this->_displayBlockUpgradeButton();
$this->_displayComparisonBlock();
$this->_displayBlockActivityLog();
$this->_displayRollbackForm();
$this->_html .= '<br/>';
$this->_html .= '<form action="'.$this->currentIndex.'&amp;customSubmitAutoUpgrade=1&amp;token='.$this->token.'" method="post" enctype="multipart/form-data">';
$this->_displayForm('backupOptions',$this->_fieldsBackupOptions,'<a href="#" name="backup-options" id="backup-options">'.$this->l('Backup Options').'</a>', '','database_gear');
$this->_displayForm('upgradeOptions',$this->_fieldsUpgradeOptions,'<a href="#" name="upgrade-options" id="upgrade-options">'.$this->l('Upgrade Options').'</a>', '','prefs');
$this->_html .= '</form>';
$this->_html .= '<script type="text/javascript" src="'.__PS_BASE_URI__.'modules/autoupgrade/js/jquery.xml2json.js"></script>';
$this->_html .= '<script type="text/javascript">'.$this->_getJsInit().'</script>';
echo $this->_html;
}
private function _getJsInit()
{
global $cookie;
$js = '';
$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<br/>");
}
function updateInfoStep(msg){
if (msg)
{
$("#infoStep").append(msg+"<div class=\"clear\"></div>");
$("#infoStep").prop({ scrollTop: $("#infoStep").prop("scrollHeight")},1);
}
}
function addError(arrError){
if (typeof(arrError) != "undefined" && arrError.length)
{
$("#errorDuringUpgrade").show();
for(i=0;i<arrError.length;i++)
$("#infoError").append(arrError[i]+"<div class=\"clear\"></div>");
// Note : jquery 1.6 make uses of prop() instead of attr()
$("#infoError").prop({ scrollTop: $("#infoError").prop("scrollHeight")},1);
}
}
function addQuickInfo(arrQuickInfo){
if (arrQuickInfo)
{
$("#quickInfo").show();
for(i=0;i<arrQuickInfo.length;i++)
$("#quickInfo").append(arrQuickInfo[i]+"<div class=\"clear\"></div>");
// Note : jquery 1.6 make uses of prop() instead of attr()
$("#quickInfo").prop({ scrollTop: $("#quickInfo").prop("scrollHeight")},1);
}
}'."\n";
if ($this->manualMode)
$js .= 'var manualMode = true;'."\n";
else
$js .= 'var manualMode = false;'."\n";
// relative admin dir
$admin_dir = trim(str_replace($this->prodRootDir, '', $this->adminDir), DIRECTORY_SEPARATOR);
// _PS_MODE_DEV_ will be available in js
if (defined('_PS_MODE_DEV_') AND _PS_MODE_DEV_)
$js .= 'var _PS_MODE_DEV_ = true;'."\n";
if ($this->getConfig('PS_AUTOUP_BACKUP'))
$js .= 'var PS_AUTOUP_BACKUP = true;'."\n";
$js .= $this->_getJsErrorMsgs();
$js .= '
var firstTimeParams = '.$this->buildAjaxResult().';
firstTimeParams = firstTimeParams.nextParams;
firstTimeParams.firstTime = "1";
// js initialization : prepare upgrade and rollback buttons
$(document).ready(function(){
$("select[name=channel]").change(function(e){
$("select[name=channel]").find("option").each(function()
{
if ($(this).is(":selected"))
$("#for-"+$(this).attr("id")).show();
else
$("#for-"+$(this).attr("id")).hide();
});
refreshChannelInfos();
});
function refreshChannelInfos()
{
val = $("select[name=channel]").find("option:selected").val();
$.ajax({
type:"POST",
url : "'. __PS_BASE_URI__ . $admin_dir.'/autoupgrade/ajax-upgradetab.php",
async: true,
data : {
dir:"'.$admin_dir.'",
token : "'.$this->token.'",
tab : "AdminSelfUpgrade",
action : "getChannelInfo",
ajaxMode : "1",
params : { channel : val}
},
success : function(res,textStatus,jqXHR)
{
if (isJsonString(res))
res = $.parseJSON(res);
else
res = {nextParams:{status:"error"}};
answer = res.nextParams.result;
if (typeof(answer) != "undefined")
$("#channel-infos").replaceWith(answer.div);
if (typeof(answer) != "undefined" && answer.available)
{
$("#channel-infos .all-infos").show();
}
else if (typeof(answer) != "undefined")
{
$("#channel-infos").html(answer.div);
$("#channel-infos .all-infos").hide();
}
},
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', 'AdminSelfUpgrade', true).'");
}
else
{
// technical error : no translation needed
$("#checkPrestaShopFilesVersion").html("<img src=\"../img/admin/warning.gif\" /> Error Unable to check md5 files");
}
}
})
}
$(document).ready(function(){
$("div[id|=for]").hide();
$("select[name=channel]").change();
});
// the following prevents to leave the page at the innappropriate time
$.xhrPool = [];
$.xhrPool.abortAll = function()
{
$.each(this, function(jqXHR)
{
if (jqXHR && (jqXHR.readystate != 4))
{
jqXHR.abort();
}
});
}
$(".upgradestep").click(function(e)
{
e.preventDefault();
// $.scrollTo("#options")
});
// set timeout to 20 minutes (before aborting an ajax request)
$.ajaxSetup({timeout:1200000});
// 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("<a class=\"button confirmBeforeDelete\" href=\"index.php?tab=AdminSelfUpgrade&token='
.$this->token
.'&amp;deletebackup&amp;name="+$(this).val()+"\">'
.'<img src=\"../img/admin/disabled.gif\" />'.$this->l('Delete').'</a>");
$(this).next().click(function(e){
if (!confirm("'.$this->l('Are you sure you want to delete this backup ?', 'AdminSelfUpgrade', true, false).'"))
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");
});
});
function showConfigResult(msg, type){
if (type == null)
type = "conf";
$("#configResult").html("<div class=\""+type+"\">"+msg+"</div>").show();
if (type == "conf")
{
$("#configResult").delay(3000).fadeOut("slow", function() {
location.reload();
});
}
}
// 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 afterUpdateConfig(res)
{
params = res.nextParams
config = params.config
oldChannel = $("select[name=channel] option.current");
if (config.channel != oldChannel.val())
{
newChannel = $("select[name=channel] option[value="+config.channel+"]");
oldChannel.removeClass("current");
oldChannel.html(oldChannel.html().substr(2));
newChannel.addClass("current");
newChannel.html("* "+newChannel.html());
}
if (res.error == 1)
showConfigResult(res.next_desc, "error");
else
showConfigResult(res.next_desc);
$("#upgradeNow").unbind();
$("#upgradeNow").replaceWith("<a class=\"button-autoupgrade\" href=\"'.$this->currentIndex.'&token='.$this->token.'\" >'.$this->l('Click to refresh the page and use the new configuration', 'AdminSelfUpgrade', true).'</a>");
}
function startProcess(type){
// hide useless divs, show activity log
$("#informationBlock,#comparisonBlock,#currentConfigurationBlock,#backupOptionsBlock,#upgradeOptionsBlock,#upgradeButtonBlock").slideUp("fast");
$(".autoupgradeSteps a").addClass("button");
$("#activityLogBlock").fadeIn("slow");
$(window).bind("beforeunload", function(e)
{
if (confirm("'.$this->l('An update is currently in progress... Click "OK" to abort.', 'AdminTab', true, false).'"))
{
$.xhrPool.abortAll();
$(window).unbind("beforeunload");
return true;
}
else
{
if (type == "upgrade")
{
e.returnValue = false;
e.cancelBubble = true;
if (e.stopPropagation)
{
e.stopPropagation();
}
if (e.preventDefault)
{
e.preventDefault();
}
}
}
});
}
function afterUpgradeNow(res)
{
startProcess("upgrade");
$("#upgradeNow").unbind();
$("#upgradeNow").replaceWith("<span id=\"upgradeNow\" class=\"button-autoupgrade\">'.$this->l('Upgrading PrestaShop', 'AdminSelfUpgrade', true).' ...</span>");
}
function afterUpgradeComplete(res)
{
params = res.nextParams
$("#pleaseWait").hide();
if (params.warning_exists == "false")
{
$("#upgradeResultCheck")
.addClass("conf")
.removeClass("fail")
.html("<p>'.$this->l('Upgrade complete').'</p>")
.show();
$("#infoStep").html("<h3>'.$this->l('Upgrade Complete!', 'AdminSelfUpgrade', true).'</h3>");
}
else
{
params = res.nextParams
$("#pleaseWait").hide();
$("#upgradeResultCheck")
.addClass("fail")
.removeClass("ok")
.html("<p>'.$this->l('Upgrade complete, but warning notifications has been found.').'</p>")
.show("slow");
$("#infoStep").html("<h3>'.$this->l('Upgrade complete, but warning notifications has been found.', 'AdminSelfUpgrade', true).'</h3>");
}
todo_list = [
"'.$this->l('Cookies have changed, you will need to log in again once you refreshed the page', 'AdminSelfUpgrade', true).'",
"'.$this->l('Javascript and CSS files have changed, please clear your browser cache with CTRL-F5', 'AdminSelfUpgrade', true).'",
"'.$this->l('Please check that your front-office theme is functional (try to create an account, place an order...)', 'AdminSelfUpgrade', true).'",
"'.$this->l('Product images do not appear in the front-office? Try regenerating the thumbnails in Preferences > Images', 'AdminSelfUpgrade', true).'",
"'.$this->l('Do not forget to reactivate your shop once you have checked everything!', 'AdminSelfUpgrade', true).'",
];
todo_ul = "<ul>";
$("#upgradeResultToDoList")
.addClass("hint clear")
.html("<h3>'.$this->l('ToDo list:').'</h3>")
for(var i in todo_list)
{
todo_ul += "<li>"+todo_list[i]+"</li>";
}
todo_ul += "</ul>";
$("#upgradeResultToDoList").append(todo_ul)
$("#upgradeResultToDoList").show();
$(window).unbind("beforeunload");
}
function afterError(res)
{
params = res.nextParams;
if (params.next == "")
$(window).unbind("beforeunload");
$("#pleaseWait").hide();
addQuickInfo(["unbind :) "]);
}
function afterRollback(res)
{
startProcess("rollback");
}
function afterRollbackComplete(res)
{
params = res.nextParams
$("#pleaseWait").hide();
$("#upgradeResultCheck")
.addClass("ok")
.removeClass("fail")
.html("<p>'.$this->l('Restoration complete.').'</p>")
.show("slow");
updateInfoStep("<h3>'.$this->l('Restoration complete.').'</h3>");
$(window).unbind();
}
function afterRestoreDb(params)
{
// $("#restoreBackupContainer").hide();
}
function afterRestoreFiles(params)
{
// $("#restoreFilesContainer").hide();
}
function afterBackupFiles(res)
{
params = res.nextParams;
// if (params.stepDone)
}
/**
* afterBackupDb display the button
*
*/
function afterBackupDb(res)
{
params = res.nextParams;
if (res.stepDone && typeof(PS_AUTOUP_BACKUP) != "undefined" && PS_AUTOUP_BACKUP == true)
{
$("#restoreBackupContainer").show();
$("select[name=restoreName]").children("options").removeAttr("selected");
$("select[name=restoreName]")
.append("<option selected=\"selected\" value=\""+params.backupName+"\">"+params.backupName+"</option>")
$("select[name=restoreName]").change();
}
}
function call_function(func){
this[func].apply(this, Array.prototype.slice.call(arguments, 1));
}
function doAjaxRequest(action, nextParams){
if (typeof(_PS_MODE_DEV_) != "undefined" && _PS_MODE_DEV_ == true)
addQuickInfo(["[DEV] ajax request : " + action]);
$("#pleaseWait").show();
req = $.ajax({
type:"POST",
url : "'. __PS_BASE_URI__.$admin_dir.'/autoupgrade/ajax-upgradetab.php'.'",
async: true,
data : {
dir:"'.$admin_dir.'",
ajaxMode : "1",
token : "'.$this->token.'",
tab : "AdminSelfUpgrade",
action : action,
params : nextParams
},
beforeSend: function(jqXHR)
{
$.xhrPool.push(jqXHR);
},
complete: function(jqXHR)
{
// just remove the item to the "abort list"
$.xhrPool.pop();
// $(window).unbind("beforeunload");
},
success : function(res, textStatus, jqXHR)
{
$("#pleaseWait").hide();
try{
res = $.parseJSON(res);
}
catch(e){
res = {status : "error", nextParams:nextParams};
alert("'.$this->l('Javascript error (parseJSON) detected for action ', __CLASS__, true, false).'\""+action+"\".'
.$this->l('Starting restoration...', __CLASS__, true, false).'");
}
addQuickInfo(res.nextQuickInfo);
addError(res.nextErrors);
updateInfoStep(res.next_desc);
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, res);
handleSuccess(res, action);
}
else
{
// display progression
$("#"+action).addClass("done");
$("#"+action).addClass("steperror");
if (action != "rollback"
&& action != "rollbackComplete"
&& action != "restoreFiles"
&& action != "restoreDb"
&& action != "rollback"
&& action != "noRollbackFound"
)
handleError(res, action);
else
alert("'.$this->l('Error detected during', __CLASS__, true, false).' [" + action + "].");
}
},
error: function(jqXHR, textStatus, errorThrown)
{
$("#pleaseWait").hide();
if (textStatus == "timeout")
{
if (action == "download")
updateInfoStep("'.addslashes($this->l('Your server cannot download the file. Please upload it first by ftp in your admin/autoupgrade directory')).'");
else
updateInfoStep("[Server Error] Timeout:'.addslashes($this->l('The request exceeded the max_time_limit. Please change your server configuration.')).'");
}
else
updateInfoStep("[Ajax / Server Error for action " + action + "] textStatus: \"" + textStatus + " \" errorThrown:\"" + errorThrown + " \" jqXHR: \" " + jqXHR.responseText + "\"");
}
});
return req;
};
/**
* 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:, next_desc:, nextParams:, nextQuickInfo:,status:"ok"}
* @param res $res
* @return void
*/
function handleSuccess(res, action)
{
if (res.next != "")
{
$("#"+res.next).addClass("nextStep");
if (manualMode && (action != "rollback"
&& action != "rollbackComplete"
&& action != "restoreFiles"
&& action != "restoreDb"
&& action != "rollback"
&& action != "noRollbackFound"))
{
prepareNextButton("#"+res.next,res.nextParams);
alert("'.sprintf($this->l('Manually go to %s button', __CLASS__, true, false), '"+res.next+"').'");
}
else
{
// if next is rollback, prepare nextParams with rollbackDbFilename and rollbackFilesFilename
if ( res.next == "rollback")
{
res.nextParams.restoreName = ""
}
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(["'.$this->l('End of process').'"]);
}
}
// res = {nextParams, next_desc}
function handleError(res, action)
{
// display error message in the main process thing
// In case the rollback button has been deactivated, just re-enable it
$("#rollback").removeAttr("disabled");
// auto rollback only if current action is upgradeFiles or upgradeDb
if (action == "upgradeFiles" || action == "upgradeDb" || action == "upgradeModules" )
{
$(".button-autoupgrade").html("'.$this->l('Operation canceled. Checking for restoration...').'");
res.nextParams.restoreName = res.nextParams.backupName;
if (confirm("'.$this->l('Do you want to restore').' " + "'.$this->backupName.'" + " ?"))
doAjaxRequest("rollback",res.nextParams);
}
else
{
$(".button-autoupgrade").html("'.$this->l('Operation canceled. An error happened.').'");
$(window).unbind();
}
}';
// ajax to check md5 files
$js .= 'function addModifiedFileList(title, fileList, css_class, container)
{
subList = $("<ul class=\"changedFileList "+css_class+"\"></ul>");
$(fileList).each(function(k,v){
$(subList).append("<li>"+v+"</li>");
});
$(container).append("<h3><a class=\"toggleSublist\" href=\"#\" >"+title+"</a> (" + fileList.length + ")</h3>");
$(container).append(subList);
$(container).append("<br/>");
}';
if(!file_exists($this->autoupgradePath.DIRECTORY_SEPARATOR.'ajax-upgradetab.php'))
$js .= '$(document).ready(function(){
$("#checkPrestaShopFilesVersion").html("<img src=\"../img/admin/warning.gif\" /> [TECHNICAL ERROR] ajax-upgradetab.php '.$this->l('is missing. please reinstall the module').'");
})';
else
$js .= '
function isJsonString(str) {
try {
typeof(str) != "undefined" && JSON.parse(str);
} catch (e) {
return false;
}
return true;
}
$(document).ready(function(){
$.ajax({
type:"POST",
url : "'. __PS_BASE_URI__ . $admin_dir.'/autoupgrade/ajax-upgradetab.php",
async: true,
data : {
dir:"'.$admin_dir.'",
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("<span> "+answer.msg+" </span> ");
if ((answer.status == "error") || (typeof(answer.result) == "undefined"))
$("#checkPrestaShopFilesVersion").prepend("<img src=\"../img/admin/warning.gif\" /> ");
else
{
$("#checkPrestaShopFilesVersion").prepend("<img src=\"../img/admin/warning.gif\" /> ");
$("#checkPrestaShopFilesVersion").append("<a id=\"toggleChangedList\" class=\"button\" href=\"\">'.$this->l('See or hide the list').'</a><br/>");
$("#checkPrestaShopFilesVersion").append("<div id=\"changedList\" style=\"display:none \"><br/>");
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").die().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 to your FTP server, and put it in your /[admin]/autoupgrade directory.').'");
}
else
{
// technical error : no translation needed
$("#checkPrestaShopFilesVersion").html("<img src=\"../img/admin/warning.gif\" /> Error: Unable to check md5 files");
}
}
})
$.ajax({
type:"POST",
url : "'. __PS_BASE_URI__ . $admin_dir.'/autoupgrade/ajax-upgradetab.php",
async: true,
data : {
dir:"'.$admin_dir.'",
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("<span> "+answer.msg+" </span> ");
if ((answer.status == "error") || (typeof(answer.result) == "undefined"))
$("#checkPrestaShopModifiedFiles").prepend("<img src=\"../img/admin/warning.gif\" /> ");
else
{
$("#checkPrestaShopModifiedFiles").prepend("<img src=\"../img/admin/warning.gif\" /> ");
$("#checkPrestaShopModifiedFiles").append("<a id=\"toggleDiffList\" class=\"button\" href=\"\">'.$this->l('See or hide the list').'</a><br/>");
$("#checkPrestaShopModifiedFiles").append("<div id=\"diffList\" style=\"display:none \"><br/>");
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("<img src=\"../img/admin/warning.gif\" /> Error: Unable to check md5 files");
}
}
})
});';
// advanced/normal mode
$js .= '
$("input[name=btn_adv]").click(function(e)
{
if ($("#advanced:visible").length)
switch_to_normal();
else
switch_to_advanced();
});
function switch_to_advanced(){
$("input[name=btn_adv]").val("'.$this->l('Less options', 'AdminTab', true, false).'");
$("#advanced").show();
}
function switch_to_normal(){
$("input[name=btn_adv]").val("'.$this->l('More options (Expert mode)', 'AdminTab', true, false).'");
$("#advanced").hide();
}
$(document).ready(function(){
'.($this->getConfig('channel') == 'major' ? 'switch_to_normal();' : 'switch_to_advanced();').'
});
';
$js .= '
$(document).ready(function()
{
$("input[name|=submitConf]").bind("click", function(e){
params = {};
newChannel = $("select[name=channel] option:selected").val();
oldChannel = $("select[name=channel] option.current").val();
oldChannel = "";
if (oldChannel != newChannel)
{
if( newChannel == "major"
|| newChannel == "minor"
|| newChannel == "rc"
|| newChannel == "beta"
|| newChannel == "alpha" )
params.channel = newChannel;
if(newChannel == "private")
{
if (($("input[name=private_release_link]").val() == "") || ($("input[name=private_release_md5]").val() == ""))
{
showConfigResult("'.$this->l('Link and MD5 hash cannot be empty').'", "error");
return false;
}
params.channel = "private";
params.private_release_link = $("input[name=private_release_link]").val();
params.private_release_md5 = $("input[name=private_release_md5]").val();
if ($("input[name=private_allow_major]").is(":checked"))
params.private_allow_major = 1;
else
params.private_allow_major = 0;
}
if(newChannel == "archive")
{
archive_prestashop = $("select[name=archive_prestashop] option:selected").val();
archive_num = $("input[name=archive_num]").val();
if (archive_num == "")
{
showConfigResult("'.$this->l('You need to enter the version number associated with the archive.').'", "error");
return false;
}
if (archive_prestashop == "")
{
showConfigResult("'.$this->l('No archive has been selected.').'", "error");
return false;
}
params.channel = "archive";
params.archive_prestashop = archive_prestashop;
params.archive_num = archive_num;
}
if(newChannel == "directory")
{
params.channel = "directory";
params.directory_prestashop = $("select[name=directory_prestashop] option:selected").val();
directory_num = $("input[name=directory_num]").val();
if (directory_num == "" || directory_num.indexOf(".") == -1)
{
showConfigResult("'.$this->l('You need to enter the version number associated with the directory.').'", "error");
return false;
}
params.directory_num = $("input[name=directory_num]").val();
}
}
// note: skipBackup is currently not used
if ($(this).attr("name") == "submitConf-skipBackup")
{
skipBackup = $("input[name=submitConf-skipBackup]:checked").length;
if (skipBackup == 0 || confirm("'.$this->l('Please confirm that you want to skip the backup.').'"))
params.skip_backup = $("input[name=submitConf-skipBackup]:checked").length;
else
{
$("input[name=submitConf-skipBackup]:checked").removeAttr("checked");
return false;
}
}
// note: preserveFiles is currently not used
if ($(this).attr("name") == "submitConf-preserveFiles")
{
preserveFiles = $("input[name=submitConf-preserveFiles]:checked").length;
if (confirm("'.$this->l('Please confirm that you want to preserve file options.').'"))
params.preserve_files = $("input[name=submitConf-preserveFiles]:checked").length;
else
{
$("input[name=submitConf-skipBackup]:checked").removeAttr("checked");
return false;
}
}
res = doAjaxRequest("updateConfig", params);
});
});
';
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($from_file, $to_dir)
{
if (!is_file($from_file))
{
$this->next = 'error';
$this->nextQuickInfo[] = sprintf($this->l('%s is not a file'), $from_file);
$this->nextErrors[] = sprintf($this->l('%s is not a file'), $from_file);
return false;
}
if (!file_exists($to_dir))
if (!mkdir($to_dir))
{
$this->next = 'error';
$this->nextQuickInfo[] = sprintf($this->l('Unable to create directory %s.'), $to_dir);
$this->nextErrors[] = sprintf($this->l('Unable to create directory %s.'), $to_dir);
return false;
}
else
chmod($to_dir, 0775);
$res = false;
if (!self::$force_pclZip && class_exists('ZipArchive', false))
{
$this->nextQuickInfo[] = $this->l('Using class ZipArchive...');
$zip = new ZipArchive();
if ($zip->open($from_file) === true && isset($zip->filename) && $zip->filename)
{
$extract_result = true;
$res = true;
// We extract file by file, it is very fast
for ($i = 0; $i < $zip->numFiles; $i++)
$extract_result &= $zip->extractTo($to_dir, array($zip->getNameIndex($i)));
if ($extract_result)
{
$this->nextQuickInfo[] = $this->l('Archive extracted');
return true;
}
else
{
$this->nextQuickInfo[] = sprintf($this->l('zip->extractTo(): unable to use %s as extract destination.'), $to_dir);
$this->nextErrors[] = sprintf($this->l('zip->extractTo(): unable to use %s as extract destination.'), $to_dir);
return false;
}
}
elseif(isset($zip->filename) && $zip->filename)
{
$this->nextQuickInfo[] = sprintf($this->l('Unable to open zipFile %s'), $from_file);
$this->nextErrors[] = sprintf($this->l('Unable to open zipFile %s'), $from_file);
return false;
}
}
if(!$res)
{
if (!class_exists('PclZip', false))
require_once(_PS_ROOT_DIR_.'/modules/autoupgrade/classes/pclzip.lib.php');
$this->nextQuickInfo[] = $this->l('Using class PclZip...');
$zip = new PclZip($from_file);
if (($file_list = $zip->listContent()) == 0)
{
$this->next = 'error';
$this->nextQuickInfo[] = sprintf($this->l('[ERROR] Error on extracting archive using PclZip: %s.'), $zip->errorInfo(true));
return false;
}
// PCL is very slow, so we need to extract files 500 by 500
$i = 0;
$j = 1;
foreach ($file_list as $file)
{
if (!isset($indexes[$i]))
$indexes[$i] = array();
$indexes[$i][] = $file['index'];
if ($j++ % 500 == 0)
$i++;
}
// replace also modified files
foreach ($indexes as $index)
if (($extract_result = $zip->extract(PCLZIP_OPT_BY_INDEX, $index, PCLZIP_OPT_PATH, $to_dir, PCLZIP_OPT_REPLACE_NEWER)) == 0)
{
$this->next = 'error';
$this->nextErrors[] = sprintf($this->l('[ERROR] Error on extracting archive using PclZip: %s.'), $zip->errorInfo(true));
return false;
}
else
{
foreach ($extract_result as $extractedFile)
{
$file = str_replace($this->prodRootDir, '', $extractedFile['filename']);
if ($extractedFile['status'] != 'ok' && $extractedFile['status'] != 'already_a_directory')
{
$this->nextQuickInfo[] = sprintf($this->l('[ERROR] %s has not been unzipped: '.$extractedFile['status']), $file);
$this->nextErrors[] = sprintf($this->l('[ERROR] %s has not been unzipped: '.$extractedFile['status']), $file);
$this->next = 'error';
}
else
$this->nextQuickInfo[] = sprintf('%1$s unzipped into %2$s', $file, str_replace(_PS_ROOT_DIR_, '', $to_dir));
}
if ($this->next === 'error')
return false;
}
return true;
}
}
private function _listArchivedFiles($zipfile)
{
if (file_exists($zipfile))
{
$res = false;
if (!self::$force_pclZip && class_exists('ZipArchive', false))
{
$this->nextQuickInfo[] = $this->l('Using class ZipArchive...');
$files = array();
$zip = new ZipArchive();
$res = $zip->open($zipfile);
if ($res)
$res = (isset($zip->filename) && $zip->filename) ? true : false;
if ($zip && $res === true){
for ($i = 0; $i < $zip->numFiles; $i++)
$files[] = $zip->getNameIndex($i);
return $files;
}
elseif($res)
{
$this->nextQuickInfo[] = $this->l('[ERROR] Unable to list archived files');
return false;
}
}
if (!$res)
{
$this->nextQuickInfo[] = $this->l('Using class PclZip...');
if (!class_exists('PclZip',false))
require_once(dirname(__FILE__).'/classes/pclzip.lib.php');
if ($zip = new PclZip($zipfile));
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/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);
$admin_dir = 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(DIRECTORY_SEPARATOR.'admin', DIRECTORY_SEPARATOR.$admin_dir, $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':
if (in_array($file, $this->restoreIgnoreFiles))
return true;
foreach ($this->restoreIgnoreAbsoluteFiles as $path)
{
$path = str_replace(DIRECTORY_SEPARATOR.'admin', DIRECTORY_SEPARATOR.$admin_dir, $path);
if ($fullpath == $rootpath.$path)
return true;
}
break;
case 'upgrade':
// keep mail : will skip only if already exists
if (!$this->keepMails) /* If set to false, we will not upgrade/replace the "mails" directory */
{
if (strpos(str_replace('/', DIRECTORY_SEPARATOR, $fullpath), DIRECTORY_SEPARATOR.'mails'.DIRECTORY_SEPARATOR))
return true;
}
if (in_array($file, $this->excludeFilesFromUpgrade))
{
if ($file[0] != '.')
{
$this->nextQuickInfo[] = sprintf($this->l('File %s is preserved'), $file);
}
return true;
}
foreach ($this->excludeAbsoluteFilesFromUpgrade as $path)
{
$path = str_replace(DIRECTORY_SEPARATOR.'admin', DIRECTORY_SEPARATOR.$admin_dir, $path);
if (strpos($fullpath, $rootpath.$path) !== false)
{
$this->nextQuickInfo[] = sprintf($this->l('File %s is preserved'), $fullpath);
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 optionDisplayErrors()
{
if ($this->getConfig('PS_DISPLAY_ERRORS'))
{
error_reporting(E_ALL);
ini_set('display_errors', 'on');
}
else
ini_set('display_errors', 'off');
}
}