diff --git a/admin-dev/tabs/AdminThemes.php b/admin-dev/tabs/AdminThemes.php
index 938ff03aa..a35d3fff4 100644
--- a/admin-dev/tabs/AdminThemes.php
+++ b/admin-dev/tabs/AdminThemes.php
@@ -266,6 +266,7 @@ class AdminThemes extends AdminPreferences
Configuration::updateValue('PS_IMG_UPDATE_TIME', time());
if (!empty($val) AND !$this->_isThemeCompatible($val)) // don't submit if errors
unset($_POST['submitThemes'.$this->table]);
+ Tools::clearCache($smarty);
parent::postProcess();
}
}
diff --git a/admin-dev/tabs/AdminTranslations.php b/admin-dev/tabs/AdminTranslations.php
index 90e2ecbab..baba819e0 100644
--- a/admin-dev/tabs/AdminTranslations.php
+++ b/admin-dev/tabs/AdminTranslations.php
@@ -157,7 +157,7 @@ class AdminTranslations extends AdminTab
}
}
if ($bool)
- Tools::redirectLink(self::$currentIndex.'&conf=14&token='.$this->token);
+ Tools::redirectAdmin(self::$currentIndex.'&conf=14&token='.$this->token);
$this->_errors[] = $this->l('a part of the data has been copied but some language files could not be found or copied');
}
diff --git a/admin-dev/tabs/AdminUpgrade.php b/admin-dev/tabs/AdminUpgrade.php
new file mode 100755
index 000000000..d429103f3
--- /dev/null
+++ b/admin-dev/tabs/AdminUpgrade.php
@@ -0,0 +1,1798 @@
+
+* @copyright 2007-2011 PrestaShop SA
+* @version Release: $Revision$
+* @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
+* International Registered Trademark & Property of PrestaShop SA
+*/
+
+include_once(_PS_ADMIN_DIR_.'/tabs/AdminPreferences.php');
+
+class AdminUpgrade extends AdminPreferences
+{
+ public $ajax = false;
+ public $nextResponseType = 'json'; // json, xml
+ public $next = 'N/A';
+
+ /**
+ * set to false if the current step is a loop
+ *
+ * @var boolean
+ */
+ public $stepDone = true;
+ public $error ='0';
+ public $nextDesc = '.';
+ public $nextParams = array();
+ public $nextQuickInfo = array();
+ public $currentParams = array();
+ public $autoupgradePath = '';
+ /**
+ * autoupgradeDir
+ *
+ * @var string directory relative to admin dir
+ */
+ public $autoupgradeDir = 'autoupgrade';
+ public $latestRootDir = '';
+ public $prodRootDir = '';
+ public $rootWritable = false;
+ public $svnDir = 'svn';
+ public $destDownloadFilename = 'prestashop.zip';
+ public $toUpgradeFileList = array();
+ public $backupFileList = array();
+ public $sampleFileList = array();
+ private $backupIgnoreFiles = array();
+ private $backupIgnoreAbsoluteFiles = array();
+ private $excludeFilesFromUpgrade = array();
+ private $excludeAbsoluteFilesFromUpgrade = array();
+
+/**
+ * int loopBackupFiles : if your server has a low memory size, lower this value
+ * @TODO remove the static, add a const, and use it like this : min(AdminUpgrade::DEFAULT_LOOP_ADD_FILE_TO_ZIP,Configuration::get('LOOP_ADD_FILE_TO_ZIP');
+ */
+ public static $loopBackupFiles = 1000;
+/**
+ * int loopUpgradeFiles : if your server has a low memory size, lower this value
+ */
+ public static $loopUpgradeFiles = 1000;
+/**
+ * intloopRemoveSamples : if your server has a low memory size, lower this value
+ */
+ public static $loopRemoveSamples = 1000;
+
+// public static $skipAction = array('unzip'=>'listSampleFiles');
+ public static $skipAction;
+ public $useSvn;
+
+ protected $_includeContainer = false;
+
+ public function __construct()
+ {
+ @set_time_limit(0);
+ @ini_set('max_execution_time', '0');
+
+ $this->init();
+ parent::__construct();
+ }
+
+ /**
+ * _setFields function to set fields (only when we need it).
+ *
+ * @return void
+ */
+ private function _setFields()
+ {
+ $this->_fieldsAutoUpgrade['PS_AUTOUP_DONT_SAVE_IMAGES'] = array(
+ 'title' => $this->l('Don\'t save images'), 'cast' => 'intval', 'validation' => 'isBool',
+ 'type' => 'bool', 'desc'=>$this->l('You can exclude the image directory from backup if you already saved it by an other method. (not recommended)'),
+ );
+
+ $this->_fieldsAutoUpgrade['PS_AUTOUP_KEEP_DEFAULT_THEME'] = array(
+ 'title' => $this->l('Keep theme "prestashop"'), 'cast' => 'intval', 'validation' => 'isBool',
+ 'type' => 'bool', 'desc'=>$this->l('if you have customized prestashop default theme, you can protect it from upgrade (not recommended)'),
+ );
+
+ $this->_fieldsAutoUpgrade['PS_AUTOUP_KEEP_TRAD'] = array(
+ 'title' => $this->l('Don\'t keep translations'), 'cast' => 'intval', 'validation' => 'isBool',
+ 'type' => 'bool', 'desc'=>$this->l('If set too yes, you will keep all your translations'),
+ );
+ // allow manual mode only for dev
+ if (defined('_PS_MODE_DEV_') AND _PS_MODE_DEV_)
+ $this->_fieldsAutoUpgrade['PS_AUTOUP_MANUAL_MODE'] = array(
+ 'title' => $this->l('Manual mode'), 'cast' => 'intval', 'validation' => 'isBool',
+ 'type' => 'bool', 'desc'=>$this->l('Check this if you want to stop after each step'),
+ );
+
+ if (defined('_PS_ALLOW_UPGRADE_UNSTABLE_') AND _PS_ALLOW_UPGRADE_UNSTABLE_ AND function_exists('svn_checkout'))
+ {
+ $this->_fieldsAutoUpgrade['PS_AUTOUP_USE_SVN'] = array(
+ 'title' => $this->l('Use Subversion'), 'cast' => 'intval', 'validation' => 'isBool',
+ 'type' => 'bool', 'desc' => $this->l('check this if you want to use unstable svn instead of official release'),
+ );
+ }
+ }
+
+ /**
+ * isUpgradeAllowed checks if all server configuration is valid for upgrade
+ *
+ * @return void
+ */
+ public function isUpgradeAllowed()
+ {
+ $allowed = (ConfigurationTest::test_fopen() && $this->rootWritable);
+
+ if (!defined('_PS_MODE_DEV_') OR !_PS_MODE_DEV_)
+ $allowed &= $this->upgrader->autoupgrade;
+
+ return $allowed;
+ }
+
+ /**
+ * init to build informations we need
+ *
+ * @return void
+ */
+ public function init()
+ {
+ // For later use, let's set up prodRootDir and adminDir
+ // This way it will be easier to upgrade a different path if needed
+ $this->prodRootDir = _PS_ROOT_DIR_;
+ $this->adminDir = _PS_ADMIN_DIR_;
+
+ // test writable recursively
+ if (ConfigurationTest::test_dir($this->prodRootDir,true))
+ $this->rootWritable = true;
+
+ // checkPSVersion will be not
+ $this->upgrader = new Upgrader(true);
+ $this->upgrader->checkPSVersion();
+ // If you have defined this somewhere, you know what you do
+ if (defined('_PS_ALLOW_UPGRADE_UNSTABLE_') AND _PS_ALLOW_UPGRADE_UNSTABLE_ AND function_exists('svn_checkout'))
+ {
+ $this->useSvn = Configuration::get('PS_AUTOUP_USE_SVN');
+ }
+ else
+ $this->useSvn = false;
+
+ // from $_POST or $_GET
+ $this->action = Tools::getValue('action');
+ $this->currentParams = Tools::getValue('params');
+
+ // If not exists in this sessions, "create"
+ // session handling : from current to next params
+ if (isset($this->currentParams['removeList']))
+ $this->nextParams['removeList'] = $this->currentParams['removeList'];
+
+ if (isset($this->currentParams['filesToUpgrade']))
+ $this->nextParams['filesToUpgrade'] = $this->currentParams['filesToUpgrade'];
+
+ $this->backupDbFilename = Configuration::get('UPGRADER_BACKUPDB_FILENAME');
+ $this->backupFilesFilename = Configuration::get('UPGRADER_BACKUPFILES_FILENAME');
+
+ $this->autoupgradePath = $this->adminDir.DIRECTORY_SEPARATOR.$this->autoupgradeDir;
+
+ if (!file_exists($this->autoupgradePath))
+ if (!@mkdir($this->autoupgradePath,0777))
+ $this->_errors[] = Tools::displayError(sprintf($this->l('unable to create directory %s'),$this->autoupgradePath));
+
+ $latest = $this->autoupgradePath.DIRECTORY_SEPARATOR.'latest';
+ if (!file_exists($latest))
+ if (!@mkdir($latest,0777))
+ $this->_errors[] = Tools::displayError(sprintf($this->l('unable to create directory %s'),$latest));
+
+ $this->latestRootDir = $latest.DIRECTORY_SEPARATOR.'prestashop';
+ $this->adminDir = str_replace($this->prodRootDir,'',$this->adminDir);
+ // @TODO future option
+ // $this->testRootDir = $this->autoupgradePath.DIRECTORY_SEPARATOR.'test';
+
+ /* optional skips */
+ $this->dontBackupImages = Configuration::get('PS_AUTOUP_DONT_SAVE_IMAGES');
+ $this->keepDefaultTheme = Configuration::get('PS_AUTOUP_KEEP_DEFAULT_THEME');
+ $this->keepTrad = Configuration::get('PS_AUTOUP_KEEP_TRAD');
+ $this->manualMode = Configuration::get('PS_AUTOUP_MANUAL_MODE');
+ // We can add any file or directory in the exclude dir : theses files will be not removed or overwritten
+ // @TODO cache should be ignored recursively, but we have to reconstruct it after upgrade
+ // - compiled from smarty
+ // - .svn
+ $this->backupIgnoreAbsoluteFiles[] = "/tools/smarty_v2/compile";
+ $this->backupIgnoreAbsoluteFiles[] = "/tools/smarty_v2/cache";
+ $this->backupIgnoreAbsoluteFiles[] = "/tools/smarty/compile";
+ $this->backupIgnoreAbsoluteFiles[] = "/tools/smarty/cache";
+
+ $this->excludeFilesFromUpgrade[] = '.';
+ $this->excludeFilesFromUpgrade[] = '..';
+ $this->excludeFilesFromUpgrade[] = '.svn';
+ $this->excludeFilesFromUpgrade[] = 'install';
+ $this->excludeFilesFromUpgrade[] = 'settings.inc.php';
+ $this->excludeFilesFromUpgrade[] = 'autoupgrade';
+ $this->backupIgnoreFiles[] = '.';
+ $this->backupIgnoreFiles[] = '..';
+ $this->backupIgnoreFiles[] = '.svn';
+ $this->backupIgnoreFiles[] = 'autoupgrade';
+
+ if ($this->dontBackupImages)
+ $this->backupIgnoreAbsoluteFiles[] = "/img";
+
+
+ if ($this->keepDefaultTheme)
+ $this->excludeAbsoluteFilesFromUpgrade[] = "/themes/prestashop";
+
+ if ($this->keepTrad)
+ $this->excludeFilesFromUpgrade[] = "translations";
+ }
+
+ /**
+ * getFilePath return the path to the zipfile containing prestashop.
+ *
+ * @return void
+ */
+ private function getFilePath()
+ {
+ return $this->autoupgradePath.DIRECTORY_SEPARATOR.$this->destDownloadFilename;
+ }
+
+ public function postProcess()
+ {
+ $this->_setFields();
+
+ if (!empty($_POST))
+ $this->_postConfig($this->_fieldsAutoUpgrade);
+ }
+
+ public function ajaxProcessUpgradeNow()
+ {
+ $this->nextDesc = 'Starting upgrade ...';
+ $this->next = 'desactiveShop';
+ }
+ public function ajaxProcessSvnExport()
+ {
+ if ($this->useSvn)
+ {
+ // first of all, delete the content of the latest root dir just in case
+ if (is_dir($this->latestRootDir))
+ Tools::deleteDirectory($this->latestRootDir, false);
+
+ if (!file_exists($this->latestRootDir))
+ {
+ @mkdir($this->latestRootDir);
+ }
+
+ if (svn_export($this->autoupgradePath . DIRECTORY_SEPARATOR . $this->svnDir, $this->latestRootDir))
+ {
+
+ // export means svn means install-dev and admin-dev.
+ // let's rename admin to the correct admin dir
+ // and rename install-dev to install
+ $adminDir = str_replace($this->prodRootDir, '', $this->adminDir);
+ rename($this->latestRootDir.DIRECTORY_SEPARATOR.'install-dev', $this->latestRootDir.DIRECTORY_SEPARATOR.'install');
+ rename($this->latestRootDir.DIRECTORY_SEPARATOR.'admin-dev', $this->latestRootDir.DIRECTORY_SEPARATOR.$adminDir);
+
+ // Unsetting to force listing
+ unset($this->nextParams['removeList']);
+ $this->next = "removeSamples";
+ $this->nextDesc = $this->l('Export svn complete. removing sample files...');
+ return true;
+ }
+ else
+ {
+ $this->next = 'error';
+ $this->nextDesc = $this->l('error when svn export ');
+ }
+ }
+ }
+
+ public function ajaxProcessUnzip(){
+ // @TODO : not require_once like that.
+ $filepath = $this->getFilePath();
+ $destExtract = $this->autoupgradePath.DIRECTORY_SEPARATOR.'latest';
+ if (file_exists($destExtract))
+ Tools::deletedirectory($destExtract);
+
+ if (Tools::ZipExtract($filepath,$destExtract))
+ {
+ $adminDir = str_replace($this->prodRootDir, '', $this->adminDir);
+ rename($this->latestRootDir.DIRECTORY_SEPARATOR.'admin', $this->latestRootDir.DIRECTORY_SEPARATOR.$adminDir);
+ // Unsetting to force listing
+ unset($this->nextParams['removeList']);
+ $this->next = "removeSamples";
+ $this->nextDesc = $this->l('Extract complete. removing sample files...');
+ return true;
+ }
+ else{
+ $this->next = "error";
+ $this->nextDesc = sprintf($this->l('unable to extract %2$s into %2$s ...'),$filepath,$destExtract);
+ return true;
+ }
+ }
+
+
+ /**
+ * _listSampleFiles will make a recursive call to scandir() function
+ * and list all file which match to the $fileext suffixe (this can be an extension or whole filename)
+ *
+ * @TODO maybe $regex instead of $fileext ?
+ * @param string $dir directory to look in
+ * @param string $fileext suffixe filename
+ * @return void
+ */
+ private function _listSampleFiles($dir, $fileext = '.jpg'){
+ $res = true;
+ $dir = rtrim($dir,'/').DIRECTORY_SEPARATOR;
+
+ $toDel = scandir($dir);
+ // copied (and kind of) adapted from AdminImages.php
+ foreach ($toDel AS $file)
+ {
+ if ($file!='.' AND $file != '..' AND $file != '.svn')
+ {
+
+ if (preg_match('#'.preg_quote($fileext,'#').'$#i',$file))
+ {
+ $this->sampleFileList[] = $dir.$file;
+ }
+ else if (is_dir($dir.$file))
+ {
+ $res &= $this->_listSampleFiles($dir.$file);
+ }
+ }
+ }
+ return $res;
+ }
+
+ public function _listBackupFiles($dir)
+ {
+ $allFiles = scandir($dir);
+ foreach ($allFiles as $file)
+ {
+ $fullPath = $dir.DIRECTORY_SEPARATOR.$file;
+
+ if (!$this->_skipFile($file, $fullPath,'backup'))
+ {
+ if (is_dir($fullPath))
+ $this->_listBackupFiles($fullPath);
+ else
+ $this->backupFileList[] = $fullPath;
+ }
+ else
+ $this->backupIgnoreFiles[] = $fullPath;
+
+ }
+ }
+
+ public function _listFilesToUpgrade($dir)
+ {
+ $allFiles = scandir($dir);
+ foreach ($allFiles as $file)
+ {
+ $fullPath = $dir.DIRECTORY_SEPARATOR.$file;
+
+ if (!$this->_skipFile($file, $fullPath, "upgrade"))
+ {
+ if (is_dir($fullPath))
+ {
+ // if is_dir, we will create it :)e it :)
+ $this->toUpgradeFileList[] = $fullPath;
+ if (strpos($dir.DIRECTORY_SEPARATOR.$file, 'install') === false)
+ {
+ $this->_listFilesToUpgrade($fullPath);
+ }
+ }
+ else
+ $this->toUpgradeFileList[] = $fullPath;
+ }
+ }
+
+ $this->nextParams['filesToUpgrade'] = $this->toUpgradeFileList;
+ }
+
+ public function ajaxProcessDesactiveShop()
+ {
+ $this->ShopActiveValue = Configuration::get('PS_SHOP_ENABLE');
+ Configuration::updateValue('PS_SHOP_ENABLE',0);
+ if ($this->useSvn)
+ {
+ $this->next = 'svnCheckout';
+ $this->nextDesc = $this->l('switching to svn checkout (useSvn set to true)');
+ }
+ else
+ {
+ $this->next = 'download';
+ $this->nextDesc = $this->l('Shop desactivated. Now downloading (this can takes some times )...');
+ }
+ }
+
+ public function ajaxProcessUpgradeFiles(){
+ // @TODO :
+ $this->nextParams = $this->currentParams;
+ if (!isset($this->nextParams['filesToUpgrade']))
+ $this->_listFilesToUpgrade($this->latestRootDir);
+
+ // later we could choose between _PS_ROOT_DIR_ or _PS_TEST_DIR_
+ $this->destUpgradePath = $this->prodRootDir;
+
+ // upgrade files one by one like for the backup
+ // with a 1000 loop because it's funny
+ // @TODO :
+ // foreach files in latest, copy
+ $this->next = 'upgradeFiles';
+ if (!is_array($this->nextParams['filesToUpgrade']))
+ {
+ error($this->nextParams);
+ $this->next = 'error';
+ $this->nextDesc = $this->l('filesToUpgrade is not an array');
+ $this->nextQuickInfo[] = $this->l('filesToUpgrade is not an array');
+ return false;
+ }
+
+ // @TODO : does not upgrade files in modules, translations if they have not a correct md5 (or crc32, or whatever) from previous version
+ for ($i=0;$inextParams['filesToUpgrade'])<=0)
+ {
+ $this->next = 'upgradeDb';
+ $this->nextDesc = $this->l('All files upgraded. Now upgrading database');
+ $this->nextResponseType = 'xml';
+ break;
+ }
+
+ //$file = array_shift($this->nextParams['filesToUpgrade']);
+ $file = array_shift($this->nextParams['filesToUpgrade']);
+ if (!$this->upgradeThisFile($file))
+ {
+ // put the file back to the begin of the list
+ $totalFiles = array_unshift($this->nextParams['filesToUpgrade'],$file);
+ $this->next = 'error';
+ $this->nextQuickInfo[] = sprintf($this->l('error when trying to upgrade %s'),$file);
+ break;
+ }
+ else{
+ // @TODO : maybe put several files at the same times ?
+ $this->nextDesc = sprintf($this->l('%2$s files left to upgrade.'),$file,sizeof($this->nextParams['filesToUpgrade']));
+ }
+ }
+ }
+
+ /**
+ * model_doUpgrade prepare the call to doUpgrade.php file (like model.php)
+ *
+ * @return void
+ */
+ public function _modelDoUpgrade()
+ {
+ // a. set logger
+ // it will be used later
+ global $logger;
+ $logger = new FileLogger();
+ if (function_exists('date_default_timezone_set'))
+ date_default_timezone_set('Europe/Paris');
+ // use autoupgrade as log dir
+ $logger->setFilename($this->latestRootDir.'/'.date('Ymd').'_autoupgrade.log');
+
+ // init env.
+ @set_time_limit(0);
+ @ini_set('max_execution_time', '0');
+ // setting the memory limit to 128M only if current is lower
+ $memory_limit = ini_get('memory_limit');
+ if (substr($memory_limit,-1) != 'G'
+ AND ((substr($memory_limit,-1) == 'M' AND substr($memory_limit,0,-1) < 128)
+ OR is_numeric($memory_limit) AND (intval($memory_limit) < 131072))
+ ){
+ @ini_set('memory_limit','128M');
+ }
+
+ /* Redefine REQUEST_URI if empty (on some webservers...) */
+ if (!isset($_SERVER['REQUEST_URI']) || $_SERVER['REQUEST_URI'] == '')
+ $_SERVER['REQUEST_URI'] = $_SERVER['SCRIPT_NAME'];
+
+ if ($tmp = strpos($_SERVER['REQUEST_URI'], '?'))
+ $_SERVER['REQUEST_URI'] = substr($_SERVER['REQUEST_URI'], 0, $tmp);
+
+ $_SERVER['REQUEST_URI'] = str_replace('//', '/', $_SERVER['REQUEST_URI']);
+
+ ///////////////////////
+ // Copy from model.php
+ ///////////////////////
+ $upgrader = $this->upgrader;
+ $upgrader->checkPSVersion();
+
+ define('INSTALL_VERSION', $upgrader->version_num);
+ // now the install dir to use is in a subdirectory of the admin dir
+ define('INSTALL_PATH', realpath($this->latestRootDir.DIRECTORY_SEPARATOR.'install'));
+
+ define('PS_INSTALLATION_IN_PROGRESS', true);
+ // Note : we don't need ToolsInstall.php
+ // include_once(INSTALL_PATH.'/classes/ToolsInstall.php');
+
+ define('SETTINGS_FILE', $this->prodRootDir . '/config/settings.inc.php');
+ define('DEFINES_FILE', $this->prodRootDir .'/config/defines.inc.php');
+ define('INSTALLER__PS_BASE_URI', substr($_SERVER['REQUEST_URI'], 0, -1 * (strlen($_SERVER['REQUEST_URI']) - strrpos($_SERVER['REQUEST_URI'], '/')) - strlen(substr(dirname($_SERVER['REQUEST_URI']), strrpos(dirname($_SERVER['REQUEST_URI']), '/')+1))));
+
+ // Note : INSTALLER__PS_BASE_URI_ABSOLUTE is not used for upgrade
+ // define('INSTALLER__PS_BASE_URI_ABSOLUTE', 'http://'.ToolsInstall::getHttpHost(false, true).INSTALLER__PS_BASE_URI);
+
+ // XML Header
+ header('Content-Type: text/xml');
+ require_once(INSTALL_PATH.DIRECTORY_SEPARATOR.'xml'.DIRECTORY_SEPARATOR.'doUpgrade.php');
+
+ //////////////////////////////
+ // End of copy from model.php
+ //////////////////////////////
+
+ }
+
+ public function ajaxProcessUpgradeDb(){
+ // @TODO : 1/2/3 have to be done at the beginning !!!!!!!!!!!!!!!!!!!!!!
+
+ // use something like actual in install-dev
+ // Notice : xml used here ...
+
+ // 1) confirm version is correct(DB)
+ // install/model.php?method=getVersionFromDb&language=0
+ // later
+
+ // 2) confirm config is correct (r/w rights)
+ // install/model.php?method=checkConfig&firsttime=0
+ // later
+
+ // 3) save current activated modules in nextParams, or don't desactivate them ?
+ // @TODO
+ // 4) upgrade
+ // install/model.php?_=1309193641470&method=doUpgrade&customModule=desactivate
+ if (isset($this->currentParams['customModule']))
+ $_GET['customModule'] = $this->currentParams['customModule'];
+
+ if (!$this->_modelDoUpgrade())
+ {
+ $this->next = 'error';
+ $this->nextDesc = $this->l('error during upgrade Db');
+ }
+
+ // 5) compare activated modules and reactivate them
+ // @TODO
+
+ }
+
+
+ /**
+ * upgradeThisFile
+ *
+ * @param mixed $file
+ * @return void
+ */
+ public function upgradeThisFile($file)
+ {
+ // @TODO : later, we could handle customization with some kind of diff functions
+ // for now, just copy $file in str_replace($this->latestRootDir,_PS_ROOT_DIR_)
+ // $file comes from scandir function, no need to lost time and memory with file_exists()
+ if ($this->_skipFile('', $file,'upgrade'))
+ {
+ $this->nextQuickInfo[] = $this->l('%s ignored');
+ return true;
+ }
+ else
+ {
+ $dest = str_replace($this->latestRootDir, $this->destUpgradePath,$file);
+
+ if (is_dir($file))
+ {
+ if (!file_exists($dest))
+ {
+ if (@mkdir($dest))
+ {
+ $this->nextQuickInfo[] = sprintf($this->l('created dir %2$s. %3$s files left to upgrade.'),$file, $dest, sizeof($this->nextParams['filesToUpgrade']));
+ return true;
+ }
+ else
+ {
+ $this->next = 'error';
+ $this->nextQuickInfo[] = sprintf($this->l('error when creating directory %s'),$dest);
+ $this->nextDesc = sprintf($this->l('error when creating directory %s'),$dest);
+ return false;
+ }
+ }
+ else
+ {
+ // directory already exists
+ return true;
+ }
+ }
+ else
+ {
+ if (copy($file,$dest))
+ {
+ $this->nextQuickInfo[] = sprintf($this->l('copied %1$s in %2$s. %3$s files left to upgrade.'),$file, $dest, sizeof($this->nextParams['filesToUpgrade']));
+ return true;
+ }
+ else
+ {
+ $this->next = 'error';
+ $this->nextQuickInfo[] = sprintf($this->l('error for copy %1$s in %2$s'),$file,$dest);
+ $this->nextDesc = sprintf($this->l('error for copy %1$s in %2$s'),$file,$dest);
+ return false;
+ }
+ }
+ }
+
+ }
+
+ public function ajaxProcessRollback()
+ {
+ // 1st, need to analyse what was wrong.
+
+ $this->nextParams = $this->currentParams;
+ if (!empty($this->backupFilesFilename))
+ {
+ $this->next = 'restoreFiles';
+ $this->status = 'ok';
+ $this->nextDesc = $this->l('Files restored, now restoring database.');
+ }
+ else
+ {
+ if (!empty($this->backupDbFilename))
+ {
+ $this->next = 'restoreDb';
+ $this->status = 'ok';
+ $this->nextDesc = $this->l('Database restored');
+ }
+ else
+ {
+ // 2nd case if upgradeFiles made an error
+ // 3rd case if no upgrade has been done
+ // all theses cases are handled by the method ajaxRequestRollback()
+ $this->next = ''; // next is empty : nothing next :)
+ $this->status = 'ok';
+ $this->nextDesc = $this->l('All your site is restored... ');
+ }
+ }
+ }
+
+ /**
+ * ajaxProcessRestoreFiles restore the previously saved files.
+ *
+ * @return boolean true if succeed
+ */
+ public function ajaxProcessRestoreFiles()
+ {
+ // @TODO : workaround max_execution_time / ajax batch unzip
+ if (!empty($this->backupFilesFilename))
+ {
+ // cleanup current PS tree
+ $list = $this->_listArchivedFiles();
+ if (count($list) > 0)
+ {
+ $this->_cleanUp($this->prodRootDir.'/');
+ $this->nextQuickInfo[] = $this->l('root directory cleaned.');
+
+ $filepath = $this->backupFilesFilename;
+ $destExtract = $this->prodRootDir;
+
+ if (self::ZipExtract($filepath, $destExtract))
+ {
+ // once it's restored, delete the file !
+ unlink($this->backupFilesFilename);
+ Configuration::updateValue('UPGRADER_BACKUPFILES_FILENAME', '');
+ if (!empty($this->backupDbFilename))
+ {
+ $this->nextDesc = $this->l('Files restored. No database backup found. Restoration done.');
+ $this->next = '';
+ }
+ else
+ {
+ $this->nextDesc = $this->l('Files restored.');
+ $this->next = 'rollback';
+ }
+ return true;
+ }
+ else
+ {
+ $this->next = "error";
+ $this->nextDesc = sprintf($this->l('unable to extract $1$s into %2$s .'), $filepath, $destExtract);
+ return false;
+ }
+ }
+ }
+ else
+ {
+ $this->next = 'error';
+ $this->nextDesc = $this->l('no known backup. nothing to restore.');
+ return false;
+ }
+ }
+
+ /**
+ * try to restore db backup file
+ * @return type : hey , what you expect ? well mysql errors array .....
+ * @TODO : maybe this could be in the Backup class
+ */
+ public function ajaxProcessRestoreDb()
+ {
+ $exts = explode('.', $this->backupDbFilename);
+ $fileext = $exts[count($exts)-1];
+ $requests = array();
+ $errors = array();
+ $content = '';
+ switch ($fileext)
+ {
+ case 'bz':
+ case 'bz2':
+ if ($fp = bzopen($this->backupDbFilename, 'r'))
+ {
+ while(!feof($fp))
+ $content .= bzread($fp, filesize($this->backupDbFilename));
+ bzclose($fp);
+ }
+ break;
+ case 'gz':
+ if ($fp = gzopen($this->backupDbFilename, 'r'))
+ {
+ while(!feof($fp))
+ $content = gzread($fp, filesize($this->backupDbFilename));
+ gzclose($fp);
+ }
+ break;
+ // default means sql ?
+ default :
+ if ($fp = fopen($this->backupDbFilename, 'r'))
+ {
+ while(!feof($fp))
+ $content = fread($fp, filesize($this->backupDbFilename));
+ fclose($fp);
+ }
+ }
+
+ if ($content=='')
+ return false;
+
+ // preg_match_all is better than preg_split (what is used in doUpgrade.php)
+ // This way we avoid extra blank lines
+ // option s (PCRE_DOTALL) added
+ // @TODO need to check if a ";" in description could block that (I suppose it can at the end of a line)
+ preg_match_all('/.*;[\n]\+/s', $content, $requests);
+ /* @TODO maybe improve regex pattern ... */
+ $db = Db::getInstance();
+ if (count($requests)>0)
+ {
+ foreach ($requests as $request)
+ if (!empty($request))
+ if (!$db->Execute($request))
+ $this->nextQuickInfo[] = $db->getMsgError();
+
+ // once it's restored, delete the file !
+ unlink($this->backupDbFilename);
+ Configuration::updateValue('UPGRADER_BACKUPDB_FILENAME','');
+ }
+ else
+ $this->nextQuickInfo[] = $this->l('Nothing to restore (no request found)');
+
+ $this->next = 'rollback';
+ $this->nextDesc = 'Database restore done.';
+ }
+
+ public function ajaxProcessBackupDb()
+ {
+ $backup = new Backup();
+ // for backup db, use autoupgrade directory
+ // @TODO : autoupgrade must not be static
+ $backup->setCustomBackupPath('autoupgrade');
+ // maybe for big tables we should save them in more than one file ?
+ $res = $backup->add();
+ if ($res)
+ {
+ Configuration::updateValue('UPGRADER_BACKUPDB_FILENAME', $backup->id);
+
+ $this->next = 'upgradeFiles';
+ $this->nextDesc = sprintf($this->l('Database backup done in %s. Now updating files'),$backup->id);
+ }
+ // if an error occur, we assume the file is not saved
+ }
+
+ public function ajaxProcessBackupFiles()
+ {
+ $this->nextParams = $this->currentParams;
+ $this->stepDone = false;
+ /////////////////////
+
+ if (!isset($this->nextParams['filesForBackup']))
+ {
+ $list = $this->_listBackupFiles($this->prodRootDir);
+ $this->nextQuickInfo[] = sprintf($this->l('%s Files to backup.'), sizeof($this->backupFileList));
+ $this->nextParams['filesForBackup'] = $this->backupFileList;
+
+ // delete old backup, create new
+ if (file_exists($this->backupFilesFilename))
+ unlink($this->backupFilesFilename);
+
+ $time = time();
+ $this->backupFilesFilename = $this->autoupgradePath . DIRECTORY_SEPARATOR . 'backupfile-'.date('Y-m-d').'-'.$time.'.zip';
+
+ Configuration::updateValue('UPGRADER_BACKUPFILES_FILENAME', $this->backupFilesFilename);
+ $this->nextQuickInfo[] = sprintf($this->l('backup files initialized in %s'), $this->backupFilesFilename);
+ }
+
+ /////////////////////
+ $this->next = 'backupFiles';
+ // @TODO : display % instead of this
+ $this->nextDesc = sprintf($this->l('Backup files in progress. %s files left'), sizeof($this->nextParams['filesForBackup']));
+ if (is_array($this->nextParams['filesForBackup']))
+ {
+ // @TODO later
+ // 1) calculate crc32 of next file
+ // 2) use the provided xml with crc32 calculated from previous versions ?
+ // or simply use the latest dir ?
+ //$current = crc32(file_get_contents($file));
+ //$file = $this->nextParams['filesForBackup'][0];
+ //$latestFile = str_replace(_PS_ROOT_DIR_,$this->latestRootDir,$file);
+
+ // if (file_exists($latestFile))
+ // $latest = crc32($latestFile);
+ // else
+ // $latest = '';
+
+ $zip = new ZipArchive();
+ if ($zip->open($this->backupFilesFilename, ZIPARCHIVE::CREATE))
+ {
+ $this->next = 'backupFiles';
+ // @TODO all in one time will be probably too long
+ // 1000 ok during test, but 10 by 10 to be sure
+ $this->stepok = false;
+ // @TODO min(self::$loopBackupFiles, sizeof())
+ for($i=0;$inextParams['filesForBackup'])<=0)
+ {
+ $this->stepok = true;
+ $this->status = 'ok';
+ $this->next = 'backupDb';
+ $this->nextDesc = $this->l('All files saved. Now backup Database');
+ $this->nextQuickInfo[] = $this->l('all files have been added to archive.');
+ break;
+ }
+ // filesForBackup already contains all the correct files
+ $file = array_shift($this->nextParams['filesForBackup']);
+ $archiveFilename = str_replace($this->prodRootDir,'',$file);
+ // @TODO : maybe put several files at the same times ?
+ if ($zip->addFile($file,$archiveFilename))
+ $this->nextQuickInfo[] = sprintf($this->l('%1$s added to archive. %2$s left.'),$file, sizeof($this->nextParams['filesForBackup']));
+ else
+ {
+ // if an error occur, it's more safe to delete the corrupted backup
+ if (file_exists($this->backupFilesFilename))
+ unlink($this->backupFilesFilename);
+ $this->next = 'error';
+ $this->nextDesc = sprintf($this->l('error when trying to add %1$s to archive %2$s.'),$file, $backupFilePath);
+ break;
+ }
+ }
+ $zip->close();
+ return true;
+ }
+ else{
+ $this->next = 'error';
+ $this->nextDesc = $this->l('unable to open archive');
+ return false;
+ }
+ }
+ else
+ {
+ $this->next = 'backupDb';
+ $this->nextDesc = 'All files saved. Now backup Database';
+ return true;
+ }
+ // 4) save for display.
+ }
+
+
+ private function _removeOneSample($removeList)
+ {
+ if (is_array($removeList) AND sizeof($removeList)>0)
+ {
+ if (file_exists($removeList[0]) AND unlink($removeList[0]))
+ {
+ $item = array_shift($removeList);
+ $this->next = 'removeSamples';
+ $this->nextParams['removeList'] = $removeList;
+ $this->nextQuickInfo[] = sprintf($this->l('%1$s removed. %2$s items left'), $item, sizeof($removeList));
+ }
+ else
+ {
+ $this->next = 'error';
+ $this->nextParams['removeList'] = $removeList;
+ $this->nextQuickInfo[] = sprintf($this->l('error when removing %1$s, %2$s items left'), $removeList[0], sizeof($removeList));
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public function ajaxProcessRemoveSamples(){
+ $this->stepDone = false;
+ // @TODO : list exaustive list of files to remove :
+ // all images from img dir exept admin ?
+ // all images like logo, favicon, ?.
+ // all custom image from modules ?
+ // all custom image from theme ?
+ if (!isset($this->currentParams['removeList']))
+ {
+ $this->_listSampleFiles($this->autoupgradePath.'/latest/prestashop/img', 'jpg');
+ $this->_listSampleFiles($this->autoupgradePath.'/latest/prestashop/modules/editorial/', 'homepage_logo.jpg');
+ // @TODO handle this bad thing
+ $this->nextQuickInfo[] = sprintf($this->l('Starting to remove %1$s sample files'), sizeof($this->sampleFileList));
+ $this->nextParams['removeList'] = $this->sampleFileList;
+ }
+
+
+ // @TODO : removing @, adding if file_exists
+// @unlink(_PS_ROOT_DIR_.'modules'.DIRECTORY_SEPARATOR.'editorial'.DIRECTORY_SEPARATOR.'editorial.xml');
+// @unlink(_PS_ROOT_DIR_.'modules'.DIRECTORY_SEPARATOR.'editorial'.DIRECTORY_SEPARATOR.'homepage_logo.jpg'); // homepage custom ?
+// @unlink(_PS_ROOT_DIR_.'img'.DIRECTORY_SEPARATOR.'logo.jpg');
+// @unlink(_PS_ROOT_DIR_.'img'.DIRECTORY_SEPARATOR.'favicon.ico');
+ $resRemove = true;
+ for($i=0;$inextParams['removeList']) <= 0 )
+ {
+ $this->stepDone = true;
+ $this->next = 'backupFiles';
+ $this->nextDesc = $this->l('All sample files removed. Now backup files.');
+ // break the loop, all sample already removed
+ return true;
+ }
+ $resRemove &= $this->_removeOneSample($this->nextParams['removeList']);
+ if (!$resRemove)
+ break;
+ }
+
+ return $resRemove;
+ }
+
+ public function ajaxProcessSvnCheckout()
+ {
+ $this->nextParams = $this->currentParams;
+ if ($this->useSvn){
+ $svnLink = 'http://svn.prestashop.com/trunk';
+ $dest = $this->autoupgradePath . DIRECTORY_SEPARATOR . $this->svnDir;
+
+ $svnStatus = svn_status($dest);
+ if (is_array($svnStatus))
+ {
+ if (sizeof($svnStatus) == 0)
+ {
+ $this->next = 'svnExport';
+ $this->nextDesc = sprintf($this->l('working copy already %s up-to-date. now exporting it into latest dir'),$dest);
+ }
+ else
+ {
+ // we assume no modification has been done
+ // @TODO a svn revert ?
+ if ($svnUpdate = svn_update($dest))
+ {
+ $this->next = 'svnExport';
+ $this->nextDesc = sprintf($this->l('SVN Update done for working copy %s . now exporting it into latest...'),$dest);
+ }
+ }
+ }
+ else
+ {
+ // no valid status found
+ // @TODO : is 0777 good idea ?
+ if (!file_exists($dest))
+ if (!@mkdir($dest,0777))
+ {
+ $this->next = 'error';
+ $this->nextDesc = sprintf($this->l('unable to create directory %s'),$dest);
+ return false;
+ }
+
+ if (svn_checkout($svnLink, $dest))
+ {
+ $this->next = 'svnExport';
+ $this->nextDesc = sprintf($this->l('SVN Checkout done from %s . now exporting it into latest...'),$svnLink);
+ return true;
+ }
+ else
+ {
+ $this->next = 'error';
+ $this->nextDesc = $this->l('SVN Checkout error...');
+ }
+ }
+ }
+ else
+ {
+ $this->next = 'error';
+ $this->nextDesc = $this->l('not allowed to use svn');
+ }
+ }
+
+ public function ajaxProcessDownload()
+ {
+ if (@ini_get('allow_url_fopen'))
+ {
+ $res = $this->upgrader->downloadLast($this->autoupgradePath,$this->destDownloadFilename);
+ if ($res){
+ $this->next = 'unzip';
+ $this->nextDesc = $this->l('Download complete. Now extracting');
+ }
+ else
+ {
+ $this->next = 'error';
+ $this->nextDesc = $this->l('Error during download');
+ }
+ }
+ else
+ {
+ // @TODO : ftp mode
+ $this->next = 'error';
+ $this->nextDesc = sprintf($this->l('you need allow_url_fopen for automatic download. You can also manually upload it in %s'),$this->autoupgradePath.$this->destDownloadFilename);
+ }
+ }
+
+ public function buildAjaxResult()
+ {
+ $return['error'] = $this->error;
+ $return['stepDone'] = $this->stepDone;
+ $return['next'] = $this->next;
+ $return['status'] = $this->next == 'error' ? 'error' : 'ok';
+ $return['nextDesc'] = $this->nextDesc;
+ if (!empty($this->nextParams))
+ $return['nextParams'] = $this->nextParams;
+ else
+ $return['nextParams'] = array();
+
+ $return['nextParams']['typeResult'] = $this->nextResponseType;
+
+ $return['nextQuickInfo'] = $this->nextQuickInfo;
+ return Tools::jsonEncode($return);
+ }
+
+ /**
+ * displayConf
+ *
+ * @return void
+ */
+ public function displayConf()
+ {
+ if (version_compare(_PS_VERSION_,'1.4.4.0','<') AND false)
+ {
+ $this->_errors[] = Tools::displayError('This class depends of several files modified in 1.4.4.0 version and should not be used in an older version');
+ }
+ parent::displayConf();
+
+ }
+
+ public function ajaxPreProcess()
+ {
+ if (Tools::getValue('responseType') == 'json')
+ header('Content-Type: application/json');
+ $action = (Tools::getValue('action'));
+
+ if (isset(self::$skipAction[strtolower($action)]))
+ {
+ $this->next = self::$skipAction[$action];
+ $this->nextDesc = sprintf($this->l('action %s skipped'),$action);
+ $this->nextQuickInfo[] = sprintf($this->l('action %s skipped'),$action);
+ unset($_POST['action']);
+ }
+ else if (!method_exists(get_class($this), 'ajaxProcess'.$action))
+ {
+ $this->nextDesc = sprintf($this->l('action "%1$s" non trouvée '), $action);
+ $this->next = 'error';
+ $this->error = '1';
+ }
+
+ if ($this->apacheModExists('mod_evasive'))
+ sleep(1);
+ }
+ /**
+ * apacheModExists return true if the apache module $name is loaded
+ * @TODO move this method in class Information (when it will exist)
+ *
+ * @param string $name module name
+ * @return boolean true if exists
+ */
+ function apacheModExists($name)
+ {
+ static $apacheModuleList = null;
+
+ if (!is_array($apacheModuleList))
+ $apacheModuleList = apache_get_modules();
+
+ // we need strpos (example can be evasive20
+ foreach($apacheModuleList as $module)
+ if (strpos($name, $module)!==false)
+ return true;
+
+ return false;
+ }
+
+ private function _getJsErrorMsgs()
+ {
+ $INSTALL_VERSION = $this->upgrader->version_num;
+ $ret = '
+var txtError = new Array();
+txtError[0] = "'.$this->l('Required field').'";
+txtError[1] = "'.$this->l('Too long!').'";
+txtError[2] = "'.$this->l('Fields are different!').'";
+txtError[3] = "'.$this->l('This email adress is wrong!').'";
+txtError[4] = "'.$this->l('Impossible to send the email!').'";
+txtError[5] = "'.$this->l('Can\'t create settings file, if /config/settings.inc.php exists, please give the public write permissions to this file, else please create a file named settings.inc.php in config directory.').'";
+txtError[6] = "'.$this->l('Can\'t write settings file, please create a file named settings.inc.php in config directory.').'";
+txtError[7] = "'.$this->l('Impossible to upload the file!').'";
+txtError[8] = "'.$this->l('Data integrity is not valided. Hack attempt?').'";
+txtError[9] = "'.$this->l('Impossible to read the content of a MySQL content file.').'";
+txtError[10] = "'.$this->l('Impossible the access the a MySQL content file.').'";
+txtError[11] = "'.$this->l('Error while inserting data in the database:').'";
+txtError[12] = "'.$this->l('The password is incorrect (alphanumeric string at least 8 characters).').'";
+txtError[14] = "'.$this->l('A Prestashop database already exists, please drop it or change the prefix.').'";
+txtError[15] = "'.$this->l('This is not a valid file name.').'";
+txtError[16] = "'.$this->l('This is not a valid image file.').'";
+txtError[17] = "'.$this->l('Error while creating the /config/settings.inc.php file.').'";
+txtError[18] = "'.$this->l('Error:').'";
+txtError[19] = "'.$this->l('This PrestaShop database already exists. Please revalidate your authentication informations to the database.').'";
+txtError[22] = "'.$this->l('An error occurred while resizing the picture.').'";
+txtError[23] = "'.$this->l('Database connection is available!').'";
+txtError[24] = "'.$this->l('Database Server is available but database is not found').'";
+txtError[25] = "'.$this->l('Database Server is not found. Please verify the login, password and server fields.').'";
+txtError[26] = "'.$this->l('An error occurred while sending email, please verify your parameters.').'";
+txtError[37] = "'.$this->l('Impossible to write the image /img/logo.jpg. If this image already exists, please delete it.').'";
+txtError[38] = "'.$this->l('The uploaded file exceeds the upload_max_filesize directive in php.ini').'";
+txtError[39] = "'.$this->l('The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form').'";
+txtError[40] = "'.$this->l('The uploaded file was only partially uploaded').'";
+txtError[41] = "'.$this->l('No file was uploaded.').'";
+txtError[42] = "'.$this->l('Missing a temporary folder').'";
+txtError[43] = "'.$this->l('Failed to write file to disk').'";
+txtError[44] = "'.$this->l('File upload stopped by extension').'";
+txtError[45] = "'.$this->l('Cannot convert your database\'s data to utf-8.').'";
+txtError[46] = "'.$this->l('Invalid shop name').'";
+txtError[47] = "'.$this->l('Your firstname contains some invalid characters').'";
+txtError[48] = "'.$this->l('Your lastname contains some invalid characters').'";
+txtError[49] = "'.$this->l('Your database server does not support the utf-8 charset.').'";
+txtError[50] = "'.$this->l('Your MySQL server doesn\'t support this engine, please use another one like MyISAM').'";
+txtError[51] = "'.$this->l('The file /img/logo.jpg is not writable, please CHMOD 755 this file or CHMOD 777').'";
+txtError[52] = "'.$this->l('Invalid catalog mode').'";
+txtError[999] = "'.$this->l('No error code available').'";
+//upgrader
+txtError[27] = "'.$this->l('This installer is too old.').'";
+txtError[28] = "'.sprintf($this->l('You already have the %s version.'),$INSTALL_VERSION).'";
+txtError[29] = "'.$this->l('There is no older version. Did you delete or rename the config/settings.inc.php file?').'";
+txtError[30] = "'.$this->l('The config/settings.inc.php file was not found. Did you delete or rename this file?').'";
+txtError[31] = "'.$this->l('Can\'t find the sql upgrade files. Please verify that the /install/sql/upgrade folder is not empty)').'";
+txtError[32] = "'.$this->l('No upgrade is possible.').'";
+txtError[33] = "'.$this->l('Error while loading sql upgrade file.').'";
+txtError[34] = "'.$this->l('Error while inserting content into the database').'";
+txtError[35] = "'.$this->l('Unfortunately,').'";
+txtError[36] = "'.$this->l('SQL errors have occurred.').'";
+txtError[37] = "'.$this->l('The config/defines.inc.php file was not found. Where did you move it?').'";';
+ return $ret;
+ }
+
+ public function displayAjax(){
+ echo $this->buildAjaxResult();
+ }
+
+ private function _displayRollbackForm()
+ {
+ echo '';
+ }
+
+ private function _displayUpgraderForm()
+ {
+ $pleaseUpdate = $this->upgrader->checkPSVersion();
+
+ echo '';
+
+ echo ' ';
+
+ echo '';
+
+
+ echo ' ';
+
+ if (defined('_PS_MODE_DEV_') AND _PS_MODE_DEV_)
+ {
+ echo '';
+/* echo '';
+ *
+ */
+ // information to keep will be in #infoStep
+ // temporary infoUpdate will be in #tmpInformation
+ echo '';
+ }
+ public function display()
+ {
+ $this->displayWarning('This function is experimental. It\'s currently recommended to make a backup of your files and database.');
+
+ global $currentIndex;
+ if ($this->apacheModExists('mod_evasive'))
+ sleep(1);
+ // update['name'] = version name
+ // update['num'] = only the version
+ // update['link'] = download link
+ // @TODO
+ if ($this->useSvn AND FALSE)
+ echo '
'.$this->l('Unstable upgrade').'
+
'.$this->l('Your current configuration indicate you want to upgrade your system from the unstable development branch, with no version number. If you upgrade, you will not be able to follow the official release process anymore').'.
+
';
+
+ $this->_displayUpgraderForm();
+ echo ' ';
+ $this->_displayRollbackForm();
+
+ echo ' ';
+ $this->_displayForm('autoUpgradeOptions',$this->_fieldsAutoUpgrade,''.$this->l('Options').'', 'my-autoupgrade-option','');
+ echo '';
+ }
+
+ private function _getJsInit()
+ {
+ global $currentIndex;
+ $js = '';
+ $js .= '
+function ucFirst(str) {
+ if (str.length > 0) {
+ return str[0].toUpperCase() + str.substring(1);
+ }
+ else {
+ return str;
+ }
+}
+
+function cleanInfo(){
+ $("#infoStep").html("reset ");
+}
+
+function updateInfoStep(msg){
+ if (msg)
+ {
+ $("#infoStep").html(msg);
+ $("#infoStep").attr({ scrollTop: $("#infoStep").attr("scrollHeight") });
+ }
+}
+
+function addError(msg){
+ if (msg)
+ $("#errorWindow").html(msg);
+}
+
+function addQuickInfo(arrQuickInfo){
+ if (arrQuickInfo)
+ {
+ $("#quickInfo").show();
+ for(i=0;i");
+
+ $("#quickInfo").attr({ scrollTop: $("#quickInfo").attr("scrollHeight") });
+ }
+}';
+
+ if ($this->manualMode)
+ $js .= 'var manualMode = true;';
+ else
+ $js .= 'var manualMode = false;';
+
+ $js .= '
+
+$(document).ready(function(){
+ $(".upgradestep").click(function(e)
+ {
+ e.preventDefault();
+ // $.scrollTo("#options")
+ });
+
+ // more convenient to have that param for handling success and error
+ var requestParams;
+
+ // set timeout to 5 minutes (download can be long)?
+ $.ajaxSetup({timeout:300000});
+
+
+ // prepare available button here, without params ?
+ prepareNextButton("#upgradeNow",{});
+ prepareNextButton("#rollback",{});
+ prepareNextButton("#restoreDb",{});
+ prepareNextButton("#restoreFiles",{});
+
+});
+
+/**
+ * parseXMLResult is used to handle the return value of the doUpgrade method
+ *
+ */
+function parseXMLResult(xmlRet)
+{
+ ret = $(xmlRet);
+ ret = ret.find("action")[0];
+ if (ret.getAttribute("result") == "ok")
+ {
+ $("#dbResultCheck")
+ .addClass("ok")
+ .removeClass("fail")
+ .html("
'.$this->l('upgrade complete. Please check your front-office (try to make an order, check theme)').'
")
+ .show("slow");
+ $("#dbCreateResultCheck")
+ .hide("slow");
+
+ // difference with the original function
+ ret = {next:"upgradeComplete",nextParams:""};
+
+ }
+ else
+ {
+ $("#dbResultCheck")
+ .addClass("fail")
+ .removeClass("ok")
+ .html(txtError[parseInt(ret.getAttribute("error"))])
+ .show("slow");
+ $("#dbCreateResultCheck")
+ .hide("slow");
+
+ // propose rollback if there is an error
+ if (confirm(txtError[parseInt(ret.getAttribute("error"))]+"\r\n\r\n'.$this->l('Do you want to rollback ?').'"))
+ ret = {next:"rollback","nextParams":nextParams};
+ }
+
+ return ret
+};
+
+/**
+ * afterBackupDb display the button
+ *
+ */
+function afterBackupDb()
+{
+ $("#restoreDbContainer").html("restoreDb '.$this->l('click to restore database').'");
+ prepareNextButton("#restoreDb",{});
+}
+
+function afterRestoreDb()
+{
+ $("#restoreDbContainer").html("");
+}
+
+function afterRestoreFiles()
+{
+ $("#restoreFilesContainer").html("");
+}
+
+function afterBackupFiles()
+{
+ $("#restoreFilesContainer").html("
restoreFiles '.$this->l('click to restore files').'");
+ prepareNextButton("#restoreFiles",{});
+
+}
+
+function doAjaxRequest(action, nextParams){
+ req = $.ajax({
+ type:"POST",
+ url : "'.str_replace('index','ajax-tab',$currentIndex).'",
+ async: true,
+ data : {
+ dir:"'.$this->adminDir.'",
+ ajaxMode : "1",
+ token : "'.$this->token.'",
+ tab : "AdminUpgrade",
+ action : action,
+ params : nextParams
+ },
+ success : function(res,textStatus,jqXHR)
+ {
+ if (nextParams.typeResult == "xml")
+ {
+ xmlRes = parseXMLResult(res);
+ res = {};
+ res.next = xmlRes.next;
+ // if xml, we keep the next params
+ nextParams = myNext;
+
+ // res.status = "ok",
+ }
+ else
+ {
+ res = $.parseJSON(res);
+ if (res.status == "ok")
+ {
+ // a
+ $("#"+action).addClass("done");
+ if (res.stepDone)
+ $("#"+action).addClass("stepok");
+
+ // if a function "after[action name]" exists, it should be called.
+ // This is used for enabling restore buttons for example
+ funcName = "after"+ucFirst(action);
+ if (typeof funcName == "string" &&
+ eval("typeof " + funcName) == "function") {
+
+ eval(funcName+"()");
+ }
+
+ handleSuccess(res,nextParams.typeResult);
+ }
+ else
+ {
+ // display progression
+ $("#"+action).addClass("done");
+ $("#"+action).addClass("steperror");
+ handleError(res);
+ }
+ }
+ },
+ error: function(res,textStatus,jqXHR)
+ {
+ if (textStatus == "timeout" && action == "download")
+ {
+ updateInfoStep("'.$this->l('Your server can\'t download the file. Please upload it first by ftp in your admin/autoupgrade directory').'");
+ }
+ else
+ {
+ //console.log(res);
+ //console.log(jqXHR);
+ updateInfoStep("[Server Error] Status message : " + textStatus);
+ }
+ }
+ });
+ };
+
+/**
+ * prepareNextButton make the button button_selector available, and update the nextParams values
+ *
+ * @param button_selector $button_selector
+ * @param nextParams $nextParams
+ * @return void
+ */
+function prepareNextButton(button_selector, nextParams)
+{
+// myNext;
+ myNext = nextParams;
+ $(button_selector).unbind();
+ $(button_selector).click(function(e){
+ e.preventDefault();
+ $("#currentlyProcessing").show();
+';
+ if (defined('_PS_MODE_DEV_') AND _PS_MODE_DEV_)
+ $js .= 'addQuickInfo(["[DEV] request : "+$(this).attr("id")]);';
+ $js .= '
+ action = button_selector.substr(1);
+ res = doAjaxRequest(action, nextParams);
+ });
+}
+
+/**
+ * handleSuccess
+ * res = {error:, next:, nextDesc:, nextParams:, nextQuickInfo:,status:"ok"}
+ * @param res $res
+ * @return void
+ */
+function handleSuccess(res)
+{
+ if (res.next != "")
+ {
+ updateInfoStep(res.nextDesc);
+ addQuickInfo(res.nextQuickInfo);
+
+ $("#"+res.next).addClass("nextStep");
+ if (manualMode)
+ {
+ prepareNextButton("#"+res.next,res.nextParams);
+ alert("manually go to "+res.next+" button ");
+ }
+ else
+ {
+ // @TODO :
+ // 1) instead of click(), call a function.
+ doAjaxRequest(res.next,res.nextParams);
+ // 2) remove all step link (or show them only in dev mode)
+ // 3) when steps link displayed, they should change color when passed
+ }
+ }
+ else
+ {
+ // Way To Go, end of upgrade process
+ addQuickInfo(["End of upgrade process"]);
+ }
+}
+
+// res = {nextParams, NextDesc}
+function handleError(res)
+{
+ // display error message in the main process thing
+ updateInfoStep(res.nextDesc);
+ addQuickInfo(res.nextQuickInfo);
+ // In case the rollback button has been desactivated, just re-enable it
+ prepareNextButton("#rollback",res.nextParams);
+ // ask if you want to rollback
+ // @TODO !!!
+ if (confirm(res.NextDesc+"\r\r'.$this->l('Do you want to rollback ?').'"))
+ {
+ if (manualMode)
+ alert("'.$this->l('Please go manually go to rollback button').'");
+ else
+ {
+ $("#rollback").click();
+ }
+
+ }
+}
+';
+ return $js;
+ }
+ private function _cleanUp($path)
+ {
+ // as we need theses files for restore operation, we can't remove them.
+ // They will be overwritten
+ $skipDirs = array('backups', 'pclzip', 'autoupgrade', '.', '..', '.svn');
+ $skipFiles = array('autoload.php', 'init.php', 'settings.inc.php', 'config.inc.php', 'Tools.php', 'AdminUpgrade.php', 'ajax-tab.php');
+ if (is_dir($path))
+ {
+ $fp = opendir($path);
+ while ($file = readdir($fp))
+ {
+ if (!in_array($file, $skipDirs) AND !$this->_skipFile('', $path.$file, 'backup'))
+ {
+ $fullpath = $path.$file;
+ if (is_dir($fullpath))
+ $this->_cleanUp($fullpath.'/');
+ else
+ {
+ if (!in_array($file, $skipFiles))
+ {
+ unlink($fullpath);
+ }
+ }
+ }
+ }
+ closedir($fp);
+ /* fortunately not empty dir won't be removed by the following */
+ @rmdir($path);
+ }
+ else
+ if (!$this->_skipFile($file, '', 'backup')) unlink($path);
+
+ return true;
+ }
+
+ /**
+ * @desc extract a zip file to the given directory
+ * @return bool success
+ * we need a copy of it to be able to restore without keeping Tools and Autoload stuff
+ */
+ private static function ZipExtract($fromFile, $toDir)
+ {
+ if (!file_exists($toDir))
+ if (!@mkdir($toDir,0777))
+ {
+ $this->next = 'error';
+ $this->nextDesc = sprintf($this->l('unable to create directory %s'),$toDir);
+ return false;
+ }
+
+ if (class_exists('ZipArchive', false))
+ {
+ $zip = new ZipArchive();
+ if ($zip->open($fromFile) === true AND $zip->extractTo($toDir) AND $zip->close())
+ return true;
+ return false;
+ }
+ else
+ {
+
+ if (!class_exists('PclZip',false))
+ require_once($this->prodRootDir.'/tools/pclzip/pclzip.lib.php');
+
+ $zip = new PclZip($fromFile);
+ $list = $zip->extract(PCLZIP_OPT_PATH, $toDir);
+ foreach ($list as $extractedFile)
+ if ($extractedFile['status'] != 'ok')
+ return false;
+
+ return true;
+ }
+ }
+
+ private function _listArchivedFiles()
+ {
+ if (!empty($this->nextParams['backupFilesFilepath']))
+ {
+ if (class_exists('ZipArchive', false))
+ {
+ $files=array();
+ if ($zip = zip_open($this->currentParams['backupFilesFilepath']))
+ {
+ while ($entry=zip_read($zip))
+ $files[] = zip_entry_name($entry);
+
+ zip_close($zip);
+ }
+ }
+ else
+ {
+ require_once($this->prodRootDir.'/tools/pclzip/pclzip.lib.php');
+ if ($zip = new PclZip($this->currentParams['backupFilesFilepath']))
+ return $zip->listContent();
+ }
+ }
+ return false;
+ }
+
+ /**
+ * bool _skipFile : check whether a file is in backup or restore skip list
+ *
+ * @param type $file : current file or directory name eg:'.svn' , 'settings.inc.php'
+ * @param type $fullpath : current file or directory fullpath eg:'/home/web/www/prestashop/img'
+ * @param type $way : 'backup' , 'upgrade'
+ */
+ private function _skipFile($file,$fullpath,$way='backup')
+ {
+ $fullpath = str_replace('\\','/', $fullpath); // wamp compliant
+ $rootpath = str_replace('\\','/', $this->prodRootDir);
+ switch ($way)
+ {
+ case 'backup':
+ if (in_array($file, $this->backupIgnoreFiles))
+ return true;
+
+ foreach($this->backupIgnoreAbsoluteFiles as $path)
+ if ($file == 'img')
+ if (strpos($fullpath, $rootpath.$path) !== false)
+ return true;
+ break;
+
+ case 'upgrade':
+ if (in_array($file, $this->excludeFilesFromUpgrade))
+ return true;
+
+ foreach ($this->excludeAbsoluteFilesFromUpgrade as $path)
+ if (strpos($fullpath, $rootpath.$path) !== false)
+ return true;
+ break;
+ // default : if it's not a backup or an upgrade, juste skip the file
+ default:
+ return false;
+ }
+ }
+}
+
diff --git a/admin-dev/tabs/AdminWebservice.php b/admin-dev/tabs/AdminWebservice.php
index a5064cfbd..bfef21577 100755
--- a/admin-dev/tabs/AdminWebservice.php
+++ b/admin-dev/tabs/AdminWebservice.php
@@ -37,7 +37,6 @@ class AdminWebservice extends AdminTab
$this->lang = false;
$this->edit = true;
$this->delete = true;
-
$this->id_lang_default = Configuration::get('PS_LANG_DEFAULT');
$this->fieldsDisplay = array(
@@ -93,6 +92,13 @@ class AdminWebservice extends AdminTab
$warnings[] = $this->l('If possible, it is preferable to use SSL (https) for webservice calls, as it avoids the security issues of type "man in the middle".');
$this->displayWarning($warnings);
+
+ foreach ($this->_list as $k => $item)
+ if ($item['is_module'] && $item['class_name'] && $item['module_name'] &&
+ ($instance = Module::getInstanceByName($item['module_name'])) &&
+ !$instance->useNormalPermissionBehaviour())
+ unset($this->_list[$k]);
+ parent::displayList();
}
public function displayForm($isMainTab = true)
@@ -221,6 +227,8 @@ echo '
{
if (Tools::getValue('key') && strlen(Tools::getValue('key')) < 32)
$this->_errors[] = Tools::displayError($this->l('Key length must be 32 character long'));
+ if (WebserviceKey::keyExists(Tools::getValue('key')) && !Tools::getValue('id_webservice_account'))
+ $this->_errors[] = Tools::displayError($this->l('Key already exists'));
return parent::postProcess();
}
diff --git a/admin-dev/themes/flashyturtle/admin.css b/admin-dev/themes/flashyturtle/admin.css
index bdc012462..ab0a01999 100644
--- a/admin-dev/themes/flashyturtle/admin.css
+++ b/admin-dev/themes/flashyturtle/admin.css
@@ -59,3 +59,10 @@ a.action_module{color: #488C40;text-decoration: underline;}
a.header_module_toggle{font-weight: bold;color: #812143;display:block;}
a.module_toggle_all{color:#FFF;text-decoration:none;display:inherit;}
.nbr_module{float:right;margin-right:10px;font-style:italic;font-size:12px;color: #812143;}
+
+.autoupgradeSteps div { line-height: 30px; }
+.upgradestep { margin-right: 5px;padding-left: 10px; padding-right: 5px;}
+#upgradeNow.stepok, .autoupgradeSteps a.stepok { background-image: url("../img/admin/enabled.gif");background-position: left center;background-repeat: no-repeat;padding-left: 15px;}
+#upgradeNow {-moz-border-bottom-colors: none;-moz-border-image: none;-moz-border-left-colors: none;-moz-border-right-colors: none;-moz-border-top-colors: none;border-color: #FFF6D3 #DFD5AF #DFD5AF #FFF6D3;border-right: 1px solid #DFD5AF;border-style: solid;border-width: 1px;color: #268CCD;font-size: medium;padding: 5px;}
+.button-autoupgrade {-moz-border-bottom-colors: none;-moz-border-image: none;-moz-border-left-colors: none;-moz-border-right-colors: none;-moz-border-top-colors: none;border-color: #FFF6D3 #DFD5AF #DFD5AF #FFF6D3;border-right: 1px solid #DFD5AF;border-style: solid;border-width: 1px;color: #268CCD;font-size: medium;padding: 5px;}
+.processing {overflow: auto;}
diff --git a/admin-dev/themes/oldschool/admin.css b/admin-dev/themes/oldschool/admin.css
index a141bfc6b..a1b138ef4 100644
--- a/admin-dev/themes/oldschool/admin.css
+++ b/admin-dev/themes/oldschool/admin.css
@@ -53,4 +53,11 @@ select {border: 1px solid #E0D0B1}
a.action_module{color: #268CCD;text-decoration: underline;}
a.header_module_toggle{font-weight: bold;color: #268CCD;display:block;}
a.module_toggle_all{color: #268CCD;}
-.nbr_module{float:right;margin-right:10px;font-style:italic;font-size:12px;color: #268CCD;}
\ No newline at end of file
+.nbr_module{float:right;margin-right:10px;font-style:italic;font-size:12px;color: #268CCD;}
+
+.autoupgradeSteps div { line-height: 30px; }
+.upgradestep { margin-right: 5px;padding-left: 10px; padding-right: 5px;}
+#upgradeNow.stepok, .autoupgradeSteps a.stepok { background-image: url("../img/admin/enabled.gif");background-position: left center;background-repeat: no-repeat;padding-left: 15px;}
+#upgradeNow {-moz-border-bottom-colors: none;-moz-border-image: none;-moz-border-left-colors: none;-moz-border-right-colors: none;-moz-border-top-colors: none;border-color: #FFF6D3 #DFD5AF #DFD5AF #FFF6D3;border-right: 1px solid #DFD5AF;border-style: solid;border-width: 1px;color: #268CCD;font-size: medium;padding: 5px;}
+.button-autoupgrade {-moz-border-bottom-colors: none;-moz-border-image: none;-moz-border-left-colors: none;-moz-border-right-colors: none;-moz-border-top-colors: none;border-color: #FFF6D3 #DFD5AF #DFD5AF #FFF6D3;border-right: 1px solid #DFD5AF;border-style: solid;border-width: 1px;color: #268CCD;font-size: medium;padding: 5px;}
+.processing {overflow: auto;}
diff --git a/admin-dev/themes/origins/admin.css b/admin-dev/themes/origins/admin.css
index 04b4768a5..6c1532ec1 100644
--- a/admin-dev/themes/origins/admin.css
+++ b/admin-dev/themes/origins/admin.css
@@ -57,3 +57,9 @@ a.header_module_toggle{font-weight: bold;color: #268CCD;display:block;}
a.module_toggle_all{color: #268CCD;}
.nbr_module{float:right;margin-right:10px;font-style:italic;font-size:12px;color: #268CCD;}
+.autoupgradeSteps div { line-height: 30px; }
+.upgradestep { margin-right: 5px;padding-left: 10px; padding-right: 5px;}
+#upgradeNow.stepok, .autoupgradeSteps a.stepok { background-image: url("../img/admin/enabled.gif");background-position: left center;background-repeat: no-repeat;padding-left: 15px;}
+#upgradeNow {-moz-border-bottom-colors: none;-moz-border-image: none;-moz-border-left-colors: none;-moz-border-right-colors: none;-moz-border-top-colors: none;border-color: #FFF6D3 #DFD5AF #DFD5AF #FFF6D3;border-right: 1px solid #DFD5AF;border-style: solid;border-width: 1px;color: #268CCD;font-size: medium;padding: 5px;}
+.button-autoupgrade {-moz-border-bottom-colors: none;-moz-border-image: none;-moz-border-left-colors: none;-moz-border-right-colors: none;-moz-border-top-colors: none;border-color: #FFF6D3 #DFD5AF #DFD5AF #FFF6D3;border-right: 1px solid #DFD5AF;border-style: solid;border-width: 1px;color: #268CCD;font-size: medium;padding: 5px;}
+.processing {overflow: auto;}
diff --git a/classes/AdminTab.php b/classes/AdminTab.php
index 8decf6609..0d61fd91a 100644
--- a/classes/AdminTab.php
+++ b/classes/AdminTab.php
@@ -159,6 +159,8 @@ abstract class AdminTabCore
protected $_includeVars = false;
protected $_includeContainer = true;
+ public $ajax = false;
+
public static $tabParenting = array(
'AdminProducts' => 'AdminCatalog',
'AdminCategories' => 'AdminCatalog',
@@ -233,6 +235,14 @@ abstract class AdminTabCore
return str_replace('"', '"', ($addslashes ? addslashes($str) : stripslashes($str)));
}
+ /**
+ * ajaxDisplay is the default ajax return sytem
+ *
+ * @return void
+ */
+ public function displayAjax()
+ {
+ }
/**
* Manage page display (form, list...)
*/
@@ -506,6 +516,24 @@ abstract class AdminTabCore
return true;
}
+ /**
+ * ajaxPreProcess is a method called in ajax-tab.php before displayConf().
+ *
+ * @return void
+ */
+ public function ajaxPreProcess()
+ {
+ }
+
+ /**
+ * ajaxProcess is the default handle method for request with ajax-tab.php
+ *
+ * @return void
+ */
+ public function ajaxProcess()
+ {
+ }
+
/**
* Manage page processing
*/
@@ -1107,9 +1135,9 @@ abstract class AdminTabCore
public function displayConf()
{
if ($conf = Tools::getValue('conf'))
- echo '
-
- '.$this->_conf[(int)($conf)].'
+ echo '
+
+ '.$this->_conf[(int)($conf)].'
';
}
diff --git a/classes/Alias.php b/classes/Alias.php
index bfb386534..3bfc13100 100644
--- a/classes/Alias.php
+++ b/classes/Alias.php
@@ -70,7 +70,7 @@ class AliasCore extends ObjectModel
FROM `'._DB_PREFIX_.'alias`
WHERE `search` LIKE \''.pSQL($search).'\'');
}
-
+
public function getAliases()
{
$aliases = Db::getInstance()->ExecuteS('
diff --git a/classes/Attachment.php b/classes/Attachment.php
index 561e8f4fe..a3f235f50 100644
--- a/classes/Attachment.php
+++ b/classes/Attachment.php
@@ -37,7 +37,7 @@ class AttachmentCore extends ObjectModel
public $position;
protected $fieldsRequired = array('file', 'mime');
- protected $fieldsSize = array('file' => 40, 'mime' => 64, 'file_name' => 128);
+ protected $fieldsSize = array('file' => 40, 'mime' => 128, 'file_name' => 128);
protected $fieldsValidate = array('file' => 'isGenericName', 'mime' => 'isCleanHtml', 'file_name' => 'isGenericName');
protected $fieldsRequiredLang = array('name');
diff --git a/classes/Cart.php b/classes/Cart.php
index e1920040e..4828dc171 100644
--- a/classes/Cart.php
+++ b/classes/Cart.php
@@ -1232,7 +1232,10 @@ class CartCore extends ObjectModel
{
$moduleName = $carrier->external_module_name;
$module = Module::getInstanceByName($moduleName);
- if (key_exists('id_carrier', $module))
+
+ if (Validate::isLoadedObject($module))
+ {
+ if (array_key_exists('id_carrier', $module))
$module->id_carrier = $carrier->id;
if($carrier->need_range)
$shipping_cost = $module->getOrderShippingCost($this, $shipping_cost);
@@ -1243,6 +1246,9 @@ class CartCore extends ObjectModel
if ($shipping_cost === false)
return false;
}
+ else
+ return false;
+ }
// Apply tax
if (isset($carrierTax))
@@ -1327,7 +1333,7 @@ class CartCore extends ObjectModel
$groups = Customer::getGroupsStatic($this->id_customer);
- if (($discountObj->id_customer OR $discountObj->id_group) AND ($this->id_customer != $discountObj->id_customer AND !in_array($discountObj->id_group, $groups)))
+ if (($discountObj->id_customer OR $discountObj->id_group) AND ((($this->id_customer != $discountObj->id_customer) OR ($this->id_customer == 0)) AND !in_array($discountObj->id_group, $groups)))
{
if (!$customer->isLogged())
return Tools::displayError('You cannot use this voucher.').' - '.Tools::displayError('Please log in.');
@@ -1699,6 +1705,8 @@ class CartCore extends ObjectModel
{
$carrier = new Carrier((int)$id_carrier, Configuration::get('PS_LANG_DEFAULT'));
$shippingMethod = $carrier->getShippingMethod();
+ if (!$carrier->range_behavior)
+ return true;
if ($shippingMethod == Carrier::SHIPPING_METHOD_FREE)
return true;
diff --git a/classes/Category.php b/classes/Category.php
index ee4d7461b..e863e1ecf 100644
--- a/classes/Category.php
+++ b/classes/Category.php
@@ -179,6 +179,16 @@ class CategoryCore extends ObjectModel
return $ret;
}
+ /**
+ * @see ObjectModel::toggleStatus()
+ */
+ public function toggleStatus()
+ {
+ $result = parent::toggleStatus();
+ Module::hookExec('categoryUpdate');
+ return $result;
+ }
+
/**
* Recursive scan of subcategories
*
@@ -594,37 +604,19 @@ class CategoryCore extends ObjectModel
* @param int $id_lang
* @return array
*/
- public static function getChildrenWithNbSelectedSubCatForProduct($id_parent, $id_product = 0, $ids_categories = null, $id_lang)
+ public static function getChildrenWithNbSelectedSubCat($id_parent, $selectedCat, $id_lang)
{
- $categories_product_str = '';
- if ($id_product)
- {
- $categories_product = Db::getInstance()->ExecuteS('
- SELECT `id_category`
- FROM `'._DB_PREFIX_.'category_product`
- WHERE `id_product` = '.(int)$id_product);
- if (sizeof($categories_product))
- foreach ($categories_product as $category_product)
- $categories_product_str .= $category_product['id_category'].',';
- }
- else
- {
- $categories_product = array();
- $categories_product_str = $ids_categories;
- }
- $categories_product_str = rtrim($categories_product_str, ',');
-
return Db::getInstance(_PS_USE_SQL_SLAVE_)->ExecuteS('
- SELECT c.`id_category`, cl.`name`, IF((
+ SELECT c.`id_category`, c.`level_depth`, cl.`name`, IF((
SELECT COUNT(*)
FROM `'._DB_PREFIX_.'category` c2
WHERE c2.`id_parent` = c.`id_category`
- ) > 0, 1, 0) AS has_children, '.($categories_product_str ? '(
+ ) > 0, 1, 0) AS has_children, '.($selectedCat ? '(
SELECT count(c3.`id_category`)
FROM `'._DB_PREFIX_.'category` c3
WHERE c3.`nleft` > c.`nleft`
AND c3.`nright` < c.`nright`
- AND c3.`id_category` IN ('.$categories_product_str.')
+ AND c3.`id_category` IN ('.$selectedCat.')
)' : '0').' AS nbSelectedSubCat
FROM `'._DB_PREFIX_.'category` c
LEFT JOIN `'._DB_PREFIX_.'category_lang` cl ON c.`id_category` = cl.`id_category`
diff --git a/classes/CompareProduct.php b/classes/CompareProduct.php
new file mode 100644
index 000000000..af3129b22
--- /dev/null
+++ b/classes/CompareProduct.php
@@ -0,0 +1,198 @@
+
+* @copyright 2007-2011 PrestaShop SA
+* @version Release: $Revision$
+* @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
+* International Registered Trademark & Property of PrestaShop SA
+*/
+
+class CompareProduct extends ObjectModel
+{
+ public $id;
+
+ public $id_product;
+
+ public $id_guest;
+
+ public $id_customer;
+
+ public $date_add;
+
+ public $date_upd;
+
+ protected $fieldRequired = array(
+ 'id_product',
+ 'id_guest',
+ 'id_customer');
+
+ protected $fieldsValidate = array(
+ 'id_product' => 'isUnsignedInt',
+ 'id_guest' => 'isUnsignedInt',
+ 'id_customer' => 'isUnsignedInt'
+ );
+
+ protected $table = 'compare_product';
+
+ protected $identifier = 'id_compare_product';
+
+
+ /**
+ * Get all compare products of the guest
+ * @param int $id_guest
+ * @return array
+ */
+ public static function getGuestCompareProducts($id_guest)
+ {
+ $results = Db::getInstance()->ExecuteS('
+ SELECT DISTINCT `id_product`
+ FROM `'._DB_PREFIX_.'compare_product`
+ WHERE `id_guest` = '.(int)($id_guest));
+
+ $compareProducts = null;
+
+ foreach($results as $result)
+ $compareProducts[] = $result['id_product'];
+
+ return $compareProducts;
+ }
+
+
+ /**
+ * Add a compare product for the guest
+ * @param int $id_guest, int $id_product
+ * @return boolean
+ */
+ public static function addGuestCompareProduct($id_guest, $id_product)
+ {
+ return Db::getInstance()->Execute('
+ INSERT INTO `'._DB_PREFIX_.'compare_product` (`id_product`, `id_guest`, `date_add`, `date_upd`)
+ VALUES ('.(int)($id_product).', '.(int)($id_guest).', NOW(), NOW())
+ ');
+ }
+
+
+ /**
+ * Remove a compare product for the guest
+ * @param int $id_guest, int $id_product
+ * @return boolean
+ */
+ public static function removeGuestCompareProduct($id_guest, $id_product)
+ {
+ return Db::getInstance()->Execute('DELETE FROM `'._DB_PREFIX_.'compare_product` WHERE `id_guest` = '.(int)($id_guest).' AND `id_product` = '.(int)($id_product));
+ }
+
+
+ /**
+ * Get the number of compare products of the guest
+ * @param int $id_guest
+ * @return int
+ */
+ public static function getGuestNumberProducts($id_guest)
+ {
+ return (int)(Db::getInstance()->getValue('
+ SELECT count(`id_compare_product`)
+ FROM `'._DB_PREFIX_.'compare_product`
+ WHERE `id_guest` = '.(int)($id_guest)));;
+ }
+
+
+ /**
+ * Get all comapare products of the customer
+ * @param int $id_customer
+ * @return array
+ */
+ public static function getCustomerCompareProducts($id_customer)
+ {
+ $results = Db::getInstance()->ExecuteS('
+ SELECT DISTINCT `id_product`
+ FROM `'._DB_PREFIX_.'compare_product`
+ WHERE `id_customer` = '.(int)($id_customer));
+
+ $compareProducts = null;
+
+ foreach($results as $result)
+ $compareProducts[] = $result['id_product'];
+
+ return $compareProducts;
+ }
+
+
+ /**
+ * Add a compare product for the customer
+ * @param int $id_customer, int $id_product
+ * @return boolean
+ */
+ public static function addCustomerCompareProduct($id_customer, $id_product)
+ {
+ return Db::getInstance()->Execute('INSERT INTO `'._DB_PREFIX_.'compare_product` (`id_product`, `id_customer`, `date_add`, `date_upd`) VALUES ('.(int)($id_product).', '.(int)($id_customer).', NOW(), NOW())');
+ }
+
+
+ /**
+ * Remove a compare product for the customer
+ * @param int $id_customer, int $id_product
+ * @return boolean
+ */
+ public static function removeCustomerCompareProduct($id_customer, $id_product)
+ {
+ return Db::getInstance()->Execute('DELETE FROM `'._DB_PREFIX_.'compare_product` WHERE `id_customer` = '.(int)($id_customer).' AND `id_product` = '.(int)($id_product));
+ }
+
+
+ /**
+ * Get the number of compare products of the customer
+ * @param int $id_customer
+ * @return int
+ */
+ public static function getCustomerNumberProducts($id_customer)
+ {
+ return (int)(Db::getInstance()->getValue('
+ SELECT count(`id_compare_product`)
+ FROM `'._DB_PREFIX_.'compare_product`
+ WHERE `id_customer` = '.(int)($id_customer)));
+ }
+
+
+ /**
+ * Clean entries which are older than the period
+ * @param string $period
+ * @return void
+ */
+ public static function cleanCompareProducts($period = 'week')
+ {
+ if ($period === 'week')
+ $interval = '1 WEEK';
+ elseif ($period === 'month')
+ $interval = '1 MONTH';
+ elseif ($period === 'year')
+ $interval = '1 YEAR';
+ else
+ return;
+
+ if ($interval != null)
+ {
+ Db::getInstance()->Execute('
+ DELETE FROM `'._DB_PREFIX_.'compare_product`
+ WHERE date_upd < DATE_SUB(NOW(), INTERVAL '.pSQL($interval).')');
+ }
+ }
+}
\ No newline at end of file
diff --git a/install-dev/classes/ConfigurationTest.php b/classes/ConfigurationTest.php
similarity index 70%
rename from install-dev/classes/ConfigurationTest.php
rename to classes/ConfigurationTest.php
index 6b5492e2d..f74539405 100644
--- a/install-dev/classes/ConfigurationTest.php
+++ b/classes/ConfigurationTest.php
@@ -20,14 +20,14 @@
*
* @author PrestaShop SA
* @copyright 2007-2011 PrestaShop SA
-* @version Release: $Revision: 7040 $
+* @version Release: $Revision$
* @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
-class ConfigurationTest
+class ConfigurationTestCore
{
- static function check($tests)
+ static function check($tests)
{
$res = array();
foreach ($tests AS $key => $test)
@@ -35,7 +35,7 @@ class ConfigurationTest
return $res;
}
- static function run($ptr, $arg = 0)
+ static function run($ptr, $arg = 0)
{
if (call_user_func(array('ConfigurationTest', 'test_'.$ptr), $arg))
return ('ok');
@@ -43,32 +43,32 @@ class ConfigurationTest
}
// Misc functions
- static function test_phpversion()
+ static function test_phpversion()
{
return version_compare(substr(phpversion(), 0, 3), '5.0', '>=');
}
- static function test_mysql_support()
+ static function test_mysql_support()
{
return function_exists('mysql_connect');
}
- static function test_magicquotes()
+ static function test_magicquotes()
{
return !ini_get('magic_quotes_gpc');
}
- static function test_upload()
+ static function test_upload()
{
return ini_get('file_uploads');
}
- static function test_fopen()
+ static function test_fopen()
{
return ini_get('allow_url_fopen');
}
- static function test_system($funcs)
+ static function test_system($funcs)
{
foreach ($funcs AS $func)
if (!function_exists($func))
@@ -76,17 +76,17 @@ class ConfigurationTest
return true;
}
- static function test_gd()
+ static function test_gd()
{
return function_exists('imagecreatetruecolor');
}
- static function test_register_globals()
+ static function test_register_globals()
{
return !ini_get('register_globals');
}
- static function test_gz()
+ static function test_gz()
{
if (function_exists('gzencode'))
return !(@gzencode('dd') === false);
@@ -94,7 +94,7 @@ class ConfigurationTest
}
// is_writable dirs
- static function test_dir($dir, $recursive = false)
+ static function test_dir($dir, $recursive = false)
{
if (!file_exists($dir) OR !$dh = opendir($dir))
return false;
@@ -119,107 +119,112 @@ class ConfigurationTest
}
// is_writable files
- static function test_file($file)
+ static function test_file($file)
{
return (file_exists($file) AND is_writable($file));
}
- static function test_config_dir($dir)
+ static function test_config_dir($dir)
{
return self::test_dir($dir);
}
- static function test_sitemap($dir)
+ static function test_sitemap($dir)
{
return self::test_file($dir);
}
- static function test_root_dir($dir)
+ static function test_root_dir($dir)
{
return self::test_dir($dir);
}
- static function test_log_dir($dir)
+ static function test_log_dir($dir)
{
return self::test_dir($dir);
}
- static function test_admin_dir($dir)
+ static function test_admin_dir($dir)
{
return self::test_dir($dir);
}
- static function test_img_dir($dir)
+ static function test_img_dir($dir)
{
return self::test_dir($dir, true);
}
- static function test_module_dir($dir)
+ static function test_module_dir($dir)
{
return self::test_dir($dir, true);
}
- static function test_tools_dir($dir)
+ static function test_tools_dir($dir)
{
return self::test_dir($dir);
}
- static function test_cache_dir($dir)
+ static function test_cache_dir($dir)
{
return self::test_dir($dir);
}
- static function test_tools_v2_dir($dir)
+ static function test_tools_v2_dir($dir)
{
return self::test_dir($dir);
}
- static function test_cache_v2_dir($dir)
+ static function test_cache_v2_dir($dir)
{
return self::test_dir($dir);
}
- static function test_download_dir($dir)
+ static function test_download_dir($dir)
{
return self::test_dir($dir);
}
- static function test_mails_dir($dir)
+ static function test_mails_dir($dir)
{
return self::test_dir($dir, true);
}
- static function test_translations_dir($dir)
+ static function test_translations_dir($dir)
{
return self::test_dir($dir, true);
}
- static function test_theme_lang_dir($dir)
+ static function test_theme_lang_dir($dir)
{
if (!file_exists($dir))
return true;
return self::test_dir($dir, true);
}
- static function test_theme_cache_dir($dir)
+ static function test_theme_cache_dir($dir)
{
if (!file_exists($dir))
return true;
return self::test_dir($dir, true);
}
- static function test_customizable_products_dir($dir)
+ static function test_customizable_products_dir($dir)
{
return self::test_dir($dir);
}
- static function test_virtual_products_dir($dir)
+ static function test_virtual_products_dir($dir)
{
return self::test_dir($dir);
}
- static function test_mcrypt()
+ static function test_mcrypt()
{
return function_exists('mcrypt_encrypt');
}
+
+ static function test_dom()
+ {
+ return extension_loaded('Dom');
+ }
}
diff --git a/classes/Helper.php b/classes/Helper.php
new file mode 100755
index 000000000..85ab54aec
--- /dev/null
+++ b/classes/Helper.php
@@ -0,0 +1,118 @@
+
+* @copyright 2007-2011 PrestaShop SA
+* @version Release: $Revision$
+* @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
+* International Registered Trademark & Property of PrestaShop SA
+*/
+
+/*
+ * TODO : move HTML code in template files
+ */
+
+class HelperCore
+{
+ public static $translationsKeysForAdminCategorieTree = array(
+ 'Home', 'selected', 'selecteds', 'Collapse All', 'Expand All', 'Check All', 'Uncheck All'
+ );
+
+ /**
+ *
+ * @param type $trads values of translations keys
+ * For the moment, translation are not automatic
+ * @param type $selected_cat array of selected categories
+ * Format
+ * Array
+ (
+ [0] => 1
+ [1] => 2
+ )
+ * OR
+ Array
+ (
+ [1] => Array
+ (
+ [id_category] => 1
+ [name] => Home page
+ [link_rewrite] => home
+ )
+ )
+ * @param type $input_name name of input
+ * @return string
+ */
+ public static function renderAdminCategorieTree($trads, $selected_cat = array(), $input_name = 'categoryBox')
+ {
+ $html = '
+
+
+
+
+
+
+
+ ';
+
+ $html .= '
+
'.lang('Estimated time to complete the').' '.(int)$totalInstructions.' '.lang('modifications:').' '.(int)$minutes.' '.($minutes > 1 ? lang('minutes') : lang('minute')).' '.(int)$seconds.' '.($seconds > 1 ? lang('seconds') : lang('second')).' '.lang('Depending on your server and the size of your shop').'
';
if ($majorReleases > 1)
@@ -1011,7 +1117,7 @@ if ($lm->getIncludeTradFilename())
'.lang('Hosting parameters').'
'.lang('PrestaShop tries to automatically set the best settings for your server in order the update to be successful.').'
-
+
'.lang('PHP parameter').'
'.lang('Description').'
@@ -1028,28 +1134,32 @@ if ($lm->getIncludeTradFilename())
'.ini_get('memory_limit').'
-
';
+
';
if ($color == '#D9F2D0')
- echo ' '.lang('All your settings seem to be OK, go for it!');
+ echo ' '.lang('All your settings seem to be OK, go for it!');
elseif ($color == '#FFDEB7')
- echo ' '.lang('Beware, your settings look correct but are not optimal, if you encounter problems (upgrade too long, memory error...), please ask your hosting provider to increase the values of these parameters (max_execution_time & memory_limit).');
+ echo ' '.lang('Beware, your settings look correct but are not optimal, if you encounter problems (upgrade too long, memory error...), please ask your hosting provider to increase the values of these parameters (max_execution_time & memory_limit).');
elseif ($color == '#FAE2E3')
- echo ' '.lang('We strongly recommend that you inform your hosting provider to modify the settings before process to the update.');
-
- echo '
-
';
-
+ echo ' '.lang('We strongly recommend that you inform your hosting provider to modify the settings before process to the update.');
+ echo '
';
?>
-
+
+
+
-
-
-
+
+
Etape 1 ok
+
Etape 2 ok
+
Etape 3
+
Etape 4
+
+
+
@@ -1058,7 +1168,7 @@ if ($lm->getIncludeTradFilename())
-
+
@@ -1099,21 +1209,44 @@ if ($lm->getIncludeTradFilename())
-
-
+
+
+
+
+
+
Etape 1 ok
+
Etape 2 ok
+
Etape 3
+
Etape 4
+
+
+
-
-
+
+
+
+
-
-
+
+
+
-
-
-
+
+
+
Etape 1 ok
+
Etape 2 ok
+
Etape 3
+
Etape 4
+
+
+
+
+
+
+
-
+
getIncludeTradFilename())
{
echo '
'.lang('New features in PrestaShop v').INSTALL_VERSION.'