['id' => 1, 'object_class' => null, 'module' => null],
'category' => ['id' => 2, 'object_class' => null, 'module' => null],
'product' => ['id' => 3, 'object_class' => null, 'module' => null],
'cms' => ['id' => 4, 'object_class' => null, 'module' => null],
'newproducts' => ['id' => 5, 'object_class' => null, 'module' => null],
'bestsales' => ['id' => 6, 'object_class' => null, 'module' => null],
'supplier' => ['id' => 7, 'object_class' => null, 'module' => null],
'manufacturer' => ['id' => 8, 'object_class' => null, 'module' => null],
'contact' => ['id' => 9, 'object_class' => null, 'module' => null],
'pricesdrop' => ['id' => 10, 'object_class' => null, 'module' => null],
'sitemap' => ['id' => 11, 'object_class' => null, 'module' => null]);
public static $managed_controllers = null;
public static $managed_object_classes = null;
private static $default_dyn_hooks = array(
'displayproducttabcontent',
'displayrightcolumn',
'displayleftcolumn',
'displaytop',
'displaynav',
'displayproducttab',
'actionproductoutofstock',
'displayfooterproduct',
'displayleftcolumnproduct',
'displayhome',
'displayfooter',
'displaysidebarright',
'displayrightbar');
private static $default_dyn_modules = array(
'blockuserinfo',
'blockviewed',
'blockmyaccount',
'favoriteproducts',
'blockwishlist',
'blockviewed_mod',
'stcompare',
'ps_shoppingcart',
'ps_customersignin'
);
private static $cookies_to_preserve = array(
// From Prestashop
'id_currency' => 'id_currency',
'id_lang' => 'id_lang',
'no_mobile' => 'no_mobile',
'iso_code_country' => 'iso_code_country',
'detect_language' => 'detect_language',
// From autolanguagecurrency module
'autolocation' => 'autolocation',
'autolocation_isocode' => 'autolocation_isocode',
'id_currency_by_location' => 'id_currency_by_location',
'id_language_by_location' => 'id_language_by_location',
// From stthemeeditor module
'st_category_columns_nbr' => 'st_category_columns_nbr',
// From gdprpro module
'gdpr_conf' => 'gdpr_conf',
'gdpr_windows_was_opened' => 'gdpr_windows_was_opened',
// From cookiesplus (Idnovate)
'psnotice' => 'psnotice',
'psnoticeexiry' => 'psnoticeexiry',
'cookiesplus' => 'cookiesplus',
// From megacookies (presta.design)
'megacookie_consents' => 'megacookie_consents',
// From medcookiefirst (Mediacom87)
'cookiefirst-consent' => 'cookiefirst-consent',
// From APC poup (Idnovate)
'apc_popup' => 'apc_popup',
// From Age Verify module
'age_verify' => 'age_verify',
// VAT number validate
'guest_taxes' => 'guest_taxes',
// Amazon Pay - Login and Pay with Amazon by patworx
'customer_firstname' => '',
'customer_lastname' => '',
// From hicookie module
'hiThirdPartyCookies' => 'hiThirdPartyCookies',
// From dm_cookies module
'DmCookiesAnalytics' => 'DmCookiesAnalytics',
'DmCookiesMarketing' => 'DmCookiesMarketing',
'DmCookiesAccepted' => 'DmCookiesAccepted',
// From iqitthemeeditor
'product_list_view' => 'product_list_view',
// From module app4less, see ticket #4376
'isApp4Less' =>'isApp4Less',
'app4less' => 'app4less',
'tiendaApp4Less' => 'tiendaApp4Less',
'timestampApp4Less' => 'timestampApp4Less',
);
const JPRESTA_PROTO = 'http://';
const JPRESTA_DOMAIN = 'jpresta';
public function __construct()
{
$this->name = 'pagecache';
$this->tab = 'administration';
$this->version = '9.3.6';
$this->author = 'JPresta.com';
$this->author_address = '0x7951ec451376e076369022B91cb41B7824898C24';
$this->module_key = 'e00d068863a4c8a3684e984f80756e61';
$this->ps_versions_compliancy = array('min' => '1.7.1.0', 'max' => '8.999.999');
$this->bootstrap = true;
parent::__construct();
$this->displayName = 'JPresta - Page Cache Ultimate';
$this->description = $this->l('Enable full page caching for home, categories, products, CMS and much more pages. Even with page caching you can enable some modules like \'viewed products\' or \'my account\' blocks to load dynamically in ajax. Go from seconds to few milliseconds of loading time!');
// Check tokens
$token_enabled = (int)(Configuration::get('PS_TOKEN_ENABLE')) == 1 ? true : false;
if ($token_enabled) {
$this->warning = $this->l('You must disable tokens in order for cached pages to do ajax call.');
}
// Check for bvkdispatcher module
if (Module::isInstalled('bvkseodispatcher')) {
$this->warning = $this->l('Module "SEO Pretty URL Module" (bvkseodispatcher) is not compatible with PageCache because it does not respect Prestashop standards. You have to choose between this module and PageCache.');
}
// Check for overrides (after an upgrade it is disabled)
if (!self::isOverridesEnabled()) {
$this->warning = $this->l('Overrides are disabled in Performances tab so PageCache is disabled.');
}
if ((Tools::getIsset('logout') || Tools::getIsset('mylogout')) && JprestaUtils::getConfigurationOfCurrentShop('pagecache_logout_nocache')) {
// Add a dummy parameter to avoid the browser cache to be used when logging out
if (array_key_exists('HTTP_REFERER', $_SERVER)) {
if (strpos($_SERVER['HTTP_REFERER'], '?') !== false) {
$_SERVER['HTTP_REFERER'] = $_SERVER['HTTP_REFERER'] . '&nocache=' . time();
}
else {
$_SERVER['HTTP_REFERER'] = $_SERVER['HTTP_REFERER'] . '?nocache=' . time();
}
}
}
}
public static function getModulesToCheck()
{
static $modulesName = null;
if ($modulesName === null) {
$modulesName = [];
foreach (self::getManagedControllersNames() as $controller) {
$modulesName = array_merge($modulesName, explode(' ', Configuration::get('pagecache_'.$controller.'_a_mods')));
$modulesName = array_merge($modulesName, explode(' ', Configuration::get('pagecache_'.$controller.'_u_mods')));
$modulesName = array_merge($modulesName, explode(' ', Configuration::get('pagecache_'.$controller.'_d_mods')));
}
$modulesName = array_merge($modulesName, explode(' ', Configuration::get('pagecache_product_home_a_mods')));
$modulesName = array_merge($modulesName, explode(' ', Configuration::get('pagecache_product_home_u_mods')));
$modulesName = array_merge($modulesName, explode(' ', Configuration::get('pagecache_product_home_d_mods')));
$modulesName = array_unique($modulesName);
}
return $modulesName;
}
public function install()
{
// Be aware that only the last message in _errors will be displayed.
// Check PS version compliancy first
if (method_exists($this, 'checkCompliancy') && !$this->checkCompliancy()) {
$this->_errors[] = $this->l('The version of your module is not compliant with your PrestaShop version.');
return false;
}
// Be sure the script will end correctly (not sure if it's taken into account)
set_time_limit(300);
// Check buggy version 1.6.0.8
if (Tools::version_compare(_PS_VERSION_,'1.6.0.8','=')) {
// Check that a fix has been applied
$moduleClass = Tools::file_get_contents(_PS_CLASS_DIR_ . 'module/Module.php');
if (substr_count($moduleClass, '#^\s*<\?(?:php)?#') != 4) {
$this->_errors[] = $this->l('Prestashop 1.6.0.8 has a bug (http://forge.prestashop.com/browse/PSCSX-2500) that must be fixed in order to install PageCache. Please upgrade your shop or apply a patch (replace 4 occurences of "#^\s*<\?(?:php)?\s#" by "#^\s*<\?(?:php)?#" in file ' . _PS_CLASS_DIR_ . 'module/Module.php).');
return false;
}
}
// Check for similar modules (split string to avoid the build to replace it)
if ($this->name !== 'jpresta'.'speedpack' && (Module::isInstalled('jpresta'.'speedpack') || file_exists(_PS_MODULE_DIR_ . 'jpresta'.'speedpack'))) {
$this->_errors[] = $this->l('Before installing this module you must uninstall "Speed Pack" module and delete its directory') . ': ' . _PS_MODULE_DIR_ . 'jpresta'.'speedpack';
return false;
}
if ($this->name !== 'pagecache' && (Module::isInstalled('pagecache') || file_exists(_PS_MODULE_DIR_ . 'pagecache'))) {
$this->_errors[] = $this->l('Before installing this module you must uninstall "Page Cache Ultimate" module and delete its directory') . ': ' . _PS_MODULE_DIR_ . 'pagecache';
return false;
}
if ($this->name !== 'pagecachestd' && (Module::isInstalled('pagecachestd') || file_exists(_PS_MODULE_DIR_ . 'pagecachestd'))) {
$this->_errors[] = $this->l('Before installing this module you must uninstall "Page Cache Standard" module and delete its directory') . ': ' . _PS_MODULE_DIR_ . 'pagecachestd';
return false;
}
// Check for bvkdispatcher module
if (Module::isInstalled('bvkseodispatcher')) {
$this->_errors[] = $this->l('Module "SEO Pretty URL Module" (bvkseodispatcher) is not compatible with PageCache because it does not respect Prestashop standards. You have to choose between this module and PageCache.');
return false;
}
// Check for expresscache module
if (Module::isInstalled('expresscache') && file_exists(_PS_MODULE_DIR_ . 'expresscache')) {
$this->_errors[] = $this->l('Module "Express Cache" (expresscache) cannot be used with Page Cache because you can have only one HTML cache module. In order to install Page Cache you must uninstall Express Cache.');
return false;
}
// Check for stadvancedcache module
if (Module::isInstalled('stadvancedcache') && file_exists(_PS_MODULE_DIR_ . 'stadvancedcache')) {
$this->_errors[] = $this->l('Module "Advanced page cache" (stadvancedcache) cannot be used with Page Cache because you can have only one HTML cache module. In order to install Page Cache you must uninstall Advanced page cache.');
return false;
}
// Install module
$install_ok = parent::install();
if (!$install_ok) {
foreach (Tools::scandir($this->getLocalPath().'override', 'php', '', true) as $file) {
$class = basename($file, '.php');
if (Tools::version_compare(_PS_VERSION_,'1.6','>=')) {
if (PrestaShopAutoload::getInstance()->getClassPath($class.'Core')) {
$this->removeOverride($class);
}
} else {
if (Autoload::getInstance()->getClassPath($class.'Core')) {
$this->removeOverride($class);
}
}
}
// Retry after uninstalling overrides with our own method
$install_ok = parent::install();
}
if ($install_ok) {
try {
// Make sure old database tables are deleted
PageCacheDAO::dropTables();
// Create database tables
PageCacheDAO::createTables();
$this->_setDefaultConfiguration();
$this->patchSmartyConfigFront();
$this->patchSmartyConfigFrontWidgetBlock();
$this->installStaticCode();
$this->installOverridesForModules();
JprestaApi::setPrestashopIsClone(false);
JprestaUtils::dbCreateIndexIfNotExists(_DB_PREFIX_ . 'specific_price_rule', ['id_country', 'to']);
JprestaUtils::dbCreateIndexIfNotExists(_DB_PREFIX_ . 'specific_price_rule', ['id_group', 'to']);
JprestaUtils::dbCreateIndexIfNotExists(_DB_PREFIX_ . 'specific_price', ['id_country', 'to']);
JprestaUtils::dbCreateIndexIfNotExists(_DB_PREFIX_ . 'specific_price', ['id_group', 'to']);
}
catch (PrestaShopException $e) {
$install_ok = false;
$this->_errors[] = $e->getMessage() . '. ' . $this->l('Please, contact the support of this module with this error message.');
try {
// An error occured while setting up the module, uninstall it to avoid a bad installation
parent::uninstall();
}
catch (PrestaShopException $e2) {
JprestaUtils::addLog('PageCache | Cannot uninstall module ' . $this->name . ' after having this error during installation: "' . $e->getMessage() . '"" -> Got this error: ' . $e2->getMessage(), 4);
}
}
}
if ((bool) $install_ok) {
JprestaUtils::addLog('PageCache | Module ' . $this->name . ' version ' . $this->version . ' installed', 1);
}
else {
JprestaUtils::addLog('PageCache | Install of module ' . $this->name . ' version ' . $this->version . ' failed', 3);
}
return (bool) $install_ok;
}
/**
* Disable / enable Hook and Context override if jprestathemeconfigurator is enabled
* @return bool
*/
public function installOverrides() {
$relPathHook = 'classes/Hook.php';
$overrideFullPathHook = _PS_MODULE_DIR_ . $this->name . '/override/' . $relPathHook;
$relPathContext = 'classes/Context.php';
$overrideFullPathContext = _PS_MODULE_DIR_ . $this->name . '/override/' . $relPathContext;
if (JprestaUtils::isModuleEnabled('jprestathemeconfigurator')) {
if (file_exists($overrideFullPathHook)) {
rename($overrideFullPathHook, $overrideFullPathHook . '.off');
}
if (file_exists($overrideFullPathContext)) {
rename($overrideFullPathContext, $overrideFullPathContext . '.off');
}
}
$ret = parent::installOverrides();
if (JprestaUtils::isModuleEnabled('jprestathemeconfigurator')) {
if (file_exists($overrideFullPathHook . '.off')) {
rename($overrideFullPathHook . '.off', $overrideFullPathHook);
}
if (file_exists($overrideFullPathContext . '.off')) {
rename($overrideFullPathContext . '.off', $overrideFullPathContext);
}
}
return $ret;
}
public function installOverridesForModules($replace = false) {
JprestaUtils::copyFiles(_PS_MODULE_DIR_ . $this->name . '/override/modules', _PS_OVERRIDE_DIR_ . 'modules', $replace);
}
public function installTab($adminController, $name = false, $id_parent = -1)
{
$isUpdate = true;
$tab = Tab::getInstanceFromClassName($adminController);
if (!$tab || !$tab->id) {
$tab = new Tab();
$tab->class_name = $adminController;
$isUpdate = false;
}
$tab->active = 1;
$tab->name = array();
foreach (Language::getLanguages(true) as $lang) {
// Translation for modules are cached in a global variable but the local is ignored >:(
if (is_array($name)) {
if (array_key_exists($lang['iso_code'], $name)) {
$trans = $name[$lang['iso_code']];
}
elseif (array_key_exists('en', $name)) {
$trans = $name['en'];
}
}
else {
$trans = $name;
}
$tab->name[$lang['id_lang']] = !$trans ? $this->name : $trans;
}
$tab->id_parent = $id_parent;
$tab->module = $this->name;
if ($isUpdate) {
return $tab->update();
}
else {
return $tab->add();
}
}
public function uninstallTab($adminController)
{
$id_tab = (int)Tab::getIdFromClassName($adminController);
if ($id_tab) {
$tab = new Tab($id_tab);
if (Validate::isLoadedObject($tab)) {
return ($tab->delete());
} else {
$return = false;
}
} else {
$return = true;
}
return $return;
}
private function uninstallAllTab() {
$tabs = Tab::getCollectionFromModule($this->name);
if (JprestaUtils::isIterable($tabs)) {
foreach ($tabs as $tab) {
$tab->delete();
}
return true;
}
}
public function checkTabAccesses($adminController) {
try {
if (Tools::version_compare(_PS_VERSION_,'1.7','>=')) {
$slug = Access::sluggifyTab(array('class_name' => $adminController), 'READ');
$granted = Access::isGranted($slug, $this->context->employee->id_profile);
if (!$granted) {
$id_role = JprestaUtils::dbGetValue('SELECT `id_authorization_role` FROM `' . _DB_PREFIX_ . 'authorization_role` WHERE slug = \'' . pSql($slug) . '\'');
if ($id_role) {
$sql = '
INSERT IGNORE INTO `' . _DB_PREFIX_ . 'access` (`id_profile`, `id_authorization_role`)
VALUES (' . (int)$this->context->employee->id_profile . ',' . (int)$id_role . ')
';
Db::getInstance()->execute($sql);
}
}
}
else {
$id_tab = Tab::getIdFromClassName($adminController);
$profile = Profile::getProfileAccess($this->context->employee->id_profile, $id_tab);
if (!$profile['view']) {
$sql = 'UPDATE `' . _DB_PREFIX_ . 'access` SET `view`=1, `add`=1, `edit`=1, `delete`=1 WHERE id_profile=' . (int)$this->context->employee->id_profile . ' AND id_tab=' . (int)$id_tab;
Db::getInstance()->execute($sql);
}
}
}
catch (Throwable $e) {
// ignore
JprestaUtils::addLog("PageCache | Error in checkTabAccesses(): " . $e->getMessage(), 1);
}
}
public function runUpgradeModule()
{
$startTime = microtime(true);
$oldVersion = $newVersion = $this->version;
$upgrade = parent::runUpgradeModule();
if ($upgrade['upgraded_to']) {
$newVersion = $upgrade['upgraded_to'];
}
JprestaUtils::addLog("PageCache | Upgrading module " . $this->name . " from version $oldVersion to version $newVersion in " . number_format(microtime(true) - $startTime, 3) . " second(s)", 1, null, null, null, true);
return $upgrade;
}
/**
* To be called first
*/
public static function init() {
if (!self::$initialised) {
// Avoid doing it multiple times and also recursively
self::$initialised = true;
$controller = self::getControllerName();
self::checkModuleController();
if (!JprestaUtils::isAjax()
&& self::isGetRequest()
&& !defined('_PS_ADMIN_DIR_')
&& self::isCacheEnabledForController($controller)) {
if (JprestaUtils::getConfigurationAllShop("pagecache_cachekey_usergroups_upd", false)) {
self::updateCacheKeyForUserGroups();
}
if (self::isCacheWarmer()) {
// Setup the context for cache warmer
self::setCacheWarmerContext();
}
// We must set the country before calling self::preDisplayStats() or getAddressForTaxes() will be called
// and the country will not be correctly set.
// This will also set the restrictedCountry variable of the controller
$country = self::getCountry(Context::getContext());
if ($country) {
Context::getContext()->country = $country;
}
}
}
}
public function needsUpgrade() {
if (Tools::version_compare(_PS_VERSION_, '1.7', '>')) {
$database_version = JprestaUtils::dbGetValue('SELECT version FROM `' . _DB_PREFIX_ . 'module` WHERE name=\'' . pSQL($this->name) . '\'');
return Tools::version_compare($this->version, $database_version, '>');
}
return false;
}
public function hookDisplayAdminAfterHeader() {
if ($this->needsUpgrade()) {
try {
$database_version = JprestaUtils::dbGetValue('SELECT version FROM `' . _DB_PREFIX_ . 'module` WHERE name=\'' . pSQL($this->name) . '\'');
$smarty = Context::getContext()->smarty;
$smarty->assign('jpresta_module_name', $this->displayName);
$smarty->assign('jpresta_module_new_version', $this->version);
$smarty->assign('jpresta_module_current_version', $database_version);
return $this->display(__FILE__, '/views/templates/admin/need-upgrade.tpl');
} catch (Throwable $e) {
// Just ignore
}
}
if (JprestaApi::getPrestashopIsClone()) {
return $this->display(__FILE__, '/views/templates/admin/need-confirm-clone.tpl');
}
return '';
}
public function upgradeIfNeeded() {
if ($this->needsUpgrade()) {
$moduleManagerBuilder = \PrestaShop\PrestaShop\Core\Addon\Module\ModuleManagerBuilder::getInstance();
$moduleManager = $moduleManagerBuilder->build();
if (method_exists($moduleManager, 'setActionParams')) {
// Does not exist in PS8
// Clearing the cache is really long so I disable it. If needed the user will do it manually.
$moduleManager->setActionParams(['cacheClearEnabled' => false]);
}
try {
// Upgrade the module
$startTime = microtime(true);
$oldVersion = JprestaUtils::dbGetValue('SELECT version FROM `' . _DB_PREFIX_ . 'module` WHERE name=\'' . pSQL($this->name) . '\'');
if ($moduleManager->upgrade($this->name)) {
$this->_confirmations[] = $this->l('The module has been upgraded to version', 'jprestaspeedpack') . ' ' . $this->version;
$newVersion = $this->version;
JprestaUtils::addLog("PageCache | Upgrading module " . $this->name . " from version $oldVersion to version $newVersion in " . number_format(microtime(true) - $startTime, 3) . " second(s)", 1, null, null, null, true);
}
else {
$this->_errors[] = $this->l('The module could not be upgraded correctly, contact the support with above error messages', 'jprestaspeedpack');
}
} catch (Throwable $e) {
// Just ignore
}
}
}
public static function getCache($id_shop = false) {
$cacheInstance = null;
if (!$id_shop) {
$id_shop = Shop::getContextShopID();
}
if ($id_shop === null) {
// Happens in back office when a group of shop is selected. Is used during hooks for cache refreshment.
$ids_shops = Shop::getShops(true, Shop::getContextShopGroupID(), true);
$cacheInstance = new PageCacheCacheMultiStore();
foreach ($ids_shops as $id_shop) {
$cacheInstance->addCache(self::getCacheInstance($id_shop));
}
}
else {
$cacheInstance = self::getCacheInstance($id_shop);
}
return $cacheInstance;
}
public static function getParentCacheDirectory() {
if (JprestaUtils::startsWith(_PS_CACHE_DIR_, _PS_ROOT_DIR_ . '/var/cache/')) {
// Prestashop clears the cache everytime a module is enabled/disabled and we don't want this behavior.
// Also having different cache for DEV and PROD is not needed for this module.
return _PS_ROOT_DIR_ . '/var/cache/' . self::PAGECACHE_DIR;
}
else {
return _PS_CACHE_DIR_ . self::PAGECACHE_DIR;
}
}
/**
* @return void
*/
public function checkInstallCache() {
$typecache = JprestaUtils::getConfigurationAllShop('pagecache_typecache');
$allShopIds = Shop::getCompleteListOfShopsID();
foreach ($allShopIds as $shopId) {
self::getCacheInstance((int)$shopId, $typecache)->checkInstall($this);
}
}
/**
* @return void
*/
public function installCache() {
$typecache = JprestaUtils::getConfigurationAllShop('pagecache_typecache');
$allShopIds = Shop::getCompleteListOfShopsID();
foreach ($allShopIds as $shopId) {
self::getCacheInstance((int)$shopId, $typecache)->install($this);
}
}
/**
* @param $typecache string
* @return void
*/
public function uninstallCache($typecache) {
$allShopIds = Shop::getCompleteListOfShopsID();
foreach ($allShopIds as $shopId) {
self::getCacheInstance($shopId, $typecache)->uninstall($this);
}
}
/**
* @param $id_shop int
* @param $type string
* @return PageCacheCacheMemcache|PageCacheCacheMemcached|PageCacheCacheSimpleFS|PageCacheCacheStatic|PageCacheCacheZipFS
*/
private static function getCacheInstance($id_shop, $type = null)
{
if ($type === null) {
$type = JprestaUtils::getConfigurationAllShop('pagecache_typecache');
}
$key = $type . (int) $id_shop;
static $cacheInstances = array();
if (array_key_exists($key, $cacheInstances)) {
return $cacheInstances[$key];
}
// ULTIMATE
if (strcmp('static', $type) === 0 && PageCacheCacheStatic::isCompatible()) {
$cachedir = self::getParentCacheDirectory() . '/static';
$cacheInstances[$key] = new PageCacheCacheStatic($cachedir, Configuration::get('pagecache_logs', null, null, $id_shop) > 1);
}
else if (strcmp('stdzip', $type) === 0 && PageCacheCacheZipFS::isCompatible()) {
$cachedir = self::getParentCacheDirectory() . '/stdzip/' . $id_shop;
$cacheInstances[$key] = new PageCacheCacheZipFS($cachedir, Configuration::get('pagecache_logs', null, null, $id_shop) > 1);
}
else if (strcmp('memcache', $type) === 0 && PageCacheCacheMemcache::isCompatible()) {
$cacheInstances[$key] = new PageCacheCacheMemcache(Configuration::get('pagecache_typecache_memcache_host'), (int) Configuration::get('pagecache_typecache_memcache_port'));
}
else if (strcmp('memcached', $type) === 0 && PageCacheCacheMemcached::isCompatible()) {
$cacheInstances[$key] = new PageCacheCacheMemcached(Configuration::get('pagecache_typecache_memcached_host'), (int) Configuration::get('pagecache_typecache_memcached_port'));
}
// ULTIMATE£
if (!array_key_exists($key, $cacheInstances)) {
$cachedir = self::getParentCacheDirectory() . '/std/' . $id_shop;
$cacheInstances[$key] = new PageCacheCacheSimpleFS($cachedir, Configuration::get('pagecache_logs', null, null, $id_shop) > 1);
}
return $cacheInstances[$key];
}
public static function getWidgetBlockDir() {
return _PS_MODULE_DIR_ . 'pagecache/widget_blocks/';
}
private static function getWidgetBlockTemplate($blockKey) {
return self::getWidgetBlockDir() . $blockKey . '.tpl';
}
public static function setWidgetBlockTemplate($blockKey, $content) {
$cachedir = self::getWidgetBlockDir();
if (! file_exists($cachedir)) {
// Creates subdirectory with 777 to be sure it will work
$grants = 0777;
if (! @mkdir($cachedir, $grants, true)) {
$mkdirErrorArray = error_get_last();
if (! file_exists($cachedir)) {
if ($mkdirErrorArray !== null) {
JprestaUtils::addLog("PageCache | Cannot create directory " . $cachedir . " with grants $grants: " . $mkdirErrorArray['message'], 4);
}
else {
JprestaUtils::addLog("PageCache | Cannot create directory " . $cachedir . " with grants $grants", 4);
}
}
}
}
$cachefile = $cachedir . $blockKey . '.tpl';
$write_ok = file_put_contents($cachefile, $content);
if ($write_ok === false) {
$mkdirErrorArray = error_get_last();
if ($mkdirErrorArray !== null) {
JprestaUtils::addLog("PageCache | Cannot write file $cachefile: " . $mkdirErrorArray['message'], 4);
}
else {
JprestaUtils::addLog("PageCache | Cannot write file $cachefile", 4);
}
}
}
/**
* Override Module::updateModuleTranslations()
*/
public function updateModuleTranslations()
{
// Speeds up installation: do nothing because PageCache translation are not in Prestashop language pack
}
public function disable($force_all = false) {
$ret = parent::disable($force_all);
$this->uninstallCache(JprestaUtils::getConfigurationAllShop('pagecache_typecache'));
return (bool) $ret;
}
public function enable($force_all = false) {
$ret = parent::enable($force_all);
$this->installCache();
self::updateCacheKeyForCountries();
self::updateCacheKeyForUserGroups();
// Disable tokens on the front end
Configuration::updateValue('PS_TOKEN_ENABLE', 0);
if (JprestaUtils::isModuleEnabled('lgcookieslaw')) {
// Enable compatibility with LG Cookies law module
Configuration::updateValue('PS_LGCOOKIES_PUC_COMPATIBILITY', 1);
}
return (bool) $ret;
}
public function uninstall()
{
try {
$this->clearCache('uninstall');
JprestaCustomer::deleteAllFakeUsers();
}
catch (Throwable $e) {
// Ignore because it's not a big deal if cache is not cleared
}
Configuration::deleteByName('pagecache_install_step');
Configuration::deleteByName('pagecache_always_infosbox');
Configuration::deleteByName('pagecache_debug');
Configuration::deleteByName('pagecache_maxrows');
Configuration::deleteByName('pagecache_skiplogged');
Configuration::deleteByName('pagecache_normalize_urls');
Configuration::deleteByName('pagecache_logout_nocache');
Configuration::deleteByName('pagecache_logs');
Configuration::deleteByName('pagecache_depend_on_device_auto');
Configuration::deleteByName('pagecache_depend_on_css_js');
Configuration::deleteByName('pagecache_tablet_is_mobile');
Configuration::deleteByName('pagecache_exec_header_hook');
Configuration::deleteByName('pagecache_use_dispatcher_hook');
Configuration::deleteByName('pagecache_stats');
Configuration::deleteByName('pagecache_profiling');
Configuration::deleteByName('pagecache_typecache');
Configuration::deleteByName('pagecache_show_stats');
Configuration::deleteByName('pagecache_groups');
Configuration::deleteByName('pagecache_seller');
Configuration::deleteByName('pagecache_ignored_params');
Configuration::deleteByName('pagecache_dyn_hooks');
Configuration::deleteByName('pagecache_dyn_widgets');
foreach (self::getManagedControllersNames() as $controller) {
Configuration::deleteByName('pagecache_'.$controller);
Configuration::deleteByName('pagecache_'.$controller.'_timeout');
Configuration::deleteByName('pagecache_'.$controller.'_expires');
Configuration::deleteByName('pagecache_'.$controller.'_u_bl');
Configuration::deleteByName('pagecache_'.$controller.'_d_bl');
Configuration::deleteByName('pagecache_'.$controller.'_a_mods');
Configuration::deleteByName('pagecache_'.$controller.'_u_mods');
Configuration::deleteByName('pagecache_'.$controller.'_d_mods');
}
Configuration::deleteByName('pagecache_static_expires');
Configuration::deleteByName('pagecache_product_home_u_bl');
Configuration::deleteByName('pagecache_product_home_d_bl');
Configuration::deleteByName('pagecache_product_home_a_mods');
Configuration::deleteByName('pagecache_product_home_u_mods');
Configuration::deleteByName('pagecache_product_home_d_mods');
Configuration::deleteByName('pagecache_cache_warmer_settings');
Configuration::deleteByName('pagecache_profiling_min_ms');
Configuration::deleteByName('pagecache_statsttfb');
Configuration::deleteByName('pagecache_stats_perf');
Configuration::deleteByName('pagecache_ignore_after_pattern');
Configuration::deleteByName('pagecache_ignore_before_pattern');
Configuration::deleteByName('pagecache_currencies_to_cache');
Configuration::deleteByName('pagecache_max_exec_time');
Configuration::deleteByName('pagecache_ignore_url_regex');
Configuration::deleteByName('pagecache_cachekey_usergroups');
Configuration::deleteByName('pagecache_cachekey_countries');
Configuration::deleteByName('pagecache_cfgadvancedjs');
Configuration::deleteByName('pagecache_cachekey_usergroups');
PageCacheDAO::dropTables();
$this->uninstallAllTab();
$this->uninstallStaticCode();
$ret = parent::uninstall();
// Clean cache in case of a reset
Cache::clean('Module::getModuleIdByName_'.pSQL($this->name));
if ((bool) $ret) {
JprestaUtils::addLog('PageCache | Module ' . $this->name . ' version ' . $this->version . ' uninstalled', 1);
}
else {
JprestaUtils::addLog('PageCache | Uninstall of module ' . $this->name . ' version ' . $this->version . ' failed', 3);
}
return (bool) $ret;
}
public function isSpeedPack() {
return $this->name === 'jprestaspeedpack';
}
private function _setDefaultConfiguration($id_shop_group = null, $id_shop = null)
{
if ($this->isSpeedPack()) {
$this->installTab('AdminParentSpeedPack', 'JPresta - Speed pack', (int)Tab::getIdFromClassName('AdminAdvancedParameters'));
$this->installTab('AdminPageCacheConfiguration', 'Page Cache Ultimate', (int)Tab::getIdFromClassName('AdminParentSpeedPack'));
$this->installTab('AdminJprestaWebpConfiguration', array(
'en' => 'Compression of images',
'fr' => 'Compression des images',
'es' => 'Compresión de imágenes'
), (int)Tab::getIdFromClassName('AdminParentSpeedPack'));
$this->installTab('AdminJprestaSQLProfilerConfiguration', array(
'en' => 'SQL Profiler',
'fr' => 'Profilage SQL',
'es' => 'SQL Profiler'
), (int)Tab::getIdFromClassName('AdminParentSpeedPack'));
$this->installTab('AdminJprestaLazyLoadingConfiguration', array(
'en' => 'Lazy load of images',
'fr' => 'Chargement différé des images',
'es' => 'Carga bajo demanda de imágenes'
), (int)Tab::getIdFromClassName('AdminParentSpeedPack'));
$this->installTab('AdminJprestaDbOptimizerConfiguration', array(
'en' => 'Database optimisation',
'fr' => 'Nettoyage de la base de données',
'es' => 'Limpieza de la base de datos'
), (int)Tab::getIdFromClassName('AdminParentSpeedPack'));
}
else {
if (Tools::version_compare(_PS_VERSION_, '1.6', '>')) {
$idTab = (int)Tab::getIdFromClassName('AdminAdvancedParameters');
if (!$idTab) {
$idTab = (int)Tab::getIdFromClassName('AdminTools');
}
$this->installTab('AdminPageCacheConfiguration', 'JPresta - Page Cache Ultimate', $idTab);
}
elseif (Tools::version_compare(_PS_VERSION_, '1.5', '>')) {
$this->installTab('AdminPageCacheConfiguration', 'JPresta - Page Cache Ultimate', 17);
}
}
$this->installTab('AdminPageCacheMemcachedTest');
$this->installTab('AdminPageCacheMemcacheTest');
$this->installTab('AdminPageCacheProfilingDatas');
$this->installTab('AdminPageCacheDatas');
// Register hooks
$this->registerHook('displayAdminAfterHeader');
if (Tools::version_compare(_PS_VERSION_, '1.7', '>')) {
$this->registerHook('actionDispatcherBefore');
$this->registerHook('actionDispatcherAfter');
$this->registerHook('actionOutputHTMLBefore');
}
if (Tools::version_compare(_PS_VERSION_, '1.7', '<')) {
$this->registerHook('actionAdminProductsControllerSaveAfter');
}
$this->registerHook('actionDispatcher');
$this->registerHook('displayHeader');
if (Tools::version_compare(_PS_VERSION_,'1.6','>')) {
$hookHeaderId = Hook::getIdByName('Header');
$this->updatePosition($hookHeaderId, 0, 1);
}
$this->registerHook('actionTaxManager');
if (Tools::version_compare(_PS_VERSION_,'1.6','>')) {
$hookHeaderId = Hook::getIdByName('actionTaxManager');
$this->updatePosition($hookHeaderId, 0, 1);
}
$this->registerHook('actionJPrestaClearCache');
$this->registerHook('displayMobileHeader');
$this->registerHook('actionAdminSaveBefore');
$this->registerHook('actionAdminProductsControllerSaveBefore');
$this->registerHook('actionObjectAddAfter');
$this->registerHook('actionObjectUpdateAfter');
$this->registerHook('actionObjectDeleteAfter');
$this->registerHook('actionCategoryAdd');
$this->registerHook('actionCategoryUpdate');
$this->registerHook('actionCategoryDelete');
$this->registerHook('actionObjectCmsAddAfter');
$this->registerHook('actionObjectCmsUpdateAfter');
$this->registerHook('actionObjectCmsDeleteBefore');
$this->registerHook('actionObjectStockAvailableUpdateBefore');
$this->registerHook('actionObjectStockAvailableUpdateAfter');
$this->registerHook('actionObjectStockAddBefore');
$this->registerHook('actionObjectStockAddAfter');
$this->registerHook('actionObjectStockUpdateBefore');
$this->registerHook('actionObjectStockUpdateAfter');
$this->registerHook('actionObjectWarehouseProductLocationAddBefore');
$this->registerHook('actionObjectWarehouseProductLocationAddAfter');
$this->registerHook('actionObjectWarehouseProductLocationDeleteBefore');
$this->registerHook('actionObjectWarehouseProductLocationDeleteAfter');
$this->registerHook('actionObjectManufacturerAddAfter');
$this->registerHook('actionObjectManufacturerUpdateAfter');
$this->registerHook('actionObjectManufacturerDeleteBefore');
$this->registerHook('actionObjectAddressAddAfter');
$this->registerHook('actionObjectAddressUpdateAfter');
$this->registerHook('actionObjectAddressDeleteBefore');
$this->registerHook('actionAttributeSave');
$this->registerHook('actionAttributeDelete');
$this->registerHook('actionAttributeGroupDelete');
$this->registerHook('actionAttributeGroupSave');
$this->registerHook('actionFeatureSave');
$this->registerHook('actionFeatureDelete');
$this->registerHook('actionFeatureValueSave');
$this->registerHook('actionFeatureValueDelete');
$this->registerHook('actionProductAdd');
$this->registerHook('actionObjectProductUpdateBefore');
$this->registerHook('actionObjectProductUpdateAfter');
$this->registerHook('actionObjectProductDeleteBefore');
$this->registerHook('actionObjectCombinationUpdateBefore');
$this->registerHook('actionObjectCombinationUpdateAfter');
$this->registerHook('actionObjectCombinationDeleteAfter');
$this->registerHook('actionHtaccessCreate');
$this->registerHook('actionAdminPerformanceControllerAfter');
// New shop creation
$this->registerHook('actionShopDataDuplication');
// Add hook for specific prices
$this->registerHook('actionObjectSpecificPriceAddAfter');
$this->registerHook('actionObjectSpecificPriceUpdateAfter');
$this->registerHook('actionObjectSpecificPriceDeleteBefore');
$this->registerHook('actionObjectSpecificPriceDeleteAfter');
// Hook called when images are changed
$this->registerHook('actionObjectImageAddAfter');
$this->registerHook('actionObjectImageUpdateAfter');
$this->registerHook('actionObjectImageDeleteBefore');
$this->registerHook('actionObjectSpecificPriceRuleAddAfter');
$this->registerHook('actionObjectSpecificPriceRuleUpdateAfter');
$this->registerHook('actionObjectSpecificPriceRuleDeleteAfter');
$this->registerHook('actionObjectGroupAddAfter');
$this->registerHook('actionObjectGroupUpdateAfter');
$this->registerHook('actionObjectGroupDeleteAfter');
$this->registerHook('actionCustomerAccountAdd');
$this->registerHook('actionAuthentication');
$this->registerHook('actionCustomerLogoutAfter');
// Use backlink heuristic...
Configuration::updateValue('pagecache_cms_u_bl', true, false, $id_shop_group, $id_shop);
Configuration::updateValue('pagecache_cms_d_bl', true, false, $id_shop_group, $id_shop);
Configuration::updateValue('pagecache_supplier_u_bl', true, false, $id_shop_group, $id_shop);
Configuration::updateValue('pagecache_supplier_d_bl', true, false, $id_shop_group, $id_shop);
Configuration::updateValue('pagecache_manufacturer_u_bl', true, false, $id_shop_group, $id_shop);
Configuration::updateValue('pagecache_manufacturer_d_bl', true, false, $id_shop_group, $id_shop);
Configuration::updateValue('pagecache_product_u_bl', true, false, $id_shop_group, $id_shop);
Configuration::updateValue('pagecache_product_d_bl', true, false, $id_shop_group, $id_shop);
Configuration::updateValue('pagecache_product_home_u_bl', true, false, $id_shop_group, $id_shop);
Configuration::updateValue('pagecache_product_home_d_bl', true, false, $id_shop_group, $id_shop);
Configuration::updateValue('pagecache_category_u_bl', true, false, $id_shop_group, $id_shop);
Configuration::updateValue('pagecache_category_d_bl', true, false, $id_shop_group, $id_shop);
// Default impacted modules
Configuration::updateValue('pagecache_category_a_mods', 'blockcategories ps_categorytree iqitcontentcreator', false, $id_shop_group, $id_shop);
Configuration::updateValue('pagecache_category_u_mods', 'blockcategories ps_categorytree iqitcontentcreator', false, $id_shop_group, $id_shop);
Configuration::updateValue('pagecache_category_d_mods', 'blockcategories ps_categorytree iqitcontentcreator', false, $id_shop_group, $id_shop);
Configuration::updateValue('pagecache_supplier_a_mods', 'blocksupplier', false, $id_shop_group, $id_shop);
Configuration::updateValue('pagecache_supplier_u_mods', 'blocksupplier', false, $id_shop_group, $id_shop);
Configuration::updateValue('pagecache_supplier_d_mods', 'blocksupplier', false, $id_shop_group, $id_shop);
Configuration::updateValue('pagecache_manufacturer_a_mods', 'blockmanufacturer', false, $id_shop_group, $id_shop);
Configuration::updateValue('pagecache_manufacturer_u_mods', 'blockmanufacturer', false, $id_shop_group, $id_shop);
Configuration::updateValue('pagecache_manufacturer_d_mods', 'blockmanufacturer', false, $id_shop_group, $id_shop);
Configuration::updateValue('pagecache_product_a_mods', 'newsellerincategory sthomenew blocknewproducts ps_newproducts posnewproduct zonehomeblocks wtnewproducts iqitcontentcreator', false, $id_shop_group, $id_shop);
Configuration::updateValue('pagecache_product_home_a_mods', 'homefeatured ps_featuredproducts', false, $id_shop_group, $id_shop);
Configuration::updateValue('pagecache_product_home_u_mods', 'homefeatured ps_featuredproducts', false, $id_shop_group, $id_shop);
Configuration::updateValue('pagecache_product_home_d_mods', 'homefeatured ps_featuredproducts', false, $id_shop_group, $id_shop);
Configuration::updateValue('pagecache_cms_a_mods', 'blockcms', false, $id_shop_group, $id_shop);
// Enable cache on all managed_controllers and timeout = 7 days
foreach (self::getManagedControllersNames() as $controller) {
Configuration::updateValue('pagecache_'.$controller, 1, false, $id_shop_group, $id_shop);
Configuration::updateValue('pagecache_'.$controller.'_timeout', 60 * 24 * 7, false, $id_shop_group, $id_shop);
}
// Do not cache contact form by default anymore (anti-spam system)
Configuration::updateValue('pagecache_contact', 0, false, $id_shop_group, $id_shop);
Configuration::updateValue('pagecache_contact_timeout', 0, false, $id_shop_group, $id_shop);
// Set default dynamic hooks
$pagecache_dyn_hooks = '';
$module_list = Hook::getHookModuleExecList();
if (JprestaUtils::isIterable($module_list)) {
foreach ($module_list as $hook_name => $modules) {
foreach ($modules as $module) {
if (in_array($hook_name, self::$default_dyn_hooks) && in_array($module['module'],
self::$default_dyn_modules)) {
$pagecache_dyn_hooks .= $hook_name . '|' . $module['module'] . ',';
} /** Special case: blockcart will be dynamic if ajax is disabled */
elseif (in_array($hook_name, self::$default_dyn_hooks) && strcmp($module['module'],
'blockcart') == 0) {
if (!(int)(Configuration::get('PS_BLOCK_CART_AJAX'))) {
$pagecache_dyn_hooks .= $hook_name . '|' . $module['module'] . ',';
}
}
}
}
}
Configuration::updateValue('pagecache_dyn_hooks', $pagecache_dyn_hooks, false, $id_shop_group, $id_shop);
// Set default javascript to execute (empty since autoconf)
Configuration::updateValue('pagecache_cfgadvancedjs', '', false, $id_shop_group, $id_shop);
// First install step is 0 (none)
Configuration::updateValue('pagecache_install_step', 0, false, $id_shop_group, $id_shop);
// Do not always display infos box by default
Configuration::updateValue('pagecache_always_infosbox', false, false, $id_shop_group, $id_shop);
// Not in production by default
Configuration::updateValue('pagecache_debug', true, false, $id_shop_group, $id_shop);
// Cache logged in users by default
Configuration::updateValue('pagecache_skiplogged', false, false, $id_shop_group, $id_shop);
// Normalize URLs by default
Configuration::updateValue('pagecache_normalize_urls', true, false, $id_shop_group, $id_shop);
// Disable logs by default
Configuration::updateValue('pagecache_logs', false, false, $id_shop_group, $id_shop);
// Auto detect mobile version
Configuration::updateValue('pagecache_depend_on_device_auto', true, false, $id_shop_group, $id_shop);
// Tablet is not considered as mobile by default
Configuration::updateValue('pagecache_tablet_is_mobile', false, false, $id_shop_group, $id_shop);
// Do not add CSS and JS version in the cache key by default
Configuration::updateValue('pagecache_depend_on_css_js', false, false, $id_shop_group, $id_shop);
// Must we call header hook for dynamic request
Configuration::updateValue('pagecache_exec_header_hook', false, false, $id_shop_group, $id_shop);
// Ignore all backlinks before tag /header>
Configuration::updateValue('pagecache_ignore_before_pattern',
JprestaUtils::encodeConfiguration('/header>'), $id_shop_group, $id_shop);
// Ignore all backlinks after tag /footer>
Configuration::updateValue('pagecache_ignore_after_pattern',
JprestaUtils::encodeConfiguration('/footer>'), $id_shop_group, $id_shop);
// Ignore faceted searches and currency change
Configuration::updateValue('pagecache_ignore_url_regex',
JprestaUtils::encodeConfiguration('.*[\?&]q=.*|.*SubmitCurrency=1.*'), $id_shop_group, $id_shop);
// Disable profiling by default
JprestaUtils::saveConfigurationAllShop('pagecache_profiling', false);
JprestaUtils::saveConfigurationAllShop('pagecache_profiling_min_ms', 100);
JprestaUtils::saveConfigurationAllShop('pagecache_profiling_max_reached', false);
// Enable static cache system by default
Configuration::updateValue('pagecache_typecache', 'static', false, $id_shop_group, $id_shop);
// Disable cache for customizable products by default
Configuration::updateValue('pagecache_cache_customizable', false, false, $id_shop_group, $id_shop);
// Default browser cache to 15 minutes
foreach (self::getManagedControllersNames() as $controller) {
Configuration::updateValue('pagecache_'.$controller.'_expires', 15, false, $id_shop_group, $id_shop);
}
Configuration::updateValue('pagecache_static_expires', 15, false, $id_shop_group, $id_shop);
// Default ad tracking parameters
Configuration::updateValue('pagecache_ignored_params', 'fbclid,gclid,utm_id,utm_campaign,utm_content,utm_medium,utm_source,utm_term,_openstat,cm_cat,cm_ite,cm_pla,cm_ven,owa_ad,owa_ad_type,owa_campaign,owa_medium,owa_source,pk_campaign,pk_kwd,WT.mc_t,kwkuniv,srsltid,gad_source,_gl,_ga,cto_pld,gad_source', false, $id_shop_group, $id_shop);
// Max execution time for cache warmer
Configuration::updateValue('pagecache_max_exec_time', min(480, max(10, (int) ini_get('max_execution_time') - 5)), false, $id_shop_group, $id_shop);
// Disable tokens on front
Configuration::updateValue('PS_TOKEN_ENABLE', 0, false, $id_shop_group, $id_shop);
// Enable stats on TTFB by default
Configuration::updateValue('pagecache_statsttfb', true, false, $id_shop_group, $id_shop);
// Enable all currencies for cache
$this->enableAllCurrencies();
}
public function enableAllCurrencies() {
$pagecache_currencies_to_cache = [];
foreach (Currency::getCurrenciesByIdShop() as $currency) {
if ($currency['active']) {
$pagecache_currencies_to_cache[] = $currency['iso_code'];
}
}
JprestaUtils::saveConfigurationAllShop('pagecache_currencies_to_cache', implode(',', $pagecache_currencies_to_cache));
}
public function patchSmartyConfigFront() {
if (Tools::version_compare(_PS_VERSION_,'1.7','>')) {
// This modification has been accepted on github https://github.com/PrestaShop/PrestaShop/pull/8744
$smartyFrontCongigFile = _PS_CONFIG_DIR_ . '/smartyfront.config.inc.php';
$str = Tools::file_get_contents($smartyFrontCongigFile);
if (strpos($str, "\$widget->renderWidget(null, \$params)") !== false) {
file_put_contents($smartyFrontCongigFile . '.before_' . $this->name , $str);
$str = str_replace("\$widget->renderWidget(null, \$params)", "Hook::coreRenderWidget(\$widget, isset(\$params['hook']) ? \$params['hook'] : null, \$params)", $str);
file_put_contents($smartyFrontCongigFile, $str);
}
else if (strpos($str, "\$widget->renderWidget(isset(\$params['hook']) ? \$params['hook'] : null, \$params)") !== false) {
file_put_contents($smartyFrontCongigFile . '.before_' . $this->name , $str);
$str = str_replace("\$widget->renderWidget(isset(\$params['hook']) ? \$params['hook'] : null, \$params)", "Hook::coreRenderWidget(\$widget, isset(\$params['hook']) ? \$params['hook'] : null, \$params)", $str);
file_put_contents($smartyFrontCongigFile, $str);
}
}
}
public function patchSmartyConfigFrontWidgetBlock() {
if (Tools::version_compare(_PS_VERSION_,'1.7','>')) {
$smartyFrontCongigFile = _PS_CONFIG_DIR_ . '/smartyfront.config.inc.php';
$str = Tools::file_get_contents($smartyFrontCongigFile);
if (strpos($str, "smartyWidgetBlockPageCache") === false) {
file_put_contents($smartyFrontCongigFile . '.before_' . $this->name . '_widget_block' , $str);
$str = preg_replace(
"/smartyRegisterFunction\s*\(\s*\\\$smarty\s*,\s*'block'\s*,\s*'widget_block'\s*,\s*'smartyWidgetBlock'\s*\)\s*;/",
"if (Module::isEnabled('".$this->name."')) {\n\trequire_once _PS_MODULE_DIR_ . '".$this->name."/".$this->name.".php';\n\tsmartyRegisterFunction(\$smarty, 'block', 'widget_block', array('" . get_class($this) . "', 'smartyWidgetBlockPageCache'));\n\t\$smarty->registerFilter('pre', array('" . get_class($this) . "', 'smartyWidgetBlockPageCachePrefilter'));\n} else {\n\tsmartyRegisterFunction(\$smarty, 'block', 'widget_block', 'smartyWidgetBlock');\n}",
$str);
}
else {
// Make sure it uses the correct class
$str = str_replace('\'pagecachestd/pagecachestd.php\'', '\''.$this->name.'/'.$this->name.'.php\'', $str);
$str = str_replace('\'pagecachestd\'', '\''.$this->name.'\'', $str);
$str = str_replace('\'PageCacheStd\'', '\''.get_class($this).'\'', $str);
$str = str_replace('\'pagecache/pagecache.php\'', '\''.$this->name.'/'.$this->name.'.php\'', $str);
$str = str_replace('\'pagecache\'', '\''.$this->name.'\'', $str);
$str = str_replace('\'PageCache\'', '\''.get_class($this).'\'', $str);
$str = str_replace('\'jprestaspeedpack/jprestaspeedpack.php\'', '\''.$this->name.'/'.$this->name.'.php\'', $str);
$str = str_replace('\'jprestaspeedpack\'', '\''.$this->name.'\'', $str);
$str = str_replace('\'Jprestaspeedpack\'', '\''.get_class($this).'\'', $str);
}
file_put_contents($smartyFrontCongigFile, $str);
// Now clear the cache to recompile everything
Tools::clearCompile();
}
}
public function installStaticCode() {
$path = _PS_ROOT_DIR_ . DIRECTORY_SEPARATOR . 'index.php';
// Uninstall first to be sure we install the latest code
$this->uninstallStaticCode();
$scriptCode = '// ~~start-pagecacheultimate~~ Do not remove this comment, '.$this->name.' will update it automatically
$staticCacheScript = dirname(__FILE__).\'/modules/'.$this->name.'/static.config.php\';
if (file_exists($staticCacheScript)) {
try {
require_once $staticCacheScript;
} catch (Throwable $e) {
error_log("Page Cache Ultimate - Cannot use the static cache, an error occured: " . $e->getMessage());
}
}
// ~~end-pagecacheultimate~~ Do not remove this comment, '.$this->name.' will update it automatically
';
if (file_exists($path)) {
$content = Tools::file_get_contents($path);
if ($content) {
$newContent = preg_replace('#require[\s(]+dirname\s*\(\s*__FILE__\s*\)\s*\.\s*\'/config/config.inc.php\'[\s)]*;\s*\r?\n#',$scriptCode . '$0', $content);
file_put_contents($path, $newContent);
}
}
}
public function checkStaticCode() {
$path = _PS_ROOT_DIR_ . DIRECTORY_SEPARATOR . 'index.php';
if (file_exists($path)) {
$content = Tools::file_get_contents($path);
if (JprestaUtils::strpos($content, '~~start-pagecacheultimate~~') === false) {
$this->installStaticCode();
}
}
}
private function uninstallStaticCode() {
$path = _PS_ROOT_DIR_ . DIRECTORY_SEPARATOR . 'index.php';
if (file_exists($path)) {
$content = Tools::file_get_contents($path);
if ($content) {
$newContent = preg_replace('#// ~~start-pagecacheultimate~~.*~~end-pagecacheultimate~~[^\n\r]*\r?\n#s', '', $content);
file_put_contents($path, $newContent);
}
}
}
public function getContent()
{
$link = $this->context->link->getAdminLink('AdminPageCacheConfiguration');
if (Tools::version_compare(_PS_VERSION_, '1.7', '>')) {
Tools::redirect($link);
} else {
// There is a bug in redirect and getAdminLink in PS1.5 and PS1.6 so we do it ourselves
$path = parse_url($_SERVER['REQUEST_URI'])['path'];
header('Loc'.'ation: //' . $_SERVER['HTTP_HOST'] . dirname($path) . '/' . $link);
exit;
}
}
public function hookDisplayHeader() {
// Forward to sub modules
foreach ($this->jpresta_submodules as $jpresta_submodule) {
$jpresta_submodule->displayHeader();
}
if (self::canBeCached() || self::isDisplayStats()) {
// A bug in PS 1.6.0.6 insert jquery multiple times in CCC mode
$already_inserted = false;
foreach ($this->context->controller->js_files as $js_uri)
{
$already_inserted = $already_inserted || (strstr($js_uri, 'jquery-') !== false) || (strstr($js_uri, 'jquery.js') !== false);
}
if (!$already_inserted) {
$this->context->controller->addJquery();
}
$this->context->controller->addJS($this->_path.'views/js/pagecache-v9-0-9.js');
if (self::isDisplayStats()) {
$this->context->controller->addCSS($this->_path.'views/css/pagecache.css');
}
if (Tools::version_compare(_PS_VERSION_,'1.6','<=')) {
// Make sure pagecache will be the first javascript to be loaded. This avoid
// other javascript errors to block pagecache treatments. So we place it just after
// jquery.
$new_js_files = array();
$pagecache_js_file = null;
$jquery_js_files = array();
foreach ($this->context->controller->js_files as $js_file) {
if (strstr($js_file, '/js/jquery/') !== false || strstr($js_file, 'jquery.js') !== false) {
$jquery_js_files[] = $js_file;
}
elseif (empty($pagecache_js_file) && strstr($js_file, 'pagecache-v9-0-9.js') !== false) {
$pagecache_js_file = $js_file;
} else {
$new_js_files[] = $js_file;
}
}
if (!empty($pagecache_js_file)) {
array_unshift($new_js_files, $pagecache_js_file);
}
$jquery_js_files = array_reverse($jquery_js_files);
foreach ($jquery_js_files as $jquery_js_file) {
array_unshift($new_js_files, $jquery_js_file);
}
$this->context->controller->js_files = $new_js_files;
}
if (self::canBeCached()) {
// There is no escape method available to allow to display javascript code
// so we cannot use a template
$js = trim(Configuration::get('pagecache_cfgadvancedjs'));
if (JprestaUtils::isModuleEnabled('creativeelements')) {
Context::getContext()->smarty->assign('jpresta_cart_module', 'creativeelements');
$modifiedJs = str_replace("$.ajax({url:prestashop.urls.pages.cart,method:'POST',dataType:'json',data:{ajax:1,action:'update'}}).then(function(a){a.success&&a.cart&&prestashop.emit('updateCart',{reason:{linkAction:'refresh'},resp:a})});", "// Cart is refreshed from HTML to be faster", $js);
}
else {
Context::getContext()->smarty->assign('jpresta_cart_module', 'default');
$modifiedJs = str_replace("setTimeout(\"prestashop.emit('updateCart', {reason: {linkAction: 'refresh'}, resp: {errors:[]}});\", 10);", "// Cart is refreshed from HTML to be faster", $js);
}
$updateCartInTpl = $js != $modifiedJs;
$dynJs = '
var jprestaUpdateCartDirectly = '.($updateCartInTpl ? '1' : '0').';
var jprestaUseCreativeElements = '.(JprestaUtils::isModuleEnabled('creativeelements') ? '1' : '0').';
pcRunDynamicModulesJs = function() {
'; // Let the new line here!
if (!empty($modifiedJs)) {
$dynJs .= $modifiedJs;
}
$dynJs .= '
};'; // Let the new line here!
return $dynJs;
}
else {
return '';
}
}
elseif (Configuration::get('pagecache_skiplogged') && Context::getContext()->customer->isLogged()) {
// User want to disable cache for logged in users so we add a random URL parameter
// to all links to disable previous cache done by browser
return $this->display(__FILE__, 'pagecache-disablecache.tpl');
} else {
return '';
}
}
public function hookdisplayMobileHeader() {
$this->hookDisplayHeader();
}
public function hookActionShopDataDuplication($params) {
//(int)$params['new_id_shop']
//(int)$params['old_id_shop']
$new_id_shop = (int)$params['new_id_shop'];
$this->_setDefaultConfiguration(Shop::getGroupFromShop($new_id_shop), $new_id_shop);
}
public function hookActionOutputHTMLBefore($params) {
if (self::canBeCached()) {
// Save the generated HTML into a file and display it => create a cache
$this->cacheThis($params['html']);
if (self::isCacheWarmer() || self::isStatusChecker()) {
// Reduce the size of the response to the minimum (save bandwidth and time)
if (!headers_sent()) {
if (!self::isNotCode200()) {
// Here our cache-warmer/status-checker do not care about these headers, just remove them
header_remove();
// Indicates that there is no content so it removes "Content-Length" and "Content-Type" headers
header("HTTP/1.1 204 CACHE CREATED");
}
// Unset PHP session (to avoid a useless cookie)
session_abort();
// Don't send any cookies
Context::getContext()->cookie->disallowWriting();
}
die();
}
}
}
public function hookActionDispatcherBefore()
{
self::init();
$this->pre_display_html = self::preDisplayStats();
if (!Configuration::get('pagecache_use_dispatcher_hook') && self::displayCacheIfExists()) {
self::displayStats(true, $this->pre_display_html);
die();
}
}
public function hookActionAdminProductsControllerSaveAfter($params) {
if (self::$updatingProductFromAdminController) {
// Many datas are saved AFTER the postProcess of AdminProductsController so we use this hook for PS < 1.7.
self::$updatingProductFromAdminController = false;
$this->hookActionObjectProductUpdateAfter(['object' => new Product(Tools::getValue('id_product'))]);
}
}
public function hookActionDispatcherAfter()
{
self::sendContextCookie();
self::$skipUpdateCacheKey = false;
if (self::$needUpdateCacheKey) {
self::updateCacheKeyForCountries();
self::updateCacheKeyForUserGroups();
// In case it is called multiple times
self::$needUpdateCacheKey = false;
}
if (self::$updatingProductFromAdminController) {
// Many datas are saved AFTER the postProcess of AdminProductsController so we use this hook.
self::$updatingProductFromAdminController = false;
$this->hookActionObjectProductUpdateAfter(['object' => new Product(Tools::getValue('id_product'))]);
}
self::displayStats(false, $this->pre_display_html);
}
/**
* Update the context after customer has been created
*/
public function hookActionCustomerAccountAdd() {
self::sendContextCookie();
}
/**
* Update the context after customer has logged in
*/
public function hookActionAuthentication() {
self::sendContextCookie();
}
/**
* Update the context after customer has logged out
*/
public function hookActionCustomerLogoutAfter() {
self::sendContextCookie();
}
public function hookActionDispatcher() {
if (self::canBeCached())
{
if (Configuration::get('pagecache_use_dispatcher_hook') && self::displayCacheIfExists()) {
self::displayStats(true, $this->pre_display_html);
die();
}
// Remove cookie, cart and customer informations to cache
// a 'standard' page
Tools::setCookieLanguage($this->context->cookie);
// Write cookie if needed (language changed, etc.) before we remove it
$this->context->cookie->write();
$anonymousCookie = new Cookie($this->name, '', 1);
$anonymousCookie->id_lang = $this->context->language->id;
unset($anonymousCookie->detect_language);
foreach (self::$cookies_to_preserve as $cookie_name => $cookie_value) {
if (isset($this->context->cookie->{$cookie_name})) {
if ($cookie_name === $cookie_value) {
$anonymousCookie->{$cookie_name} = $this->context->cookie->{$cookie_name};
}
else {
$anonymousCookie->{$cookie_name} = $cookie_value;
}
}
}
// Some cookies are set in header like for autolanguagecurrency module. We need to preserve them and remove
// the others
if (method_exists($anonymousCookie, 'getAll')) {
foreach ($anonymousCookie->getAll() as $anonymousCookieName => $anonymousCookieValue) {
if (!array_key_exists($anonymousCookieName, self::$cookies_to_preserve)) {
unset($anonymousCookie->{$anonymousCookieName});
}
}
}
$anonymousCustomer = JprestaCustomer::getOrCreateCustomerWithSameGroups($this->context->customer);
$addressForTaxes = $this->getAddressForTaxes($this->context);
$this->context->customer = $anonymousCustomer;
if ($addressForTaxes) {
$this->context->customer->geoloc_id_country = $addressForTaxes->id_country;
$this->context->customer->id_state = $addressForTaxes->id_state;
$this->context->customer->postcode = $addressForTaxes->postcode;
// The address of current customer will be used to generate the cache
// We cheat the memory cache (restricted to the execution of this script) to get the correct address
// while computing taxes.
Cache::store('Address::getFirstCustomerAddressId_' . (int)$this->context->customer->id . '-' . (bool)true,
$addressForTaxes->id);
Cache::store('Address::initialize_' . md5((int)$this->context->customer->geoloc_id_country . '-' . (int)$addressForTaxes->id_state . '-' . $addressForTaxes->postcode),
$addressForTaxes);
}
$this->context->cookie = $anonymousCookie;
$this->context->cart = new Cart();
$this->context->cookie->id_customer = $this->context->customer->id;
}
else {
if ($_SERVER['REQUEST_METHOD'] === 'POST' && !headers_sent()) {
// Be sure that the cache directive is set to improve GTMetrix and PageSpeed Insight score
header("Cache-Control: no-store, no-cache, must-revalidate, max-age=0");
}
}
}
private static function sendContextCookie() {
static $cookieSent = false;
if (!$cookieSent
&& !defined('_PS_ADMIN_DIR_')
&& !headers_sent()
&& self::getCache()->needsContextCookie()
) {
$cookieSent = true;
// Set the cache context in the cookies that will be used by the static cache
if (PHP_VERSION_ID <= 50200) /* PHP version > 5.2.0 */
setcookie('jpresta_cache_context', PageCacheDAO::getContextKeyById(PageCacheDAO::getOrCreateContextId(self::getCacheKeyInfos(true))), time()+60*60*1, '/', '', 0);
else
setcookie('jpresta_cache_context', PageCacheDAO::getContextKeyById(PageCacheDAO::getOrCreateContextId(self::getCacheKeyInfos(true))), time()+60*60*1, '/', '', 0, false);
}
}
/**
* Create a cache key depending on address used to determine taxes. This cache key can be configured to reduce the
* number of different value.
*/
public static function getCountryStateZipcodeForTaxes($context)
{
static $current_loc_tax_key = null;
if ($current_loc_tax_key === null) {
$current_loc_tax_key = '-/-/-';
// Taxes are determined by country, state and zipcode of the delivery or invoice address
// If there is no cart or no address defined in cart then standard localization will be used for taxes
$addressForTaxes = self::getAddressForTaxes($context);
if ($addressForTaxes) {
$cacheKey = '';
if ((int)$addressForTaxes->id_country > 0) {
$country = new Country((int)$addressForTaxes->id_country);
$cacheKey .= $country->getFieldByLang('name') . '/';
}
else {
$cacheKey .= '*/';
}
if ((int)$addressForTaxes->id_state > 0) {
$state = new State((int)$addressForTaxes->id_state);
$cacheKey .= $state->getFieldByLang('name') . '/';
}
else {
$cacheKey .= '*/';
}
if ($addressForTaxes->postcode) {
$cacheKey .= $addressForTaxes->postcode;
}
else {
$cacheKey .= '*';
}
// Only set it once it is complete
$current_loc_tax_key = $cacheKey;
}
}
return $current_loc_tax_key;
}
public static function getTaxManagerDetails($context) {
static $current_tax_manager_details = null;
if ($current_tax_manager_details === null) {
$current_tax_manager_details = false;
$addressForTaxes = self::getAddressForTaxes($context);
if ($addressForTaxes && (bool) Configuration::get('PS_TAX')) {
$json = JprestaUtilsTaxManager::toJson($context->shop->id, $addressForTaxes);
$current_tax_manager_details = PageCacheDAO::getOrCreateDetailsId($json);
}
}
return $current_tax_manager_details;
}
protected static function getAddressForTaxes($context) {
static $current_tax_address = null;
if ($current_tax_address === null) {
$current_tax_address = false;
// Taxes are determined by country, state and zipcode of the delivery or invoice address
// If there is no cart or no address defined in cart then standard localization will be used for taxes
if ($context->cookie->jpresta_id_adress_for_taxes) {
// Set by the Cache-Warmer
$current_tax_address = Address::initialize($context->cookie->jpresta_id_adress_for_taxes);
}
else {
/* Cart is initialized in FrontController::init which is after first call to this function */
if ((int)$context->cookie->id_cart) {
$cart = new Cart($context->cookie->id_cart);
$id_address = $cart->{Configuration::get('PS_TAX_ADDRESS_TYPE')};
/* If address is not set then FrontController::init will set the it with the first address of the customer */
if (!isset($id_address) || $id_address == 0) {
$id_address = (int)Address::getFirstCustomerAddressId($cart->id_customer);
}
if ($id_address) {
$current_tax_address = Address::initialize($id_address);
if ($current_tax_address->id_customer != $context->cookie->id_customer) {
// In some cases the address associated to the cart does not belong to the current user, maybe when using a superuser module
header('x-jpresta-fix-address: 1');
$current_tax_address = false;
$id_address = (int)Address::getFirstCustomerAddressId($context->cookie->id_customer);
if ($id_address) {
$current_tax_address = Address::initialize($id_address);
}
}
}
} else {
if ($context->cookie->id_customer) {
/* There is no cart but a customer is logged in */
$id_address = (int)(Address::getFirstCustomerAddressId($context->cookie->id_customer));
if ($id_address) {
/* Take his first address */
$current_tax_address = Address::initialize($id_address);
}
}
}
}
if (!$current_tax_address) {
// As it is done in Product::getPriceStatic when there is no adress given
$current_tax_address = Address::initialize(null, true);
}
}
return $current_tax_address;
}
private static function getGroupsIds($context) {
if (isset($context->customer) && $context->customer->isLogged()) {
// Compute groups IDs like in dispatcher hook
if ((int)$context->customer->id === 0) {
// Handle it here because it is not in PS1.5
$groupsIds = array((int)Configuration::get('PS_UNIDENTIFIED_GROUP'));
}
else {
if (!Group::isFeatureActive()) {
$groupsIds = [(int) $context->customer->id_default_group];
}
else {
$groupsIds = Customer::getGroupsStatic((int)$context->customer->id);
}
// Put the default group at the beginning
foreach ($groupsIds as $arrayKey => $groupId) {
if ($groupId === (int) $context->customer->id_default_group) {
$groupsIds[$arrayKey] = $groupsIds[0];
$groupsIds[0] = (int) $context->customer->id_default_group;
}
}
}
} else {
$groupsIds = [(int) Configuration::get('PS_UNIDENTIFIED_GROUP')];
}
return $groupsIds;
}
private static function _getDynamicHookInfos($hookName, $module) {
if (!self::canBeCached()) {
return false;
}
$dyn_hooks = Configuration::get('pagecache_dyn_hooks', '');
$dyn_hook_part = strstr($dyn_hooks, Tools::strtolower($hookName) . '|' . $module);
if ($dyn_hook_part !== false) {
$comma_pos = strpos($dyn_hook_part, ',');
if ($comma_pos !== false) {
$dyn_hook_part = Tools::substr($dyn_hook_part, 0, $comma_pos);
}
$dyn_hook_part_array = array_pad(explode('|', $dyn_hook_part), 3, 0);
$dyn_hook_part = array('empty_box' => $dyn_hook_part_array[2]);
}
return $dyn_hook_part;
}
private static function _getHookCacheDirectives($moduleName, $hookName) {
$directives = array('wrapper' => false, 'content' => true);
// Remove 'hook' prefix
$hookName = str_replace('hook', '', $hookName);
$infos = self::_getDynamicHookInfos($hookName, $moduleName);
if ($infos !== false) {
$directives['wrapper'] = true;
$directives['content'] = !$infos['empty_box'];
}
return $directives;
}
private static function getDynamicWidgetInfos($moduleName, $hookName) {
if (!self::canBeCached()) {
return false;
}
$dyn_widgets = Configuration::get('pagecache_dyn_widgets', '');
$dyn_widget_part = strstr($dyn_widgets, Tools::strtolower($moduleName) . '|' . Tools::strtolower($hookName));
if ($dyn_widget_part === false) {
// Kept for compatibility reason (before empty box for widget)
$dyn_widget_part = strstr($dyn_widgets, Tools::strtolower($moduleName) . '|,');
}
if ($dyn_widget_part === false) {
$dyn_widget_part = strstr($dyn_widgets, Tools::strtolower($moduleName) . '||');
}
if ($dyn_widget_part !== false) {
$comma_pos = strpos($dyn_widget_part, ',');
if ($comma_pos !== false) {
$dyn_widget_part = Tools::substr($dyn_widget_part, 0, $comma_pos);
}
$dyn_widget_part_array = array_pad(explode('|', $dyn_widget_part), 3, 0);
$dyn_widget_part = array('empty_box' => $dyn_widget_part_array[2]);
}
return $dyn_widget_part;
}
private static function _getWidgetCacheDirectives($moduleName, $hookName) {
$directives = array('wrapper' => false, 'content' => true);
$infos = self::getDynamicWidgetInfos($moduleName, $hookName);
if ($infos !== false) {
$directives['wrapper'] = true;
$directives['content'] = !$infos['empty_box'];
}
return $directives;
}
public static function getManagedControllers() {
if (self::$managed_controllers === null) {
self::$managed_controllers = json_decode(JprestaUtils::getConfigurationOfCurrentShop('pagecache_managed_ctrl'), true);
if (!self::$managed_controllers) {
self::$managed_controllers = self::$managed_controllers_default;
}
self::$managed_object_classes = array_flip(
array_filter(
array_map(
function ($eltToMap) {
return $eltToMap['object_class'];
},
self::$managed_controllers
)
)
);
}
return self::$managed_controllers;
}
/**
* @return string[]
*/
public static function getManagedControllersNames() {
return array_keys(self::getManagedControllers());
}
private static function getCurrentObjectId() {
$id_object = null;
$controllerName = self::getControllerName();
if ($controllerName) {
// Make sure managed controllers are initialized
self::getManagedControllers();
$controllerClassName = isset(self::$managed_controllers[$controllerName]['ctrl_class']) ? self::$managed_controllers[$controllerName]['ctrl_class'] : null;
if ($controllerClassName && !class_exists($controllerClassName) && Tools::strpos($controllerName, '__') !== false) {
// Sometimes the override class is not loaded so we do it here
list($moduleName, $controllerShortName) = explode('__', $controllerName, 2);
$overrideFile = _PS_OVERRIDE_DIR_ . 'modules/' . $moduleName . '/controllers/front/' . $controllerShortName . '.php';
if (file_exists($overrideFile)) {
include_once $overrideFile;
}
}
if ($controllerClassName && class_exists($controllerClassName)) {
$controllerInstance = Controller::getController($controllerClassName);
if ($controllerInstance && method_exists($controllerInstance, 'getJprestaModelObjectId')) {
try {
$id_object = $controllerInstance->getJprestaModelObjectId();
}
catch (Exception $e) {
JprestaUtils::addLog("PageCache | An exception occured while executing $controllerClassName::getJprestaModelObjectId(): " . $e->getMessage());
}
}
}
else {
$id_object = Tools::getValue('id_' . $controllerName);
}
}
return $id_object;
}
public static function getManagedControllerId($controllerName) {
// Make sure managed controllers are initialized
self::getManagedControllers();
if (array_key_exists($controllerName, self::$managed_controllers)) {
return self::$managed_controllers[$controllerName]['id'];
}
else {
return null;
}
}
public static function getManagedControllerNameById($controllerId) {
// Make sure managed controllers are initialized
self::getManagedControllers();
foreach (self::$managed_controllers as $controllerName => $controllerInfos) {
if ($controllerId == $controllerInfos['id']) {
return $controllerName;
}
}
return 'Unknown controller';
}
public static function addManagedControllerName($controllerName, $objectClass, $moduleName, $controllerClassName) {
// Make sure managed controllers are initialized
self::getManagedControllers();
if (!in_array($controllerName, self::$managed_controllers)) {
self::$managed_controllers[$controllerName] = [];
self::$managed_controllers[$controllerName]['id'] = count(self::$managed_controllers);
self::$managed_controllers[$controllerName]['object_class'] = $objectClass;
self::$managed_controllers[$controllerName]['module'] = $moduleName;
self::$managed_controllers[$controllerName]['ctrl_class'] = $controllerClassName;
JprestaUtils::saveConfigurationOfCurrentShop('pagecache_managed_ctrl', json_encode(self::$managed_controllers));
}
}
public static function removeManagedControllerName($controllerName) {
// Make sure managed controllers are initialized
self::getManagedControllers();
if (array_key_exists($controllerName, self::$managed_controllers)) {
unset(self::$managed_controllers[$controllerName]);
JprestaUtils::saveConfigurationOfCurrentShop('pagecache_managed_ctrl', json_encode(self::$managed_controllers));
}
}
public static function canBeCached() {
// static variable avoid computing the canBeCached multiple times
static $canBeCached = null;
if ($canBeCached === null) {
if (JprestaUtils::isAjax()) {
$canBeCached = false;
self::$status_reason = 'ajax';
}
elseif (Tools::getIsset('jprestalivetoken')) {
$canBeCached = false;
self::$status_reason = 'live-theme-configurator';
}
elseif (Tools::getIsset('adtoken')) {
$canBeCached = false;
self::$status_reason = 'preview';
}
elseif (defined('_PS_ADMIN_DIR_')) {
$canBeCached = false;
self::$status_reason = 'not-front-controller';
}
elseif (Tools::getIsset('open') && JprestaUtils::isModuleEnabled('gsnippetsreviews')) {
$canBeCached = false;
self::$status_reason = 'open-gsnippetsreviews';
}
else {
if (!Configuration::get('pagecache_debug') && !Configuration::get('pagecache_always_infosbox') && (Tools::getIsset('dbgpagecache') || Tools::getIsset('delpagecache'))) {
// Remove module's parameters in production mode to avoid them to be referenced in search engines
$url = self::getCurrentURL();
$url = preg_replace('/&?dbgpagecache=[0-1]?/', '', $url);
$url = preg_replace('/&?delpagecache=[0-1]?/', '', $url);
$url = str_replace('?&', '?', $url);
$url = preg_replace('/\?$/', '', $url);
header('Status: 301 Moved Permanently', false, 301);
Tools::redirect($url);
}
$canBeCached = self::isGetRequest()
&& self::isCacheEnabledOrDebugOn()
&& self::isTokenDisabled()
&& self::isOverridesEnabled()
&& !self::isCustomerWithSpecificPricesOrPermissions()
&& !self::isGoingToBeRedirected()
&& !self::isRestrictedCountry()
&& !self::isExcludedByRegex()
&& self::isCacheEnabledForCurrency()
&& self::isNotLogout()
&& self::isNotSkipLoggedUsers()
&& !self::isUsingAffiliateCode()
&& self::isCacheEnabledForController(self::getControllerName())
&& !self::isCustomizedProduct(self::getControllerName())
&& !self::isInEditionWithCreativeElements()
;
}
}
return $canBeCached;
}
/**
* If the page is generated by a module controller, then check if this controller can be cached
*/
private static function checkModuleController() {
$controllerFullName = self::getControllerName();
if (JprestaUtils::getConfigurationAllShop('pagecache_'.$controllerFullName, 'no') === 'no' || Tools::getIsset('initcache')) {
if (JprestaUtilsModule::isModuleController($controllerFullName)) {
// Force the update of the values
self::removeManagedControllerName($controllerFullName);
if (!JprestaUtilsModule::canBeCached($controllerFullName)) {
Configuration::updateValue('pagecache_'.$controllerFullName, 0);
}
else {
Configuration::updateValue('pagecache_'.$controllerFullName, 1);
Configuration::updateValue('pagecache_'.$controllerFullName.'_timeout', 60 * 24 * 7);
Configuration::updateValue('pagecache_'.$controllerFullName.'_expires', 15);
self::addManagedControllerName($controllerFullName,
JprestaUtilsModule::getModelObjectClassName($controllerFullName),
JprestaUtilsModule::getModuleName($controllerFullName),
JprestaUtilsModule::getControllerClassName($controllerFullName));
}
}
}
}
/**
* @param $url string URL to check. If not given then the current URL will be checked
* @return bool true if the URL is excluded from the cache
*/
public static function isExcludedByRegex($url = null) {
$regex = JprestaUtils::decodeConfiguration(Configuration::get('pagecache_ignore_url_regex'));
if ($regex) {
$ret = preg_match('/' . $regex . '/', $url === null ? self::getCurrentURL() : $url);
if ($ret === 1) {
self::$status_reason = 'excluded-by-regex';
return true;
}
elseif ($ret === false) {
JprestaUtils::addLog('PageCache | Error #' . preg_last_error() . ' in the regex "' . $regex . '"', 2);
}
}
return false;
}
private static function isGetRequest() {
$isGet = strcmp(self::getServerValue('REQUEST_METHOD'), 'GET') == 0;
if (!$isGet) {
self::$status_reason = 'not-a-get-request';
}
return $isGet;
}
private static function isCacheEnabledForController($controller) {
$isEnabled = Configuration::get('pagecache_'.$controller);
if (!$isEnabled) {
self::$status_reason = 'disabled-controller: ' . $controller;
}
return $isEnabled;
}
/**
* @param $iso_code string ISO code of the currency to check. If not given the current currency will be checked.
* @return bool true if the currency can be cached
*/
public static function isCacheEnabledForCurrency($iso_code = null) {
if ($iso_code === null) {
$context = Context::getContext();
// Make sure the currency is set
self::getCurrencyId($context);
$isEnabled = !$context->currency || array_key_exists($context->currency->iso_code,
array_flip(explode(',', Configuration::get('pagecache_currencies_to_cache'))));
}
else {
$isEnabled = array_key_exists($iso_code, array_flip(explode(',', Configuration::get('pagecache_currencies_to_cache'))));
}
if (!$isEnabled) {
self::$status_reason = 'disabled-currency';
}
return $isEnabled;
}
private static function isCacheEnabledOrDebugOn() {
$isEnabled = !Configuration::get('pagecache_debug') || ((int)Tools::getValue('dbgpagecache', 0) == 1);
if (!$isEnabled) {
self::$status_reason = 'test-mode';
}
return $isEnabled;
}
private static function isTokenDisabled() {
$isDisabled = (int)(Configuration::get('PS_TOKEN_ENABLE')) != 1;
if (!$isDisabled) {
self::$status_reason = 'tokens-enabled';
}
return $isDisabled;
}
private static function isNotLogout() {
$isNotLogout = Tools::getValue('logout') === false && Tools::getValue('mylogout') === false;
if (!$isNotLogout) {
self::$status_reason = 'logout';
}
return $isNotLogout;
}
private static function isNotSkipLoggedUsers() {
$isNotSkipLoggedUsers = !Configuration::get('pagecache_skiplogged') || !Context::getContext()->customer->isLogged();
if (!$isNotSkipLoggedUsers) {
self::$status_reason = 'skip-logged-users';
}
return $isNotSkipLoggedUsers;
}
/**
* Customization is not a module and therefore cannot be refreshed. The workaround is to disable
* cache for these products
* @param string $controller Controller name
* @return boolean true if current page is a customized product
*/
private static function isCustomizedProduct($controller) {
if (strcmp($controller, 'product') != 0
|| !Customization::isFeatureActive()
|| Configuration::get('pagecache_cache_customizable')) {
return false;
}
if ($id_product = (int)Tools::getValue('id_product')) {
$customizationFieldCount = (int) JprestaUtils::dbGetValue('
SELECT COUNT(*)
FROM `'._DB_PREFIX_.'customization_field`
WHERE `id_product` = '.(int)$id_product);
if ($customizationFieldCount > 0) {
self::$status_reason = 'customized-product';
return true;
}
}
return false;
}
/**
* Do not cache if status code is not 200
* @return boolean true if user will be redirected to an other page or if statuts is not 200
*/
private static function isGoingToBeRedirected() {
$reason = 'redirect';
$redirect = self::isNotCode200() || self::isSSLRedirected() || self::isMaintenanceEnabled();
if (!$redirect && JprestaUtils::isModuleEnabled('autolanguagecurrency') && Configuration::get('AUTOCURRLANG_ENABLED')) {
if (class_exists('AutoLanguageCurrency') && method_exists('AutoLanguageCurrency', 'needsGeolocate')) {
$redirect = AutoLanguageCurrency::needsGeolocate();
}
else {
$context = Context::getContext();
if (!isset($context->cookie->autolocation) || $context->cookie->autolocation == '0' || !$context->cookie->autolocation) {
$redirect = true;
}
}
$reason = 'redirect-autolanguagecurrency';
}
if (!$redirect && JprestaUtils::isModuleEnabled('configb2b')) {
$module = Module::getInstanceByName('configb2b');
if (method_exists($module, 'isB2b')) {
if (Dispatcher::getInstance()->getController()== 'index' && $module->isB2b() && Tools::getValue('mylogout') !== '1') {
$redirect = true;
}
if (Dispatcher::getInstance()->getController() == 'cms'){
if (in_array(Tools::getValue('id_cms'),explode(" ", Configuration::get('CONFIGB2B_CMS'))) && $module->isB2b() == false) {
$redirect = true;
}
}
}
$reason = 'redirect-configb2b';
}
if (!$redirect && JprestaUtils::isModuleEnabled('pkamp')) {
$context = Context::getContext();
$ampClass = new Promokit\Module\Pkamp\Classes\Amp();
$ampConfig = $ampClass->getAmpConfiguration();
if ((isset($ampConfig['general_force_amp']) && $ampConfig['general_force_amp']) && $context->isMobile() && !$context->isTablet()) {
$redirect = true;
}
if (isset($ampConfig['general_force_ontablet']) && @$ampConfig['general_force_ontablet'] && ($context->isTablet() || stristr(@$_SERVER['HTTP_USER_AGENT'], 'iPad'))) {
$redirect = true;
}
$reason = 'redirect-pkamp';
}
if ($redirect) {
self::$status_reason = $reason;
}
return $redirect;
}
private static function isNotCode200() {
if (function_exists('http_response_code') && !defined('HHVM_VERSION')) {
$code = http_response_code();
if (!empty($code)) {
if (http_response_code() !== 200) {
return true;
}
}
}
return false;
}
private static function isSSLRedirected() {
return (Configuration::get('PS_SSL_ENABLED') && self::getServerValue('REQUEST_METHOD') != 'POST' && Configuration::get('PS_SSL_ENABLED_EVERYWHERE') && !Tools::usingSecureMode());
}
public static function isMaintenanceEnabled() {
$isMaintenanceEnabled = false;
if (!(int)Configuration::get('PS_SHOP_ENABLE')) {
$isMaintenanceEnabled = true;
// Check if admins can display the site and if current user is an admin
$maintenance_allow_admins = (bool) Configuration::get('PS_MAINTENANCE_ALLOW_ADMINS');
$is_admin = (int) (new Cookie('psAdmin'))->id_employee;
if ($is_admin && $maintenance_allow_admins) {
$isMaintenanceEnabled = false;
}
// Check if current IP is allowed to display the site
$allowed_ips = array_map('trim', explode(',', Configuration::get('PS_MAINTENANCE_IP')));
if (class_exists('IpUtils')
&& method_exists('IpUtils', 'checkIp')
&& IpUtils::checkIp(Tools::getRemoteAddr(), $allowed_ips)) {
$isMaintenanceEnabled = false;
}
else if (in_array(Tools::getRemoteAddr(), $allowed_ips)) {
$isMaintenanceEnabled = false;
}
}
return $isMaintenanceEnabled;
}
/**
* Must be called after geolocalisation has been done
* @return bool true if the visitor is located in a restricted country
*/
private static function isRestrictedCountry() {
$restrictedCountry = false;
$controller_instance = self::getControllerInstance();
if ($controller_instance !== false && method_exists($controller_instance, 'isRestrictedCountry')) {
$restrictedCountry = $controller_instance->isRestrictedCountry();
if ($restrictedCountry) {
self::$status_reason = 'restricted-country';
}
}
return $restrictedCountry;
}
/**
* Cache must be disabled if a customer has a specific price, discount, permissions, etc.
*/
private static function isCustomerWithSpecificPricesOrPermissions() {
$context = Context::getContext();
$id_customer = $context->customer ? $context->customer->id : 0;
if ($id_customer > 0) {
$now = date('Y-m-d H:i:00');
$count_existing = 'SELECT count(*) FROM `'._DB_PREFIX_.'specific_price` WHERE id_customer='.(int)$id_customer.
' AND (`from` = \'0000-00-00 00:00:00\' OR \'' . pSQL($now) . '\' >= `from`)' .
' AND (`to` = \'0000-00-00 00:00:00\' OR \'' . pSQL($now) . '\' <= `to`)'
;
if ((int) JprestaUtils::dbGetValue($count_existing) > 0) {
// Current customer has specific prices for him so cache must be disabled
self::$status_reason = 'customer-specific-prices';
return true;
}
}
// Compatibility with superuser module by MassonVincent
if (JprestaUtils::isModuleEnabled('superuser')) {
$ips = Configuration::get('superuser_ips');
$ip = explode(',', $ips);
if (!defined('_PS_ADMIN_DIR_')
&& (in_array('*', $ip) || in_array(Tools::getRemoteAddr(), $ip) && strstr($_SERVER['REQUEST_URI'],'mentions-legales'))
) {
// Disable cache so Super User module can work
self::$status_reason = 'superuser';
return true;
}
}
// Compatibility with atssuperuser module by ATSinfosystem Sotwares
if (Tools::getIsset('superuser') && JprestaUtils::isModuleEnabled('atssuperuser')) {
self::$status_reason = 'atssuperuser';
return true;
}
// Compatibility with wkadminloginascustomer module by Webkul
if (Tools::getIsset('id_customer') && JprestaUtils::isModuleEnabled('wkadminloginascustomer')) {
self::$status_reason = 'wkadminloginascustomer';
return true;
}
// Compatibility with groupinc module by Idnovate
if (JprestaUtils::isModuleEnabled('groupinc')) {
$count_rules_with_customers = 'SELECT count(*) FROM `'._DB_PREFIX_.'groupinc_configuration` WHERE customers <> \'\'';
if ((int) JprestaUtils::dbGetValue($count_rules_with_customers) > 0) {
// Some rules are specific to some customers
$count_rules_with_this_customer = 'SELECT count(*) FROM `'._DB_PREFIX_.'groupinc_configuration` WHERE customers = \''.(int)$id_customer.'\' OR customers like \''.(int)$id_customer.';%\' or customers like \'%;'.(int)$id_customer.';%\' or customers like \'%;'.(int)$id_customer.'\'';
if ((int) JprestaUtils::dbGetValue($count_rules_with_this_customer) > 0) {
self::$status_reason = 'groupinc';
return true;
}
}
}
// Compatibility with shaim_gdpr module by Dominik Shaim
if (JprestaUtils::isModuleEnabled('shaim_gdpr')) {
if ((int)Configuration::get('shaim_gdpr_zpetny_souhlas_active') == 1 && $id_customer > 0) {
$active = Db::getInstance()->executeS('SELECT `shaim_gdpr_active` FROM `' . _DB_PREFIX_ . 'customer` WHERE `id_customer` = ' . (int)$id_customer . ';');
$active = (isset($active[0]['shaim_gdpr_active']) ? (int)$active[0]['shaim_gdpr_active'] : 0);
if (!$active) {
self::$status_reason = 'shaim_gdpr';
return true;
}
}
}
}
/**
* Cache must be disabled if the visitor is using an affiliate code ()
*/
private static function isUsingAffiliateCode() {
if ((Tools::getIsset('affp') || Context::getContext()->cookie->__get('eam_aff_customer_cookie')) && JprestaUtils::isModuleEnabled('ets_affiliatemarketing')) {
self::$status_reason = 'using-affiliate-code';
return true;
}
return false;
}
/**
* @return bool
*/
private static function isInEditionWithCreativeElements() {
if (JprestaUtils::isModuleEnabled('creativeelements') && Configuration::get('elementor_frontend_edit')) {
$lifetime = max((int) Configuration::get('PS_COOKIE_LIFETIME_BO'), 1);
$cookie = new Cookie('psAdmin', '', time() + $lifetime * 3600);
$id_employee = isset($cookie->id_employee) ? (int) $cookie->id_employee : 0;
if ($id_employee) {
if ((bool) glob(_PS_ROOT_DIR_ . '/*/filemanager', GLOB_ONLYDIR)) {
self::$status_reason = 'editing-with-creative-element';
return true;
}
}
}
return false;
}
public static function isCacheWarmer() {
return isset($_SERVER['HTTP_USER_AGENT']) && strpos($_SERVER['HTTP_USER_AGENT'], 'JPresta-Cache-Warmer') === 0;
}
public static function isStatusChecker() {
return isset($_SERVER['HTTP_USER_AGENT']) && strpos($_SERVER['HTTP_USER_AGENT'], 'JPresta-Cache-Warmer/Status-Checker') === 0;
}
private static function setCacheWarmerContext()
{
if (Configuration::get('pagecache_debug')) {
die('Module is in test mode, warmup ignored.');
}
$context = Context::getContext();
// Disable redirect from autolanguagecurrency module
$context->cookie->autolocation = 1;
// Currency
$currencyIsoCode = JprestaUtils::getRequestHeaderValue('jpresta-currency');
if ($currencyIsoCode) {
$id_currency = Currency::getIdByIsoCode($currencyIsoCode);
if ($id_currency) {
$context->cookie->id_currency = $id_currency;
}
else {
die('Currency ' . $currencyIsoCode . ' not found, currency is not available anymore, warmup ignored.');
}
}
// Country
$countryIsoCode = JprestaUtils::getRequestHeaderValue('jpresta-country');
if ($countryIsoCode) {
if (!Validate::isLanguageIsoCode($countryIsoCode)) {
die('Country ' . $countryIsoCode . ' not found, warmup ignored.');
}
$id_country = Country::getByIso($countryIsoCode);
if ($id_country) {
if (Configuration::get('PS_GEOLOCATION_ENABLED')) {
// Disable geolocalization
$_SERVER['REMOTE_ADDR'] = 'localhost';
}
// In case PS detects country from browser language
$_SERVER['HTTP_ACCEPT_LANGUAGE'] = Tools::strtolower($countryIsoCode);
// Set country
$context->cookie->iso_code_country = $countryIsoCode;
$context->country = new Country($id_country);
} else {
die('Country ' . $countryIsoCode . ' not found, country is not available anymore, warmup ignored.');
}
}
// Device: handled in override of Context::getMobileDetect() with JprestaUtilsMobileDetect
// Customer groups
$fakeUserEmail = JprestaUtils::getRequestHeaderValue('jpresta-group');
if ($fakeUserEmail) {
$customer = new Customer();
if ($customer->getByEmail($fakeUserEmail)) {
if (!JprestaCustomer::isVisitor($customer->id)) {
// Set a JprestaCustomer object so the isLogged() method returns true
$context->customer = JprestaCustomer::getOrCreateCustomerWithSameGroups($customer, true);
$context->cookie->id_customer = $customer->id;
}
}
else {
die('User-group ' . $fakeUserEmail . ' not found, fake user has probably been deleted, warmup ignored.');
}
}
// Taxes: handled in hookTaxManager()
// Specifics
$id_specifics = JprestaUtils::getRequestHeaderValue('jpresta-id-specifics');
if ($id_specifics) {
$specifics = PageCacheDAO::getDetailsById($id_specifics);
if ($specifics) {
$jcks = new JprestaCacheKeySpecifics($specifics);
self::restoreJprestaCacheKeySpecifics($jcks);
}
else {
die('No specific context found, cache has probably been reset shortly, warmup ignored.');
}
}
}
/**
* @param $params array {'address' => address of the customer, 'params' => id_tax_rules_group / type}
* @return TaxManagerInterface|false
*/
public function hookTaxManager($params) {
static $tax_manager = array();
$id_tax_rules_group = $params['params'];
if ($id_tax_rules_group && !array_key_exists($id_tax_rules_group, $tax_manager)) {
$tax_manager[$id_tax_rules_group] = false;
if (self::isCacheWarmer()) {
$id_tax_manager = JprestaUtils::getRequestHeaderValue('jpresta-id-tax-manager');
if ($id_tax_manager) {
$taxManagerJson = PageCacheDAO::getDetailsById($id_tax_manager);
if ($taxManagerJson) {
try {
$tax_manager[$id_tax_rules_group] = new JprestaUtilsTaxManager($taxManagerJson, $id_tax_rules_group);
}
catch (Exception $e) {
die('Cannot build the tax manager from context, cache has probably been reset shortly, warmup ignored (Error: ' . $e->getMessage() . ')');
}
} else {
die('No tax manager context found, cache has probably been reset shortly, warmup ignored.');
}
}
}
}
if (!$id_tax_rules_group) {
return false;
}
return $tax_manager[$id_tax_rules_group];
}
public static function isOverridesEnabled() {
$isOverridesEnabled = Tools::version_compare(_PS_VERSION_,'1.6','<') || ((int)(Configuration::get('PS_DISABLE_OVERRIDES')) != 1);
if (!$isOverridesEnabled) {
self::$status_reason = 'overrides-disabled';
}
return $isOverridesEnabled;
}
/**
* return true if it is available, false otherwise
*/
public static function displayCacheIfExists() {
$cache = false;
$can_be_cached = self::canBeCached();
if ($can_be_cached) {
// Before checking cache, lets check cache refreshment triggers (specific prices)
PageCacheDAO::triggerReffreshment();
$controller = self::getControllerName();
$cache_ttl = 60 * ((int)Configuration::get('pagecache_'.$controller.'_timeout'));
$jprestaCacheKey = self::getCacheKeyInfos();
if (Tools::getIsset('delpagecache') && self::isDisplayStats()) {
self::$disableBrowserCache = true;
self::getCache()->delete($jprestaCacheKey->get('url'), PageCacheDAO::getContextKeyById(PageCacheDAO::getOrCreateContextId($jprestaCacheKey)));
}
$cache = self::getCache()->get($jprestaCacheKey->get('url'), PageCacheDAO::getContextKeyById(PageCacheDAO::getOrCreateContextId($jprestaCacheKey)), $cache_ttl);
if (self::isCacheWarmer()) {
// Remember that this context is used by the cache-warmer
PageCacheDAO::setContextUsedByCacheWarmer(PageCacheDAO::getOrCreateContextId($jprestaCacheKey));
}
$cache_age = 0;
if ($cache !== false) {
$stats = PageCacheDAO::getStats($jprestaCacheKey);
$cache_age = $stats['age'];
if (!$cache_age) {
// This should not happen BUT it happens sometimes, when the file still exists but is not in
// database anymore. In this case it is not up-to-date so we delete it
self::getCache()->delete($jprestaCacheKey->get('url'), PageCacheDAO::getContextKeyById(PageCacheDAO::getOrCreateContextId($jprestaCacheKey)));
$cache = false;
}
}
if ($cache !== false && self::isCacheWarmer()) {
// Force cache re-generation if the request is done by the cache warmer and TTL is less than 1 day
$ttl = PageCacheDAO::getTtl($jprestaCacheKey, $cache_ttl / 60);
if ($cache_ttl >= 0 && $ttl < (24 * 60) && !headers_sent()) {
header(self::HTTP_HEADER_CACHE_INFO . ': status=on, reason=cache-warmer-regenerate, age=0');
return false;
}
// Cache already warmed up
// Reduce the size of the response to the minimum (save bandwidth and time)
if (!headers_sent()) {
// Here our cache-warmer/status-checker do not care about these headers, just remove them
header_remove();
// Indicates that there is no content so it removes "Content-Length" and "Content-Type" headers
header("HTTP/1.1 204 CACHE EXISTS");
header("x-jpresta-exists: 1");
// Unset PHP session (to avoid a useless cookie)
session_abort();
// Don't send any cookies
Context::getContext()->cookie->disallowWriting();
}
die();
}
if (JprestaUtils::isSearchEngine()) {
PageCacheDAO::incrementCountBot($jprestaCacheKey);
}
// Store cache used in a readable cookie (0=no cache; 1=server cache; 2=browser cache, 3=static cache, 4=back/forward cache)
$cache_type = 0; // no cache available
if ($cache !== false) {
// Server cache
$cache_type = 1;
}
if (!headers_sent()) {
header('Server-Timing: jpresta_cache;desc=' . $cache_type);
header('Timing-Allow-Origin: *');
if (Tools::getIsset('dbgpagecache') || Tools::getIsset('delpagecache')) {
// Don't want the debug URL to be indexed
header('X-Robots-Tag: noindex');
}
}
// ULTIMATE
// Set up the browser cache directives
if (!$cache && session_status() == PHP_SESSION_NONE && !headers_sent() && !self::isCacheWarmer() && !self::isStatusChecker()) {
// We do it here otherwise the cache directives will be removed
session_start();
}
$offset = 60 * Configuration::get('pagecache_'.$controller.'_expires', 0);
if ($offset > 0) {
if (headers_sent()) {
JprestaUtils::addLog("PageCache | Cannot use browser cache because headers have already been sent", 3);
}
elseif (!self::$disableBrowserCache && !PageCacheDAO::hasTriggerIn2H()) {
header('Expires: ' . gmdate('D, d M Y H:i:s', time() + $offset) . ' GMT');
header('Cache-Control: max-age='.$offset.', private');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s', time()) . ' GMT');
header_remove('Pragma');
}
else {
// Browser cache is disabled, force the browser to not use it (specially for back/forward cache)
header('Expires: Wed, 19 Oct 1977 18:00:00 GMT');
header("Cache-Control: no-store, no-cache, must-revalidate, max-age=0");
}
}
else {
// Browser cache is disabled, force the browser to not use it (specially for back/forward cache)
header('Expires: Wed, 19 Oct 1977 18:00:00 GMT');
header("Cache-Control: no-store, no-cache, must-revalidate, max-age=0");
}
// ULTIMATE£
// Display the cached HTML if any
if ($cache !== false) {
if (!headers_sent()) {
header(self::HTTP_HEADER_CACHE_INFO . ': status=on, reason=' . self::$status_reason . ', age=' . $cache_age);
}
self::sendContextCookie();
// Compatibility with module softizyconditionalcache (Ticket #421)
$softizyconditionalcache = Module::getInstanceByName('softizyconditionalcache');
if ($softizyconditionalcache && method_exists($softizyconditionalcache, 'hookActionDispatcherAfter')) {
$softizyconditionalcache->hookActionDispatcherAfter();
}
echo $cache;
return true;
}
else {
if (!headers_sent()) {
header(self::HTTP_HEADER_CACHE_INFO . ': status=on, reason=' . self::$status_reason . ', age=0');
}
}
}
if (!$can_be_cached && !headers_sent()) {
header('Server-Timing: jpresta_cache;desc=-1');
header('Timing-Allow-Origin: *');
if (Tools::getIsset('dbgpagecache') || Tools::getIsset('delpagecache')) {
// Don't want the debug URL to be indexed
header('X-Robots-Tag: noindex');
}
header(self::HTTP_HEADER_CACHE_INFO . ': status=off, reason=' . self::$status_reason);
header("Cache-Control: no-store, no-cache, must-revalidate, max-age=0");
}
return $cache;
}
/**
* Generates a key for the cache depending on URL, currency, user group, country, etc.
* Return array[0]=hashed key (int), array[1]=cache key infos (array)
* @return JprestaCacheKey
*/
public static function getCacheKeyInfos($refresh = false) {
/**
* @var JprestaCacheKey
*/
static $current_cache_key_infos = false;
if ($current_cache_key_infos === false || $refresh) {
$context = Context::getContext();
$cacheKey = new JprestaCacheKey();
//
// SHOP
//
$cacheKey->add('id_shop', $context->shop->id);
//
// URL
//
// Normalize the URL
$normalized_url = self::normalizeUrl(self::getCurrentURL());
// Remove HTML anchor
$anchorPos = strpos($normalized_url, '#');
if ($anchorPos !== FALSE) {
$normalized_url = Tools::substr($normalized_url, 0, $anchorPos);
}
// Strip ignored parameters (tracking data that do not change page content)
// and sort them
$ignored_params = explode(',', Configuration::get('pagecache_ignored_params'));
$ignored_params[] = 'delpagecache';
$ignored_params[] = 'dbgpagecache';
$ignored_params[] = 'cfgpagecache';
$query_string = parse_url($normalized_url, PHP_URL_QUERY);
$new_query_string = self::filterAndSortParams($query_string, $ignored_params);
if ($new_query_string) {
$normalized_url = http_build_url($normalized_url, array("query" => $new_query_string));
}
else {
$normalized_url = http_build_url($normalized_url, array(), HTTP_URL_STRIP_QUERY);
}
$cacheKey->add('url', $normalized_url);
//
// CURRENCY
//
$cacheKey->add('id_currency', self::getCurrencyId($context));
//
// LANGUAGE
//
$cacheKey->add('id_lang', $context->language->id);
//
// CUSTOMER GROUP
//
$anonymousCustomer = JprestaCustomer::getOrCreateCustomerWithSameGroups($context->customer);
$cacheKey->add('id_fake_customer', $anonymousCustomer ? $anonymousCustomer->id : null);
//
// DEVICE (computer, mobile, tablet)
//
if (self::isDependsOnDevice()) {
if (method_exists($context, 'getDevice')) {
if ($context->getDevice() === Context::DEVICE_MOBILE || (Configuration::get('pagecache_tablet_is_mobile') && $context->getDevice() === Context::DEVICE_TABLET)) {
$cacheKey->add('id_device', self::DEVICE_MOBILE);
}
else {
$cacheKey->add('id_device', self::DEVICE_COMPUTER);
}
}
elseif ($context->getMobileDevice() == true) {
$cacheKey->add('id_device', self::DEVICE_MOBILE);
}
else {
$cacheKey->add('id_device', self::DEVICE_COMPUTER);
}
}
else {
$cacheKey->add('id_device', self::DEVICE_COMPUTER);
}
//
// COUNTRY
//
$country = self::getCountry($context);
if ($country) {
$currentCacheKeyCountryConf = json_decode(JprestaUtils::getConfigurationByShopId('pagecache_cachekey_countries', Shop::getContextShopID(), '{}'), true);
if (array_key_exists($country->id, $currentCacheKeyCountryConf)
&& $currentCacheKeyCountryConf[$country->id]['specific_cache']) {
// Only create a specific cache if it is configured like that
$cacheKey->add('id_country', $country->id);
}
else {
// Otherwise set the country key as 'other' (null)
$cacheKey->add('id_country', null);
}
}
else {
// Normally we should not be here because getCountry() will return the default country
$cacheKey->add('id_country', null);
}
//
// TAXES MANAGER
//
$tax_manager_details = self::getTaxManagerDetails($context);
if ($tax_manager_details) {
$cacheKey->add('id_tax_manager', $tax_manager_details);
}
//
// RGPD and other specific determinants
//
$cacheKey->add('specifics', self::getJprestaCacheKeySpecifics());
//
// Other determinants
//
// Version of CSS and JS to avoid cache to reference old CSS and JS files
if (Configuration::get('pagecache_depend_on_css_js')) {
$cacheKey->add('css_version', Configuration::get('PS_CCCCSS_VERSION'));
$cacheKey->add('js_version', Configuration::get('PS_CCCJS_VERSION'));
}
$current_cache_key_infos = $cacheKey;
}
return $current_cache_key_infos;
}
/**
* @param $url string URL of the backlink
* @return int Cache key as an unsigned integer
*/
public static function getCacheKeyForBacklink($url)
{
// We supposed that URLs into our shop are well formatted
// Remove HTML anchor
$anchorPos = strpos($url, '#');
if ($anchorPos !== FALSE) {
$url = Tools::substr($url, 0, $anchorPos);
}
// Remove protocol as it is done in JprestaUtils::parseLinks for backlinks
$url = preg_replace('/https?:\/\//', '//', $url);
$jprestaCacheKey = new JprestaCacheKey();
$jprestaCacheKey->add('url', $url);
return $jprestaCacheKey->toInt();
}
private static function normalizeUrl($url) {
$normalized_url = html_entity_decode($url);
$un = new PageCacheURLNormalizer();
$un->setUrl($normalized_url);
$normalized_url = $un->normalize();
return $normalized_url;
}
private static function getCookieValue($cookieName, $defaultValue = '') {
if (array_key_exists($cookieName, $_COOKIE)) {
// Necessary to avoid errors in Prestashop Addons validator
foreach ($_COOKIE as $key => $cookieValue) {
if ($key === $cookieName) {
return $cookieValue;
}
}
}
return $defaultValue;
}
/**
* @param $specifics JprestaCacheKeySpecifics
*/
private static function restoreJprestaCacheKeySpecifics($specifics)
{
// Restore cookies and sessions datas
$specifics->restoreCookies();
// Now restore other specifics behavior for specific modules
$context = Context::getContext();
// For gdprpro (2.1.1) module by PrestaChamps
if (JprestaUtils::isModuleEnabled('gdprpro')) {
if (file_exists(_PS_MODULE_DIR_ . 'gdprpro/src/GdprProConfig.php')
&& file_exists(_PS_MODULE_DIR_ . 'gdprpro/src/GdprProCookie.php')) {
require_once _PS_MODULE_DIR_ . 'gdprpro/src/GdprProConfig.php';
require_once _PS_MODULE_DIR_ . 'gdprpro/src/GdprProCookie.php';
if (class_exists('GdprProCookie') && method_exists('GdprProCookie', 'getInstance')) {
// The cookie is read before any hook in getHookModuleExecList() so we need to read it again
GdprProCookie::getInstance()->content = json_decode($context->cookie->gdpr_conf, true);
}
}
}
// Handle vat_view (for shop cinelight.eu)
if ($context->customer && property_exists($context->customer, 'vat_view')) {
$context->customer->vat_view = $specifics->getValue('vat_view');
}
if (JprestaUtils::isModuleEnabled('lgcookieslaw')) {
if (Configuration::hasKey('PS_LGCOOKIES_COOKIE_NAME', 0, (int) Shop::getContextShopID(true), (int) Shop::getContextShopGroupID(true))) {
// lgcookieslaw v2
$lgModule = Module::getInstanceByName('lgcookieslaw');
if ($lgModule) {
if ($specifics->getValue('lgcookieslaw') == 'access not granted') {
// Simulate Googlebot to force lgcookieslaw to block the access
$_SERVER['HTTP_USER_AGENT'] = $_SERVER['HTTP_USER_AGENT'] . ' - Googlebot';
} else {
$lgcookieslaw_cookie_name = Configuration::get('PS_LGCOOKIES_COOKIE_NAME');
$lgcookieslaw_cookie_value = $specifics->getValue($lgcookieslaw_cookie_name);
if (method_exists($lgModule, 'encryptCookie')) {
$lgcookieslaw_cookie_values = $lgModule->encryptCookie($lgcookieslaw_cookie_value);
} else {
$lgcookieslaw_cookie_values = base64_encode(json_encode($lgcookieslaw_cookie_value));
}
if (Configuration::get('PS_LGCOOKIES_USE_COOKIE_VAR')) {
$_COOKIE[$lgcookieslaw_cookie_name] = $lgcookieslaw_cookie_values;
} else {
$context->cookie->__set($lgcookieslaw_cookie_name, $lgcookieslaw_cookie_values);
}
}
}
}
else {
// lgcookieslaw v1
$lgcookieslaw_cookie_value = $specifics->getValue('lgcookieslaw_bots');
if ($lgcookieslaw_cookie_value) {
// Simulate Googlebot to force lgcookieslaw to block the access
$_SERVER['HTTP_USER_AGENT'] = $_SERVER['HTTP_USER_AGENT'] . ' - Googlebot';
}
}
}
// For iubenda module by iubenda
if (Module::isEnabled('iubenda')) {
$purposes = $specifics->getValue('iubenda_purposes');
if ($purposes) {
$_COOKIE['_iub_cs-s-cache-warmer'] = json_encode(['purposes' => json_decode($purposes, true)]);
}
}
//
// WEBP
//
$acceptWebp = true;
// For webpgenerator module by PrestaChamps
if (JprestaUtils::isModuleEnabled('webpgenerator')) {
$acceptWebp = $specifics->getValue('webpgenerator') === 'acceptwebp' || (bool)$specifics->getValue('webp');
}
// For jprestaspeedpack and jprestawebp modules by JPresta
if ((JprestaUtils::isModuleEnabled('jprestaspeedpack') || JprestaUtils::isModuleEnabled('jprestawebp'))
&& (int)Configuration::get('SPEED_PACK_WEBP_ENABLE')
&& (int)Configuration::get('SPEED_PACK_WEBP_FORCE_EXTENSION')) {
$acceptWebp = (bool)$specifics->getValue('webp');
}
// For ultimateimagetool module by advancedplugins
if (JprestaUtils::isModuleEnabled('ultimateimagetool')
&& (int)Configuration::get('uit_use_webp') == 1
&& (int)Configuration::get('uit_use_webp_termination') == 1)
{
$acceptWebp = (bool)$specifics->getValue('webp');
}
if ($acceptWebp) {
$_SERVER['HTTP_ACCEPT'] = (isset($_SERVER['HTTP_ACCEPT']) ? $_SERVER['HTTP_ACCEPT'] : '') . ',image/webp';
}
else {
$_SERVER['HTTP_ACCEPT'] = str_replace('image/webp', 'image/jpg', isset($_SERVER['HTTP_ACCEPT']) ? $_SERVER['HTTP_ACCEPT'] : '');
}
if ($specifics->getValue('display_applepay')) {
// Simulate an Apple device
$_SERVER['HTTP_USER_AGENT'] .= ' Version/10 Safari/x';
}
// Restore context handled by modules
$hookParams = $specifics->getValue('hooks');
if ($hookParams) {
foreach ($hookParams as $moduleName => $hookParam) {
Hook::exec('actionJPrestaRestoreSpecificCacheKeyInfos', ['specifics' => $hookParam], Module::getModuleIdByName($moduleName));
}
}
}
/**
* @return JprestaCacheKeySpecifics|null
*/
private static function getJprestaCacheKeySpecifics() {
$specifics = new JprestaCacheKeySpecifics();
$context = Context::getContext();
// For gdprpro (2.1.1) module by PrestaChamps
if (JprestaUtils::isModuleEnabled('gdprpro')) {
$specifics->keepPsCookie('gdpr_conf');
$specifics->keepCookie('gdpr_windows_was_opened');
}
// For generaldataprotectionregulation (2.0.11) module by Active Design
if (JprestaUtils::isModuleEnabled('generaldataprotectionregulation')) {
$specifics->keepCookie('Accepted');
$specifics->keepCookie('cookiesDenied');
$specifics->keepCookie('cookiesAccepted');
}
// For ageverify module by Musaffar Patel
if (JprestaUtils::isModuleEnabled('ageverify')) {
// session based (each visit) check
if (Configuration::get('av_display_frequency') == 'each_visit') {
$specifics->keepOtherPsCookie('age_verify_session');
} else {
$specifics->keepPsCookie('age_verify');
}
}
// For ageverifyer module by Simon Agostini
if (JprestaUtils::isModuleEnabled('ageverifyer')) {
// To uncomment if needed (forbidden by Prestashop Addons)
//session_start();
$specifics->keepSessionProperty('over18');
}
// For kbgdpr module by Knowband
if (JprestaUtils::isModuleEnabled('kbgdpr')) {
$cookie_law_settings = json_decode(Configuration::get('GDPR_COOKIE_LAW_SETTINGS'), true);
$specifics->keepCookie($cookie_law_settings['cookie_name']);
}
// For uecookie module by MyPresta.eu
if (JprestaUtils::isModuleEnabled('uecookie')) {
$specifics->keepCookie('cookie_ue');
}
// For idxcookies module by Idnovate
if (JprestaUtils::isModuleEnabled('idxcookies')) {
$specifics->keepCookie('idxcookiesWarningCheck');
if (isset($specifics->cookies['idxcookiesWarningCheck'])) {
$cookieValue = json_decode($specifics->cookies['idxcookiesWarningCheck'], true);
$cookieValue['date'] = '2000-01-01 00:00:00';
$specifics->cookies['idxcookiesWarningCheck'] = json_encode($cookieValue);
}
}
// For validatevatnumber module by ActiveDesign
if (JprestaUtils::isModuleEnabled('validatevatnumber')) {
$specifics->keepPsCookie('guest_taxes');
}
// For megacookies module by presta.design
if (JprestaUtils::isModuleEnabled('megacookies')) {
$var = Context::getContext()->cookie->megacookie_consents;
if (!$var) {
// Avoid duplicate contexts
Context::getContext()->cookie->megacookie_consents = 'denied';
}
$specifics->keepPsCookie('megacookie_consents');
}
// For deluxecookies module by innovadeluxe
if (JprestaUtils::isModuleEnabled('deluxecookies')) {
$specifics->keepCookie('deluxecookies');
$specifics->keepCookie('deluxecookiesWarningCheck');
if (isset($specifics->cookies['deluxecookiesWarningCheck'])) {
$cookieValue = json_decode($specifics->cookies['deluxecookiesWarningCheck'], true);
}
else {
$cookieValue = [];
}
$cookieValue['date'] = '2000-01-01 00:00:00';
$specifics->cookies['deluxecookiesWarningCheck'] = json_encode($cookieValue);
}
// For tnzcookie (1.6.6) module by Tanzo
if (JprestaUtils::isModuleEnabled('tnzcookie')) {
$specifics->keepOtherPsCookie('TNZCOOKIE_COOKIE');
}
// For lgcookieslaw module by Línea Gráfica
if (JprestaUtils::isModuleEnabled('lgcookieslaw')) {
if (Configuration::hasKey('PS_LGCOOKIES_COOKIE_NAME', 0, (int) Shop::getContextShopID(true), (int) Shop::getContextShopGroupID(true))) {
// lgcookieslaw v2
$lgModule = Module::getInstanceByName('lgcookieslaw');
if ($lgModule) {
if (!$lgModule->checkAccessGranted($context)) {
$specifics->keepValue('lgcookieslaw', 'access not granted');
} else {
$lgcookieslaw_cookie_name = Configuration::get('PS_LGCOOKIES_COOKIE_NAME');
if (method_exists($lgModule, 'getCookieValues')) {
$lgcookieslaw_cookie_values = (array)$lgModule->getCookieValues();
} else {
$lgcookieslaw_cookie_value = $context->cookie->__get($lgcookieslaw_cookie_name);
if (JprestaUtils::startsWith($lgcookieslaw_cookie_value, '{')) {
$lgcookieslaw_cookie_values = json_decode($context->cookie->__get($lgcookieslaw_cookie_name),
true);
} else {
$lgcookieslaw_cookie_values = json_decode(base64_decode($context->cookie->__get($lgcookieslaw_cookie_name)),
true);
}
}
if (is_array($lgcookieslaw_cookie_values) && count($lgcookieslaw_cookie_values) > 0) {
// Override other values not usefull for the cache key
if (isset($lgcookieslaw_cookie_values['lgcookieslaw_user_consent_consent_date'])) {
$lgcookieslaw_cookie_values['lgcookieslaw_user_consent_consent_date'] = '2000-01-01 00:00:00';
}
if (isset($lgcookieslaw_cookie_values['lgcookieslaw_user_consent_ip_address'])) {
$lgcookieslaw_cookie_values['lgcookieslaw_user_consent_ip_address'] = '::1';
}
if (isset($lgcookieslaw_cookie_values['lgcookieslaw_user_consent_download_url'])) {
$lgcookieslaw_cookie_values['lgcookieslaw_user_consent_download_url'] = 'https://foo.com';
}
if (isset($lgcookieslaw_cookie_values['download_hash'])) {
$lgcookieslaw_cookie_values['download_hash'] = 'foo';
}
if (isset($lgcookieslaw_cookie_values['lgcookieslaw_user_consent_download_hash'])) {
$lgcookieslaw_cookie_values['lgcookieslaw_user_consent_download_hash'] = 'foo';
}
} else {
// Disable browser cache until the visitor select cookies that he want
self::$disableBrowserCache = true;
}
$specifics->keepValue($lgcookieslaw_cookie_name, $lgcookieslaw_cookie_values);
// Preserve this cookie
self::$cookies_to_preserve[$lgcookieslaw_cookie_name] = $lgcookieslaw_cookie_name;
}
}
}
else {
// lgcookieslaw v1
$specifics->keepCookie(Configuration::get('PS_LGCOOKIES_NAME'));
if (isset($_SERVER['HTTP_USER_AGENT'])) {
$bots = Configuration::get('PS_LGCOOKIES_BOTS');
$botlist = explode(',', $bots);
foreach ($botlist as $bot) {
if ($bot && strpos($_SERVER['HTTP_USER_AGENT'], $bot) !== false) {
$specifics->keepValue('lgcookieslaw_bots', 'Bot detected');
}
}
}
if (Configuration::get('PS_LGCOOKIES_TESTMODE') == 1 && Configuration::get('PS_LGCOOKIES_IPTESTMODE') == $_SERVER['REMOTE_ADDR']) {
$specifics->keepValue('lgcookieslaw_mode', 'Test mode');
}
}
}
// For cookiesplus by idnovate
if (JprestaUtils::isModuleEnabled('cookiesplus')) {
$cookiesplus = Module::getInstanceByName('cookiesplus');
if (Tools::version_compare($cookiesplus->version,'1.3','>=')) {
if (Tools::version_compare($cookiesplus->version,'1.5.2','>=') || Configuration::get('C_P_COOKIE')) {
$specifics->keepCookie('cookiesplus');
if (isset($specifics->cookies['cookiesplus'])) {
$cookieValue = json_decode($specifics->cookies['cookiesplus'], true);
$cookieValue['consent_date'] = '2000-01-01 00:00:00';
$cookieValue['consent_ip'] = '::1';
$cookieValue['consent_hash'] = 'foo';
$cookieValue['consent_link'] = 'foo';
$cookieValue['expiry'] = 1924988399; // 2030-12-31 23:59:59
if (!isset($cookieValue['cookiesplus-finality-1'])) {
$cookieValue['cookiesplus-finality-1'] = false;
}
if (!isset($cookieValue['cookiesplus-finality-2'])) {
$cookieValue['cookiesplus-finality-2'] = 'na';
}
if (!isset($cookieValue['cookiesplus-finality-3'])) {
$cookieValue['cookiesplus-finality-3'] = 'na';
}
if (!isset($cookieValue['cookiesplus-finality-4'])) {
$cookieValue['cookiesplus-finality-4'] = 'na';
}
$specifics->cookies['cookiesplus'] = json_encode($cookieValue);
}
}
else {
$specifics->keepPsCookie('cookiesplus');
if (isset($specifics->psCookies['cookiesplus'])) {
$cookieValue = json_decode($specifics->psCookies['cookiesplus'], true);
$cookieValue['consent_date'] = '2000-01-01 00:00:00';
$cookieValue['consent_ip'] = '::1';
$cookieValue['consent_hash'] = 'foo';
$cookieValue['consent_link'] = 'foo';
if (!isset($cookieValue['cookiesplus-finality-1'])) {
$cookieValue['cookiesplus-finality-1'] = false;
}
if (!isset($cookieValue['cookiesplus-finality-2'])) {
$cookieValue['cookiesplus-finality-2'] = 'na';
}
if (!isset($cookieValue['cookiesplus-finality-3'])) {
$cookieValue['cookiesplus-finality-3'] = 'na';
}
if (!isset($cookieValue['cookiesplus-finality-4'])) {
$cookieValue['cookiesplus-finality-4'] = 'na';
}
$specifics->psCookies['cookiesplus'] = json_encode($cookieValue);
}
}
}
else {
if (Configuration::get('C_P_ENABLE')
&& (!isset($context->cookie->psnotice) || $context->cookie->psnotice != '2')
&& (!isset($_SERVER['HTTP_USER_AGENT']) || !preg_match('/' . Configuration::get('C_P_BOTS') . '/i',
$_SERVER['HTTP_USER_AGENT']))
&& !in_array(Tools::getRemoteAddr(), explode('|', Configuration::get('C_P_IPS')))) {
$specifics->keepPsCookie('psnotice');
$specifics->keepValue('cookiesplus', 'withoutcookie');
} else {
$specifics->keepValue('cookiesplus', 'withcookie');
}
}
}
// For medcookiefirst by Mediacom87
if (JprestaUtils::isModuleEnabled('medcookiefirst')) {
$specifics->keepCookie('cookiefirst-consent');
if (isset($specifics->cookies['cookiefirst-consent'])) {
$cookieValue = json_decode($specifics->cookies['cookiefirst-consent'], true);
$cookieValue['timestamp'] = 1704067200;
$specifics->cookies['cookiefirst-consent'] = json_encode($cookieValue);
}
}
// For systemina_employeefilter module by Systemina (support@systemina.dk)
if (JprestaUtils::isModuleEnabled('systemina_employeefilter')) {
$cookie = new Cookie('psAdmin', '', (int)Configuration::get('PS_COOKIE_LIFETIME_BO'));
$employee = new Employee((int)$cookie->id_employee);
if (Validate::isLoadedObject($employee) && $employee->checkPassword((int)$cookie->id_employee, $cookie->passwd)
&& (!isset($cookie->remote_addr) || $cookie->remote_addr == ip2long(Tools::getRemoteAddr()) || !Configuration::get('PS_COOKIE_CHECKIP'))) {
$specifics->keepValue('systemina_employeefilter', 'EmployeeLoggedin');
} else {
$specifics->keepValue('systemina_employeefilter', 'EmployeeNotLoggedin');
}
}
// For pm_advancedcookiebanner module by Presta-Module
if (JprestaUtils::isModuleEnabled('pm_advancedcookiebanner') && class_exists('AcbCookie') && method_exists('AcbCookie', 'getConsentLevel')) {
$pmCookieContent = self::getCookieValue(AcbCookie::COOKIE_NAME, false);
$pmConfigMode = Configuration::get('PM_ACB_CONFIG_MODE');
$pmCmsPage = Tools::getIsset('acb_cms') || (Tools::getIsset('id_cms') && Tools::getValue('id_cms') == Configuration::get('PM_ACB_CMS'));
$pmGdprMode = Configuration::get('PM_ACB_GDPR_MODE');
$specifics->keepCookie(AcbCookie::COOKIE_NAME);
if ($pmGdprMode == 1) {
$specifics->keepValue('pm_advancedcookiebanner_gdpr', 1);
}
if (!$pmConfigMode) {
if ($pmCookieContent === false && !$pmCmsPage) {
$specifics->keepValue('pm_advancedcookiebanner_mode', 1);
}
else {
$specifics->keepValue('pm_advancedcookiebanner_mode', 0);
}
}
else {
$maintenance_ips = explode(',', Configuration::get('PS_MAINTENANCE_IP'));
if (in_array(Tools::getRemoteAddr(), $maintenance_ips)) {
$specifics->keepValue('pm_advancedcookiebanner_mode', 1);
} else {
$specifics->keepValue('pm_advancedcookiebanner_mode', 0);
}
}
}
// For deluxecookies module by innovadeluxe
if (JprestaUtils::isModuleEnabled('deluxecookies')) {
$module = Module::getInstanceByName('deluxecookies');
$specifics->keepValue('deluxecookies_disabled_mods', $module->getDisabledModules());
if (self::getCookieValue('deluxecookiesWarningCheck', false)) {
$specifics->keepValue('deluxecookies_dialog', 1);
}
}
// For cookiebanner module by Prestaddons
if (JprestaUtils::isModuleEnabled('cookiebanner')) {
$specifics->keepPsCookie('disabled_modules_list');
}
// For webpgenerator module by PrestaChamps
if (JprestaUtils::isModuleEnabled('webpgenerator') && JprestaUtils::currentVisitorAcceptWebp()) {
$specifics->keepValue('webp', 1);
}
// For jprestaspeedpack and jprestawebp modules by JPresta
if ((JprestaUtils::isModuleEnabled('jprestaspeedpack') || JprestaUtils::isModuleEnabled('jprestawebp'))
&& (int)Configuration::get('SPEED_PACK_WEBP_ENABLE')
&& (int)Configuration::get('SPEED_PACK_WEBP_FORCE_EXTENSION')
&& JprestaUtils::currentVisitorAcceptWebp()) {
$specifics->keepValue('webp', 1);
}
// For ultimateimagetool module by advancedplugins
if (JprestaUtils::isModuleEnabled('ultimateimagetool')
&& (int)Configuration::get('uit_use_webp') == 1
&& (int)Configuration::get('uit_use_webp_termination') == 1
&& JprestaUtils::currentVisitorAcceptWebp())
{
$specifics->keepValue('webp', 1);
}
// For hicookielaw module by hipresta
if (JprestaUtils::isModuleEnabled('hicookielaw')) {
/** @var Hicookielaw $module */
$module = Module::getInstanceByName('hicookielaw');
if ( method_exists($module, 'isIPWhiteListed') && $module->isIPWhiteListed()
|| method_exists($module, 'isBot') && $module->isBot()) {
$context->cookie->hiThirdPartyCookies = 1;
}
if (isset($context->cookie->hiThirdPartyCookies)) {
// Force the same type to reduce the context count
$context->cookie->hiThirdPartyCookies = (int) $context->cookie->hiThirdPartyCookies;
}
$specifics->keepPsCookie('hiThirdPartyCookies');
}
// Handle vat_view (for shop cinelight.eu)
if ($context->customer && property_exists($context->customer, 'vat_view')) {
$specifics->keepValue('vat_view', $context->customer->vat_view);
}
// For iubenda module by iubenda
if (Module::isEnabled('iubenda')) {
$purposes = array();
if (!empty($_COOKIE)) {
foreach ($_COOKIE as $key => $value) {
if (JprestaUtils::startsWith($key, '_iub_cs-s') || JprestaUtils::startsWith($key, '_iub_cs')) {
$consent_data = json_decode($value, true);
// read purposes if given
if (!empty($consent_data['purposes']) && is_array($consent_data['purposes'])) {
$purposes = $consent_data['purposes'];
$specifics->keepValue('iubenda_purposes', json_encode($purposes));
}
}
}
}
}
// For dm_cookies module by David Mrózek
if (JprestaUtils::isModuleEnabled('dm_cookies')) {
$specifics->keepCookie('DmCookiesAnalytics');
$specifics->keepCookie('DmCookiesMarketing');
$specifics->keepCookie('DmCookiesAccepted');
}
// For groupinc module by Idnovate
if (JprestaUtils::isModuleEnabled('groupinc') && file_exists(_PS_MODULE_DIR_.'groupinc/classes/GroupincConfiguration.php')) {
include_once(_PS_MODULE_DIR_ . 'groupinc/classes/GroupincConfiguration.php');
if (method_exists('GroupincConfiguration','isShowableBySchedule')) {
$today = date("Y-m-d H:i:s");
$datefilters = ' AND (date_from <= "' . $today . '" OR date_from = "0000-00-00 00:00:00") AND (date_to >= "' . $today . '" OR date_to = "0000-00-00 00:00:00")';
$orderby = ' ORDER BY `priority`, `id_groupinc_configuration` ASC';
$groupincConfigs = JprestaUtils::dbSelectRows('SELECT * FROM `' . _DB_PREFIX_ . 'groupinc_configuration` WHERE `active`=1 AND `id_shop`=' . (int)$context->shop->id . $datefilters . $orderby);
$ids = '';
foreach ($groupincConfigs as $groupincConfig) {
if (GroupincConfiguration::isShowableBySchedule($groupincConfig)) {
$ids .= $groupincConfig['id_groupinc_configuration'] . ' (' . $groupincConfig['date_upd'] . ')';
}
}
// Just store the md5 or it may be larger than 65535 chars, also we don't restore it, only add it to the cache key
$md5 = md5($ids);
$specifics->keepValue('groupinc.configIds', $md5);
$lastMd5 = JprestaUtils::getConfigurationOfCurrentShop('pagecache_groupinc_last');
if ($lastMd5 === false || $lastMd5 !== $md5) {
$contexts = PageCacheDAO::getAllContextsAndDetails((int)$context->shop->id);
foreach ($contexts as $id_ctx => $details) {
if (Tools::strpos($details, 'groupinc.configIds') !== false
&& Tools::strpos($details, $md5) === false) {
// This context is now obsolete
PageCacheDAO::disableContext($id_ctx);
}
}
JprestaUtils::saveConfigurationOfCurrentShop('pagecache_groupinc_last', $md5);
}
}
}
// For nxtalpricetaxswitcher by Nxtal
if (JprestaUtils::isModuleEnabled('nxtalpricetaxswitcher')) {
$defaultDisplay = (int) Group::getPriceDisplayMethod((int) Group::getCurrent()->id, true);
$cookieDisplay = self::getCookieValue('nxtalpricetaxswitcher', false);
if ($cookieDisplay !== false && $defaultDisplay != $cookieDisplay) {
$specifics->keepCookie('nxtalpricetaxswitcher');
}
}
// For pm_applepay module by Presta-Module
if (JprestaUtils::isModuleEnabled('pm_applepay')) {
$module = Module::getInstanceByName('pm_applepay');
if ($module && method_exists($module, 'isPaymentAvailable')) {
$specifics->keepValue('display_applepay', (int) $module->isPaymentAvailable());
}
else {
$specifics->keepValue('display_applepay', 0);
}
}
// For eugeneraldatapro module by PrestaBucket
if (JprestaUtils::isModuleEnabled('eugeneraldatapro')) {
$specifics->keepCookie('EUGDPRmodulesAccepted');
$specifics->keepCookie('EUGDPRmodulesDenied');
}
// For wkexitpopup module by Webkul
if (JprestaUtils::isModuleEnabled('wkexitpopup')) {
$specifics->keepCookie('do_not_show_again');
}
// For iqitthemeeditor module by IQIT-COMMERCE.COM
if (JprestaUtils::isModuleEnabled('iqitthemeeditor')) {
$specifics->keepPsCookie('product_list_view');
}
// Allow modules to handle their specifics contextual datas
$hookParams = Hook::exec('actionJPrestaGetSpecificCacheKeyInfos', [], null, true);
if ($hookParams && count($hookParams) > 0) {
$specifics->keepValue('hooks', $hookParams);
}
return ($specifics->isEmpty() ? null : $specifics);
}
public static function updateCacheKeyForCountries() {
$allShopIds = JprestaUtils::getCompleteListOfShopsID();
foreach ($allShopIds as $shopId) {
$currentConf = json_decode(JprestaUtils::getConfigurationByShopId('pagecache_cachekey_countries', $shopId, '{}'), true);
$checkedCountries = [];
$countryRows = JprestaUtils::dbSelectRows('SELECT c.id_country
FROM `' . _DB_PREFIX_ . 'country` c
LEFT JOIN `' . _DB_PREFIX_ . 'country_shop` cs ON (cs.`id_country`= c.`id_country`)
WHERE c.active=1 AND id_shop=' . (int) $shopId);
foreach ($countryRows as $countryRow) {
if (!array_key_exists((int)$countryRow['id_country'], $currentConf)) {
$currentConf[$countryRow['id_country']] = ['specific_cache' => false, 'has_impact' => false];
}
$currentConf[$countryRow['id_country']]['has_impact'] = self::hasImpactForCountries($countryRow['id_country'], $shopId);
if ($currentConf[$countryRow['id_country']]['has_impact']) {
// Force a specific cache to be created if any constraint exists
$currentConf[$countryRow['id_country']]['specific_cache'] = true;
}
$checkedCountries[$countryRow['id_country']] = true;
}
// Remove old countries from the configuration
foreach ($currentConf as $id_country => $val) {
if (!array_key_exists($id_country, $checkedCountries)) {
unset($currentConf[$id_country]);
}
}
JprestaUtils::saveConfigurationByShopId('pagecache_cachekey_countries', json_encode($currentConf), $shopId);
}
}
public static function updateCacheKeyForUserGroups() {
$allShopIds = JprestaUtils::getCompleteListOfShopsID();
foreach ($allShopIds as $shopId) {
$currentConf = json_decode(JprestaUtils::getConfigurationByShopId('pagecache_cachekey_usergroups', $shopId, '{}'), true);
$checkedUserGroups = [];
$userGroupRows = JprestaUtils::dbSelectRows('SELECT *
FROM `' . _DB_PREFIX_ . 'group` g
LEFT JOIN `' . _DB_PREFIX_ . 'group_shop` gs ON (gs.`id_group`= g.`id_group`)
WHERE gs.id_shop=' . (int) $shopId);
foreach ($userGroupRows as $userGroupRow) {
if (!array_key_exists((int)$userGroupRow['id_group'], $currentConf)) {
$currentConf[$userGroupRow['id_group']] = ['specific_cache' => false, 'has_impact_as_default' => false];
}
$currentConf[$userGroupRow['id_group']]['has_impact_as_default'] = self::hasImpactForUserGroupAsDefault($userGroupRow['id_group'], $shopId);
// The display_key will be used to find similar user group when has_impact_as_default=false
$currentConf[$userGroupRow['id_group']]['display_key'] = $userGroupRow['price_display_method'] . '|' . $userGroupRow['show_prices'];
$currentConf[$userGroupRow['id_group']]['display_key'] .= '|' . JprestaUtils::dbGetValue('SELECT MD5(GROUP_CONCAT(id_module SEPARATOR \'|\')) FROM `' . _DB_PREFIX_ . 'module` WHERE id_module NOT IN (SELECT id_module FROM `' . _DB_PREFIX_ . 'module_group` WHERE id_group=' . (int) $userGroupRow['id_group'] . ') ORDER BY id_module ASC');
$currentConf[$userGroupRow['id_group']]['display_key'] .= '|' . JprestaUtils::dbGetValue('SELECT MD5(GROUP_CONCAT(id_category SEPARATOR \'|\')) FROM `' . _DB_PREFIX_ . 'category` WHERE id_parent<> 0 AND id_category NOT IN (SELECT id_category FROM `' . _DB_PREFIX_ . 'category_group` WHERE id_group=' . (int) $userGroupRow['id_group'] . ') ORDER BY id_category ASC');
if ((int) $userGroupRow['id_group'] === (int) Configuration::get('PS_UNIDENTIFIED_GROUP')
|| (int) $userGroupRow['id_group'] === (int) Configuration::get('PS_GUEST_GROUP')) {
$currentConf[$userGroupRow['id_group']]['display_key'] .= '|not_connected';
}
else {
$currentConf[$userGroupRow['id_group']]['display_key'] .= '|connected';
}
if ($currentConf[$userGroupRow['id_group']]['has_impact_as_default']) {
// Force a specific cache to be created if any constraint exists
$currentConf[$userGroupRow['id_group']]['specific_cache'] = true;
}
$checkedUserGroups[$userGroupRow['id_group']] = true;
}
// Remove old user groups from the configuration
foreach ($currentConf as $id_group => $val) {
if (!array_key_exists($id_group, $checkedUserGroups)) {
unset($currentConf[$id_group]);
}
}
JprestaUtils::saveConfigurationByShopId('pagecache_cachekey_usergroups', json_encode($currentConf), $shopId);
}
JprestaUtils::saveConfigurationAllShop('pagecache_cachekey_usergroups_upd', false);
}
/**
* @param int $id_country
* @param int $id_shop
* @return bool true if this country has impact on the specified shop
*/
private static function hasImpactForCountries($id_country, $id_shop)
{
$andShopIdClause = '';
if (Shop::isFeatureActive()) {
$andShopIdClause = ' AND id_shop=' . (int)$id_shop;
}
// Price rules for catalog
$count = (int) JprestaUtils::dbGetValue('SELECT 1
FROM `' . _DB_PREFIX_ . 'specific_price_rule`
WHERE id_country=' . (int)$id_country . $andShopIdClause . ' AND (`to` IS NULL OR `to` > CURRENT_TIMESTAMP)');
// Price rules for a specific product
// TODO Test with specific price on shop group
if ($count === 0) {
$count += (int) JprestaUtils::dbGetValue('SELECT 1
FROM `' . _DB_PREFIX_ . 'specific_price`
WHERE id_country=' . (int)$id_country . $andShopIdClause . ' AND (`to` IS NULL OR `to` > CURRENT_TIMESTAMP)');
}
// Cart rules => cart rules do not change the price which is displayed so they do not change the cache content, they can be ignored
return $count > 0;
}
/**
* @param int $id_group
* @param int $id_shop
* @return bool true if this user group has an impact on the specified shop
*/
private static function hasImpactForUserGroupAsDefault($id_group, $id_shop)
{
$count = 0;
$andShopIdClause = '';
if (Shop::isFeatureActive()) {
$andShopIdClause = ' AND id_shop=' . (int)$id_shop;
}
// Discount for the group
$count = (int) JprestaUtils::dbGetValue('SELECT 1
FROM `' . _DB_PREFIX_ . 'group` g
LEFT JOIN `' . _DB_PREFIX_ . 'group_shop` gs ON (gs.`id_group`= g.`id_group`)
WHERE g.reduction>0.0 AND gs.id_shop=' . (int) $id_shop .' AND g.id_group=' . (int) $id_group);
// Discount for the group on categories
if ($count === 0) {
$count += (int) JprestaUtils::dbGetValue('SELECT 1
FROM `' . _DB_PREFIX_ . 'group_reduction`
WHERE id_group=' . (int)$id_group);
}
// Price rules for catalog
if ($count === 0) {
$count += (int) JprestaUtils::dbGetValue('SELECT 1
FROM `' . _DB_PREFIX_ . 'specific_price_rule`
WHERE id_group=' . (int)$id_group . $andShopIdClause . ' AND (`to` IS NULL OR `to` > CURRENT_TIMESTAMP)');
}
// Price rules for a specific product
// TODO Test with specific price on shop group
if ($count === 0) {
$count += (int) JprestaUtils::dbGetValue('SELECT 1
FROM `' . _DB_PREFIX_ . 'specific_price`
WHERE id_group=' . (int)$id_group . $andShopIdClause . ' AND (`to` IS NULL OR `to` > CURRENT_TIMESTAMP)');
}
// Cart rules => cart rules do not change the price which is displayed so they do not change the cache content, they can be ignored
return $count > 0;
}
public function hookActionObjectSpecificPriceRuleAddAfter() {
if (!self::$skipUpdateCacheKey) {
self::updateCacheKeyForCountries();
self::updateCacheKeyForUserGroups();
}
else {
self::$needUpdateCacheKey = true;
}
}
public function hookActionObjectSpecificPriceRuleUpdateAfter() {
if (!self::$skipUpdateCacheKey) {
self::updateCacheKeyForCountries();
self::updateCacheKeyForUserGroups();
}
else {
self::$needUpdateCacheKey = true;
}
}
public function hookActionObjectSpecificPriceRuleDeleteAfter() {
if (!self::$skipUpdateCacheKey) {
self::updateCacheKeyForCountries();
self::updateCacheKeyForUserGroups();
}
else {
self::$needUpdateCacheKey = true;
}
}
public function hookActionObjectGroupAddAfter() {
// Some datas are set after the hook is called :-(
JprestaUtils::saveConfigurationAllShop("pagecache_cachekey_usergroups_upd", true);
}
public function hookActionObjectGroupUpdateAfter() {
// Some datas are set after the hook is called :-(
JprestaUtils::saveConfigurationAllShop("pagecache_cachekey_usergroups_upd", true);
}
public function hookActionObjectGroupDeleteAfter() {
// Some datas are set after the hook is called :-(
JprestaUtils::saveConfigurationAllShop("pagecache_cachekey_usergroups_upd", true);
}
public function hookActionObjectSpecificPriceDeleteAfter() {
if (!self::$skipUpdateCacheKey) {
self::updateCacheKeyForCountries();
self::updateCacheKeyForUserGroups();
}
else {
self::$needUpdateCacheKey = true;
}
}
/**
* Execute all module hook/widget/widget_block for the dynamic ajax request
*/
public static function execDynamicHooks($controllerInstance = false)
{
$result = array();
if (Tools::getIsset('cache_source')) {
PageCacheDAO::incrementCountHitMissed(self::getCacheKeyInfos(), (int)Tools::getValue('cache_source'));
}
if (Configuration::get('pagecache_statsttfb')) {
PageCacheDAO::addStatsPerf(Shop::getContextShopID(), self::getControllerName(), Tools::getValue('ttfb', null), (int)Tools::getValue('cache_source'));
}
if (Tools::getIsset('stats') || JprestaUtils::isSearchEngine()) {
// A short hand to return faster when only doing stats
return $result;
}
// Execute header hook to get JS definitions
if ($controllerInstance && $controllerInstance instanceof ProductListingFrontController && Configuration::get('pagecache_exec_header_hook')) {
Tools::setCookieLanguage(Context::getContext()->cookie);
Hook::exec('displayHeader');
}
$index = 0;
do {
$val = Tools::getValue('hk_' . $index);
if ($val !== false) {
// Make it safe
$val = htmlentities($val);
list($hookId, $hookType, $id_module, $hook_name, $hook_args) = explode('|', $val);
$moduleInstance = JprestaUtils::getModuleInstanceById($id_module);
if ($moduleInstance) {
try {
// Initialize parameters from ids if any (product, category, etc.)
$args = array();
if (!empty($hook_args)) {
$argvalues = explode('^', $hook_args);
if (is_array($argvalues)) {
foreach ($argvalues as $argvalue) {
if (strpos($argvalue, '=') !== false) {
list($arg, $value) = explode('=', $argvalue);
if (strcmp('pc_ipa', $arg) === 0) {
$args['product'] = (array)new Product((int)$value);
$args['product']['id_product'] = $value;
$args['product']['quantity'] = Product::getQuantity(
(int)$value,
0,
isset($args['product']['cache_is_pack']) ? $args['product']['cache_is_pack'] : null,
Context::getContext()->cart
);
if (!array_key_exists('id_product_attribute', $args['product']) || $args['product']['id_product_attribute'] === null) {
$args['product']['id_product_attribute'] = Product::getDefaultAttribute($value);
}
$args['product']['quantity_all_versions'] = $args['product']['quantity'];
} else if (strcmp('pc_ipl', $arg) === 0) {
// This is called multiple time for the same product so we cache it
static $lazyProducts = [];
if (isset($lazyProducts[(int) $value])) {
$args['product'] = $lazyProducts[(int) $value];
}
else {
$factoryProduct = new ProductPresenterFactory(Context::getContext(),
new TaxConfiguration());
$product = (new ProductAssembler(Context::getContext()))
->assembleProduct(['id_product' => (int)$value]);
$args['product'] = $factoryProduct->getPresenter()->present(
$factoryProduct->getPresentationSettings(),
$product,
Context::getContext()->language
);
$lazyProducts[(int) $value] = $args['product'];
}
} else if (strcmp('pc_ip', $arg) === 0) {
$args['product'] = new Product((int)$value);
if (method_exists($args['product'], 'loadStockData')) {
$args['product']->loadStockData();
}
} else if (strcmp('pc_ica', $arg) === 0) {
$args['category'] = (array)new Category((int)$value);
$args['category']['id_category'] = $value;
} else if (strcmp('pc_ic', $arg) === 0) {
$args['category'] = new Category((int)$value);
} else {
$args[$arg] = urldecode($value);
}
}
}
}
}
if (strpos(self::HOOK_TYPE_MODULE, $hookType) === 0) {
// Display a module hook
$id_hook = Hook::getIdByName(str_replace('hook', '', $hook_name));
if (method_exists('Hook', 'getNameById')) {
// Hook::getNameById from PS1.5.5.0
$hook_name = Hook::getNameById($id_hook);
}
else {
$hook_name = JprestaUtils::dbGetValue('SELECT `name` FROM `'._DB_PREFIX_.'hook` WHERE `id_hook` = '.(int)$id_hook);
}
$array_return = in_array(Tools::strtolower($hook_name), array('displayproductextracontent'));
$content = Hook::exec($hook_name, $args, (int)$id_module, $array_return);
if (is_string($content)) {
$content = mb_convert_encoding($content, "UTF-8", "UTF-8");
}
$result[$hookId] = $content;
} else if (strpos(self::HOOK_TYPE_WIDGET, $hookType) === 0) {
// Display a widget tag
$result[$hookId] = mb_convert_encoding($moduleInstance->renderWidget($hook_name, $args), "UTF-8", "UTF-8");
} else if (strpos(self::HOOK_TYPE_WIDGET_BLOCK, $hookType) === 0) {
// Display a widget_block tag
$blockKey = $hook_name;
$tpl = self::getWidgetBlockTemplate($blockKey);
$scopedVariables = $moduleInstance->getWidgetVariables(null, $args);
$smarty = Context::getContext()->smarty;
foreach ($scopedVariables as $key => $value) {
$smarty->assign($key, $value);
}
$result[$hookId] = mb_convert_encoding($moduleInstance->fetch($tpl), "UTF-8", "UTF-8");
}
if (is_array($result[$hookId])
&& array_key_exists($moduleInstance->name, $result[$hookId])
&& is_array($result[$hookId][$moduleInstance->name])
&& array_key_exists('pec_idx', $args)
&& array_key_exists($args['pec_idx'], $result[$hookId][$moduleInstance->name])
&& $result[$hookId][$moduleInstance->name][$args['pec_idx']] instanceof PrestaShop\PrestaShop\Core\Product\ProductExtraContent) {
// Handle the hookDisplayProductExtraContent hook
$result[$hookId] = mb_convert_encoding($result[$hookId][$moduleInstance->name][$args['pec_idx']]->getContent(), "UTF-8", "UTF-8");
}
else if (is_array($result[$hookId])
&& array_key_exists(0, $result[$hookId])
&& $result[$hookId][0] instanceof PrestaShop\PrestaShop\Core\Product\ProductExtraContent) {
// Handle the hookDisplayProductExtraContent hook
$result[$hookId] = mb_convert_encoding($result[$hookId][0]->getContent(), "UTF-8", "UTF-8");
}
}
catch (Exception $e) {
$result[$hookId] = '';
}
}
}
$index++;
} while ($val !== false);
if (Tools::version_compare(_PS_VERSION_,'1.7.1.0','<')) {
// From PS 1.7.1 the cookie will be sent in ActionDispatcherAfter
self::sendContextCookie();
}
return $result;
}
private static function saveProfiling($moduleInstance, $description, $duration) {
static $profiling = null;
if ($profiling === null) {
$profiling = (bool) JprestaUtils::getConfigurationAllShop('pagecache_profiling');
}
static $profilingMaxReached = null;
if ($profilingMaxReached === null) {
$profilingMaxReached = (bool) JprestaUtils::getConfigurationAllShop('pagecache_profiling_max_reached');
}
static $profilingTriggerMinMs = null;
if ($profilingTriggerMinMs === null) {
$profilingTriggerMinMs = (int) JprestaUtils::getConfigurationAllShop('pagecache_profiling_min_ms');
}
if ($profiling && !$profilingMaxReached && $duration >= $profilingTriggerMinMs) {
if (!PageCacheDAO::addProfiling($moduleInstance->id, $description, $duration, self::PROFILING_MAX_RECORD)) {
JprestaUtils::saveConfigurationAllShop('pagecache_profiling_max_reached', true);
}
}
}
public static function execHook($hookType, $moduleInstance, $hookName, $hookArgs) {
$returnValue = '';
if (self::preHook($returnValue, $hookType, $moduleInstance, $hookName, $hookArgs)) {
try {
// Do it only once to optimize
static $langIsSet = null;
if ($langIsSet === null) {
$langIsSet = true;
Tools::setCookieLanguage(Context::getContext()->cookie);
}
}
catch (\ErrorException $e) {
// MoneticoPaiement module has its own error handler so Tools::setCookieLanguage may throw an exception
// that must be ignored
}
$hookValue = false;
$startExecutionTime = microtime(true);
if ($hookType === self::HOOK_TYPE_MODULE) {
$hookValue = $moduleInstance->{$hookName}($hookArgs);
// Code added by/for Idnovate (ticket #785)
if (JprestaUtils::isModuleEnabled('cookiesplus')) {
$cookiesPlus = Module::getInstanceByName('cookiesplus');
if ($cookiesPlus && method_exists($cookiesPlus, "blockModuleCode")) {
$cookiesPlus->blockModuleCode(array(
'display' => &$hookValue,
'module' => &$moduleInstance,
'hookName' => &$hookName,
'params' => &$hookArgs,
));
}
}
// Do profiling (if enabled)
self::saveProfiling($moduleInstance, "$hookName()", (microtime(true) - $startExecutionTime)*1000);
}
elseif ($hookType === self::HOOK_TYPE_WIDGET) {
$hookValue = $moduleInstance->renderWidget($hookName, $hookArgs);
// Code added by/for Idnovate (ticket #785)
if (JprestaUtils::isModuleEnabled('cookiesplus')) {
$cookiesPlus = Module::getInstanceByName('cookiesplus');
if ($cookiesPlus && method_exists($cookiesPlus, "blockModuleCode")) {
$cookiesPlus->blockModuleCode(array(
'display' => &$hookValue,
'module' => &$moduleInstance,
'hookName' => &$hookName,
'params' => &$hookArgs,
));
}
}
// Do profiling (if enabled)
self::saveProfiling($moduleInstance, "renderWidget('$hookName')", (microtime(true) - $startExecutionTime)*1000);
}
if (is_array($hookValue) && array_key_exists(0, $hookValue) && $hookValue[0] instanceof PrestaShop\PrestaShop\Core\Product\ProductExtraContent) {
// Handle the hookDisplayProductExtraContent hook
if (!is_array($hookArgs)) {
$hookArgs = array();
}
foreach ($hookValue as $pecKey => $pec) {
if ($pec instanceof PrestaShop\PrestaShop\Core\Product\ProductExtraContent) {
$extraContent = $pec->getContent();
if (is_string($extraContent)) {
$newExtraContent = '';
$hookArgs['pec_idx'] = $pecKey;
if (self::preHook($newExtraContent, $hookType, $moduleInstance, $hookName, $hookArgs)) {
$newExtraContent = $newExtraContent . $extraContent;
}
self::postHook($newExtraContent, $hookType, $moduleInstance, $hookName);
$pec->setContent($newExtraContent);
}
}
}
return $hookValue;
}
elseif (!is_string($hookValue) && $hookValue !== false && $hookValue !== null) {
// Handle non string returned values
return $hookValue;
}
else {
if ($returnValue === '') {
$returnValue = $hookValue;
}
else {
$returnValue .= $hookValue;
}
}
}
self::postHook($returnValue, $hookType, $moduleInstance, $hookName);
return $returnValue;
}
public static function preHook(&$output, $hookType, $moduleInstance, $hookName, $hookArgs)
{
// $executed_modules must be set here so it is done for all Prestashop version 1.5, 1.6, 1.7, 8, etc
if (in_array($moduleInstance->name, self::getModulesToCheck()) && !in_array($hookName, ['hookDisplayHeader', 'hookHeader', 'header'])) {
self::$executed_modules[$moduleInstance->id] = $moduleInstance->id;
}
$displayContent = true;
if (self::$initialised && self::canBeCached() && !JprestaUtils::isAjax() && strcasecmp('hookmoduleroutes', $hookName) !== 0 && strcasecmp('hookactionDispatcherBefore', $hookName) !== 0 && strcasecmp('hookActionOverrideSeoRoutes', $hookName) !== 0) {
if (strcmp(self::HOOK_TYPE_MODULE, $hookType) === 0) {
$directives = self::_getHookCacheDirectives($moduleInstance->name, $hookName);
}
else {
$directives = self::_getWidgetCacheDirectives($moduleInstance->name, $hookName);
}
if ($directives['wrapper']) {
$hookToCall = $hookName;
if (method_exists('Hook', 'normalizeHookName')) {
$hookToCall = Hook::normalizeHookName(str_replace('hook', '', $hookName));
}
$output .= '';
$displayContent = $directives['content'];
}
}
return $displayContent;
}
public static function postHook(&$output, $hookType, $moduleInstance, $hookName)
{
if (self::$initialised && self::canBeCached() && !JprestaUtils::isAjax() && strcasecmp('hookmoduleroutes', $hookName) !== 0 && strcasecmp('hookactionDispatcherBefore', $hookName) !== 0 && strcasecmp('hookActionOverrideSeoRoutes', $hookName) !== 0) {
if (strcmp(self::HOOK_TYPE_MODULE, $hookType) === 0) {
$directives = self::_getHookCacheDirectives($moduleInstance->name, $hookName);
}
else {
$directives = self::_getWidgetCacheDirectives($moduleInstance->name, $hookName);
}
if ($directives['wrapper']) {
$output .= '';
}
}
}
/**
* Call preHook and postHook for widget_block
* @param $params Parameters on widget block tag
* @param $content HTML content of the widget block
* @param $smarty Smarty instance
* @return string Modified content of the widget block
*/
public static function smartyWidgetBlockPageCache($params, $content, &$smarty)
{
$output = '';
if (null === $content) {
// Function is called twice: at the opening of the block
// and when it is closed.
// This is the first call.
$output = smartyWidgetBlock($params, $content, $smarty);
} else {
// Function gets called for the closing tag of the block.
$html = smartyWidgetBlock($params, $content, $smarty);
if (array_key_exists('pckey', $params)) {
$blockKey = $params['pckey'];
$moduleName = $params['name'];
$moduleInstance = Module::getInstanceByName($moduleName);
if (self::preHook($output, self::HOOK_TYPE_WIDGET_BLOCK, $moduleInstance, $blockKey, $params)) {
$output .= $html;
}
self::postHook($output, self::HOOK_TYPE_WIDGET_BLOCK, $moduleInstance, $blockKey);
}
else {
$output = $html;
}
}
return $output;
}
/**
* Called just before smarty compilation. It adds an attribute 'pckey' to all widget_block tag to extract and
* save the template block into a file to be able to refresh this part separately (with dynamic ajax request
* @param $source Smarty template content
* @param $smarty Smarty instance
* @return string Modified template content
*/
public static function smartyWidgetBlockPageCachePrefilter($source, $smarty) {
$lastOffset = JprestaUtils::strpos($source, '{widget_block');
if ($lastOffset !== false) {
$moduleInstance = Module::getInstanceByName('pagecache');
if ($moduleInstance) {
$modifiedSource = Tools::substr($source, 0, $lastOffset);
// Find widget_block blocks, add 'key' attribute, store content of the block in cache
$pattern = '/({widget_block[\s]+name=\"([a-zA-Z0-9_]+)\"[\s]*)}(.*){\/widget_block}/sU';
$matches = array();
preg_match($pattern, $source, $matches);
while (count($matches) > 0) {
$hash = crc32($smarty->source->filepath . $matches[0]);
$blockKey = sprintf('%u', $hash);
$offset = JprestaUtils::strpos($source, $matches[0]);
$modifiedSource .= Tools::substr($source, $lastOffset, $offset - $lastOffset);
$modifiedSource .= $matches[1];
$modifiedSource .= ' pckey="' . $blockKey . '"}';
$modifiedSource .= $matches[3];
$modifiedSource .= '{/widget_block}';
$lastOffset = $offset + Tools::strlen($matches[0]);
$moduleInstance->setWidgetBlockTemplate($blockKey, $matches[3]);
// Next
$matches = array();
preg_match($pattern, $source, $matches, 0, $lastOffset);
}
$modifiedSource .= Tools::substr($source, $lastOffset, Tools::strlen($source));
return $modifiedSource;
}
}
return $source;
}
public static function getJsDef() {
if (Tools::version_compare(_PS_VERSION_,'1.6','>')) {
$context = Context::getContext();
Media::addJsDef(array(
'isLogged' => (bool)$context->customer->isLogged(),
'isGuest' => (bool)$context->customer->isGuest(),
'comparedProductsIds' => $context->smarty->getTemplateVars('compared_products'),
));
$defs = Media::getJsDef();
$defs['prestashop_pc'] = $defs['prestashop'];
if ($context->customer->isLogged() && array_key_exists('customer', $defs['prestashop_pc']) && !array_key_exists('id_customer', $defs['prestashop_pc']['customer'])) {
// For some modules we need the id of current visitor
$defs['prestashop_pc']['customer']['id_customer'] = $context->customer->id;
}
unset($defs['prestashop']);
unset($defs['baseDir']);
unset($defs['baseUrl']);
// Fix for module Revolition Slider
unset($defs['SdsJsOnLoadActions']);
return $defs;
}
return array();
}
public static function getCurrencyId($context) {
$context->currency = Tools::setCurrency($context->cookie);
if (!isset($context->cookie->id_currency)) {
$id_currency = Configuration::get('PS_CURRENCY_DEFAULT');
} else {
$id_currency = $context->cookie->id_currency;
}
return (int) $id_currency;
}
public static function getCountry($context) {
// static variable avoid computing the country multiple times
static $current_country = null;
if ($current_country == null) {
$current_country = false;
if (Configuration::get('PS_GEOLOCATION_ENABLED')) {
// Detect country now to get it right
$current_country = self::getCountryByGeolocation($context);
} elseif (Configuration::get('PS_DETECT_COUNTRY')) {
$has_currency = isset($context->cookie->id_currency) && (int) $context->cookie->id_currency;
$has_country = isset($context->cookie->iso_code_country) && $context->cookie->iso_code_country;
$has_address_type = false;
if ((int) $context->cookie->id_cart && ($cart = new Cart($context->cookie->id_cart)) && Validate::isLoadedObject($cart)) {
$has_address_type = isset($cart->{Configuration::get('PS_TAX_ADDRESS_TYPE')}) && $cart->{Configuration::get('PS_TAX_ADDRESS_TYPE')};
}
if ((!$has_currency || $has_country) && !$has_address_type) {
$id_country = $has_country && Validate::isLanguageIsoCode($context->cookie->iso_code_country) ?
(int) Country::getByIso(Tools::strtoupper($context->cookie->iso_code_country)) : (int) Tools::getCountry();
try {
$country = new Country($id_country, (int)$context->cookie->id_lang);
if (validate::isLoadedObject($country)) {
$current_country = $country;
}
}
catch (PrestaShopException $e) {
// Ignore
}
}
} elseif ($context->country) {
$current_country = $context->country;
}
// Address of customer, if any, has higher priority
$current_tax_address = false;
if ((int)$context->cookie->id_cart) {
$cart = new Cart($context->cookie->id_cart);
$id_address = $cart->{Configuration::get('PS_TAX_ADDRESS_TYPE')};
/* If address is not set then FrontController::init will set the it with the first address of the customer */
if ($cart->id_customer && (!isset($id_address) || $id_address == 0)) {
$id_address = (int)Address::getFirstCustomerAddressId($cart->id_customer);
}
if ($id_address) {
$current_tax_address = Address::initialize($id_address);
}
} else {
if ($context->cookie->id_customer) {
/* There is no cart but a customer is logged in */
$id_address = (int)(Address::getFirstCustomerAddressId($context->cookie->id_customer));
if ($id_address) {
/* Take his first address */
$current_tax_address = Address::initialize($id_address);
}
}
}
if ($current_tax_address && $current_tax_address->id_country) {
$current_country = new Country($current_tax_address->id_country);
}
// No country found? Then return default country of the shop.
if (!$current_country) {
$current_country = new Country((int)Configuration::get('PS_COUNTRY_DEFAULT'));
}
}
return $current_country;
}
private static function getCountryByGeolocation($context)
{
$country = null;
$controller_instance = self::getControllerInstance();
if ($controller_instance !== false && method_exists($controller_instance, 'geolocationManagementPublic')) {
if (($newDefault = $controller_instance->geolocationManagementPublic($context->country)) && Validate::isLoadedObject($newDefault)) {
$country = $newDefault;
}
}
return $country;
}
private static function isDependsOnDevice() {
static $depends_on_devices = null;
if ($depends_on_devices == null) {
$val = Configuration::get('pagecache_depend_on_device_auto');
if ($val) {
$depends_on_devices = true;
}
else {
$depends_on_devices = false;
}
}
return $depends_on_devices;
}
private static function getControllerName() {
static $controller = false;
if (self::$initialised && !$controller) {
$request = null;
if (array_key_exists('request', $GLOBALS) && $GLOBALS['request'] instanceof Symfony\Component\HttpFoundation\Request) {
$request = $GLOBALS['request'];
}
$controller = Dispatcher::getInstance($request)->getController();
if (Tools::getIsset('fc') && Tools::getValue('fc') === 'module') {
$controller = Tools::getValue('module') . '__' . $controller;
}
if ($controller === 'pagenotfound' && JprestaUtils::isModuleEnabled('smartseourl')) {
$smartseourlInstance = Module::getInstanceByName('smartseourl');
$controllerClass = $smartseourlInstance->dispatch();
$controller = str_replace('controller', '', Tools::strtolower($controllerClass));
}
}
return $controller;
}
private static function getControllerInstance() {
// static variable avoid computing the controller multiple times
static $controller = null;
if ($controller == null) {
$controller = false;
// Load controllers classes
$controllers = Dispatcher::getControllers(array(_PS_FRONT_CONTROLLER_DIR_, _PS_OVERRIDE_DIR_.'controllers/front/'));
$controllers['index'] = 'IndexController';
// Get controller name
$controller_name = self::getControllerName();
if (isset($controllers[Tools::strtolower($controller_name)])) {
// Create controller instance
$controller_class = $controllers[Tools::strtolower($controller_name)];
$context = Context::getContext();
if ($context->controller) {
$controller = $context->controller;
} else {
if (!isset($context->link)) {
/* Link should be initialized in the context but sometimes it is not */
$https_link = (Tools::usingSecureMode() && Configuration::get('PS_SSL_ENABLED')) ? 'https://' : 'http://';
$context->link = new Link($https_link, $https_link);
}
if (JprestaUtils::isModuleEnabled('smartseourl') && in_array($controller_class, array('ProductController', 'CategoryController', 'CmsController', 'SupplierController', 'ManufacturerController'))) {
$controller = Controller::getController('PageNotFoundController');
}
else {
$controller = Controller::getController($controller_class);
}
}
}
}
return $controller;
}
public static function getCurrentURL() {
$https = self::getServerValue('HTTPS');
if (!empty($https) && $https !== 'off' || self::getServerValue('SERVER_PORT') == 443) {
$pageURL = 'https://' . $_SERVER['HTTP_HOST'] . urldecode($_SERVER['REQUEST_URI']);
}
else {
$proto = self::getServerValue('HTTP_X_FORWARDED_PROTO');
if ($proto) {
$pageURL = $proto . '://' . $_SERVER['HTTP_HOST'] . urldecode($_SERVER['REQUEST_URI']);
}
else {
$pageURL = 'http://' . $_SERVER['HTTP_HOST'] . urldecode($_SERVER['REQUEST_URI']);
}
}
return $pageURL;
}
public static function filterAndSortParams($query_string, $ignored_params) {
$new_query_string = '';
if ($query_string) {
$keyvalues = explode('&', $query_string);
sort($keyvalues);
foreach ($keyvalues as $keyvalue) {
if ($keyvalue !== '') {
$key = '';
$value = '';
$current_key_value = explode('=', $keyvalue);
if (count($current_key_value) > 0) {
$key = Tools::strtolower($current_key_value[0]);
}
if (count($current_key_value) > 1) {
$value = $current_key_value[1];
}
if (!in_array($key, $ignored_params)) {
$new_query_string .= '&' . $key . '=' . $value;
}
}
}
if ($new_query_string !== '') {
$new_query_string = Tools::substr($new_query_string, 1);
}
}
return $new_query_string;
}
public static function cacheThis($html) {
if (self::isNotCode200()) {
return;
}
$maxrows = (int) Configuration::get('pagecache_maxrows');
if ($maxrows > 0 && PageCacheDAO::getMainRowsCount() > $maxrows) {
// Try to purge the cache
PageCacheDAO::deleteCachedPages(PageCacheDAO::getCachedPages(24, 10, null), true);
if (!headers_sent()) {
header(self::HTTP_HEADER_CACHE_INFO . ': status=off, reason=maxrows-reach');
}
return;
}
// Some old theme are calling smartyOutputContent multiple times
static $cumulHtml = false;
if ($cumulHtml === false || Tools::version_compare(_PS_VERSION_,'1.7','>=')) {
$cumulHtml = $html;
}
else {
$cumulHtml .= $html;
$html = $cumulHtml;
}
// Save the html into the cache
$controller = self::getControllerName();
$cache_ttl = 60 * ((int)Configuration::get('pagecache_'.$controller.'_timeout'));
$jprestaCacheKey = self::getCacheKeyInfos();
self::getCache()->set($jprestaCacheKey->get('url'), PageCacheDAO::getContextKeyById(PageCacheDAO::getOrCreateContextId($jprestaCacheKey)), $html, $cache_ttl);
// Parse this file to find all backlinks
$backlinks = array();
$shop_url = new ShopUrl(Shop::getContextShopID());
$base = $shop_url->getURL();
$links = JprestaUtils::parseLinks($html, $base, self::getManagedControllersNames(), '*PCIGN*', '**PCIGN**', JprestaUtils::decodeConfiguration(Configuration::get('pagecache_ignore_before_pattern')), JprestaUtils::decodeConfiguration(Configuration::get('pagecache_ignore_after_pattern')));
foreach ($links as $link) {
$linkCacheKey = self::getCacheKeyForBacklink($link);
$backlinks[$linkCacheKey] = $linkCacheKey;
}
// Insert in database
PageCacheDAO::insert(
$jprestaCacheKey,
$controller,
Shop::getContextShopID(),
self::getCurrentObjectId(),
self::$executed_modules,
$backlinks,
Configuration::get('pagecache_logs'),
!self::isCacheWarmer() && !self::isStatusChecker());
// Reduce the cache continuously (remove 2 expired row when it adds one new row)
PageCacheDAO::deleteCachedPages(PageCacheDAO::getCachedPages(24, 2, null), true);
}
public static function preDisplayStats() {
if (JprestaUtils::isAjax()) {
// Skip useless work
return array();
}
$infos = array();
if (self::isDisplayStats()) {
$context = Context::getContext();
$currency = new Currency(self::getCurrencyId($context));
$controller = self::getControllerName();
if (in_array($controller, self::getManagedControllersNames())) {
$country = self::getCountry($context);
$infos['cacheable'] = self::canBeCached() ? 'true' : 'false';
$infos['cacheable_reason'] = self::$status_reason;
$timeoutValue = (int) Configuration::get('pagecache_'.$controller.'_timeout');
if ($timeoutValue === 0) {
$timeoutValue = 'Disabled';
}
elseif ($timeoutValue === -1) {
$timeoutValue = 'Never';
}
else {
$timeoutValue = ($timeoutValue / 1440) . ' day(s)';
}
$expiresValue = (int) Configuration::get('pagecache_'.$controller.'_expires');
if ($expiresValue === 0) {
$expiresValue = 'Disabled';
}
else {
$expiresValue = $expiresValue . ' minute(s)';
}
$infos['loc_tax'] = self::getCountryStateZipcodeForTaxes($context);
$infos['timeout_server'] = $timeoutValue;
$infos['timeout_browser'] = $expiresValue;
$infos['controller'] = $controller;
$infos['currency'] = $currency->name;
if ($country) {
$infos['country'] = $country->getFieldByLang('name');
} else {
$infos['country'] = '-';
}
$infos['cache_key'] = json_encode(self::getCacheKeyInfos()->compute()->infos, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT);
}
}
return $infos;
}
public static function displayStats($from_cache, $infos) {
if (self::isDisplayStats()) {
$controller = self::getControllerName();
if (in_array($controller, self::getManagedControllersNames())) {
// Prepare datas
$context = Context::getContext();
$infos['groups'] = '';
$groupsIds = self::getGroupsIds($context);
foreach ($groupsIds as $arrayKey => $groupId) {
if (((int)$groupId) > 0) {
$group = new Group($groupId);
$infos['groups'] = $infos['groups'] . $group->name[$context->language->id] . ($arrayKey === 0 ? '*' : '') . ', ';
}
}
$infos['from_cache'] = $from_cache;
$stats = PageCacheDAO::getStats(self::getCacheKeyInfos());
if ($stats['hit'] != -1) {
$infos['hit'] = $stats['hit'];
$infos['missed'] = $stats['missed'];
$infos['perfs'] = ($stats['hit']+$stats['missed'] !== 0) ? number_format((100*$stats['hit']/($stats['hit']+$stats['missed'])), 1).'%' : '-';
} else {
$infos['hit'] = '-';
$infos['missed'] = '-';
$infos['perfs'] = '-';
}
$infos['pagehash'] = self::getCacheKeyInfos()->toString();
$infos['url_on_off'] = http_build_url(self::getCleanURL(), array("query" => 'dbgpagecache='.((int)Tools::getValue('dbgpagecache', 0) == 0 ? 1 : 0)), HTTP_URL_JOIN_QUERY);
$infos['url_on_off_disabled'] = Configuration::get('pagecache_always_infosbox') && !Configuration::get('pagecache_debug');
$infos['url_del'] = http_build_url(self::getCleanURL(), array("query" => 'dbgpagecache='.Tools::getValue('dbgpagecache', 0).'&delpagecache=1'), HTTP_URL_JOIN_QUERY);
$infos['url_reload'] = http_build_url(self::getCleanURL(), array("query" => 'dbgpagecache='.Tools::getValue('dbgpagecache', 1)), HTTP_URL_JOIN_QUERY);
$infos['url_close'] = self::getCleanURL();
$infos['url_close_disabled'] = Configuration::get('pagecache_always_infosbox');
$infos['dbgpagecache'] = (int)Tools::getValue('dbgpagecache', 0) || !Configuration::get('pagecache_debug') ? 1 : 0;
$infos['base_dir'] = _PS_BASE_URL_.__PS_BASE_URI__;
// Display the box
$context->smarty->assign($infos);
$context->smarty->display(_PS_MODULE_DIR_.basename(__FILE__, '.php').'/views/templates/hook/pagecache-infos.tpl');
}
}
}
public static function getCleanURL($url = null)
{
if ($url == null) {
$url = self::getCurrentURL();
}
$new_query = '';
$query = parse_url($url, PHP_URL_QUERY);
if ($query != null) {
$query = html_entity_decode($query);
$keyvals = explode('&', $query);
foreach($keyvals as $keyval) {
$x = explode('=', $keyval);
if (strcmp($x[0], 'dbgpagecache') != 0 && strcmp($x[0], 'delpagecache') != 0) {
$new_query .= '&'.$x[0].'='.(count($x)>1 ? $x[1] : '');
}
}
}
$un = new PageCacheURLNormalizer();
$un->setUrl (http_build_url($url, array("query" => $new_query), HTTP_URL_REPLACE));
return $un->normalize();
}
/**
* @return bool true if the cache was correctly cleared
*/
public static function clearCache($reason = 'unknown') {
$startTime = microtime(true);
$clearOK = true;
// Delete cache of current shop(s)
if (Shop::isFeatureActive()) {
foreach (Shop::getContextListShopID() as $id_shop) {
$clearOK = $clearOK && self::getCache($id_shop)->flush(self::FLUSH_MAX_SECONDS);
}
} else {
$clearOK = $clearOK && self::getCache()->flush(self::FLUSH_MAX_SECONDS);
}
// TODO pourquoi on vide tout et pas par boutique?
PageCacheDAO::clearAllCache();
try {
$softizyconditionalcache = Module::getInstanceByName('softizyconditionalcache');
if ($softizyconditionalcache) {
if (method_exists($softizyconditionalcache, 'hookActionClearCache')) {
$softizyconditionalcache->hookActionClearCache();
}
elseif (method_exists($softizyconditionalcache, 'hookActionClearSf2Cache')) {
$softizyconditionalcache->hookActionClearSf2Cache();
}
}
}
catch (Exception $e) {
// Ignore
}
if (Configuration::get('pagecache_logs') > 0) {
$msg = '';
$stacks = debug_backtrace();
for ($i = 0; $i < count($stacks); $i++) {
if (array_key_exists('file', $stacks[$i])) {
$msg .= $stacks[$i]['function'] . '(' . basename($stacks[$i]['file']) . ':' . $stacks[$i]['line'] . ')';
}
else {
$msg .= $stacks[$i]['function'] . '(?)';
}
if ($i + 1 < count($stacks)) {
$msg .= ' - ';
}
}
JprestaUtils::addLog("PageCache | clearCache($reason) | $msg = " . number_format(microtime(true) - $startTime, 3) . " second(s)", 1, null, null, null, true);
}
else {
JprestaUtils::addLog("PageCache | clearCache($reason) | " . number_format(microtime(true) - $startTime, 3) . " second(s)", 1, null, null, null, true);
}
// Update database stats
PageCacheDAO::analyzeTables();
return $clearOK;
}
public function clearCacheAndStats($reason = 'unknown') {
$startTime = microtime(true);
$clearOK = true;
// Delete cache and stats of current shop(s)
if (Shop::isFeatureActive()) {
foreach (Shop::getContextListShopID() as $id_shop) {
$clearOK = $clearOK && self::getCache($id_shop)->flush(self::FLUSH_MAX_SECONDS);
}
PageCacheDAO::resetCache(Shop::getContextListShopID());
if (!Configuration::get('pagecache_statsttfb')) {
PageCacheDAO::resetStatPerfs(Shop::getContextListShopID());
}
} else {
$clearOK = $clearOK && self::getCache()->flush(self::FLUSH_MAX_SECONDS);
PageCacheDAO::resetCache();
if (!Configuration::get('pagecache_statsttfb')) {
PageCacheDAO::resetStatPerfs();
}
}
// Update database stats
PageCacheDAO::analyzeTables();
JprestaUtils::addLog("PageCache | reset($reason) | " . number_format(microtime(true) - $startTime, 3) . " second(s)", 1, null, null, null, true);
return $clearOK;
}
public function purgeCache($id_shop, $reason = 'unknown') {
$clearOK = true;
$deleteDuration = 0;
$round = 1;
$startTime = microtime(true);
$obsoleteContextsIds = PageCacheDAO::getObsoleteContextsIds($id_shop);
$rowsTodelete = PageCacheDAO::getCachedPages(24, 500, true, $obsoleteContextsIds);
$searchDuration = microtime(true) - $startTime;
while ($clearOK && count($rowsTodelete) > 0) {
$startTimeDelete = microtime(true);
PageCacheDAO::deleteCachedPages($rowsTodelete, true);
$deleteDuration += microtime(true) - $startTimeDelete;
// Check duration to avoid timeouts
if ((microtime(true) - $startTime) > self::FLUSH_MAX_SECONDS) {
$clearOK = false;
break;
}
$round++;
$startTimeSearch = microtime(true);
$rowsTodelete = PageCacheDAO::getCachedPages(24, 500, true, $obsoleteContextsIds);
$searchDuration += microtime(true) - $startTimeSearch;
}
PageCacheDAO::deleteUnusedContexts();
PageCacheDAO::deleteUnusedFakeUsers();
JprestaUtils::addLog("PageCache | purge($reason) done in " . $round . ' rounds: search during ' . round($searchDuration,
3) . 's, deletion tooks ' . round($deleteDuration, 3) . 's');
// Update database stats
PageCacheDAO::analyzeTables();
if ((microtime(true) - $startTime) < self::FLUSH_MAX_SECONDS) {
$this->getCacheInstance($id_shop)->purge(self::FLUSH_MAX_SECONDS - (microtime(true) - $startTime));
}
return $clearOK;
}
private function _clearCacheModules($event, $action_origin='') {
$val = Configuration::get($event.'_mods');
if ($val) {
$mods = explode(' ', $val);
foreach ($mods as $mod) {
$module_name = trim($mod);
if (Tools::strlen($mod) > 0) {
PageCacheDAO::clearCacheOfModule($module_name, $action_origin,
Configuration::get('pagecache_logs'));
}
}
}
}
public function hookActionAttributeDelete($params) {
$this->hookActionAttributeSave($params);
}
public function hookActionAttributeSave($params) {
if (isset($params['id_attribute'])) {
// An attribute has been modified, it can be its label, its URL, etc. so all products using it must
// be refreshed (only the product page)
$productsIds = Db::getInstance()->executeS('
SELECT DISTINCT pa.id_product
FROM '._DB_PREFIX_.'product_attribute pa
LEFT JOIN '._DB_PREFIX_.'product_attribute_combination pac ON (pac.id_product_attribute = pa.id_product_attribute)
WHERE pac.id_attribute = '.(int)$params['id_attribute']
);
foreach ($productsIds as $productId) {
$this->onProductUpdate($productId['id_product'], 'modification/deletion of Attribute#' . $params['id_attribute']);
}
}
}
public function hookActionAttributeGroupDelete($params) {
$this->hookActionAttributeGroupSave($params);
}
public function hookActionAttributeGroupSave($params) {
if (isset($params['id_attribute_group'])) {
// An attribute group has been modified, it can be its label, its URL, etc. so all products using it must
// be refreshed (only the product page)
$productsIds = Db::getInstance()->executeS('
SELECT DISTINCT pa.id_product
FROM '._DB_PREFIX_.'product_attribute pa
LEFT JOIN '._DB_PREFIX_.'product_attribute_combination pac ON (pac.id_product_attribute = pa.id_product_attribute)
LEFT JOIN '._DB_PREFIX_.'attribute a ON (a.id_attribute = pac.id_attribute)
WHERE a.id_attribute_group = '.(int)$params['id_attribute_group']
);
foreach ($productsIds as $productId) {
$this->onProductUpdate($productId['id_product'], 'modification/deletion of AttributeGroup#' . $params['id_attribute_group']);
}
}
}
public function hookActionFeatureDelete($params) {
$this->hookActionFeatureSave($params);
}
public function hookActionFeatureSave($params) {
if (isset($params['id_feature'])) {
// An feature has been modified, it can be its label, etc. so all products using it must
// be refreshed (only the product page)
$id_feature = $params['id_feature'];
$productsIds = Db::getInstance()->executeS('
SELECT DISTINCT p.id_product
FROM '._DB_PREFIX_.'product p
LEFT JOIN '._DB_PREFIX_.'feature_product f ON (f.id_product = p.id_product)
WHERE f.id_feature = '.(int)$id_feature
);
foreach ($productsIds as $productId) {
$this->onProductUpdate($productId['id_product'], 'modification/deletion of Feature#' . $id_feature);
}
}
}
public function hookActionFeatureValueDelete($params) {
$this->hookActionFeatureValueSave($params);
}
public function hookActionFeatureValueSave($params) {
if (isset($params['id_feature_value'])) {
// An feature value has been modified, it can be its label, etc. so all products using it must
// be refreshed (only the product page)
$id_feature_value = $params['id_feature_value'];
$productsIds = Db::getInstance()->executeS('
SELECT DISTINCT p.id_product
FROM '._DB_PREFIX_.'product p
LEFT JOIN '._DB_PREFIX_.'feature_product fp ON (fp.id_product = p.id_product)
WHERE fp.id_feature = '.(int)$id_feature_value
);
foreach ($productsIds as $productId) {
$this->onProductUpdate($productId['id_product'], 'modification/deletion of FeatureValue#' . $id_feature_value);
}
}
}
public function hookActionObjectCmsAddAfter($params) {
if (isset($params['object'])) {
PageCacheDAO::clearCacheOfObject('cms', $params['object']->id, false, 'creation of CMS page #' . $params['object']->id, Configuration::get('pagecache_logs'));
}
$this->_clearCacheModules('pagecache_cms_a', 'creation of CMS page #' . $params['object']->id);
}
public function hookActionObjectCmsUpdateAfter($params) {
if (isset($params['object'])) {
PageCacheDAO::clearCacheOfObject('cms', $params['object']->id, Configuration::get('pagecache_cms_u_bl'), 'modification of CMS page #' . $params['object']->id, Configuration::get('pagecache_logs'));
}
$this->_clearCacheModules('pagecache_cms_u', 'modification of CMS page #' . $params['object']->id);
}
public function hookActionObjectCmsDeleteBefore($params) {
if (isset($params['object'])) {
PageCacheDAO::clearCacheOfObject('cms', $params['object']->id, Configuration::get('pagecache_cms_d_bl'), 'deletion of CMS page #' . $params['object']->id, Configuration::get('pagecache_logs'));
}
$this->_clearCacheModules('pagecache_cms_d', 'deletion of CMS page #' . $params['object']->id);
}
public function hookActionObjectManufacturerAddAfter($params) {
if (isset($params['object'])) {
PageCacheDAO::clearCacheOfObject('manufacturer', $params['object']->id, false, 'hookActionObjectManufacturerAddAfter', Configuration::get('pagecache_logs'));
// Also clear the page with all manufacturers
PageCacheDAO::clearCacheOfObject('manufacturer', null, false, 'hookActionObjectManufacturerAddAfter', Configuration::get('pagecache_logs'));
}
$this->_clearCacheModules('pagecache_manufacturer_a', 'hookActionObjectManufacturerAddAfter');
}
public function hookActionObjectManufacturerUpdateAfter($params) {
if (isset($params['object'])) {
PageCacheDAO::clearCacheOfObject('manufacturer', $params['object']->id, Configuration::get('pagecache_manufacturer_u_bl'), 'hookActionObjectManufacturerUpdateAfter', Configuration::get('pagecache_logs'));
// Also clear the page with all manufacturers
PageCacheDAO::clearCacheOfObject('manufacturer', null, Configuration::get('pagecache_manufacturer_u_bl'), 'hookActionObjectManufacturerUpdateAfter', Configuration::get('pagecache_logs'));
}
$this->_clearCacheModules('pagecache_manufacturer_u', 'hookActionObjectManufacturerUpdateAfter');
}
public function hookActionObjectManufacturerDeleteBefore($params) {
if (isset($params['object'])) {
PageCacheDAO::clearCacheOfObject('manufacturer', $params['object']->id, Configuration::get('pagecache_manufacturer_d_bl'), 'hookActionObjectManufacturerDeleteBefore', Configuration::get('pagecache_logs'));
// Also clear the page with all manufacturers
PageCacheDAO::clearCacheOfObject('manufacturer', null, Configuration::get('pagecache_manufacturer_d_bl'), 'hookActionObjectManufacturerDeleteBefore', Configuration::get('pagecache_logs'));
}
$this->_clearCacheModules('pagecache_manufacturer_d', 'hookActionObjectManufacturerDeleteBefore');
}
private static $lastStockAvailableCanOrder = null;
private static $lastStockAvailableQuantity = null;
/**
* Called when a warehouse is associated to a product with advanced stock management enabled
*/
public function hookActionObjectWarehouseProductLocationAddBefore($params) {
if (isset($params['object'])) {
$newWarehouseProductLocation = $params['object'];
$product = new Product($newWarehouseProductLocation->id_product);
$product->id_product_attribute = (int)$newWarehouseProductLocation->id_product_attribute;
self::$lastStockAvailableCanOrder = $product->checkQty(1);
self::$lastStockAvailableQuantity = StockManagerFactory::getManager()->getProductRealQuantities($product->id, $product->id_product_attribute, null, true);
}
}
/**
* Called when a new warehouse is associated to a product with advanced stock management enabled
*/
public function hookActionObjectWarehouseProductLocationAddAfter($params) {
if (isset($params['object'])) {
$newWarehouseProductLocation = $params['object'];
$product = new Product($newWarehouseProductLocation->id_product);
$product->id_product_attribute = (int)$newWarehouseProductLocation->id_product_attribute;
$newQuantity = StockManagerFactory::getManager()->getProductRealQuantities($product->id, $product->id_product_attribute, null, true);
$this->handleStock($product, $product->id_product_attribute, $newQuantity);
}
}
/**
* Called when a warehouse is disassociated to a product with advanced stock management enabled
*/
public function hookActionObjectWarehouseProductLocationDeleteBefore($params) {
if (isset($params['object'])) {
$deletedWarehouseProductLocation = $params['object'];
$product = new Product($deletedWarehouseProductLocation->id_product);
$product->id_product_attribute = (int)$deletedWarehouseProductLocation->id_product_attribute;
self::$lastStockAvailableCanOrder = $product->checkQty(1);
self::$lastStockAvailableQuantity = StockManagerFactory::getManager()->getProductRealQuantities($product->id, $product->id_product_attribute, null, true);
}
}
/**
* Called when a new warehouse is disassociated to a product with advanced stock management enabled
*/
public function hookActionObjectWarehouseProductLocationDeleteAfter($params) {
if (isset($params['object'])) {
$deletedWarehouseProductLocation = $params['object'];
$product = new Product($deletedWarehouseProductLocation->id_product);
$product->id_product_attribute = (int)$deletedWarehouseProductLocation->id_product_attribute;
$newQuantity = StockManagerFactory::getManager()->getProductRealQuantities($product->id, $product->id_product_attribute, null, true);
$this->handleStock($product, $product->id_product_attribute, $newQuantity);
}
}
/**
* Called when a new warehouse is created with advanced stock management enabled
*/
public function hookActionObjectStockAddBefore($params) {
if (isset($params['object'])) {
$newStock = $params['object'];
$product = new Product($newStock->id_product);
$product->id_product_attribute = (int)$newStock->id_product_attribute;
self::$lastStockAvailableCanOrder = $product->checkQty(1);
self::$lastStockAvailableQuantity = StockManagerFactory::getManager()->getProductRealQuantities($product->id, $product->id_product_attribute, null, true);
}
}
/**
* Called when a new warehouse is created with advanced stock management enabled
*/
public function hookActionObjectStockAddAfter($params) {
if (isset($params['object'])) {
$newStock = $params['object'];
$product = new Product($newStock->id_product);
$product->id_product_attribute = (int)$newStock->id_product_attribute;
$newQuantity = StockManagerFactory::getManager()->getProductRealQuantities($product->id, $product->id_product_attribute, null, true);
$this->handleStock($product, $product->id_product_attribute, $newQuantity);
}
}
/**
* Called when stock is modified with advanced stock management enabled
*/
public function hookActionObjectStockUpdateBefore($params) {
if (isset($params['object'])) {
$newStock = $params['object'];
$product = new Product($newStock->id_product);
$product->id_product_attribute = (int)$newStock->id_product_attribute;
self::$lastStockAvailableCanOrder = $product->checkQty(1);
self::$lastStockAvailableQuantity = StockManagerFactory::getManager()->getProductRealQuantities($product->id, $product->id_product_attribute, null, true);
}
}
/**
* Called when stock is modified with advanced stock management enabled
*/
public function hookActionObjectStockUpdateAfter($params) {
if (isset($params['object'])) {
$newStock = $params['object'];
$product = new Product($newStock->id_product);
$product->id_product_attribute = (int)$newStock->id_product_attribute;
$newQuantity = StockManagerFactory::getManager()->getProductRealQuantities($product->id, $product->id_product_attribute, null, true);
$this->handleStock($product, $product->id_product_attribute, $newQuantity);
}
}
/**
* Called when stock is modified with standard stock management
*/
public function hookActionObjectStockAvailableUpdateBefore($params) {
if (isset($params['object'])) {
$newStock = $params['object'];
$product = new Product($newStock->id_product);
$product->id_product_attribute = (int)$newStock->id_product_attribute;
self::$lastStockAvailableCanOrder = $product->checkQty(1);
$currentStockId = StockAvailable::getStockAvailableIdByProductId($product->id, $product->id_product_attribute, ((int) $newStock->id_shop) === 0 ? null : (int) $newStock->id_shop);
if ($currentStockId) {
$currentStock = new StockAvailable($currentStockId);
self::$lastStockAvailableQuantity = $currentStock->quantity;
}
}
}
/**
* Called when stock is modified with standard stock management
*/
public function hookActionObjectStockAvailableUpdateAfter($params) {
if (isset($params['object'])) {
$newStock = $params['object'];
$product = new Product($newStock->id_product);
$product->id_product_attribute = (int)$newStock->id_product_attribute;
// Clear the cache to get the actual current value
Cache::clean('StockAvailable::getQuantityAvailableByProduct_' . (int) $product->id . '*');
$this->handleStock($product, $product->id_product_attribute, $newStock->quantity);
}
}
private function handleStock($product, $id_product_attribute, $newQuantity)
{
if (self::$lastStockAvailableQuantity !== null && self::$lastStockAvailableCanOrder !== null) {
$deltaQuantity = $newQuantity - self::$lastStockAvailableQuantity;
$inStockStateChange = ((int) $newQuantity === 0 || (int) self::$lastStockAvailableQuantity === 0) && $deltaQuantity !== 0;
$product->id_product_attribute = (int)$id_product_attribute;
$canOrder = $product->checkQty(1);
if ($canOrder !== self::$lastStockAvailableCanOrder || $inStockStateChange) {
if ($canOrder) {
// Refresh like a product update
if (Configuration::get('pagecache_instockisadd')) {
$this->onProductAdd($product, 'Product#' . $product->id . ($id_product_attribute ? '-' . $id_product_attribute : '') . ' is now available for order');
}
else {
$this->onProductUpdate($product, 'Product#' . $product->id . ($id_product_attribute ? '-' . $id_product_attribute : '') . ' is now available for order', false);
}
} else {
// Refresh like a product deletion
$this->onProductDelete($product, 'Product#' . $product->id . ($id_product_attribute ? '-' . $id_product_attribute : '') . ' is no more available for order');
}
} else {
if ($deltaQuantity !== 0) {
$lastItemsQuantities = max(1, (int)Configuration::get('PS_LAST_QTIES'));
if (Tools::version_compare(_PS_VERSION_, '1.7.3.0', '>=')) {
$lastItemsQuantities = max(1, (int)$product->low_stock_threshold, $lastItemsQuantities);
if (((int)$id_product_attribute) > 0) {
$lastItemsQuantitiesAttribute = (int) JprestaUtils::dbGetValue(
'SELECT pa.low_stock_threshold' .
' FROM `' . _DB_PREFIX_ . 'product_attribute` pa' .
' WHERE pa.`id_product` = ' . (int)$product->id . ' AND pa.`id_product_attribute` = ' . (int)$id_product_attribute
);
$lastItemsQuantities = max(1, $lastItemsQuantities, $lastItemsQuantitiesAttribute);
}
}
if (self::$lastStockAvailableQuantity > $lastItemsQuantities && $newQuantity > $lastItemsQuantities) {
// It was and it is still over the limit of alert so we only refresh the product page
// every X sales (by default X = 1)
$everyX = max(1, (int)Configuration::get('pagecache_product_refreshEveryX', null, null, null, 1));
if ((($newQuantity - $lastItemsQuantities) % $everyX) === 0) {
$this->onProductUpdate($product, 'stock update (every X=' . $everyX . ')');
}
} else {
// It is or it was under the limit of alert so we refresh the product page
$this->onProductUpdate($product, 'stock update (alert=' . $lastItemsQuantities . ')');
}
}
}
}
self::$lastStockAvailableCanOrder = null;
self::$lastStockAvailableQuantity = null;
}
public function hookActionObjectAddAfter($params) {
$controllers = self::getManagedControllers();
$objectClassName = get_class($params['object']);
if (array_key_exists($objectClassName, self::$managed_object_classes)) {
foreach ($controllers as $controllerName => $controllerInfos) {
if (isset($controllerInfos['object_class']) && $controllerInfos['object_class'] === $objectClassName) {
PageCacheDAO::clearCacheOfObject($controllerName, $params['object']->id, false,
'creation of page by module "' . $controllerInfos['module'] . '" for object ' . $objectClassName . '#' . $params['object']->id,
Configuration::get('pagecache_logs'));
}
}
}
if ($objectClassName === 'StBlogCommentClass') {
PageCacheDAO::clearCacheOfObject('stblog__article', $params['object']->id_st_blog, false, 'new comment', Configuration::get('pagecache_logs'));
}
elseif ($objectClassName === 'StBlogClass') {
PageCacheDAO::clearCacheOfObject('stblog__category', $params['object']->id_st_blog_category_default, false, 'new article', Configuration::get('pagecache_logs'));
PageCacheDAO::clearCacheOfObject('stblog__default', null, false, 'new article', Configuration::get('pagecache_logs'));
PageCacheDAO::clearCacheOfObject('stblogarchives__default', null, false, 'new article', Configuration::get('pagecache_logs'));
}
elseif ($objectClassName === 'StBlogCategory') {
PageCacheDAO::clearCacheOfObject('stblog__default', null, false, 'new category', Configuration::get('pagecache_logs'));
PageCacheDAO::clearCacheOfObject('stblogarchives__default', null, false, 'new category', Configuration::get('pagecache_logs'));
}
}
public function hookActionObjectUpdateAfter($params) {
$controllers = self::getManagedControllers();
$objectClassName = get_class($params['object']);
if (array_key_exists($objectClassName, self::$managed_object_classes)) {
foreach ($controllers as $controllerName => $controllerInfos) {
if (isset($controllerInfos['object_class']) && $controllerInfos['object_class'] === $objectClassName) {
PageCacheDAO::clearCacheOfObject($controllerName, $params['object']->id, true,
'modification of page by module "' . $controllerInfos['module'] . '" for object ' . $objectClassName . '#' . $params['object']->id,
Configuration::get('pagecache_logs'));
}
}
}
if ($objectClassName === 'StBlogCommentClass') {
PageCacheDAO::clearCacheOfObject('stblog__article', $params['object']->id_st_blog, false, 'update comment', Configuration::get('pagecache_logs'));
}
elseif ($objectClassName === 'StBlogClass') {
PageCacheDAO::clearCacheOfObject('stblog__category', $params['object']->id_st_blog_category_default, false, 'update article', Configuration::get('pagecache_logs'));
PageCacheDAO::clearCacheOfObject('stblog__default', null, false, 'update article', Configuration::get('pagecache_logs'));
PageCacheDAO::clearCacheOfObject('stblogarchives__default', null, false, 'update article', Configuration::get('pagecache_logs'));
}
elseif ($objectClassName === 'StBlogCategory') {
PageCacheDAO::clearCacheOfObject('stblog__default', null, false, 'update category', Configuration::get('pagecache_logs'));
PageCacheDAO::clearCacheOfObject('stblogarchives__default', null, false, 'update category', Configuration::get('pagecache_logs'));
}
}
public function hookActionObjectDeleteAfter($params) {
$this->hookActionObjectUpdateAfter($params);
}
public function hookActionObjectAddressAddAfter($params) {
if (isset($params['object']) && !empty($params['object']->id_supplier)) {
$this->_clearCacheModules('pagecache_supplier_a', 'hookActionObjectAddressAddAfter');
}
}
public function hookActionObjectAddressUpdateAfter($params) {
if (isset($params['object']) && !empty($params['object']->id_supplier)) {
PageCacheDAO::clearCacheOfObject('supplier', $params['object']->id_supplier, Configuration::get('pagecache_supplier_u_bl'), 'hookActionObjectAddressUpdateAfter', Configuration::get('pagecache_logs'));
$this->_clearCacheModules('pagecache_supplier_u', 'hookActionObjectAddressUpdateAfter');
}
}
public function hookActionObjectAddressDeleteBefore($params) {
if (isset($params['object']) && !empty($params['object']->id_supplier)) {
PageCacheDAO::clearCacheOfObject('supplier', $params['object']->id_supplier, Configuration::get('pagecache_supplier_d_bl'), 'hookActionObjectAddressDeleteBefore', Configuration::get('pagecache_logs'));
$this->_clearCacheModules('pagecache_supplier_d', 'hookActionObjectAddressDeleteBefore');
}
}
public function hookActionCategoryAdd($params) {
if (isset($params['category'])) {
PageCacheDAO::clearCacheOfObject('category', $params['category']->id, false, 'creation of Category#' . $params['category']->id, Configuration::get('pagecache_logs'));
$this->_checkRootCategory($params['category']->id, 'a', 'creation of Category#' . $params['category']->id);
}
$this->_clearCacheModules('pagecache_category_a', 'creation of Category#' . $params['category']->id);
}
public function hookActionCategoryUpdate($params) {
if (isset($params['category'])) {
PageCacheDAO::clearCacheOfObject('category', $params['category']->id, Configuration::get('pagecache_category_u_bl'), 'modification of Category#' . $params['category']->id, Configuration::get('pagecache_logs'));
$this->_checkRootCategory($params['category']->id, 'u', 'modification of Category#' . $params['category']->id);
}
$this->_clearCacheModules('pagecache_category_u', 'modification of Category#' . $params['category']->id);
}
public function hookActionCategoryDelete($params) {
if (isset($params['category'])) {
PageCacheDAO::clearCacheOfObject('category', $params['category']->id, Configuration::get('pagecache_category_d_bl'), 'deletion of Category#' . $params['category']->id, Configuration::get('pagecache_logs'));
$this->_checkRootCategory($params['category']->id, 'd', 'deletion of Category#' . $params['category']->id);
}
$this->_clearCacheModules('pagecache_category_d', 'deletion of Category#' . $params['category']->id);
}
public function onProductAdd($product, $logMessage) {
// New products pages
PageCacheDAO::clearCacheOfObject('newproducts', null, false, $logMessage, Configuration::get('pagecache_logs'));
// Categories of the new product
$categoriesIds = $product->getCategories();
foreach ($categoriesIds as $categoryId) {
PageCacheDAO::clearCacheOfObject('category', $categoryId, false, $logMessage, Configuration::get('pagecache_logs'));
$this->_checkRootCategory($categoryId, 'a', $logMessage);
}
// Supplier pages
PageCacheDAO::clearCacheOfObject('supplier', $product->id_supplier, false, $logMessage, Configuration::get('pagecache_logs'));
// Manufacturer pages
PageCacheDAO::clearCacheOfObject('manufacturer', $product->id_manufacturer, false, $logMessage, Configuration::get('pagecache_logs'));
// Modules attached to this hook
$this->_clearCacheModules('pagecache_product_a', $logMessage);
}
public function onProductUpdate($product, $logMessage, $onlyProductPage = true) {
if (is_numeric($product)) {
$productId = $product;
}
else {
$productId = $product->id;
}
// Product page
PageCacheDAO::clearCacheOfObject('product', $productId, !$onlyProductPage, $logMessage, Configuration::get('pagecache_logs'));
if (!$onlyProductPage) {
if (is_numeric($product)) {
$product = new Product($productId);
}
// New products pages
PageCacheDAO::clearCacheOfObject('newproducts', null, false, $logMessage, Configuration::get('pagecache_logs'));
// Categories of the new product
$categoriesIds = $product->getCategories();
foreach ($categoriesIds as $categoryId) {
PageCacheDAO::clearCacheOfObject('category', $categoryId, false, $logMessage, Configuration::get('pagecache_logs'));
$this->_checkRootCategory($categoryId, 'a', $logMessage);
}
// Supplier pages
PageCacheDAO::clearCacheOfObject('supplier', $product->id_supplier, false, $logMessage, Configuration::get('pagecache_logs'));
// Manufacturer pages
PageCacheDAO::clearCacheOfObject('manufacturer', $product->id_manufacturer, false, $logMessage, Configuration::get('pagecache_logs'));
// Modules attached to this hook
$this->_clearCacheModules('pagecache_product_u', $logMessage);
}
}
public function onProductDelete($product, $logMessage) {
// Product page
PageCacheDAO::clearCacheOfObject('product', $product->id, Configuration::get('pagecache_product_d_bl'), $logMessage, Configuration::get('pagecache_logs'));
// Categories of the new product
$categoriesIds = $product->getCategories();
foreach ($categoriesIds as $categoryId) {
PageCacheDAO::clearCacheOfObject('category', $categoryId, false, $logMessage, Configuration::get('pagecache_logs'));
$this->_checkRootCategory($categoryId, 'd', $logMessage);
}
// Supplier pages
PageCacheDAO::clearCacheOfObject('supplier', $product->id_supplier, false, $logMessage, Configuration::get('pagecache_logs'));
// Manufacturer pages
PageCacheDAO::clearCacheOfObject('manufacturer', $product->id_manufacturer, false, $logMessage, Configuration::get('pagecache_logs'));
// Modules attached to this hook
$this->_clearCacheModules('pagecache_product_d', $logMessage);
}
private static $updatingProductFromAdminController = false;
private static $lastUpdatedProduct = null;
private static $lastUpdatedProductFeatures = null;
private static $lastUpdatedProductStockAvailable = null;
public function hookActionAdminProductsControllerSaveBefore($params) {
$this->hookActionObjectProductUpdateBefore(['object' => (object) ['id' => Tools::getValue('id_product')]]);
self::$updatingProductFromAdminController = true;
// The 'after' will be done in hookActionDispatcherAfter
}
public function hookActionAdminSaveBefore($params) {
// The updateCacheKey will be done in the "hookActionAdminSaveAfter" hook
self::$skipUpdateCacheKey = true;
}
public function hookActionObjectProductUpdateBefore($params) {
if (isset($params['object']) && !self::$updatingProductFromAdminController) {
$newProduct = $params['object'];
// Load the current product from the database and keep it for hookActionObjectProductUpdateAfter
self::$lastUpdatedProduct = new Product($newProduct->id);
self::$lastUpdatedProductFeatures = Product::getFrontFeaturesStatic((int) Configuration::get('PS_LANG_DEFAULT'), $newProduct->id);
self::$lastUpdatedProductStockAvailable = null;
if (Tools::getIsset('out_of_stock')) {
// Check if out_of_stock has been modified
$currentStockId = (int)StockAvailable::getStockAvailableIdByProductId((int)$newProduct->id);
if ($currentStockId) {
// 'out_of_stock' is not stored in ps_product table but in StockAvailable
self::$lastUpdatedProductStockAvailable = new StockAvailable($currentStockId);
}
}
}
}
public function hookActionObjectProductUpdateAfter($params) {
if (isset($params['object']) && !self::$updatingProductFromAdminController) {
$updatedProduct = $params['object'];
// Compare with database version because of boolean stored as int, null as empty, integer being formatted, etc.
$productFromDb = new Product($updatedProduct->id);
$diffs = JprestaUtils::getObjectDifferences(self::$lastUpdatedProduct, $productFromDb);
if (!is_array($diffs) || count($diffs) === 0) {
// Check modifications in features if necessary
if (method_exists('Product', 'resetStaticCache')) {
Product::resetStaticCache();
}
$diffsInFeatures = !JprestaUtils::arraysAreIdentical(self::$lastUpdatedProductFeatures, Product::getFrontFeaturesStatic((int) Configuration::get('PS_LANG_DEFAULT'), $productFromDb->id));
}
else {
$diffsInFeatures = false;
}
if (self::$lastUpdatedProductStockAvailable
&& self::$lastUpdatedProductStockAvailable->out_of_stock != Tools::getValue('out_of_stock')) {
$diffs['out_of_stock'] = JprestaUtils::toString(self::$lastUpdatedProductStockAvailable->out_of_stock) . ' <> ' . JprestaUtils::toString(Tools::getValue('out_of_stock'));
}
self::$lastUpdatedProduct = null;
if ((is_array($diffs) && count($diffs) > 0) || $diffsInFeatures) {
if (array_key_exists('active', $diffs)) {
if ($updatedProduct->active) {
// Product is back, act like a new product
$this->onProductAdd($updatedProduct, 'activation of Product#' . $updatedProduct->id);
}
else {
// Product is disabled, act like a deletion
$this->onProductDelete($updatedProduct, 'deactivation of Product#' . $updatedProduct->id);
}
}
else {
// The product has been modified, it can be the name, description, price, url, etc. so we need to
// refresh all pages where the product is listed or displayed
$this->onProductUpdate($updatedProduct, 'modification of Product#' . $updatedProduct->id, false);
}
}
}
}
public function hookActionProductAdd($params) {
if (!isset($params['product']) && isset($params['id_product'])) {
$params['product'] = new Product($params['id_product']);
}
if (isset($params['product'])) {
$product = $params['product'];
$this->onProductAdd($product, 'creation of new Product#' . $product->id);
}
}
public function hookActionObjectProductDeleteBefore($params) {
if (isset($params['object'])) {
$product = $params['object'];
$this->onProductDelete($params['object'], 'deletion of Product#' . $product->id);
}
}
private static $lastUpdatedProductCombination = null;
public function hookActionObjectCombinationUpdateBefore($params) {
if (isset($params['object'])) {
$newCombination = $params['object'];
// Load the current product from the database and keep it for hookActionObjectCombinationUpdateAfter
self::$lastUpdatedProductCombination = new Combination($newCombination->id);
}
}
public function hookActionObjectCombinationUpdateAfter($params) {
if (isset($params['object'])) {
$updatedCombination = $params['object'];
$combinationFromDb = new Combination($updatedCombination->id);
$diffs = JprestaUtils::getObjectDifferences(self::$lastUpdatedProductCombination, $combinationFromDb);
self::$lastUpdatedProductCombination = null;
if (is_array($diffs) && count($diffs) > 0) {
// A combination has been modified (impact on price, weight, minimal quantity, etc. so we just need
// to refresh the product page, no pages that list this product.
// Product page
PageCacheDAO::clearCacheOfObject('product', $updatedCombination->id_product, false, 'modification of Combination#' . $updatedCombination->id, Configuration::get('pagecache_logs'));
}
}
}
public function hookActionObjectCombinationDeleteAfter($params) {
if (isset($params['object'])) {
$deletedCombination = $params['object'];
// A combination has been deleted so we just need
// to refresh the product page, no pages that list this product.
// Product page
PageCacheDAO::clearCacheOfObject('product', $deletedCombination->id_product, false, 'deletion of Combination#' . $deletedCombination->id, Configuration::get('pagecache_logs'));
}
}
public function hookActionObjectSpecificPriceAddAfter($params) {
if (isset($params['object'])) {
$sp = $params['object'];
PageCacheDAO::insertSpecificPrice($sp->id, $sp->id_product, $sp->from, $sp->to);
if ($sp->id_product === 0) {
// Specific case where the specific rule is global (all products are concerned)
PageCacheDAO::triggerReffreshment();
}
else {
$this->onProductUpdate($sp->id_product, 'creation of specific price #' . $sp->id);
}
}
if (!self::$skipUpdateCacheKey) {
self::updateCacheKeyForCountries();
self::updateCacheKeyForUserGroups();
}
else {
self::$needUpdateCacheKey = true;
}
}
public function hookActionObjectSpecificPriceUpdateAfter($params) {
if (isset($params['object'])) {
$sp = $params['object'];
PageCacheDAO::updateSpecificPrice($sp->id, $sp->id_product, $sp->from, $sp->to);
if ($sp->id_product === 0) {
// Specific case where the specific rule is global (all products are concerned)
PageCacheDAO::triggerReffreshment();
}
else {
$this->onProductUpdate($sp->id_product, 'modification of specific price #' . $sp->id);
}
}
if (!self::$skipUpdateCacheKey) {
self::updateCacheKeyForCountries();
self::updateCacheKeyForUserGroups();
}
else {
self::$needUpdateCacheKey = true;
}
}
public function hookActionObjectSpecificPriceDeleteBefore($params) {
if (isset($params['object'])) {
$sp = $params['object'];
PageCacheDAO::deleteSpecificPrice($sp->id);
if ($sp->id_product === 0) {
// Specific case where the specific rule is global (all products are concerned)
PageCacheDAO::triggerReffreshment();
}
else {
$this->onProductUpdate($sp->id_product, 'deletion of specific price #' . $sp->id);
}
}
}
public function hookActionObjectImageAddAfter($params) {
if (isset($params['object'])) {
$img = $params['object'];
$this->onProductUpdate($img->id_product, 'new image', !$img->cover);
}
}
public function hookActionObjectImageUpdateAfter($params) {
if (isset($params['object'])) {
$img = $params['object'];
$this->onProductUpdate($img->id_product, 'modification of an image', !$img->cover);
}
}
public function hookActionObjectImageDeleteBefore($params) {
if (isset($params['object'])) {
$img = $params['object'];
$this->onProductUpdate($img->id_product, 'deletion of an image', !$img->cover);
}
}
private function _checkRootCategory($id_category, $suffix, $origin_action='') {
if ((bool) JprestaUtils::dbGetValue('SELECT `id_shop` FROM `'._DB_PREFIX_.'shop` WHERE `id_category` = '.(int)$id_category)) {
$this->_clearCacheModules('pagecache_product_home_'.$suffix, $origin_action);
}
}
public function hookActionHtaccessCreate($params) {
$this->clearCache('hookActionHtaccessCreate');
}
public function hookActionObjectShopUrlAddAfter($params) {
$this->hookActionHtaccessCreate($params);
}
public function hookActionObjectShopUrlUpdateAfter($params) {
$this->hookActionHtaccessCreate($params);
}
public function hookActionObjectShopUrlDeleteAfter($params) {
$this->hookActionHtaccessCreate($params);
}
public function hookActionAdminPerformanceControllerAfter($params)
{
$this->clearCache('hookActionAdminPerformanceControllerAfter');
}
/**
* $params['controller'] : the name of the controller as it appears in the statistics table (for modules it is __)
* $params['id'] : ID of the object as it appears in the statistics table. Can be null.
* $params['delete_linking_pages'] : true if pages having a link on modified pages should also be refreshed
* $params['action_origin'] : For debug purpose, just tells shortly why the cache is refreshed
* @param $params array
*/
public function hookActionJPrestaClearCache($params) {
if ($params && is_array($params)) {
$controller_name = isset($params['controller']) ? $params['controller'] : null;
if ($controller_name) {
$id_object = isset($params['id']) ? $params['id'] : null;
$delete_linking_pages = isset($params['delete_linking_pages']) ? (bool)$params['delete_linking_pages'] : true;
$action_origin = 'Hook ActionJPrestaClearCache' . (isset($params['action_origin']) ? ': ' . $params['action_origin'] : '');
$log_level = Configuration::get('pagecache_logs');
PageCacheDAO::clearCacheOfObject($controller_name, $id_object, $delete_linking_pages, $action_origin,
$log_level);
}
}
}
public function removeOverride($class_name) {
static $already_done = array();
if (array_key_exists($class_name, $already_done)) {
return true;
}
$already_done[$class_name] = true;
if (Tools::version_compare(_PS_VERSION_,'1.6','<')
&& !file_exists(_PS_MODULE_DIR_ . '/' . $this->name . '/override/controllers/front/'.$class_name.'.php')
&& !file_exists(_PS_MODULE_DIR_ . '/' . $this->name . '/override/classes/'.$class_name.'.php')
&& !file_exists(_PS_MODULE_DIR_ . '/' . $this->name . '/override/classes/controller/'.$class_name.'.php')
&& !file_exists(_PS_MODULE_DIR_ . '/' . $this->name . '/override/classes/module/'.$class_name.'.php')
) {
// In PS 1.5 we cannot remove an override that is not defined in /overrides directory
// So they stay installed but it's better than an error during upgrade
return true;
}
return parent::removeOverride($class_name);
}
public function upgradeOverride($class_name) {
// Avoid calling this method multiple times (or it will fail)
static $already_done = array();
if (array_key_exists($class_name, $already_done)) {
return true;
}
$already_done[$class_name] = true;
if (!file_exists(_PS_MODULE_DIR_ . '/' . $this->name . '/override/controllers/front/'.$class_name.'.php')
&& !file_exists(_PS_MODULE_DIR_ . '/' . $this->name . '/override/classes/'.$class_name.'.php')
&& !file_exists(_PS_MODULE_DIR_ . '/' . $this->name . '/override/classes/controller/'.$class_name.'.php')
&& !file_exists(_PS_MODULE_DIR_ . '/' . $this->name . '/override/classes/module/'.$class_name.'.php')
) {
// The override does not exist anymore, just ignore it. It can happen in old upgrade file.
return true;
}
$reset_ok = true;
if (Tools::version_compare(_PS_VERSION_,'1.6','>=')
|| (!class_exists($class_name . 'OverrideOriginal') && (!class_exists($class_name . 'OverrideOriginal_remove')))) {
$reset_ok = $this->removeOverride($class_name) && $this->addOverride($class_name);
}
return $reset_ok;
}
/** @return bool true if infos block must be displayed on front end */
private static function isDisplayStats() {
if (JprestaUtils::isAjax()
|| strcmp(self::getServerValue('REQUEST_METHOD'), 'GET') != 0
|| defined('_PS_ADMIN_DIR_')
) {
return false;
}
return Configuration::get('pagecache_always_infosbox')
|| (Configuration::get('pagecache_debug') && Tools::getIsset('dbgpagecache'));
}
public function getContactUrl() {
$seller = Configuration::get('pagecache_seller');
if (isset($seller) && strcmp($seller, 'addons') === 0) {
// Contact URL
if (strcmp('fr', Language::getIsoById($this->context->language->id)) == 0) {
return 'https://addons.prestashop.com/fr/ecrire-au-developpeur?id_product=7939';
}
else {
return 'https://addons.prestashop.com/en/write-to-developper?id_product=7939';
}
} else {
// Contact URL
if (strcmp('fr', Language::getIsoById($this->context->language->id)) == 0) {
return self::JPRESTA_PROTO . self::JPRESTA_DOMAIN . '.com/fr/contactez-nous';
}
else {
return self::JPRESTA_PROTO . self::JPRESTA_DOMAIN . '.com/en/contact-us';
}
}
}
/**
* Used in case script is run with a command line
* @param string $key Variable name
* @return string Value of variable or empty string
*/
public static function getServerValue($key) {
if (array_key_exists($key, $_SERVER)) {
return $_SERVER[$key];
}
return '';
}
public function l($string, $specific = false, $locale = null)
{
if ($specific === 'Admin.Global') {
$parameters['legacy'] = 'htmlspecialchars';
return $this->getTranslator()->trans($string, $parameters, $specific, $locale);
}
else {
return parent::l($string, $specific, $locale);
}
}
/**
* @deprecated Just needed when upgrading to 4.00, do not remove it
*/
public static function getCacheFile() {
return false;
}
/**
* @deprecated Just needed when upgrading to 4.25, do not remove it
*/
public static function getDynamicHookInfos() {
return false;
}
/**
* @deprecated Just needed when upgrading to 4.25, do not remove it
*/
public static function getHookCacheDirectives() {
return array('wrapper' => false, 'content' => true);
}
/**
* @deprecated Just needed when upgrading to 4.25, do not remove it
*/
public static function getWidgetCacheDirectives() {
return array('wrapper' => false, 'content' => true);
}
/**
* @deprecated Just needed when upgrading, do not remove it
*/
public static function isDynamicHooks() {
return false;
}
}
?>