This commit is contained in:
2025-04-01 00:38:54 +02:00
parent d4d4c0c09d
commit 87da06293a
22351 changed files with 5168854 additions and 7538 deletions

View File

@@ -0,0 +1,94 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License 3.0 (AFL-3.0)
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* 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 https://devdocs.prestashop.com/ for more information.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
*/
namespace PrestaShop\Module\AutoUpgrade\UpgradeTools;
use Exception;
use PrestaShop\Module\AutoUpgrade\Log\LoggerInterface;
use PrestaShop\Module\AutoUpgrade\UpgradeContainer;
class CacheCleaner
{
/**
* @var UpgradeContainer
*/
private $container;
/**
* @var LoggerInterface
*/
private $logger;
public function __construct(UpgradeContainer $container, LoggerInterface $logger)
{
$this->container = $container;
$this->logger = $logger;
}
/**
* @throws Exception
*/
public function cleanFolders(): void
{
$dirsToClean = [
$this->container->getProperty(UpgradeContainer::PS_ROOT_PATH) . '/app/cache/',
$this->container->getProperty(UpgradeContainer::PS_ROOT_PATH) . '/cache/smarty/cache/',
$this->container->getProperty(UpgradeContainer::PS_ROOT_PATH) . '/cache/smarty/compile/',
$this->container->getProperty(UpgradeContainer::PS_ROOT_PATH) . '/var/cache/',
];
$defaultThemeNames = [
'default',
'prestashop',
'default-boostrap',
'classic',
];
if (defined('_THEME_NAME_') && $this->container->getUpgradeConfiguration()->shouldUpdateDefaultTheme() && in_array(_THEME_NAME_, $defaultThemeNames)) {
$dirsToClean[] = $this->container->getProperty(UpgradeContainer::PS_ROOT_PATH) . '/themes/' . _THEME_NAME_ . '/cache/';
}
foreach ($dirsToClean as $dir) {
if (!file_exists($dir)) {
$this->logger->debug($this->container->getTranslator()->trans('[SKIP] directory "%s" does not exist and cannot be emptied.', [str_replace($this->container->getProperty(UpgradeContainer::PS_ROOT_PATH), '', $dir)]));
continue;
}
foreach (scandir($dir) as $file) {
if ($file[0] === '.' || $file === 'index.php') {
continue;
}
// ToDo: Use Filesystem instead ?
if (is_file($dir . $file)) {
unlink($dir . $file);
} elseif (is_dir($dir . $file . DIRECTORY_SEPARATOR)) {
FilesystemAdapter::deleteDirectory($dir . $file . DIRECTORY_SEPARATOR);
}
$this->logger->debug($this->container->getTranslator()->trans('[CLEANING CACHE] File %s removed', [$file]));
}
}
}
}

View File

@@ -0,0 +1,816 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License 3.0 (AFL-3.0)
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* 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 https://devdocs.prestashop.com/ for more information.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
*/
namespace PrestaShop\Module\AutoUpgrade\UpgradeTools\CoreUpgrader;
use Cache;
use Exception;
use PrestaShop\Module\AutoUpgrade\Exceptions\UpgradeException;
use PrestaShop\Module\AutoUpgrade\Log\LoggerInterface;
use PrestaShop\Module\AutoUpgrade\Parameters\UpgradeConfiguration;
use PrestaShop\Module\AutoUpgrade\UpgradeContainer;
use PrestaShop\Module\AutoUpgrade\UpgradeTools\ThemeAdapter;
use PrestaShop\PrestaShop\Core\CommandBus\CommandBusInterface;
use PrestaShop\PrestaShop\Core\Domain\Theme\Command\AdaptThemeToRTLLanguagesCommand;
use PrestaShop\PrestaShop\Core\Domain\Theme\ValueObject\ThemeName;
use PrestaShop\PrestaShop\Core\Exception\CoreException;
use PrestaShop\PrestaShop\Core\Localization\RTL\Processor as RtlStylesheetProcessor;
use Symfony\Component\Filesystem\Filesystem;
/**
* Class used to modify the core of PrestaShop, on the files are copied on the filesystem.
* It will run subtasks such as database upgrade, language upgrade etc.
*/
abstract class CoreUpgrader
{
/**
* @var UpgradeContainer
*/
protected $container;
/**
* @var \Db
*/
protected $db;
/**
* @var LoggerInterface
*/
protected $logger;
/**
* @var Filesystem
*/
private $filesystem;
/**
* Version PrestaShop is upgraded to.
*
* @var string
*/
protected $destinationUpgradeVersion;
/**
* Path to the temporary install folder, where upgrade files can be found
*
* @var string
*/
protected $pathToInstallFolder;
/**
* Path to the folder containing PHP upgrade files
*
* @var string
*/
protected $pathToUpgradeScripts;
public function __construct(UpgradeContainer $container, LoggerInterface $logger)
{
$this->container = $container;
$this->logger = $logger;
$this->filesystem = new Filesystem();
}
public function doUpgrade(): void
{
$this->logger->info($this->container->getTranslator()->trans('Initializing required environment constants'));
$this->initConstants();
$this->logger->info($this->container->getTranslator()->trans('Checking version validity'));
$oldversion = $this->getPreUpgradeVersion();
$this->checkVersionIsNewer($oldversion);
//check DB access
$this->logger->info($this->container->getTranslator()->trans('Checking connection to database'));
error_reporting(E_ALL);
$resultDB = \Db::checkConnection(_DB_SERVER_, _DB_USER_, _DB_PASSWD_, _DB_NAME_);
if ($resultDB !== 0) {
throw new UpgradeException($this->container->getTranslator()->trans('Invalid database configuration'));
}
if ($this->container->getUpgradeConfiguration()->shouldDeactivateCustomModules()) {
$this->logger->info($this->container->getTranslator()->trans('Disabling all non native modules'));
$this->disableCustomModules();
} else {
$this->logger->info($this->container->getTranslator()->trans('Keeping non native modules enabled'));
}
$this->logger->info($this->container->getTranslator()->trans('Updating database data and structure'));
$this->upgradeDb($oldversion);
// 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();
$this->logger->info($this->container->getTranslator()->trans('Running generic queries'));
$this->runRecurrentQueries();
$this->logger->info($this->container->getTranslator()->trans('Database upgrade OK')); // no error!
$this->logger->info($this->container->getTranslator()->trans('Upgrading languages'));
$this->upgradeLanguages();
$this->logger->info($this->container->getTranslator()->trans('Regenerating htaccess'));
$this->generateHtaccess();
$this->logger->info($this->container->getTranslator()->trans('Cleaning XML files'));
$this->cleanXmlFiles();
if (UpgradeConfiguration::isOverrideAllowed()) {
$this->logger->info($this->container->getTranslator()->trans('Disabling overrides'));
$this->disableOverrides();
} else {
$this->logger->info($this->container->getTranslator()->trans('Keeping overrides in place'));
}
$this->updateTheme();
$this->runCoreCacheClean();
if ($this->container->getState()->getWarningExists()) {
$this->logger->warning($this->container->getTranslator()->trans('Warning detected during upgrade.'));
} else {
$this->logger->info($this->container->getTranslator()->trans('Database upgrade completed'));
}
}
/**
* @throws Exception
*/
protected function initConstants(): void
{
// 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' && substr($memory_limit, 0, -1) < 512)
|| is_numeric($memory_limit) && ((int) $memory_limit < 131072))
) {
@ini_set('memory_limit', '512M');
}
// 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']);
$this->destinationUpgradeVersion = $this->container->getState()->getInstallVersion();
$this->pathToInstallFolder = realpath($this->container->getProperty(UpgradeContainer::LATEST_PATH) . DIRECTORY_SEPARATOR . 'install');
// Kept for backward compatbility (unknown consequences on old versions of PrestaShop)
define('INSTALL_VERSION', $this->destinationUpgradeVersion);
// 1.4
define('INSTALL_PATH', $this->pathToInstallFolder);
// 1.5 ...
if (!defined('_PS_CORE_DIR_')) {
define('_PS_CORE_DIR_', _PS_ROOT_DIR_);
}
define('PS_INSTALLATION_IN_PROGRESS', true);
define('SETTINGS_FILE_PHP', $this->container->getProperty(UpgradeContainer::PS_ROOT_PATH) . '/app/config/parameters.php');
define('SETTINGS_FILE_YML', $this->container->getProperty(UpgradeContainer::PS_ROOT_PATH) . '/app/config/parameters.yml');
define('DEFINES_FILE', $this->container->getProperty(UpgradeContainer::PS_ROOT_PATH) . '/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('_PS_INSTALL_PATH_', $this->pathToInstallFolder . '/');
define('_PS_INSTALL_DATA_PATH_', _PS_INSTALL_PATH_ . 'data/');
define('_PS_INSTALL_CONTROLLERS_PATH_', _PS_INSTALL_PATH_ . 'controllers/');
define('_PS_INSTALL_MODELS_PATH_', _PS_INSTALL_PATH_ . 'models/');
define('_PS_INSTALL_LANGS_PATH_', _PS_INSTALL_PATH_ . 'langs/');
define('_PS_INSTALL_FIXTURES_PATH_', _PS_INSTALL_PATH_ . 'fixtures/');
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_') && !defined('_PS_MODULE_DIR_')) {
define('_PS_MODULE_DIR_', _PS_ROOT_DIR_ . '/modules/');
} elseif (!defined('_PS_MODULE_DIR_')) {
define('_PS_MODULE_DIR_', $this->pathToInstallFolder . '/../modules/');
}
$this->pathToUpgradeScripts = dirname(__DIR__, 3) . '/upgrade/';
define('_PS_INSTALLER_PHP_UPGRADE_DIR_', $this->pathToUpgradeScripts . 'php/');
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/');
}
if (file_exists($this->pathToInstallFolder . DIRECTORY_SEPARATOR . 'autoload.php')) {
require_once $this->pathToInstallFolder . DIRECTORY_SEPARATOR . 'autoload.php';
}
$this->db = \Db::getInstance();
}
protected function getPreUpgradeVersion(): string
{
return $this->normalizeVersion($this->container->getState()->getOriginVersion());
}
/**
* Add missing levels in version.
* Example: 1.7 will become 1.7.0.0 and 8.1 will become 8.1.0.
*
* @internal public for tests
*/
public function normalizeVersion(string $version): string
{
$arrayVersion = explode('.', $version);
$versionLevels = 1 == $arrayVersion[0] ? 4 : 3;
if (count($arrayVersion) < $versionLevels) {
$arrayVersion = array_pad($arrayVersion, $versionLevels, '0');
}
return implode('.', $arrayVersion);
}
/**
* @throws UpgradeException
*/
protected function checkVersionIsNewer(string $oldVersion): void
{
if (strpos($this->destinationUpgradeVersion, '.') === false) {
throw new UpgradeException($this->container->getTranslator()->trans('%s is not a valid version number.', [$this->destinationUpgradeVersion]));
}
$versionCompare = version_compare($this->destinationUpgradeVersion, $oldVersion);
if ($versionCompare === -1) {
throw new UpgradeException($this->container->getTranslator()->trans('[ERROR] Version to install is too old.') . ' ' . $this->container->getTranslator()->trans('Current version: %oldversion%. Version to install: %newversion%.', ['%oldversion%' => $oldVersion, '%newversion%' => $this->destinationUpgradeVersion]));
} elseif ($versionCompare === 0) {
throw new UpgradeException($this->container->getTranslator()->trans('You already have the %s version.', [$this->destinationUpgradeVersion]));
}
}
/**
* Ask the core to disable the modules not coming from PrestaShop.
*
* @throws Exception
*/
protected function disableCustomModules(): void
{
$this->container->getModuleAdapter()->disableNonNativeModules($this->pathToUpgradeScripts);
}
/**
* @throws UpgradeException
*/
protected function upgradeDb(string $oldversion): void
{
$upgrade_dir_sql = $this->pathToUpgradeScripts . '/sql/';
$sqlContentVersion = $this->applySqlParams(
$this->getUpgradeSqlFilesListToApply($upgrade_dir_sql, $oldversion)
);
foreach ($sqlContentVersion as $upgrade_file => $sqlContent) {
foreach ($sqlContent as $query) {
$this->runQuery($upgrade_file, $query);
}
}
}
/**
* @throws UpgradeException
*
* @return array<string, string>
*/
protected function getUpgradeSqlFilesListToApply(string $upgrade_dir_sql, string $oldversion): array
{
if (!file_exists($upgrade_dir_sql)) {
throw new UpgradeException($this->container->getTranslator()->trans('Unable to find upgrade directory in the installation path.'));
}
$upgradeFiles = $neededUpgradeFiles = [];
if ($handle = opendir($upgrade_dir_sql)) {
while (false !== ($file = readdir($handle))) {
if ($file[0] === '.') {
continue;
}
if (!is_readable($upgrade_dir_sql . $file)) {
throw new UpgradeException($this->container->getTranslator()->trans('Error while loading SQL upgrade file "%s".', [$file]));
}
$upgradeFiles[] = str_replace('.sql', '', $file);
}
closedir($handle);
}
if (empty($upgradeFiles)) {
throw new UpgradeException($this->container->getTranslator()->trans('Cannot find the SQL upgrade files. Please check that the %s folder is not empty.', [$upgrade_dir_sql]));
}
natcasesort($upgradeFiles);
foreach ($upgradeFiles as $version) {
if (version_compare($version, $oldversion) == 1 && version_compare($this->destinationUpgradeVersion, $version) != -1) {
$neededUpgradeFiles[$version] = $upgrade_dir_sql . $version . '.sql';
}
}
return $neededUpgradeFiles;
}
/**
* Replace some placeholders in the SQL upgrade files (prefix, engine...).
*
* @param array<string, string> $sqlFiles
*
* @return array<string, string[]> of SQL requests per version
*/
protected function applySqlParams(array $sqlFiles): array
{
$search = ['PREFIX_', 'ENGINE_TYPE', 'DB_NAME'];
$replace = [_DB_PREFIX_, (defined('_MYSQL_ENGINE_') ? _MYSQL_ENGINE_ : 'MyISAM'), _DB_NAME_];
$sqlRequests = [];
foreach ($sqlFiles as $version => $file) {
$sqlContent = file_get_contents($file) . "\n";
$sqlContent = str_replace($search, $replace, $sqlContent);
$sqlContent = preg_split("/;\s*[\r\n]+/", $sqlContent);
$sqlRequests[$version] = $sqlContent;
}
return $sqlRequests;
}
/**
* ToDo, check to move this in a database class.
*
* @param string $upgrade_file File in which the request is stored (for logs)
* @param string $query
*/
protected function runQuery(string $upgrade_file, string $query): void
{
$query = trim($query);
if (empty($query)) {
return;
}
// If php code have to be executed
if (strpos($query, '/* PHP:') !== false) {
$this->runPhpQuery($upgrade_file, $query);
return;
}
$this->runSqlQuery($upgrade_file, $query);
}
protected function runPhpQuery(string $upgrade_file, string $query): void
{
// 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);
// TODO: Could be `$parameters = $parameters[1] ?? [];` if PHP min version was > 7.0
$parameters = isset($parameters[1]) ?
$parameters[1] :
[];
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]);
$pathToPhpDirectory = $this->pathToUpgradeScripts . 'php/';
if (!file_exists($pathToPhpDirectory . strtolower($func_name) . '.php')) {
$this->logger->error('[ERROR] ' . $pathToPhpDirectory . strtolower($func_name) . ' PHP - missing file ' . $query);
$this->container->getState()->setWarningExists(true);
return;
}
require_once $pathToPhpDirectory . strtolower($func_name) . '.php';
$phpRes = call_user_func_array($func_name, $parameters);
}
// Or an object method
else {
$func_name = [$php[0], str_replace($pattern[0], '', $php[1])];
$this->logger->error('[ERROR] ' . $upgrade_file . ' PHP - Object Method call is forbidden (' . $php[0] . '::' . str_replace($pattern[0], '', $php[1]) . ')');
$this->container->getState()->setWarningExists(true);
return;
}
if (isset($phpRes) && (is_array($phpRes) && !empty($phpRes['error'])) || $phpRes === false) {
$this->logger->error('
[ERROR] PHP ' . $upgrade_file . ' ' . $query . "\n" . '
' . (empty($phpRes['error']) ? '' : $phpRes['error'] . "\n") . '
' . (empty($phpRes['msg']) ? '' : ' - ' . $phpRes['msg'] . "\n"));
$this->container->getState()->setWarningExists(true);
} else {
$this->logger->debug('<div class="upgradeDbOk">[OK] PHP ' . $upgrade_file . ' : ' . $query . '</div>');
}
}
protected function runSqlQuery(string $upgrade_file, string $query): void
{
if (strstr($query, 'CREATE TABLE') !== false) {
$pattern = '/CREATE TABLE.*[`]*' . _DB_PREFIX_ . '([^`]*)[`]*\s\(/';
preg_match($pattern, $query, $matches);
if (!empty($matches[1])) {
$drop = 'DROP TABLE IF EXISTS `' . _DB_PREFIX_ . $matches[1] . '`;';
if ($this->db->execute($drop, false)) {
$this->logger->debug('<div class="upgradeDbOk">' . $this->container->getTranslator()->trans('[DROP] SQL %s table has been dropped.', ['`' . _DB_PREFIX_ . $matches[1] . '`']) . '</div>');
}
}
}
if ($this->db->execute($query, false)) {
$this->logger->debug('<div class="upgradeDbOk">[OK] SQL ' . $upgrade_file . ' ' . $query . '</div>');
return;
}
$error = $this->db->getMsgError();
$error_number = $this->db->getNumberError();
$this->logger->warning('
<div class="upgradeDbError">
[WARNING] SQL ' . $upgrade_file . '
' . $error_number . ' in ' . $query . ': ' . $error . '</div>');
$duplicates = ['1050', '1054', '1060', '1061', '1062', '1091'];
if (!in_array($error_number, $duplicates)) {
$this->logger->error('SQL ' . $upgrade_file . ' ' . $error_number . ' in ' . $query . ': ' . $error);
$this->container->getState()->setWarningExists(true);
}
}
public function writeNewSettings(): void
{
// Do nothing
}
protected function runRecurrentQueries(): void
{
$this->db->execute('UPDATE `' . _DB_PREFIX_ . 'configuration` SET `name` = \'PS_LEGACY_IMAGES\' WHERE name LIKE \'0\' AND `value` = 1');
$this->db->execute('UPDATE `' . _DB_PREFIX_ . 'configuration` SET `value` = 0 WHERE `name` LIKE \'PS_LEGACY_IMAGES\'');
if ($this->db->getValue('SELECT COUNT(id_product_download) FROM `' . _DB_PREFIX_ . 'product_download` WHERE `active` = 1') > 0) {
$this->db->execute('UPDATE `' . _DB_PREFIX_ . 'configuration` SET `value` = 1 WHERE `name` LIKE \'PS_VIRTUAL_PROD_FEATURE_ACTIVE\'');
}
// Exported from the end of doUpgrade()
$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="' . $this->destinationUpgradeVersion . '" WHERE name = "PS_VERSION_DB"', false);
}
protected function upgradeLanguages(): void
{
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_MODULE_DIR_')) {
define('_PS_MODULE_DIR_', _PS_ROOT_DIR_ . '/modules/');
}
if (!defined('_PS_MAILS_DIR_')) {
define('_PS_MAILS_DIR_', _PS_ROOT_DIR_ . '/mails/');
}
$langs = $this->db->executeS('SELECT * FROM `' . _DB_PREFIX_ . 'lang` WHERE `active` = 1');
if (!is_array($langs)) {
return;
}
foreach ($langs as $lang) {
$this->upgradeLanguage($lang);
}
}
/**
* @param array<string, mixed> $lang
*/
abstract protected function upgradeLanguage($lang): void;
protected function generateHtaccess(): void
{
$this->loadEntityInterface();
if (file_exists(_PS_ROOT_DIR_ . '/classes/Tools.php')) {
require_once _PS_ROOT_DIR_ . '/classes/Tools.php';
}
if (!class_exists('ToolsCore') || !method_exists('ToolsCore', 'generateHtaccess')) {
return;
}
$url_rewrite = (bool) $this->db->getvalue('SELECT `value` FROM `' . _DB_PREFIX_ . 'configuration` WHERE name=\'PS_REWRITING_SETTINGS\'');
if (!defined('_MEDIA_SERVER_1_')) {
define('_MEDIA_SERVER_1_', '');
}
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) && 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) && 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) && 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) && 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) && 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) && 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) && 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) && 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) && 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) && 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) && 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) && 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) && 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) && 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) && class_exists('GroupCore')) {
eval('class Group extends GroupCore{}');
}
\ToolsCore::generateHtaccess(null, $url_rewrite);
}
protected function loadEntityInterface(): void
{
require_once _PS_ROOT_DIR_ . '/src/Core/Foundation/Database/EntityInterface.php';
}
/**
* @throws Exception
*/
protected function cleanXmlFiles(): void
{
$files = [
$this->container->getProperty(UpgradeContainer::PS_ADMIN_PATH) . DIRECTORY_SEPARATOR . 'themes' . DIRECTORY_SEPARATOR . 'default' . DIRECTORY_SEPARATOR . 'template' . DIRECTORY_SEPARATOR . 'controllers' . DIRECTORY_SEPARATOR . 'modules' . DIRECTORY_SEPARATOR . 'header.tpl',
_PS_ROOT_DIR_ . '/app/cache/dev/class_index.php',
_PS_ROOT_DIR_ . '/app/cache/prod/class_index.php',
_PS_ROOT_DIR_ . '/cache/class_index.php',
_PS_ROOT_DIR_ . '/config/xml/blog-fr.xml',
_PS_ROOT_DIR_ . '/config/xml/default_country_modules_list.xml',
_PS_ROOT_DIR_ . '/config/xml/modules_list.xml',
_PS_ROOT_DIR_ . '/config/xml/modules_native_addons.xml',
_PS_ROOT_DIR_ . '/config/xml/must_have_modules_list.xml',
_PS_ROOT_DIR_ . '/config/xml/tab_modules_list.xml',
_PS_ROOT_DIR_ . '/config/xml/trusted_modules_list.xml',
_PS_ROOT_DIR_ . '/config/xml/untrusted_modules_list.xml',
_PS_ROOT_DIR_ . '/var/cache/dev/class_index.php',
_PS_ROOT_DIR_ . '/var/cache/prod/class_index.php',
];
foreach ($files as $path) {
if (file_exists($path)) {
unlink($path);
}
}
}
protected function disableOverrides(): void
{
if (class_exists('PrestaShopAutoload') && method_exists('PrestaShopAutoload', 'generateIndex')) {
\PrestaShopAutoload::getInstance()->_include_override_path = false;
\PrestaShopAutoload::getInstance()->generateIndex();
}
}
/**
* @throws UpgradeException
* @throws Exception
*/
protected function updateTheme(): void
{
$this->updateRTLFiles();
$this->switchToDefaultTheme();
}
/**
* @throws UpgradeException
*/
protected function switchToDefaultTheme(): void
{
// The merchant can ask for keeping its current theme.
if (!$this->container->getUpgradeConfiguration()->shouldSwitchToDefaultTheme()) {
$this->logger->info($this->container->getTranslator()->trans('Keeping current theme'));
return;
}
$this->logger->info($this->container->getTranslator()->trans('Switching to default theme.'));
$themeAdapter = new ThemeAdapter($this->db);
Cache::clean('*');
$themeErrors = $themeAdapter->enableTheme(
$themeAdapter->getDefaultTheme()
);
if ($themeErrors !== true) {
throw new UpgradeException($themeErrors);
}
}
protected function updateRTLFiles(): void
{
if (!$this->container->getUpgradeConfiguration()->shouldUpdateRTLFiles()) {
return;
}
// BO theme
if (class_exists(RtlStylesheetProcessor::class)) {
$this->logger->info($this->container->getTranslator()->trans('Upgrade the RTL files of back-office themes.'));
$this->removeExistingRTLFiles([
['directory' => $this->container->getProperty(UpgradeContainer::PS_ADMIN_PATH) . DIRECTORY_SEPARATOR . 'themes'],
]);
(new RtlStylesheetProcessor(
$this->container->getProperty(UpgradeContainer::PS_ADMIN_PATH),
$this->container->getProperty(UpgradeContainer::PS_ROOT_PATH) . DIRECTORY_SEPARATOR . 'themes',
[]
))
->setProcessBOTheme(true)
->setProcessDefaultModules(true)
->process();
}
// FO themes
if (!class_exists(AdaptThemeToRTLLanguagesCommand::class)) {
return;
}
$this->logger->info($this->container->getTranslator()->trans('Upgrade the RTL files of front-office themes.'));
$themeAdapter = new ThemeAdapter($this->db);
$themes = $themeAdapter->getListFromDisk();
$this->removeExistingRTLFiles($themes);
foreach ($themes as $theme) {
$adaptThemeToTRLLanguages = new AdaptThemeToRTLLanguagesCommand(
new ThemeName($theme['name'])
);
/** @var CommandBusInterface $commandBus */
$commandBus = $this->container->getModuleAdapter()->getCommandBus();
try {
$commandBus->handle($adaptThemeToTRLLanguages);
} catch (CoreException $e) {
$this->logger->error('
[ERROR] PHP Impossible to generate RTL files for theme' . $theme['name'] . "\n" .
$e->getMessage()
);
$this->container->getState()->setWarningExists(true);
}
}
}
/**
* @param array{array{'directory':string}} $themes
*/
private function removeExistingRTLFiles(array $themes): void
{
foreach ($themes as $theme) {
$files = $this->container->getFilesystemAdapter()->listSampleFiles($theme['directory'], '_rtl.css');
$this->filesystem->remove($files);
}
}
/**
* @throws Exception
*/
protected function runCoreCacheClean(): void
{
$this->logger->info($this->container->getTranslator()->trans('Cleaning file cache'));
$this->container->getCacheCleaner()->cleanFolders();
$this->logger->info($this->container->getTranslator()->trans('Running opcache_reset'));
$this->container->resetOpcache();
}
}

View File

@@ -0,0 +1,82 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License 3.0 (AFL-3.0)
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* 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 https://devdocs.prestashop.com/ for more information.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
*/
namespace PrestaShop\Module\AutoUpgrade\UpgradeTools\CoreUpgrader;
use PrestaShop\Module\AutoUpgrade\Exceptions\UpgradeException;
/**
* Class used to modify the core of PrestaShop, on the files are copied on the filesystem.
* It will run subtasks such as database upgrade, language upgrade etc.
*/
class CoreUpgrader17 extends CoreUpgrader
{
protected function initConstants(): void
{
parent::initConstants();
// Container may be needed to run upgrade scripts
$this->container->getSymfonyAdapter()->initKernel();
}
/**
* @throws UpgradeException
*/
protected function upgradeLanguage($lang): void
{
$isoCode = $lang['iso_code'];
if (!\Validate::isLangIsoCode($isoCode)) {
return;
}
$errorsLanguage = [];
if (!\Language::downloadLanguagePack($isoCode, _PS_VERSION_, $errorsLanguage)) {
throw new UpgradeException($this->container->getTranslator()->trans('Download of the language pack %lang% failed. %details%', ['%lang%' => $isoCode, '%details%' => implode('; ', $errorsLanguage)]));
}
$lang_pack = \Language::getLangDetails($isoCode);
\Language::installSfLanguagePack($lang_pack['locale'], $errorsLanguage);
if (!$this->container->getUpgradeConfiguration()->shouldKeepMails()) {
\Language::installEmailsLanguagePack($lang_pack, $errorsLanguage);
}
if (!empty($errorsLanguage)) {
throw new UpgradeException($this->container->getTranslator()->trans('Error while updating translations for the language pack %lang%. %details%', ['%lang%' => $isoCode, '%details%' => implode('; ', $errorsLanguage)]));
}
\Language::loadLanguages();
// TODO: Update AdminTranslationsController::addNewTabs to install tabs translated
// CLDR has been updated on PS 1.7.6.0. From this version, updates are not needed anymore.
if (version_compare($this->container->getState()->getInstallVersion(), '1.7.6.0', '<')) {
$cldrUpdate = new \PrestaShop\PrestaShop\Core\Cldr\Update(_PS_TRANSLATIONS_DIR_);
$cldrUpdate->fetchLocale(\Language::getLocaleByIso($isoCode));
}
}
}

View File

@@ -0,0 +1,129 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* 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.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* 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 https://devdocs.prestashop.com/ for more information.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
*/
declare(strict_types=1);
namespace PrestaShop\Module\AutoUpgrade\UpgradeTools\CoreUpgrader;
use Exception;
use PrestaShop\Module\AutoUpgrade\Exceptions\UpgradeException;
use PrestaShop\PrestaShop\Adapter\Module\Repository\ModuleRepository;
use PrestaShop\PrestaShop\Core\CommandBus\CommandBusInterface;
use PrestaShop\PrestaShop\Core\Domain\MailTemplate\Command\GenerateThemeMailTemplatesCommand;
use PrestaShop\PrestaShop\Core\Exception\CoreException;
class CoreUpgrader80 extends CoreUpgrader
{
protected function initConstants(): void
{
$this->forceRemovingFiles();
parent::initConstants();
// Container may be needed to run upgrade scripts
$this->container->getSymfonyAdapter()->initKernel();
}
/**
* Force remove files if they aren't removed properly after files upgrade.
*/
protected function forceRemovingFiles(): void
{
$filesToForceRemove = [
'/src/PrestaShopBundle/Resources/config/services/adapter/news.yml',
];
foreach ($filesToForceRemove as $file) {
if (file_exists(_PS_ROOT_DIR_ . $file)) {
unlink(_PS_ROOT_DIR_ . $file);
}
}
}
/**
* @throws UpgradeException
* @throws Exception
*
* @param array<string, mixed> $lang
*/
protected function upgradeLanguage($lang): void
{
$isoCode = $lang['iso_code'];
if (!\Validate::isLangIsoCode($isoCode)) {
$this->logger->debug($this->container->getTranslator()->trans('%lang% is not a valid iso code, skipping', ['%lang%' => $isoCode]));
return;
}
$errorsLanguage = [];
$this->logger->debug($this->container->getTranslator()->trans('Downloading language pack for %lang%', ['%lang%' => $isoCode]));
if (!\Language::downloadLanguagePack($isoCode, _PS_VERSION_, $errorsLanguage)) {
throw new UpgradeException($this->container->getTranslator()->trans('Download of the language pack %lang% failed. %details%', ['%lang%' => $isoCode, '%details%' => implode('; ', $errorsLanguage)]));
}
$this->logger->debug($this->container->getTranslator()->trans('Installing %lang% language pack', ['%lang%' => $isoCode]));
$lang_pack = \Language::getLangDetails($isoCode);
\Language::installSfLanguagePack($lang_pack['locale'], $errorsLanguage);
if (!$this->container->getUpgradeConfiguration()->shouldKeepMails()) {
$this->logger->debug($this->container->getTranslator()->trans('Generating mail templates for %lang%', ['%lang%' => $isoCode]));
$mailTheme = \Configuration::get('PS_MAIL_THEME', null, null, null, 'modern');
$frontTheme = _THEME_NAME_;
$frontThemeMailsFolder = _PS_ALL_THEMES_DIR_ . $frontTheme . '/mails';
$frontThemeModulesFolder = _PS_ALL_THEMES_DIR_ . $frontTheme . '/modules';
$generateCommand = new GenerateThemeMailTemplatesCommand(
$mailTheme,
$lang_pack['locale'],
true,
is_dir($frontThemeMailsFolder) ? $frontThemeMailsFolder : '',
is_dir($frontThemeModulesFolder) ? $frontThemeModulesFolder : ''
);
/** @var CommandBusInterface $commandBus */
$commandBus = $this->container->getModuleAdapter()->getCommandBus();
try {
$commandBus->handle($generateCommand);
} catch (CoreException $e) {
throw new UpgradeException($this->container->getTranslator()->trans('Cannot generate email templates: %s.', [$e->getMessage()]));
}
}
if (!empty($errorsLanguage)) {
throw new UpgradeException($this->container->getTranslator()->trans('Error while updating translations for the language pack %lang%. %details%', ['%lang%' => $isoCode, '%details%' => implode('; ', $errorsLanguage)]));
}
\Language::loadLanguages();
// TODO: Update AdminTranslationsController::addNewTabs to install tabs translated
}
protected function disableCustomModules(): void
{
$moduleRepository = new ModuleRepository(_PS_ROOT_DIR_, _PS_MODULE_DIR_);
$this->container->getModuleAdapter()->disableNonNativeModules80($this->pathToUpgradeScripts, $moduleRepository);
}
}

View File

@@ -0,0 +1,89 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* 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.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* 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 https://devdocs.prestashop.com/ for more information.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
*/
declare(strict_types=1);
namespace PrestaShop\Module\AutoUpgrade\UpgradeTools\CoreUpgrader;
use PrestaShop\Module\AutoUpgrade\Exceptions\UpgradeException;
use PrestaShop\Module\AutoUpgrade\UpgradeContainer;
class CoreUpgrader81 extends CoreUpgrader80
{
/** @var bool */
private $settingsMigrated = false;
/**
* @throws UpgradeException
*/
public function doUpgrade(): void
{
// We need to write the new settings before initConstants() is called
// because the new settings are needed for the Kernel
$this->writeNewSettings();
$this->settingsMigrated = true;
parent::doUpgrade();
}
/**
* @throws UpgradeException
*/
public function writeNewSettings(): void
{
if ($this->settingsMigrated) {
return;
}
$parametersPath = $this->container->getProperty(UpgradeContainer::PS_ROOT_PATH) . '/app/config/parameters.php';
$parameters = require $parametersPath;
if (!isset($parameters['parameters']['api_public_key']) || isset($parameters['parameters']['api_private_key'])) {
$this->logger->debug($this->container->getTranslator()->trans('API keys not present in parameters, generating'));
$privateKey = openssl_pkey_new([
'private_key_bits' => 2048,
'private_key_type' => OPENSSL_KEYTYPE_RSA,
]);
$this->logger->debug($this->container->getTranslator()->trans('Keys generated using openssl_pkey_new, exporting private and public keys'));
openssl_pkey_export($privateKey, $apiPrivateKey);
$apiPublicKey = openssl_pkey_get_details($privateKey)['key'];
$parameters['parameters']['api_public_key'] = $apiPublicKey;
$parameters['parameters']['api_private_key'] = $apiPrivateKey;
$parametersContent = sprintf('<?php return %s;', var_export($parameters, true));
$this->logger->debug($this->container->getTranslator()->trans('Updating parameters file'));
if (!file_put_contents($parametersPath, $parametersContent)) {
throw new UpgradeException($this->container->getTranslator()->trans('Unable to migrate parameters'));
}
if (function_exists('opcache_invalidate')) {
$this->logger->debug($this->container->getTranslator()->trans('Invalidating opcache for parameters file'));
opcache_invalidate($parametersPath);
}
$this->logger->debug($this->container->getTranslator()->trans('Parameters file updated'));
}
}
}

View File

@@ -0,0 +1,34 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License 3.0 (AFL-3.0)
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* 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 https://devdocs.prestashop.com/ for more information.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
*/
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Cache-Control: post-check=0, pre-check=0', false);
header('Pragma: no-cache');
header('Location: ../');
exit;

View File

@@ -0,0 +1,73 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License 3.0 (AFL-3.0)
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* 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 https://devdocs.prestashop.com/ for more information.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
*/
namespace PrestaShop\Module\AutoUpgrade\UpgradeTools;
use Db;
class Database
{
/**
* @var Db
*/
private $db;
public function __construct(Db $db)
{
$this->db = $db;
}
/**
* @return array<string, string>
*/
public function getAllTables(): array
{
$tables = $this->db->executeS('SHOW TABLES LIKE "' . _DB_PREFIX_ . '%"', true, false);
$all_tables = [];
foreach ($tables as $v) {
$table = reset($v);
$all_tables[$table] = $table;
}
return $all_tables;
}
/**
* ToDo: Send tables list instead.
*
* @param string[] $tablesToClean
*/
public function cleanTablesAfterBackup(array $tablesToClean): void
{
foreach ($tablesToClean as $table) {
$this->db->execute('DROP TABLE IF EXISTS `' . bqSql($table) . '`');
$this->db->execute('DROP VIEW IF EXISTS `' . bqSql($table) . '`');
}
$this->db->execute('SET FOREIGN_KEY_CHECKS=1');
}
}

View File

@@ -0,0 +1,238 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License 3.0 (AFL-3.0)
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* 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 https://devdocs.prestashop.com/ for more information.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
*/
namespace PrestaShop\Module\AutoUpgrade\UpgradeTools;
use DirectoryIterator;
use PrestaShop\Module\AutoUpgrade\Parameters\UpgradeConfiguration;
use SplFileInfo;
class FileFilter
{
/**
* @var UpgradeConfiguration
*/
protected $configuration;
/**
* @var string Autoupgrade sub directory
*/
protected $autoupgradeDir;
/**
* @var string Root directory
*/
protected $rootDir;
/**
* @var string[]
*/
protected $excludeAbsoluteFilesFromUpgrade;
const COMPOSER_PACKAGE_TYPE = 'prestashop-module';
const ADDITIONAL_ALLOWED_MODULES = [
'autoupgrade',
];
public function __construct(
UpgradeConfiguration $configuration,
string $rootDir,
string $autoupgradeDir = 'autoupgrade'
) {
$this->configuration = $configuration;
$this->rootDir = $rootDir;
$this->autoupgradeDir = $autoupgradeDir;
}
/**
* @return string[]
*/
public function getFilesToIgnoreOnBackup(): array
{
// during backup, do not save
$backupIgnoreAbsoluteFiles = [
'/app/cache',
'/cache/smarty/compile',
'/cache/smarty/cache',
'/cache/tcpdf',
'/cache/cachefs',
'/var/cache',
// do not care about the two autoupgrade dir we use;
'/modules/autoupgrade',
'/admin/autoupgrade',
];
if (!$this->configuration->shouldBackupImages()) {
$backupIgnoreAbsoluteFiles[] = '/img';
} else {
$backupIgnoreAbsoluteFiles[] = '/img/tmp';
}
return $backupIgnoreAbsoluteFiles;
}
/**
* @return string[]
*/
public function getFilesToIgnoreOnRestore(): array
{
$restoreIgnoreAbsoluteFiles = [
'/app/config/parameters.php',
'/app/config/parameters.yml',
'/modules/autoupgrade',
'/admin/autoupgrade',
'.',
'..',
];
if (!$this->configuration->shouldBackupImages()) {
$restoreIgnoreAbsoluteFiles[] = '/img';
} else {
$restoreIgnoreAbsoluteFiles[] = '/img/tmp';
}
return $restoreIgnoreAbsoluteFiles;
}
/**
* @return string[]
*/
public function getFilesToIgnoreOnUpgrade(): array
{
if ($this->excludeAbsoluteFilesFromUpgrade) {
return $this->excludeAbsoluteFilesFromUpgrade;
}
$this->excludeAbsoluteFilesFromUpgrade = [
'/app/config/parameters.php',
'/app/config/parameters.yml',
'/img/c/*.jpg',
'/img/cms/*.jpg',
'/img/l/*.jpg',
'/img/m/*.jpg',
'/img/os/*.jpg',
'/img/p/*.jpg',
'/img/s/*.jpg',
'/img/scenes/*.jpg',
'/img/st/*.jpg',
'/img/su/*.jpg',
'/img/404.gif',
'/img/favicon.ico',
'/img/logo.jpg',
'/img/logo_stores.gif',
'/install',
'/install-dev',
// TODO: Uncomment when a better management of modules upgrades is implemented
// '/modules',
'/override',
'/override/classes',
'/override/controllers',
'/override/modules',
];
// Fetch all existing native modules
$nativeModules = $this->getNativeModules();
if (is_dir($this->rootDir . '/modules')) {
$dir = new DirectoryIterator($this->rootDir . '/modules');
foreach ($dir as $fileinfo) {
if (!$fileinfo->isDir() || $fileinfo->isDot()) {
continue;
}
if (!in_array($fileinfo->getFilename(), $nativeModules)) {
continue;
}
if (!(new SplFileInfo($this->rootDir . '/modules/' . $fileinfo->getFilename() . '/vendor'))->isDir()) {
// If a vendor folder is found in the module, this means it has been upgraded or manually installed
// and can be ignored during the upgrade process
continue;
}
$this->excludeAbsoluteFilesFromUpgrade[] = '/modules/' . $fileinfo->getFilename();
}
}
// If set to false, we need to preserve the default themes
if (!$this->configuration->shouldUpdateDefaultTheme()) {
$this->excludeAbsoluteFilesFromUpgrade[] = '/themes/classic';
}
return $this->excludeAbsoluteFilesFromUpgrade;
}
/**
* These files are checked in every subfolder of the directory tree and can match
* several time, while the others are only matching a file from the project root.
*
* @return string[]
*/
public function getExcludeFiles(): array
{
return [
'.',
'..',
'.svn',
'.git',
$this->autoupgradeDir,
];
}
/**
* Returns an array of native modules
*
* @return string[]
*/
private function getNativeModules(): array
{
$composerFile = $this->rootDir . '/composer.lock';
if (!file_exists($composerFile)) {
return [];
}
// Native modules are the one integrated in PrestaShop release via composer
// so we use the lock files to generate the list
$content = file_get_contents($composerFile);
$content = json_decode($content, true);
if (empty($content['packages'])) {
return [];
}
$modules = array_filter($content['packages'], function (array $package) {
return self::COMPOSER_PACKAGE_TYPE === $package['type'] && !empty($package['name']);
});
$modules = array_map(function (array $package) {
$vendorName = explode('/', $package['name']);
return $vendorName[1];
}, $modules);
return array_merge(
$modules,
self::ADDITIONAL_ALLOWED_MODULES
);
}
}

View File

@@ -0,0 +1,250 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License 3.0 (AFL-3.0)
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* 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 https://devdocs.prestashop.com/ for more information.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
*/
namespace PrestaShop\Module\AutoUpgrade\UpgradeTools;
use FilesystemIterator;
use PrestaShop\Module\AutoUpgrade\Tools14;
use RecursiveCallbackFilterIterator;
use RecursiveDirectoryIterator;
use RecursiveIteratorIterator;
class FilesystemAdapter
{
/**
* @var FileFilter
*/
private $fileFilter;
/**
* @var string
*/
private $autoupgradeDir;
/**
* @var string
*/
private $adminSubDir;
/**
* @var string
*/
private $prodRootDir;
/**
* Somes elements to find in a folder.
* If one of them cannot be found, we can consider that the release is invalid.
*
* @var array<string, array<string>>
*/
private $releaseFileChecks = [
'files' => [
'index.php',
'config/defines.inc.php',
],
'folders' => [
'classes',
'controllers',
],
];
public function __construct(
FileFilter $fileFilter,
string $autoupgradeDir,
string $adminSubDir,
string $prodRootDir
) {
$this->fileFilter = $fileFilter;
$this->autoupgradeDir = $autoupgradeDir;
$this->adminSubDir = $adminSubDir;
$this->prodRootDir = $prodRootDir;
}
/**
* Delete directory and subdirectories.
*
* @param string $dirname Directory name
*/
public static function deleteDirectory(string $dirname, bool $delete_self = true): bool
{
return Tools14::deleteDirectory($dirname, $delete_self);
}
/**
* @param 'upgrade'|'restore'|'backup' $way
*
* @return string[]
*/
public function listFilesInDir(string $dir, string $way, bool $listDirectories = false): array
{
$files = [];
$directory = new RecursiveDirectoryIterator(
$dir,
FilesystemIterator::SKIP_DOTS | FilesystemIterator::KEY_AS_FILENAME | FilesystemIterator::CURRENT_AS_PATHNAME | FilesystemIterator::UNIX_PATHS
);
$filter = new RecursiveCallbackFilterIterator($directory, function ($current, $key, $iterator) use ($way, $dir) {
return !$this->isFileSkipped($key, $current, $way, $dir);
});
$iterator = new \RecursiveIteratorIterator(
$filter,
$listDirectories ? RecursiveIteratorIterator::SELF_FIRST : RecursiveIteratorIterator::LEAVES_ONLY
);
foreach ($iterator as $info) {
$files[] = $info;
}
return $files;
}
/**
* this function list all files that will be remove to retrieve the filesystem states before the upgrade.
*
* @return string[] of files to delete
*/
public function listFilesToRemove(): array
{
// if we can't find the diff file list corresponding to _PS_VERSION_ and prev_version,
// let's assume to remove every files
$toRemove = $this->listFilesInDir($this->prodRootDir, 'restore', true);
// 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#', $this->adminSubDir, $file);
// this is a really sensitive part, so we add an extra checks: preserve everything that contains "autoupgrade"
if ($this->isFileSkipped($filename, $file) || strpos($file, $this->autoupgradeDir)) {
unset($toRemove[$key]);
}
}
return $toRemove;
}
/**
* 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 string[] of files
*/
public function listSampleFiles(string $dir, string $fileext = '.jpg'): array
{
$res = [];
$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)) {
$res[] = $dir . $file;
} elseif (is_dir($dir . $file)) {
$res = array_merge($res, $this->listSampleFiles($dir . $file, $fileext));
}
}
}
}
return $res;
}
/**
* @param string $file : current file or directory name eg:'.svn' , 'settings.inc.php'
* @param string $fullpath : current file or directory fullpath eg:'/home/web/www/prestashop/app/config/parameters.php'
* @param 'upgrade'|'restore'|'backup' $way
* @param string|null $temporaryWorkspace : If needed, another folder than the shop root can be used (used for releases)
*
* @return bool
*/
public function isFileSkipped(string $file, string $fullpath, string $way = 'backup', string $temporaryWorkspace = null): bool
{
$fullpath = str_replace('\\', '/', $fullpath); // wamp compliant
$rootpath = str_replace(
'\\',
'/',
(null !== $temporaryWorkspace) ? $temporaryWorkspace : $this->prodRootDir
);
if (in_array($file, $this->fileFilter->getExcludeFiles())) {
return true;
}
$ignoreList = [];
if ('backup' === $way) {
$ignoreList = $this->fileFilter->getFilesToIgnoreOnBackup();
} elseif ('restore' === $way) {
$ignoreList = $this->fileFilter->getFilesToIgnoreOnRestore();
} elseif ('upgrade' === $way) {
$ignoreList = $this->fileFilter->getFilesToIgnoreOnUpgrade();
}
foreach ($ignoreList as $path) {
$path = str_replace(DIRECTORY_SEPARATOR . 'admin', DIRECTORY_SEPARATOR . $this->adminSubDir, $path);
if (strpos($fullpath, $rootpath . $path) === 0 && /* endsWith */ substr($fullpath, -strlen($rootpath . $path)) === $rootpath . $path) {
return true;
}
if (strpos($path, '*') !== false && fnmatch($rootpath . $path, $fullpath, FNM_PATHNAME)) {
return true;
}
}
// by default, don't skip
return false;
}
/**
* Check a directory has some files available in every release of PrestaShop.
*
* @param string $path Workspace to check
*
* @return bool
*/
public function isReleaseValid(string $path): bool
{
foreach ($this->releaseFileChecks as $type => $elements) {
foreach ($elements as $element) {
$fullPath = $path . DIRECTORY_SEPARATOR . $element;
if ('files' === $type && !is_file($fullPath)) {
return false;
}
if ('folders' === $type && !is_dir($fullPath)) {
return false;
}
}
}
return true;
}
}

View File

@@ -0,0 +1,168 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License 3.0 (AFL-3.0)
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* 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 https://devdocs.prestashop.com/ for more information.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
*/
namespace PrestaShop\Module\AutoUpgrade\UpgradeTools\Module;
use PrestaShop\Module\AutoUpgrade\Exceptions\UpgradeException;
use PrestaShop\Module\AutoUpgrade\UpgradeTools\SymfonyAdapter;
use PrestaShop\Module\AutoUpgrade\UpgradeTools\Translator;
use PrestaShop\PrestaShop\Adapter\Module\Repository\ModuleRepository;
class ModuleAdapter
{
/** @var Translator */
private $translator;
/** @var string */
private $modulesPath;
/**
* @var SymfonyAdapter
*/
private $symfonyAdapter;
/** @var \PrestaShop\PrestaShop\Adapter\Module\ModuleDataUpdater */
private $moduleDataUpdater;
/** @var \PrestaShop\PrestaShop\Core\CommandBus\CommandBusInterface */
private $commandBus;
public function __construct(Translator $translator, string $modulesPath, SymfonyAdapter $symfonyAdapter)
{
$this->translator = $translator;
$this->modulesPath = $modulesPath;
$this->symfonyAdapter = $symfonyAdapter;
}
/**
* Available only from 1.7. Can't be called on PS 1.6.
*
* @return \PrestaShop\PrestaShop\Adapter\Module\ModuleDataUpdater
*/
public function getModuleDataUpdater()
{
if (null === $this->moduleDataUpdater) {
$this->moduleDataUpdater = $this->symfonyAdapter
->initKernel()
->getContainer()
->get('prestashop.core.module.updater');
}
return $this->moduleDataUpdater;
}
/**
* Available only since 1.7.6.0 Can't be called on PS 1.6.
*
* @return \PrestaShop\PrestaShop\Core\CommandBus\CommandBusInterface
*/
public function getCommandBus()
{
if (null === $this->commandBus) {
$this->commandBus = $this->symfonyAdapter
->initKernel()
->getContainer()
->get('prestashop.core.command_bus');
}
return $this->commandBus;
}
/**
* Upgrade action, disabling all modules not made by PrestaShop.
*
* It seems the 1.6 version of is the safest, as it does not actually load the modules.
*
* @param string $pathToUpgradeScripts Path to the PHP Upgrade scripts
*/
public function disableNonNativeModules(string $pathToUpgradeScripts): void
{
require_once $pathToUpgradeScripts . 'php/deactivate_custom_modules.php';
deactivate_custom_modules();
}
public function disableNonNativeModules80(string $pathToUpgradeScripts, ModuleRepository $moduleRepository): void
{
require_once $pathToUpgradeScripts . 'php/deactivate_custom_modules.php';
deactivate_custom_modules80($moduleRepository);
}
/**
* list modules to upgrade and save them in a serialized array in $this->toUpgradeModuleList.
*
* @param array<string, string> $modulesFromAddons Modules available on the marketplace for download
* @param array<string, string> $modulesVersions
*
* @return array<string, array{'id':string, 'name':string}> Module available on the local filesystem and on the marketplace
*
* @throws UpgradeException
*/
public function listModulesToUpgrade(array $modulesFromAddons, array $modulesVersions): array
{
$list = [];
$dir = $this->modulesPath;
if (!is_dir($dir)) {
throw (new UpgradeException($this->translator->trans('[ERROR] %dir% does not exist or is not a directory.', ['%dir%' => $dir])))->addQuickInfo($this->translator->trans('[ERROR] %s does not exist or is not a directory.', [$dir]))->setSeverity(UpgradeException::SEVERITY_ERROR);
}
foreach (scandir($dir) as $module_name) {
// We don't update autoupgrade module
if ($module_name === 'autoupgrade') {
continue;
}
// We have a file modules/mymodule
if (is_file($dir . $module_name)) {
continue;
}
// We don't have a file modules/mymodule/config.xml
if (!is_file($dir . $module_name . DIRECTORY_SEPARATOR . 'config.xml')) {
continue;
}
// We don't have a file modules/mymodule/mymodule.php
if (!is_file($dir . $module_name . DIRECTORY_SEPARATOR . $module_name . '.php')) {
continue;
}
$id_addons = array_search($module_name, $modulesFromAddons);
// We don't find the module on Addons
if (false === $id_addons) {
continue;
}
$configXML = file_get_contents($dir . $module_name . DIRECTORY_SEPARATOR . 'config.xml');
$moduleXML = simplexml_load_string($configXML);
// The module installed has a higher version than this available on Addons
if (version_compare((string) $moduleXML->version, $modulesVersions[$id_addons]) >= 0) {
continue;
}
$list[$module_name] = [
'id' => $id_addons,
'name' => $module_name,
];
}
return $list;
}
}

View File

@@ -0,0 +1,140 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License 3.0 (AFL-3.0)
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* 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 https://devdocs.prestashop.com/ for more information.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
*/
namespace PrestaShop\Module\AutoUpgrade\UpgradeTools\Module;
use PrestaShop\Module\AutoUpgrade\Exceptions\UpgradeException;
use PrestaShop\Module\AutoUpgrade\Log\Logger;
use PrestaShop\Module\AutoUpgrade\Tools14;
use PrestaShop\Module\AutoUpgrade\UpgradeTools\Translator;
use Symfony\Component\Filesystem\Exception\IOException;
use Symfony\Component\Filesystem\Filesystem;
class ModuleDownloader
{
/** @var Translator */
private $translator;
/** @var Logger */
private $logger;
/** @var string */
private $psVersion;
/** @var string */
private $addonsUrl = 'api.addons.prestashop.com';
public function __construct(Translator $translator, Logger $logger, string $psVersion)
{
$this->translator = $translator;
$this->logger = $logger;
$this->psVersion = $psVersion;
}
/**
* @throws UpgradeException
*/
public function downloadModule(ModuleDownloaderContext $moduleDownloaderContext): void
{
$localModuleUsed = false;
if ($moduleDownloaderContext->getModuleIsLocal()) {
$localModuleUsed = $this->downloadModuleFromLocalZip($moduleDownloaderContext);
}
if (!$localModuleUsed) {
$this->downloadModuleFromAddons($moduleDownloaderContext);
}
if (filesize($moduleDownloaderContext->getZipFullPath()) <= 300) {
throw (new UpgradeException($this->translator->trans('[WARNING] An error occurred while downloading module %s, the received file is empty.', [$moduleDownloaderContext->getModuleName()])))->setSeverity(UpgradeException::SEVERITY_WARNING);
}
}
private function downloadModuleFromLocalZip(ModuleDownloaderContext $moduleDownloaderContext): bool
{
try {
$localModuleZip = $this->getLocalModuleZipPath($moduleDownloaderContext->getModuleName());
if (empty($localModuleZip)) {
return false;
}
$filesystem = new Filesystem();
$filesystem->copy($localModuleZip, $moduleDownloaderContext->getZipFullPath());
unlink($localModuleZip);
$this->logger->notice($this->translator->trans('Local module %s successfully copied.', [$moduleDownloaderContext->getModuleName()]));
return true;
} catch (IOException $e) {
$this->logger->notice($this->translator->trans('Can not found or copy local module %s. Trying to download it from Addons.', [$moduleDownloaderContext->getModuleName()]));
}
return false;
}
/**
* @throws UpgradeException
*/
private function downloadModuleFromAddons(ModuleDownloaderContext $moduleDownloaderContext): void
{
$addonsUrl = extension_loaded('openssl')
? 'https://' . $this->addonsUrl
: 'http://' . $this->addonsUrl;
// Make the request
$context = stream_context_create([
'http' => [
'method' => 'POST',
'content' => 'version=' . $this->psVersion . '&method=module&id_module=' . $moduleDownloaderContext->getModuleId(),
'header' => 'Content-type: application/x-www-form-urlencoded',
'timeout' => 10,
],
]);
// file_get_contents can return false if https is not supported (or warning)
$content = Tools14::file_get_contents($addonsUrl, false, $context);
if (empty($content) || substr($content, 5) == '<?xml') {
throw (new UpgradeException($this->translator->trans('[WARNING] No response from Addons server.')))->setSeverity(UpgradeException::SEVERITY_WARNING);
}
if (false === (bool) file_put_contents($moduleDownloaderContext->getZipFullPath(), $content)) {
throw (new UpgradeException($this->translator->trans('[WARNING] Unable to write module %s\'s zip file in temporary directory.', [$moduleDownloaderContext->getModuleName()])))->setSeverity(UpgradeException::SEVERITY_WARNING);
}
$this->logger->notice($this->translator->trans('Module %s has been successfully downloaded from Addons.', [$moduleDownloaderContext->getModuleName()]));
}
private function getLocalModuleZipPath(string $name): ?string
{
$autoUpgradeDir = _PS_ADMIN_DIR_ . DIRECTORY_SEPARATOR . 'autoupgrade';
$module_zip = $autoUpgradeDir . DIRECTORY_SEPARATOR . 'modules' . DIRECTORY_SEPARATOR . $name . '.zip';
if (file_exists($module_zip) && is_readable($module_zip)) {
return $module_zip;
}
return null;
}
}

View File

@@ -0,0 +1,92 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License 3.0 (AFL-3.0)
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* 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 https://devdocs.prestashop.com/ for more information.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
*/
namespace PrestaShop\Module\AutoUpgrade\UpgradeTools\Module;
use LogicException;
class ModuleDownloaderContext
{
/** @var string */
private $zipFullPath;
/** @var string */
private $moduleName;
/** @var int */
private $moduleId;
/** @var bool */
private $moduleIsLocal = false;
/**
* @param array{id:string, name:string, is_local:true|null} $moduleInfos
*/
public function __construct(string $zipFullPath, array $moduleInfos)
{
$this->zipFullPath = $zipFullPath;
$this->moduleName = $moduleInfos['name'];
$this->moduleId = (int) $moduleInfos['id'];
$this->moduleIsLocal = $moduleInfos['is_local'] ?? false;
$this->validate();
}
/**
* @throws LogicException
*/
private function validate(): void
{
if (empty($this->zipFullPath)) {
throw new LogicException('Path to zip file is invalid.');
}
if (empty($this->moduleName)) {
throw new LogicException('Module name is invalid.');
}
if (empty($this->moduleId)) {
throw new LogicException('Module ID is invalid.');
}
}
public function getZipFullPath(): string
{
return $this->zipFullPath;
}
public function getModuleName(): string
{
return $this->moduleName;
}
public function getModuleId(): int
{
return $this->moduleId;
}
public function getModuleIsLocal(): bool
{
return $this->moduleIsLocal;
}
}

View File

@@ -0,0 +1,157 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License 3.0 (AFL-3.0)
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* 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 https://devdocs.prestashop.com/ for more information.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
*/
namespace PrestaShop\Module\AutoUpgrade\UpgradeTools\Module;
use LogicException;
use PrestaShop\Module\AutoUpgrade\Exceptions\UpgradeException;
use PrestaShop\Module\AutoUpgrade\Log\Logger;
use PrestaShop\Module\AutoUpgrade\UpgradeTools\Translator;
use Throwable;
class ModuleMigration
{
/** @var Translator */
private $translator;
/** @var Logger */
private $logger;
public function __construct(Translator $translator, Logger $logger)
{
$this->translator = $translator;
$this->logger = $logger;
}
public function needMigration(ModuleMigrationContext $moduleMigrationContext): bool
{
if (version_compare($moduleMigrationContext->getLocalVersion(), $moduleMigrationContext->getDbVersion(), '>')) {
if (empty($moduleMigrationContext->getMigrationFiles())) {
$migrationFiles = $this->listUpgradeFiles($moduleMigrationContext);
$moduleMigrationContext->setMigrationFiles($migrationFiles);
}
return !empty($moduleMigrationContext->getMigrationFiles());
}
return false;
}
/**
* @return string[]
*/
public function listUpgradeFiles(ModuleMigrationContext $moduleMigrationContext): array
{
if ($moduleMigrationContext->getDbVersion() === '0') {
$this->logger->notice($this->translator->trans('No version present in database for module %s, all files for upgrade will be applied.', [$moduleMigrationContext->getModuleName()]));
}
$files = glob($moduleMigrationContext->getUpgradeFilesRootPath() . '/*.php', GLOB_BRACE);
$upgradeFiles = [];
foreach ($files as $file) {
if (preg_match('/upgrade-(\d+(?:\.\d+){0,2}).php$/', basename($file), $matches)) {
$fileVersion = $matches[1];
if (version_compare($fileVersion, $moduleMigrationContext->getDbVersion(), '>') && version_compare($fileVersion, $moduleMigrationContext->getLocalVersion(), '<=')) {
$upgradeFiles[] = ['file' => $file, 'version' => $fileVersion];
}
}
}
usort($upgradeFiles, function ($a, $b) {
return version_compare($a['version'], $b['version']);
});
return array_column($upgradeFiles, 'file');
}
/**
* @throws LogicException
* @throws UpgradeException
*/
public function runMigration(ModuleMigrationContext $moduleMigrationContext): void
{
if ($moduleMigrationContext->getMigrationFiles() === null) {
throw (new LogicException('Module upgrade files are empty, please run needMigration() first.'));
}
foreach ($moduleMigrationContext->getMigrationFiles() as $index => $migrationFilePath) {
$this->logger->notice($this->translator->trans('(%s/%s) Applying migration file %s.', [($index + 1), count($moduleMigrationContext->getMigrationFiles()), basename($migrationFilePath)]));
$methodName = $this->getUpgradeMethodName($migrationFilePath);
// check if function already exists to prevent upgrade crash
if (function_exists($methodName)) {
$moduleMigrationContext->getModuleInstance()->disable();
throw (new UpgradeException($this->translator->trans('[WARNING] Method %s already exists. Migration for module %s aborted, you can try again later on the module manager. Module %s disabled.', [$methodName, $moduleMigrationContext->getModuleName(), $moduleMigrationContext->getModuleName()])))->setSeverity(UpgradeException::SEVERITY_WARNING);
}
include $migrationFilePath;
// @phpstan-ignore booleanNot.alwaysTrue (we ignore this error because we load a file with methods)
if (!function_exists($methodName)) {
$moduleMigrationContext->getModuleInstance()->disable();
throw (new UpgradeException($this->translator->trans('[WARNING] Method %s does not exist. Module %s disabled.', [$methodName, $moduleMigrationContext->getModuleName()])))->setSeverity(UpgradeException::SEVERITY_WARNING);
}
// @phpstan-ignore deadCode.unreachable (we ignore this error because the previous if can be true or false)
try {
if (!$methodName($moduleMigrationContext->getModuleInstance())) {
throw (new UpgradeException($this->translator->trans('[WARNING] Migration failed while running the file %s. Module %s disabled.', [basename($migrationFilePath), $moduleMigrationContext->getModuleName()])))->setSeverity(UpgradeException::SEVERITY_WARNING);
}
} catch (UpgradeException $e) {
$moduleMigrationContext->getModuleInstance()->disable();
throw $e;
} catch (Throwable $t) {
$moduleMigrationContext->getModuleInstance()->disable();
throw (new UpgradeException($this->translator->trans('[WARNING] Unexpected error when trying to upgrade module %s. Module %s disabled.', [$moduleMigrationContext->getModuleName(), $moduleMigrationContext->getModuleName()]), 0, $t))->setSeverity(UpgradeException::SEVERITY_WARNING);
}
}
}
/**
* @throws UpgradeException
*/
public function saveVersionInDb(ModuleMigrationContext $moduleMigrationContext): void
{
if (!\Module::upgradeModuleVersion($moduleMigrationContext->getModuleName(), $moduleMigrationContext->getLocalVersion())) {
throw (new UpgradeException($this->translator->trans('[WARNING] Module %s version could not be updated. Database might be unavailable.', [$moduleMigrationContext->getModuleName()]), 0))->setSeverity(UpgradeException::SEVERITY_WARNING);
}
}
private function getUpgradeMethodName(string $filePath): string
{
$fileName = basename($filePath);
preg_match('/upgrade-([\d.]+)\.php$/', $fileName, $matches);
$version = str_replace('.', '_', $matches[1]);
return 'upgrade_module_' . $version;
}
}

View File

@@ -0,0 +1,102 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License 3.0 (AFL-3.0)
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* 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 https://devdocs.prestashop.com/ for more information.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
*/
namespace PrestaShop\Module\AutoUpgrade\UpgradeTools\Module;
class ModuleMigrationContext
{
/** @var \Module */
private $moduleInstance;
/** @var string */
private $moduleName;
/** @var string */
private $dbVersion;
/** @var string */
private $upgradeFilesRootPath;
/** @var string */
private $localVersion;
/** @var string[]|null */
private $migrationFiles;
public function __construct(\Module $moduleInstance, ?string $dbVersion)
{
$this->moduleInstance = $moduleInstance;
$moduleName = $moduleInstance->name;
$this->moduleName = $moduleName;
$this->upgradeFilesRootPath = _PS_MODULE_DIR_ . $moduleName . DIRECTORY_SEPARATOR . 'upgrade';
$this->localVersion = $this->moduleInstance->version;
$this->dbVersion = $dbVersion ?? '0';
}
public function getModuleInstance(): \Module
{
return $this->moduleInstance;
}
public function getModuleName(): string
{
return $this->moduleName;
}
public function getUpgradeFilesRootPath(): string
{
return $this->upgradeFilesRootPath;
}
public function getLocalVersion(): string
{
return $this->localVersion;
}
public function getDbVersion(): string
{
return $this->dbVersion;
}
/**
* @param string[] $migrationFiles
*/
public function setMigrationFiles(array $migrationFiles): void
{
$this->migrationFiles = $migrationFiles;
}
/**
* @return string[]|null
*/
public function getMigrationFiles(): ?array
{
return $this->migrationFiles;
}
}

View File

@@ -0,0 +1,61 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License 3.0 (AFL-3.0)
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* 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 https://devdocs.prestashop.com/ for more information.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
*/
namespace PrestaShop\Module\AutoUpgrade\UpgradeTools\Module;
use LogicException;
use PrestaShop\Module\AutoUpgrade\Exceptions\UpgradeException;
use PrestaShop\Module\AutoUpgrade\UpgradeTools\Translator;
use PrestaShop\Module\AutoUpgrade\ZipAction;
class ModuleUnzipper
{
/** @var Translator */
private $translator;
/** @var ZipAction */
private $zipAction;
/** @var string */
private $modulesPath;
public function __construct(Translator $translator, ZipAction $zipAction, string $modulesPath)
{
$this->translator = $translator;
$this->zipAction = $zipAction;
$this->modulesPath = $modulesPath;
}
/**
* @throws LogicException|UpgradeException
*/
public function unzipModule(ModuleUnzipperContext $moduleUnzipperContext): void
{
if (!$this->zipAction->extract($moduleUnzipperContext->getZipFullPath(), $this->modulesPath)) {
throw (new UpgradeException($this->translator->trans('[WARNING] Error when trying to extract module %s.', [$moduleUnzipperContext->getModuleName()])))->setSeverity(UpgradeException::SEVERITY_WARNING);
}
}
}

View File

@@ -0,0 +1,68 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License 3.0 (AFL-3.0)
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* 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 https://devdocs.prestashop.com/ for more information.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
*/
namespace PrestaShop\Module\AutoUpgrade\UpgradeTools\Module;
use LogicException;
class ModuleUnzipperContext
{
/** @var string */
private $zipFullPath;
/** @var string */
private $moduleName;
public function __construct(string $zipFullPath, string $moduleName)
{
$this->zipFullPath = $zipFullPath;
$this->moduleName = $moduleName;
$this->validate();
}
/**
* @throws LogicException
*/
private function validate(): void
{
if (empty($this->zipFullPath)) {
throw new LogicException('Path to zip file is invalid.');
}
if (empty($this->moduleName)) {
throw new LogicException('Module name is invalid.');
}
}
public function getZipFullPath(): string
{
return $this->zipFullPath;
}
public function getModuleName(): string
{
return $this->moduleName;
}
}

View File

@@ -0,0 +1,45 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License 3.0 (AFL-3.0)
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* 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 https://devdocs.prestashop.com/ for more information.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
*/
namespace PrestaShop\Module\AutoUpgrade\UpgradeTools\Module;
class ModuleVersionAdapter
{
public static function get(string $name): ?string
{
return \Db::getInstance()->getValue(
'SELECT version FROM `' . _DB_PREFIX_ . 'module` WHERE name = "' . pSQL($name) . '"'
);
}
public static function update(string $name, string $version): bool
{
return \Db::getInstance()->execute('
UPDATE `' . _DB_PREFIX_ . 'module` m
SET m.version = \'' . pSQL($version) . '\'
WHERE m.name = \'' . pSQL($name) . '\'');
}
}

View File

@@ -0,0 +1,82 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License 3.0 (AFL-3.0)
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* 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 https://devdocs.prestashop.com/ for more information.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
*/
namespace PrestaShop\Module\AutoUpgrade\UpgradeTools;
use ReflectionClass;
/**
* TODO: Create a class for 1.7 env and another one for 1.6 ?
*/
class SymfonyAdapter
{
public function isKernelReachable(): bool
{
return defined('_PS_ROOT_DIR_') && class_exists('AppKernel', true);
}
/**
* Return the appropriate kernel if abstract or not.
*
* @return \AppKernel|\AdminKernel
*/
public function initKernel()
{
global $kernel;
if (!$kernel instanceof \AppKernel) {
// Only necessary one version before 1.7.3 because he is not classmaped on composer
require_once _PS_ROOT_DIR_ . '/app/AppKernel.php';
$env = (true == _PS_MODE_DEV_) ? 'dev' : 'prod';
// From version 9 the AppKernel becomes an abstract so we need to check if it is one to know which Kernel to use
if ($this->isAppKernelAbstract()) {
$kernelClass = 'AdminKernel';
} else {
$kernelClass = 'AppKernel';
}
$kernel = new $kernelClass($env, _PS_MODE_DEV_);
if (method_exists($kernel, 'loadClassCache')) { // This method has been deleted in Symfony 4.x
$kernel->loadClassCache();
}
$kernel->boot();
}
return $kernel;
}
/**
* Check if AppKernel is abstract or not.
*/
private function isAppKernelAbstract(): bool
{
$appKernelClass = new ReflectionClass(\AppKernel::class);
return $appKernelClass->isAbstract();
}
}

View File

@@ -0,0 +1,106 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License 3.0 (AFL-3.0)
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* 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 https://devdocs.prestashop.com/ for more information.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
*/
namespace PrestaShop\Module\AutoUpgrade\UpgradeTools;
use PrestaShop\Module\AutoUpgrade\Task\AbstractTask;
use PrestaShop\Module\AutoUpgrade\Task\Miscellaneous\CheckFilesVersion;
use PrestaShop\Module\AutoUpgrade\Task\Miscellaneous\CompareReleases;
use PrestaShop\Module\AutoUpgrade\Task\Miscellaneous\GetChannelInfo;
use PrestaShop\Module\AutoUpgrade\Task\Miscellaneous\UpdateConfig;
use PrestaShop\Module\AutoUpgrade\Task\NullTask;
use PrestaShop\Module\AutoUpgrade\Task\Rollback\NoRollbackFound;
use PrestaShop\Module\AutoUpgrade\Task\Rollback\RestoreDb;
use PrestaShop\Module\AutoUpgrade\Task\Rollback\RestoreFiles;
use PrestaShop\Module\AutoUpgrade\Task\Rollback\Rollback;
use PrestaShop\Module\AutoUpgrade\Task\Rollback\RollbackComplete;
use PrestaShop\Module\AutoUpgrade\Task\Upgrade\BackupDb;
use PrestaShop\Module\AutoUpgrade\Task\Upgrade\BackupFiles;
use PrestaShop\Module\AutoUpgrade\Task\Upgrade\CleanDatabase;
use PrestaShop\Module\AutoUpgrade\Task\Upgrade\Download;
use PrestaShop\Module\AutoUpgrade\Task\Upgrade\Unzip;
use PrestaShop\Module\AutoUpgrade\Task\Upgrade\UpgradeComplete;
use PrestaShop\Module\AutoUpgrade\Task\Upgrade\UpgradeDb;
use PrestaShop\Module\AutoUpgrade\Task\Upgrade\UpgradeFiles;
use PrestaShop\Module\AutoUpgrade\Task\Upgrade\UpgradeModules;
use PrestaShop\Module\AutoUpgrade\Task\Upgrade\UpgradeNow;
use PrestaShop\Module\AutoUpgrade\UpgradeContainer;
class TaskRepository
{
public static function get(string $step, UpgradeContainer $container): AbstractTask
{
switch ($step) {
// MISCELLANEOUS (upgrade configuration, checks etc.)
case 'checkFilesVersion':
return new CheckFilesVersion($container);
case 'compareReleases':
return new CompareReleases($container);
case 'getChannelInfo':
return new GetChannelInfo($container);
case 'updateConfig':
return new UpdateConfig($container);
// ROLLBACK
case 'noRollbackFound':
return new NoRollbackFound($container);
case 'restoreDb':
return new RestoreDb($container);
case 'restoreFiles':
return new RestoreFiles($container);
case 'rollback':
return new Rollback($container);
case 'rollbackComplete':
return new RollbackComplete($container);
// UPGRADE
case 'backupDb':
return new BackupDb($container);
case 'backupFiles':
return new BackupFiles($container);
case 'cleanDatabase':
return new CleanDatabase($container);
case 'download':
return new Download($container);
case 'upgradeComplete':
return new UpgradeComplete($container);
case 'upgradeDb':
return new UpgradeDb($container);
case 'upgradeFiles':
return new UpgradeFiles($container);
case 'upgradeModules':
return new UpgradeModules($container);
case 'upgradeNow':
return new UpgradeNow($container);
case 'unzip':
return new Unzip($container);
}
error_log('Unknown step ' . $step);
return new NullTask($container);
}
}

View File

@@ -0,0 +1,107 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License 3.0 (AFL-3.0)
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* 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 https://devdocs.prestashop.com/ for more information.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
*/
namespace PrestaShop\Module\AutoUpgrade\UpgradeTools;
use Db;
class ThemeAdapter
{
/** @var Db */
private $db;
public function __construct(Db $db)
{
$this->db = $db;
}
/**
* Get the default theme name provided with PrestaShop.
*
* @return string
*/
public function getDefaultTheme(): string
{
return 'classic';
}
/**
* Get the list of theme name.
*
* @return array{array{'name':string, 'directory':string}}
*/
public function getListFromDisk(): array
{
$suffix = 'config/theme.yml';
$themeDirectories = glob(_PS_ALL_THEMES_DIR_ . '*/' . $suffix, GLOB_NOSORT);
$themes = [];
foreach ($themeDirectories as $directory) {
$themes[] = [
'name' => basename(substr($directory, 0, -strlen($suffix))),
'directory' => substr($directory, 0, -strlen($suffix)),
];
}
return $themes;
}
/**
* Use theme manager is order to enable the new theme.
*
* @param string $themeName
*
* @return bool|string True on success, string with errors on failure
*/
public function enableTheme(string $themeName)
{
// Load up core theme manager
$themeManager = $this->getThemeManager();
// Enable the theme
$isThemeEnabled = $themeManager->enable($themeName);
if (!$isThemeEnabled) {
// Something went wrong... let's check if we have some more info
$errors = $themeManager->getErrors($themeName);
if (is_array($errors) && !empty($errors)) {
return implode(',', $errors);
}
return 'Unknown error';
}
return true;
}
/**
* @return \PrestaShop\PrestaShop\Core\Addon\Theme\ThemeManager
*/
private function getThemeManager()
{
return (new \PrestaShop\PrestaShop\Core\Addon\Theme\ThemeManagerBuilder(\Context::getContext(), $this->db))->build();
}
}

View File

@@ -0,0 +1,206 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License 3.0 (AFL-3.0)
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* 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 https://devdocs.prestashop.com/ for more information.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
*/
namespace PrestaShop\Module\AutoUpgrade\UpgradeTools;
use PrestaShop\Module\AutoUpgrade\Log\LoggerInterface;
use PrestaShop\Module\AutoUpgrade\Tools14;
class Translation
{
/** @var string[] */
private $installedLanguagesIso;
/** @var LoggerInterface */
private $logger;
/** @var Translator */
private $translator;
/**
* @param string[] $installedLanguagesIso
*/
public function __construct(Translator $translator, LoggerInterface $logger, array $installedLanguagesIso)
{
$this->logger = $logger;
$this->translator = $translator;
$this->installedLanguagesIso = $installedLanguagesIso;
}
/**
* @return string|false type of translation item
*/
public function getTranslationFileType(string $file)
{
$type = false;
// line shorter
$separator = addslashes(DIRECTORY_SEPARATOR);
$translation_dir = $separator . 'translations' . $separator;
$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;
}
public function isTranslationFile(string $file): bool
{
return $this->getTranslationFileType($file) !== 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, back office, front office, field, pdf, error)
*
* @return bool
*/
public function mergeTranslationFile(string $orig, string $dest, string $type): bool
{
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';
break;
case 'pdf':
$var_name = '_LANGPDF';
break;
case 'mail':
$var_name = '_LANGMAIL';
break;
default:
return false;
}
if (!file_exists($orig)) {
$this->logger->notice($this->translator->trans('[NOTICE] File %s does not exist, merge skipped.', [$orig]));
return true;
}
include $orig;
if (!isset($$var_name)) {
$this->logger->warning($this->translator->trans(
'[WARNING] %variablename% variable missing in file %filename%. Merge skipped.',
[
'%variablename%' => $var_name,
'%filename%' => $orig,
]
));
return true;
}
$var_orig = $$var_name;
if (!file_exists($dest)) {
$this->logger->notice($this->translator->trans('[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) {
unlink($dest);
}
$this->logger->warning($this->translator->trans(
'[WARNING] %variablename% variable missing in file %filename%. File %filename% deleted and merge skipped.',
[
'%variablename%' => $var_name,
'%filename%' => $dest,
]
));
return false;
}
$var_dest = $$var_name;
$merge = array_merge($var_orig, $var_dest);
$fd = fopen($dest, 'w');
if ($fd === false) {
return false;
}
fwrite($fd, "<?php\n\nglobal \$" . $var_name . ";\n\$" . $var_name . " = array();\n");
foreach ($merge as $k => $v) {
if ('mail' == $type) {
fwrite($fd, '$' . $var_name . '[\'' . $this->escape($k) . '\'] = \'' . $this->escape($v) . '\';' . "\n");
} else {
fwrite($fd, '$' . $var_name . '[\'' . $this->escape($k, true) . '\'] = \'' . $this->escape($v, true) . '\';' . "\n");
}
}
fwrite($fd, "\n?>");
fclose($fd);
return true;
}
/**
* Escapes illegal characters in a string.
* Extracted from DB class, in order to avoid dependancies.
*
* @param string $str
* @param bool $html_ok Does data contain HTML code ? (optional)
*
* @see DbCore::_escape()
*/
private function escape(string $str, bool $html_ok = false): string
{
$search = ['\\', "\0", "\n", "\r", "\x1a", "'", '"'];
$replace = ['\\\\', '\\0', '\\n', '\\r', "\Z", "\'", '\"'];
$str = str_replace($search, $replace, $str);
if (!$html_ok) {
return strip_tags(Tools14::nl2br($str));
}
return $str;
}
}

View File

@@ -0,0 +1,123 @@
<?php
namespace PrestaShop\Module\AutoUpgrade\UpgradeTools;
use SimpleXMLElement;
class Translator
{
const DEFAULT_LANGUAGE = 'en';
/**
* @var array<string,string>
*/
private $translations = [];
/** @var string */
private $locale;
/** @var string */
private $translationsFilesPath;
/**
* @param string $translationsFilesPath
* @param string $locale
*/
public function __construct($translationsFilesPath, $locale = self::DEFAULT_LANGUAGE)
{
$this->locale = $locale;
$this->translationsFilesPath = $translationsFilesPath;
}
/**
* Load translations from XLF files.
*
* @return void
*
* @throws \Exception
*/
private function loadTranslations()
{
// use generic language file (e.g., fr)
$path = $this->translationsFilesPath . "ModulesAutoupgradeAdmin.{$this->locale}.xlf";
if (file_exists($path)) {
$this->loadXlfFile($path);
}
}
/**
* Load translations from a specific XLF file.
*
* @param string $filePath path to the XLF file
*
* @return void
*
* @throws \Exception
*/
private function loadXlfFile($filePath)
{
$xml = new SimpleXMLElement(file_get_contents($filePath));
foreach ($xml->file as $file) {
foreach ($file->body->{'trans-unit'} as $unit) {
$this->translations[(string) $unit->source] = (string) $unit->target;
}
}
}
/**
* Translate a string to the current language.
*
* @param string $id
* @param array<int|string, mixed> $parameters
* @param string|null $domain
* @param string|null $locale
*
* @return string Translated string with parameters applied
*/
public function trans($id, array $parameters = [], $domain = null, $locale = null)
{
if (empty($this->translations)) {
try {
$this->loadTranslations();
} catch (\Exception $e) {
return $id;
}
}
$translated = isset($this->translations[$id]) ? $this->translations[$id] : $id;
return $this->applyParameters($translated, $parameters);
}
/**
* @param string $id
* @param array<int|string, string> $parameters
*
* @return string Translated string with parameters applied
*
* @internal Public for tests
*/
public function applyParameters($id, array $parameters = [])
{
// Replace placeholders for non-numeric keys
foreach ($parameters as $placeholder => $value) {
if (is_int($placeholder)) {
continue;
}
$id = str_replace($placeholder, $value, $id);
unset($parameters[$placeholder]);
}
if (!count($parameters)) {
return $id;
}
return call_user_func_array('sprintf', array_merge([$id], $parameters));
}
/**
* @return string
*/
public function getLocale()
{
return $this->locale;
}
}

View File

@@ -0,0 +1,34 @@
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License 3.0 (AFL-3.0)
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* 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 https://devdocs.prestashop.com/ for more information.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
*/
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Cache-Control: post-check=0, pre-check=0', false);
header('Pragma: no-cache');
header('Location: ../');
exit;