mobile_detect === null) { $this->mobile_detect = new MobileDetect(); } return $this->mobile_detect; } /** Checks if visitor's device is a mobile device. */ public function isMobile(): bool { if ($this->is_mobile === null) { $mobileDetect = $this->getMobileDetect(); $this->is_mobile = $mobileDetect->isMobile(); } return $this->is_mobile; } /** Checks if visitor's device is a tablet device. */ public function isTablet(): bool { if ($this->is_tablet === null) { $mobileDetect = $this->getMobileDetect(); $this->is_tablet = $mobileDetect->isTablet(); } return $this->is_tablet; } /** * @deprecated since 9.0.0 - This functionality was disabled. Function will be completely removed * in the next major. There is no replacement, all clients should have the same experience. * * Sets mobile_device context variable. */ public function getMobileDevice(): bool { @trigger_error( sprintf( '%s is deprecated since version 9.0.0. There is no replacement.', __METHOD__ ), E_USER_DEPRECATED ); return false; } /** Returns mobile device type. */ public function getDevice(): int { static $device = null; if ($device === null) { if ($this->isTablet()) { $device = Context::DEVICE_TABLET; } elseif ($this->isMobile()) { $device = Context::DEVICE_MOBILE; } else { $device = Context::DEVICE_COMPUTER; } } return $device; } /** * @return LocaleInterface|null */ public function getCurrentLocale() { return $this->currentLocale; } /** * @deprecated since 9.0.0 - This functionality was disabled. Function will be completely removed * in the next major. There is no replacement, all clients should have the same experience. * * Checks if mobile context is possible. * * @return bool */ protected function checkMobileContext() { @trigger_error( sprintf( '%s is deprecated since version 9.0.0. There is no replacement.', __METHOD__ ), E_USER_DEPRECATED ); return false; } /** * Get a singleton instance of Context object. * * @return Context|null */ public static function getContext() { if (!isset(self::$instance)) { self::$instance = new Context(); } return self::$instance; } /** * @param Context $testInstance Unit testing purpose only */ public static function setInstanceForTesting($testInstance) { self::$instance = $testInstance; } /** * Unit testing purpose only. */ public static function deleteTestingInstance() { self::$instance = null; } /** * Clone current context object. * * @return static */ public function cloneContext() { return clone $this; } /** * Updates customer in the context, updates the cookie and writes the updated cookie. * * @param Customer $customer Created customer */ public function updateCustomer(Customer $customer) { // Update the customer in context object $this->customer = $customer; // Update basic information in the cookie $this->cookie->id_customer = (int) $customer->id; $this->cookie->customer_lastname = $customer->lastname; $this->cookie->customer_firstname = $customer->firstname; $this->cookie->passwd = $customer->passwd; $this->cookie->logged = true; $customer->logged = true; $this->cookie->email = $customer->email; // Don't confuse this with "id_guest" and Guest object, that's something completely different $this->cookie->is_guest = $customer->isGuest(); /* * If "re-display cart at login" option is enabled in Prestashop configuration, * there is no cart in previous cookie or there is, but empty, * and we managed to get that cart ID, we will re-use it. * * We don't want to flush his cart, if he made it when logged out. */ if (Configuration::get('PS_CART_FOLLOWING') && (empty($this->cookie->id_cart) || Cart::getNbProducts((int) $this->cookie->id_cart) == 0) && $idCart = (int) Cart::lastNoneOrderedCart($this->customer->id) ) { $this->cart = new Cart($idCart); $this->cart->secure_key = $customer->secure_key; $this->cookie->id_guest = (int) $this->cart->id_guest; /* * Otherwise, normal cart recovery and update scenario. */ } else { // Initialize new visit only if there is no visit identifier yet if (!$this->cookie->id_guest) { Guest::setNewGuest($this->cookie); } // If there is some cart created in the context before logging in if (Validate::isLoadedObject($this->cart)) { // We need to update the cart so it matches the customer $this->cart->secure_key = $customer->secure_key; $this->cart->id_guest = (int) $this->cookie->id_guest; // Update and revalidate the selected delivery option $idCarrier = (int) $this->cart->id_carrier; $this->cart->id_carrier = 0; if (!empty($idCarrier)) { $deliveryOption = [$this->cart->id_address_delivery => $idCarrier . ',']; $this->cart->setDeliveryOption($deliveryOption); } else { $this->cart->setDeliveryOption(null); } // Set proper customer ID and assign addresses to the cart $this->cart->id_customer = (int) $customer->id; $this->cart->updateAddressId($this->cart->id_address_delivery, (int) Address::getFirstCustomerAddressId((int) $customer->id)); $this->cart->id_address_delivery = (int) Address::getFirstCustomerAddressId((int) $customer->id); $this->cart->id_address_invoice = (int) Address::getFirstCustomerAddressId((int) $customer->id); } } // If previous logic resolved to some cart to be used, save it and put this information to cookie if (Validate::isLoadedObject($this->cart)) { $this->cart->save(); $this->cookie->id_cart = (int) $this->cart->id; } // Physically save and send this cookie to the client $this->cookie->write(); // Register new logged in session in customer_session table $this->cookie->registerSession(new CustomerSession()); } /** * Returns a translator depending on service container availability and if the method * is called by the installer or not. * * @param bool $isInstaller Set to true if the method is called by the installer * * @return Translator */ public function getTranslator($isInstaller = false) { if (null !== $this->translator && $this->language->locale === $this->translator->getLocale()) { return $this->translator; } $sfContainer = SymfonyContainer::getInstance(); if ($isInstaller || null === $sfContainer) { // symfony's container isn't available in front office, so we load and configure the translator component $this->translator = $this->getTranslatorFromLocale($this->language->locale); } else { $this->translator = $sfContainer->get(TranslatorInterface::class); // We need to set the locale here because in legacy BO pages, the translator is used // before the TranslatorListener does its job of setting the locale according to the Request object $this->translator->setLocale($this->language->locale); } return $this->translator; } /** * Returns a new instance of Translator for the provided locale code. * * @param string $locale IETF language tag (eg. "en-US") * * @return Translator */ public function getTranslatorFromLocale($locale) { $cacheDir = _PS_CACHE_DIR_ . 'translations'; $translator = new Translator($locale, null, $cacheDir, false); // In case we have at least 1 translated message, we return the current translator. // If some translations are missing, clear cache if (empty($locale) || count($translator->getCatalogue($locale)->all())) { return $translator; } // However, in some case, even empty catalog were stored in the cache and then used as-is. // For this one, we drop the cache and try to regenerate it. if (is_dir($cacheDir)) { $cache_file = Finder::create() ->files() ->in($cacheDir) ->depth('==0') ->name('*.' . $locale . '.*'); (new Filesystem())->remove($cache_file); } $translator->clearLanguage($locale); $adminContext = defined('_PS_ADMIN_DIR_'); // Do not load DB translations when $this->language is InstallLanguage // because it means that we're looking for the installer translations, so we're not yet connected to the DB $withDB = !$this->language instanceof InstallLanguage; $theme = $this->shop !== null ? $this->shop->theme : null; if ($this instanceof Context) { try { $containerFinder = new ContainerFinder($this); $container = $containerFinder->getContainer(); $translatorLoader = $container->get('prestashop.translation.translator_language_loader'); } catch (ContainerNotFoundException|ServiceNotFoundException $exception) { $translatorLoader = null; } if (null === $translatorLoader) { // If a container is still not found, instantiate manually the translator loader // This will happen in the Front as we have legacy controllers, the Sf container won't be available. // As we get the translator in the controller's constructor and the container is built in the init method, we won't find it here $translatorLoader = (new TranslatorLanguageLoader(new ModuleRepository(_PS_ROOT_DIR_, _PS_MODULE_DIR_))); } $translatorLoader ->setIsAdminContext($adminContext) ->loadLanguage($translator, $locale, $withDB, $theme) ; } return $translator; } /** * Returns directories that contain translation resources * * @return array */ protected function getTranslationResourcesDirectories() { // Default common translation folder $locations = [_PS_ROOT_DIR_ . '/translations']; // Translations for currently selected theme if (null !== $this->shop) { $activeThemeLocation = _PS_ROOT_DIR_ . '/themes/' . $this->shop->theme_name . '/translations'; if (is_dir($activeThemeLocation)) { $locations[] = $activeThemeLocation; } } return $locations; } /** * Returns the computing precision according to the current currency. * If previously requested, it will be stored in priceComputingPrecision property. * * @return int */ public function getComputingPrecision() { if ($this->priceComputingPrecision === null) { $computingPrecision = new ComputingPrecision(); $this->priceComputingPrecision = $computingPrecision->getPrecision($this->currency->precision); } return $this->priceComputingPrecision; } }