* @copyright Since 2007 PrestaShop SA and Contributors * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) */ use PrestaShopBundle\Install\LanguageList; class InstallControllerHttp { /** * @var StepList List of installer steps */ protected static $steps; /** * @var string */ protected $content; /** * @var array */ protected static $instances = []; /** * @var string Current step */ public $step; /** * @var array List of errors */ public $errors = []; public $controller; /** * @var InstallSession */ public $session; /** * LanguageList */ public $language; /** * @var \Symfony\Component\Translation\Translator */ public $translator; /** * @var bool If false, disable next button access */ public $next_button = true; /** * @var bool If false, disable previous button access */ public $previous_button = true; /** * @var \PrestaShopBundle\Install\AbstractInstall */ public $model; /** * @var array Magic vars */ protected $__vars = []; /** * @var array Configuration */ protected static $config; private function initSteps() { $stepConfig = [ [ 'name' => 'welcome', 'displayName' => $this->translator->trans('Choose your language', [], 'Install'), 'controllerClass' => 'InstallControllerHttpWelcome', ], [ 'name' => 'license', 'displayName' => $this->translator->trans('License agreements', [], 'Install'), 'controllerClass' => 'InstallControllerHttpLicense', ], [ 'name' => 'system', 'displayName' => $this->translator->trans('System compatibility', [], 'Install'), 'controllerClass' => 'InstallControllerHttpSystem', ], [ 'name' => 'configure', 'displayName' => $this->translator->trans('Store information', [], 'Install'), 'controllerClass' => 'InstallControllerHttpConfigure', ], [ 'name' => 'content', 'displayName' => $this->translator->trans('Content of your store', [], 'Install'), 'controllerClass' => 'InstallControllerHttpContent', ], [ 'name' => 'database', 'displayName' => $this->translator->trans('System configuration', [], 'Install'), 'controllerClass' => 'InstallControllerHttpDatabase', ], [ 'name' => 'process', 'displayName' => $this->translator->trans('Store installation', [], 'Install'), 'controllerClass' => 'InstallControllerHttpProcess', ], ]; static::$steps = new StepList($stepConfig); } public function __construct() { $this->session = InstallSession::getInstance(); // Set current language $this->language = LanguageList::getInstance(); $detect_language = $this->language->detectLanguage(); if (empty($this->session->lang)) { $this->session->lang = $detect_language['primarytag']; } Context::getContext()->language = $this->language->getLanguage( $this->session->lang ?: false ); $this->translator = Context::getContext()->getTranslator(true); if (isset($this->session->lang)) { $lang = $this->session->lang; } else { $lang = (isset($detect_language['primarytag'])) ? $detect_language['primarytag'] : false; } if (!in_array($lang, $this->language->getIsoList())) { $lang = 'en'; } $this->language->setLanguage($lang); if (empty(self::getSteps())) { $this->initSteps(); } $this->loadConfiguration(); $this->init(); } public function setCurrentStep($step) { $this->step = $step; return $this; } final public static function execute() { $self = new static(); if (Tools::getValue('compile_templates')) { require_once _PS_INSTALL_CONTROLLERS_PATH_ . 'http/smarty_compile.php'; exit; } $session = InstallSession::getInstance(); if (!$session->last_step || $session->last_step === 'welcome') { \PrestaShop\Autoload\PrestashopAutoload::getInstance()->generateIndex(); } if (empty($session->last_step)) { $session->last_step = self::getSteps()->current()->getName(); } // Set timezone if ($session->shop_timezone) { try { // Search if the session timezone is known in the present zones list. // An unknown timezone can be stored here, in case of upgrade of PHP version (change in many timezones, as disappeared US/Eastern for example). foreach (DateTimeZone::listAbbreviations() as $abbreviations) { foreach ($abbreviations as $abbreviation) { if ($session->shop_timezone == $abbreviation['timezone_id']) { @date_default_timezone_set($session->shop_timezone); break 2; } } } // If not know, does not affect PHP settings. Another default setting is forced before. } catch (\Exception $e) { // for older behavior, keep old way to affect timezone. @date_default_timezone_set($session->shop_timezone); } } // Get current step (check first if step is changed, then take it from session) if (Tools::getValue('step')) { self::getSteps()->setOffsetFromStepName(Tools::getValue('step')); $session->step = self::getSteps()->current()->getName(); } elseif (!empty($session->step)) { self::getSteps()->setOffsetFromStepName($session->step); } // Validate all steps until current step. If a step is not valid, use it as current step. foreach (self::getSteps() as $key => $check_step) { // Do not validate current step if (self::getSteps()->current() == $check_step) { break; } // no need to validate several time the system step if (!(($check_step->getControllerInstance()) instanceof InstallControllerHttpSystem) && !$check_step->getControllerInstance()->validate()) { self::getSteps()->setOffset($key); $session->step = $session->last_step = self::getSteps()->current()->getName(); break; } } // Submit form to go to next step if (Tools::getValue('submitNext')) { self::getSteps()->current()->getControllerInstance()->processNextStep(); // If current step is validated, let's go to next step if (self::getSteps()->current()->getControllerInstance()->validate()) { self::getSteps()->next(); } // Don't display system step if mandatory requirements is valid if (self::getSteps()->current()->getName() == 'system' && self::getSteps()->current()->getControllerInstance()->validate()) { self::getSteps()->next(); } $session->step = self::getSteps()->current()->getName(); // Change last step if (self::getSteps()->getOffset() > $self->getStepOffset($session->last_step)) { $session->last_step = self::getSteps()->current()->getName(); } } // Go to previous step elseif (Tools::getValue('submitPrevious') && 0 !== self::getSteps()->getOffset()) { self::getSteps()->previous(); $session->step = self::getSteps()->current()->getName(); } self::getSteps()->current()->getControllerInstance()->process(); self::getSteps()->current()->getControllerInstance()->display(); } /** * Display controller view */ public function display(): void { } /** * Validate current step */ public function validate(): bool { return true; } /** * Initialize */ public function init(): void { } /** * Process installation */ public function process(): void { } /** * Process next step */ public function processNextStep(): void { } /** * Get steps list * * @return StepList */ public static function getSteps(): ?StepList { return static::$steps; } public function getLastStep() { return $this->session->last_step; } /** * Find offset of a step by name * * @param string $step Step name * * @return int */ public function getStepOffset($step) { return self::getSteps()->getOffsetFromStepName($step); } /** * Make a HTTP redirection to a step * * @param string $step */ public function redirect(string $step) { header('location: index.php?step=' . $step); exit; } /** * Check if current step is first step in list of steps * * @return bool */ public function isFirstStep() { return self::getSteps()->isFirstStep(); } /** * Check if current step is last step in list of steps * * @return bool */ public function isLastStep() { return self::getSteps()->isLastStep(); } /** * Check is given step is already finished * * @param string $step * * @return bool */ public function isStepFinished(string $step): bool { return $this->getStepOffset($step) < self::getSteps()->getOffset(); } /** * Send AJAX response in JSON format {success: bool, message: string} * * @param bool $success * @param string $message */ public function ajaxJsonAnswer(bool $success, $message = ''): void { if (!$success && empty($message)) { $message = print_r(@error_get_last(), true); } die(json_encode([ 'success' => (bool) $success, 'message' => $message, ])); } /** * Display a template * * @param string $template Template name */ public function getTemplate(string $template): string { $path = _PS_INSTALL_PATH_ . 'theme/views/'; $customPath = _PS_INSTALL_PATH_ . 'theme/custom/'; if (file_exists($customPath . $template . '.php')) { return $this->renderTemplate($customPath, $template); } if (file_exists($path . $template . '.php')) { return $this->renderTemplate($path, $template); } throw new PrestashopInstallerException("Template '{$template}.php' not found"); } /** * Display a hook template * * @param string $template Template name */ public function getHook(string $template): string { $path = _PS_INSTALL_PATH_ . 'theme/custom/hooks/'; if (file_exists($path . $template . '.php')) { return $this->renderTemplate($path, $template); } return ''; } protected function loadConfiguration(): void { if (self::$config === null) { $path = _PS_INSTALL_PATH_ . 'theme/config.php'; $customPath = _PS_INSTALL_PATH_ . 'theme/custom/config.php'; if (file_exists($customPath)) { self::$config = include $customPath; return; } if (file_exists($path)) { self::$config = include $path; return; } throw new PrestashopInstallerException("Config file not found"); } } public function getConfig(string $element) { return self::$config[$element] ?: null; } public function displayContent(string $content): void { $this->setContent($this->getTemplate($content)); echo $this->getTemplate('layout'); } protected function setContent(string $content): void { $this->content = $content; } protected function getContent(): string { return $this->content; } protected function renderTemplate(string $path, string $template): string { ob_start(); include $path . $template . '.php'; $content = ob_get_contents(); if (ob_get_level() && ob_get_length() > 0) { ob_end_clean(); } return $content; } public function &__get($varname) { if (isset($this->__vars[$varname])) { $ref = &$this->__vars[$varname]; } else { $null = null; $ref = &$null; } return $ref; } public function __set($varname, $value) { $this->__vars[$varname] = $value; } public function __isset($varname) { return isset($this->__vars[$varname]); } public function __unset($varname) { unset($this->__vars[$varname]); } }