Files
PrestaShop/modules/blocklayered/blocklayered.php

4263 lines
178 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
/*
* 2007-2013 PrestaShop
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License (AFL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* http://opensource.org/licenses/afl-3.0.php
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to http://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2013 PrestaShop SA
* @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0)
* International Registred Trademark & Property of PrestaShop SA
*/
if (!defined('_PS_VERSION_'))
exit;
class BlockLayered extends Module
{
private $products;
private $nbr_products;
private $page = 1;
public function __construct()
{
$this->name = 'blocklayered';
$this->tab = 'front_office_features';
$this->version = '1.9.0';
$this->author = 'PrestaShop';
$this->need_instance = 0;
$this->bootstrap = true;
parent::__construct();
$this->displayName = $this->l('Layered navigation block');
$this->description = $this->l('Displays a block with layered navigation filters.');
if ((int)Tools::getValue('p'))
$this->page = (int)Tools::getValue('p');
}
public function install()
{
if (parent::install() && $this->registerHook('leftColumn') && $this->registerHook('header') && $this->registerHook('footer')
&& $this->registerHook('categoryAddition') && $this->registerHook('categoryUpdate') && $this->registerHook('attributeGroupForm')
&& $this->registerHook('afterSaveAttributeGroup') && $this->registerHook('afterDeleteAttributeGroup') && $this->registerHook('featureForm')
&& $this->registerHook('afterDeleteFeature') && $this->registerHook('afterSaveFeature') && $this->registerHook('categoryDeletion')
&& $this->registerHook('afterSaveProduct') && $this->registerHook('productListAssign') && $this->registerHook('postProcessAttributeGroup')
&& $this->registerHook('postProcessFeature') && $this->registerHook('featureValueForm') && $this->registerHook('postProcessFeatureValue')
&& $this->registerHook('afterDeleteFeatureValue') && $this->registerHook('afterSaveFeatureValue') && $this->registerHook('attributeForm')
&& $this->registerHook('postProcessAttribute') && $this->registerHook('afterDeleteAttribute') && $this->registerHook('afterSaveAttribute')
&& $this->registerHook('displayBackOfficeHeader'))
{
Configuration::updateValue('PS_LAYERED_HIDE_0_VALUES', 1);
Configuration::updateValue('PS_LAYERED_SHOW_QTIES', 1);
Configuration::updateValue('PS_LAYERED_FULL_TREE', 1);
Configuration::updateValue('PS_LAYERED_FILTER_PRICE_USETAX', 1);
Configuration::updateValue('PS_LAYERED_FILTER_CATEGORY_DEPTH', 1);
Configuration::updateValue('PS_LAYERED_FILTER_INDEX_QTY', 0);
Configuration::updateValue('PS_LAYERED_FILTER_INDEX_CDT', 0);
Configuration::updateValue('PS_LAYERED_FILTER_INDEX_MNF', 0);
Configuration::updateValue('PS_LAYERED_FILTER_INDEX_CAT', 0);
$this->rebuildLayeredStructure();
$products_count = Db::getInstance()->getValue('SELECT COUNT(*) FROM `'._DB_PREFIX_.'product`');
if ($products_count < 20000) // Lock template filter creation if too many products
$this->rebuildLayeredCache();
self::installPriceIndexTable();
$this->installFriendlyUrlTable();
$this->installIndexableAttributeTable();
$this->installProductAttributeTable();
if ($products_count < 5000) // Lock indexation if too many products
{
$this->indexUrl();
$this->indexAttribute();
self::fullPricesIndexProcess();
}
return true;
}
else
{
// Installation failed (or hook registration) => uninstall the module
$this->uninstall();
return false;
}
}
public function uninstall()
{
/* Delete all configurations */
Configuration::deleteByName('PS_LAYERED_HIDE_0_VALUES');
Configuration::deleteByName('PS_LAYERED_SHOW_QTIES');
Configuration::deleteByName('PS_LAYERED_FULL_TREE');
Configuration::deleteByName('PS_LAYERED_INDEXED');
Configuration::deleteByName('PS_LAYERED_FILTER_PRICE_USETAX');
Configuration::deleteByName('PS_LAYERED_FILTER_CATEGORY_DEPTH');
Configuration::deleteByName('PS_LAYERED_FILTER_INDEX_QTY');
Configuration::deleteByName('PS_LAYERED_FILTER_INDEX_CDT');
Configuration::deleteByName('PS_LAYERED_FILTER_INDEX_MNF');
Configuration::deleteByName('PS_LAYERED_FILTER_INDEX_CAT');
Db::getInstance()->execute('DROP TABLE IF EXISTS '._DB_PREFIX_.'layered_price_index');
Db::getInstance()->execute('DROP TABLE IF EXISTS '._DB_PREFIX_.'layered_friendly_url');
Db::getInstance()->execute('DROP TABLE IF EXISTS '._DB_PREFIX_.'layered_indexable_attribute_group');
Db::getInstance()->execute('DROP TABLE IF EXISTS '._DB_PREFIX_.'layered_indexable_feature');
Db::getInstance()->execute('DROP TABLE IF EXISTS '._DB_PREFIX_.'layered_indexable_attribute_group_lang_value');
Db::getInstance()->execute('DROP TABLE IF EXISTS '._DB_PREFIX_.'layered_indexable_feature_lang_value');
Db::getInstance()->execute('DROP TABLE IF EXISTS '._DB_PREFIX_.'layered_category');
Db::getInstance()->execute('DROP TABLE IF EXISTS '._DB_PREFIX_.'layered_filter');
Db::getInstance()->Execute('DROP TABLE IF EXISTS '._DB_PREFIX_.'layered_filter_shop');
Db::getInstance()->execute('DROP TABLE IF EXISTS '._DB_PREFIX_.'layered_product_attribute');
return parent::uninstall();
}
private static function installPriceIndexTable()
{
Db::getInstance()->execute('DROP TABLE IF EXISTS `'._DB_PREFIX_.'layered_price_index`');
Db::getInstance()->execute('
CREATE TABLE `'._DB_PREFIX_.'layered_price_index` (
`id_product` INT NOT NULL,
`id_currency` INT NOT NULL,
`id_shop` INT NOT NULL,
`price_min` INT NOT NULL,
`price_max` INT NOT NULL,
PRIMARY KEY (`id_product`, `id_currency`, `id_shop`),
INDEX `id_currency` (`id_currency`),
INDEX `price_min` (`price_min`), INDEX `price_max` (`price_max`)
) ENGINE='._MYSQL_ENGINE_.' DEFAULT CHARSET=utf8;');
}
private function installFriendlyUrlTable()
{
Db::getInstance()->execute('DROP TABLE IF EXISTS `'._DB_PREFIX_.'layered_friendly_url`');
Db::getInstance()->execute('
CREATE TABLE `'._DB_PREFIX_.'layered_friendly_url` (
`id_layered_friendly_url` INT NOT NULL AUTO_INCREMENT,
`url_key` varchar(32) NOT NULL,
`data` varchar(200) NOT NULL,
`id_lang` INT NOT NULL,
PRIMARY KEY (`id_layered_friendly_url`),
INDEX `id_lang` (`id_lang`)
) ENGINE='._MYSQL_ENGINE_.' DEFAULT CHARSET=utf8;');
Db::getInstance()->execute('CREATE INDEX `url_key` ON `'._DB_PREFIX_.'layered_friendly_url`(url_key(5))');
}
private function installIndexableAttributeTable()
{
// Attributes Groups
Db::getInstance()->execute('DROP TABLE IF EXISTS `'._DB_PREFIX_.'layered_indexable_attribute_group`');
Db::getInstance()->execute('
CREATE TABLE `'._DB_PREFIX_.'layered_indexable_attribute_group` (
`id_attribute_group` INT NOT NULL,
`indexable` BOOL NOT NULL DEFAULT 0,
PRIMARY KEY (`id_attribute_group`)
) ENGINE='._MYSQL_ENGINE_.' DEFAULT CHARSET=utf8;');
Db::getInstance()->execute('
INSERT INTO `'._DB_PREFIX_.'layered_indexable_attribute_group`
SELECT id_attribute_group, 1 FROM `'._DB_PREFIX_.'attribute_group`');
Db::getInstance()->execute('DROP TABLE IF EXISTS `'._DB_PREFIX_.'layered_indexable_attribute_group_lang_value`');
Db::getInstance()->execute('
CREATE TABLE `'._DB_PREFIX_.'layered_indexable_attribute_group_lang_value` (
`id_attribute_group` INT NOT NULL,
`id_lang` INT NOT NULL,
`url_name` VARCHAR(20),
`meta_title` VARCHAR(20),
PRIMARY KEY (`id_attribute_group`, `id_lang`)
) ENGINE='._MYSQL_ENGINE_.' DEFAULT CHARSET=utf8;');
// Attributes
Db::getInstance()->execute('DROP TABLE IF EXISTS `'._DB_PREFIX_.'layered_indexable_attribute_lang_value`');
Db::getInstance()->execute('
CREATE TABLE `'._DB_PREFIX_.'layered_indexable_attribute_lang_value` (
`id_attribute` INT NOT NULL,
`id_lang` INT NOT NULL,
`url_name` VARCHAR(20),
`meta_title` VARCHAR(20),
PRIMARY KEY (`id_attribute`, `id_lang`)
) ENGINE='._MYSQL_ENGINE_.' DEFAULT CHARSET=utf8;');
// Features
Db::getInstance()->execute('DROP TABLE IF EXISTS `'._DB_PREFIX_.'layered_indexable_feature`');
Db::getInstance()->execute('
CREATE TABLE `'._DB_PREFIX_.'layered_indexable_feature` (
`id_feature` INT NOT NULL,
`indexable` BOOL NOT NULL DEFAULT 0,
PRIMARY KEY (`id_feature`)
) ENGINE='._MYSQL_ENGINE_.' DEFAULT CHARSET=utf8;');
Db::getInstance()->execute('
INSERT INTO `'._DB_PREFIX_.'layered_indexable_feature`
SELECT id_feature, 1 FROM `'._DB_PREFIX_.'feature`');
Db::getInstance()->execute('DROP TABLE IF EXISTS `'._DB_PREFIX_.'layered_indexable_feature_lang_value`');
Db::getInstance()->execute('
CREATE TABLE `'._DB_PREFIX_.'layered_indexable_feature_lang_value` (
`id_feature` INT NOT NULL,
`id_lang` INT NOT NULL,
`url_name` VARCHAR(20) NOT NULL,
`meta_title` VARCHAR(20),
PRIMARY KEY (`id_feature`, `id_lang`)
) ENGINE='._MYSQL_ENGINE_.' DEFAULT CHARSET=utf8;');
// Features values
Db::getInstance()->execute('DROP TABLE IF EXISTS `'._DB_PREFIX_.'layered_indexable_feature_value_lang_value`');
Db::getInstance()->execute('
CREATE TABLE `'._DB_PREFIX_.'layered_indexable_feature_value_lang_value` (
`id_feature_value` INT NOT NULL,
`id_lang` INT NOT NULL,
`url_name` VARCHAR(20),
`meta_title` VARCHAR(20),
PRIMARY KEY (`id_feature_value`, `id_lang`)
) ENGINE='._MYSQL_ENGINE_.' DEFAULT CHARSET=utf8;');
}
/**
*
* create table product attribute
*/
public function installProductAttributeTable()
{
Db::getInstance()->execute('DROP TABLE IF EXISTS `'._DB_PREFIX_.'layered_product_attribute`');
Db::getInstance()->execute('
CREATE TABLE `'._DB_PREFIX_.'layered_product_attribute` (
`id_attribute` int(10) unsigned NOT NULL,
`id_product` int(10) unsigned NOT NULL,
`id_attribute_group` int(10) unsigned NOT NULL DEFAULT "0",
`id_shop` int(10) unsigned NOT NULL DEFAULT "1",
KEY `id_attribute` (`id_attribute`)
) ENGINE='._MYSQL_ENGINE_.' DEFAULT CHARSET=utf8;');
}
/**
*
* Generate data product attribute
*/
public function indexAttribute($id_product = null)
{
if (is_null($id_product))
Db::getInstance()->execute('TRUNCATE '._DB_PREFIX_.'layered_product_attribute');
else
Db::getInstance()->execute('DELETE FROM '._DB_PREFIX_.'layered_product_attribute WHERE id_product = '.(int)$id_product);
Db::getInstance()->execute('INSERT INTO `'._DB_PREFIX_.'layered_product_attribute` (`id_attribute`, `id_product`, `id_attribute_group`, `id_shop`)
SELECT pac.id_attribute, pa.id_product, ag.id_attribute_group, product_attribute_shop.`id_shop`
FROM '._DB_PREFIX_.'product_attribute pa'.
Shop::addSqlAssociation('product_attribute', 'pa').'
INNER JOIN '._DB_PREFIX_.'product_attribute_combination pac ON pac.id_product_attribute = pa.id_product_attribute
INNER JOIN '._DB_PREFIX_.'attribute a ON (a.id_attribute = pac.id_attribute)
INNER JOIN '._DB_PREFIX_.'attribute_group ag ON ag.id_attribute_group = a.id_attribute_group
'.(is_null($id_product) ? '' : 'AND pa.id_product = '.(int)$id_product).'
GROUP BY a.id_attribute, pa.id_product , product_attribute_shop.`id_shop`');
return 1;
}
/*
* Url indexation
*/
public function indexUrl($ajax = false, $truncate = true)
{
if ($truncate)
Db::getInstance()->execute('TRUNCATE '._DB_PREFIX_.'layered_friendly_url');
$attribute_values_by_lang = array();
$filters = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS('SELECT lc.*, id_lang, name, link_rewrite, cl.id_category
FROM '._DB_PREFIX_.'layered_category lc
INNER JOIN '._DB_PREFIX_.'category_lang cl ON (cl.id_category = lc.id_category AND lc.id_category <> 1 )
GROUP BY type, id_value, id_lang');
if (!$filters)
return;
foreach ($filters as $filter)
switch ($filter['type'])
{
case 'id_attribute_group':
$attributes = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS('
SELECT agl.public_name name, a.id_attribute_group id_name, al.name value, a.id_attribute id_value, al.id_lang,
liagl.url_name name_url_name, lial.url_name value_url_name
FROM '._DB_PREFIX_.'attribute_group ag
INNER JOIN '._DB_PREFIX_.'attribute_group_lang agl ON (agl.id_attribute_group = ag.id_attribute_group)
INNER JOIN '._DB_PREFIX_.'attribute a ON (a.id_attribute_group = ag.id_attribute_group)
INNER JOIN '._DB_PREFIX_.'attribute_lang al ON (al.id_attribute = a.id_attribute)
LEFT JOIN '._DB_PREFIX_.'layered_indexable_attribute_group liag ON (liag.id_attribute_group = a.id_attribute_group)
LEFT JOIN '._DB_PREFIX_.'layered_indexable_attribute_group_lang_value liagl
ON (liagl.id_attribute_group = ag.id_attribute_group AND liagl.id_lang = '.(int)$filter['id_lang'].')
LEFT JOIN '._DB_PREFIX_.'layered_indexable_attribute_lang_value lial
ON (lial.id_attribute = a.id_attribute AND lial.id_lang = '.(int)$filter['id_lang'].')
WHERE a.id_attribute_group = '.(int)$filter['id_value'].' AND agl.id_lang = al.id_lang AND agl.id_lang = '.(int)$filter['id_lang']);
foreach ($attributes as $attribute)
{
if (!isset($attribute_values_by_lang[$attribute['id_lang']]))
$attribute_values_by_lang[$attribute['id_lang']] = array();
if (!isset($attribute_values_by_lang[$attribute['id_lang']]['c'.$attribute['id_name']]))
$attribute_values_by_lang[$attribute['id_lang']]['c'.$attribute['id_name']] = array();
$attribute_values_by_lang[$attribute['id_lang']]['c'.$attribute['id_name']][] = array(
'name' => (!empty($attribute['name_url_name']) ? $attribute['name_url_name'] : $attribute['name']),
'id_name' => 'c'.$attribute['id_name'],
'value' => (!empty($attribute['value_url_name']) ? $attribute['value_url_name'] : $attribute['value']),
'id_value' => $attribute['id_name'].'_'.$attribute['id_value'],
'id_id_value' => $attribute['id_value'],
'category_name' => $filter['link_rewrite'],
'type' => $filter['type']);
}
break;
case 'id_feature':
$features = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS('
SELECT fl.name name, fl.id_feature id_name, fvl.id_feature_value id_value, fvl.value value, fl.id_lang, fl.id_lang,
lifl.url_name name_url_name, lifvl.url_name value_url_name
FROM '._DB_PREFIX_.'feature_lang fl
LEFT JOIN '._DB_PREFIX_.'layered_indexable_feature lif ON (lif.id_feature = fl.id_feature)
INNER JOIN '._DB_PREFIX_.'feature_value fv ON (fv.id_feature = fl.id_feature)
INNER JOIN '._DB_PREFIX_.'feature_value_lang fvl ON (fvl.id_feature_value = fv.id_feature_value)
LEFT JOIN '._DB_PREFIX_.'layered_indexable_feature_lang_value lifl
ON (lifl.id_feature = fl.id_feature AND lifl.id_lang = '.(int)$filter['id_lang'].')
LEFT JOIN '._DB_PREFIX_.'layered_indexable_feature_value_lang_value lifvl
ON (lifvl.id_feature_value = fvl.id_feature_value AND lifvl.id_lang = '.(int)$filter['id_lang'].')
WHERE fl.id_feature = '.(int)$filter['id_value'].' AND fvl.id_lang = fl.id_lang AND fvl.id_lang = '.(int)$filter['id_lang']);
foreach ($features as $feature)
{
if (!isset($attribute_values_by_lang[$feature['id_lang']]))
$attribute_values_by_lang[$feature['id_lang']] = array();
if (!isset($attribute_values_by_lang[$feature['id_lang']]['f'.$feature['id_name']]))
$attribute_values_by_lang[$feature['id_lang']]['f'.$feature['id_name']] = array();
$attribute_values_by_lang[$feature['id_lang']]['f'.$feature['id_name']][] = array(
'name' => (!empty($feature['name_url_name']) ? $feature['name_url_name'] : $feature['name']),
'id_name' => 'f'.$feature['id_name'],
'value' => (!empty($feature['value_url_name']) ? $feature['value_url_name'] : $feature['value']),
'id_value' => $feature['id_name'].'_'.$feature['id_value'],
'id_id_value' => $feature['id_value'],
'category_name' => $filter['link_rewrite'],
'type' => $filter['type']);
}
break;
case 'category':
$categories = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS('
SELECT cl.name, cl.id_lang, c.id_category
FROM '._DB_PREFIX_.'category c
INNER JOIN '._DB_PREFIX_.'category_lang cl ON (c.id_category = cl.id_category)
WHERE cl.id_lang = '.(int)$filter['id_lang']);
foreach ($categories as $category)
{
if (!isset($attribute_values_by_lang[$category['id_lang']]))
$attribute_values_by_lang[$category['id_lang']] = array();
if (!isset($attribute_values_by_lang[$category['id_lang']]['category']))
$attribute_values_by_lang[$category['id_lang']]['category'] = array();
$attribute_values_by_lang[$category['id_lang']]['category'][] = array('name' => $this->translateWord('Categories', $category['id_lang']),
'id_name' => null, 'value' => $category['name'], 'id_value' => $category['id_category'],
'category_name' => $filter['link_rewrite'], 'type' => $filter['type']);
}
break;
case 'manufacturer':
$manufacturers = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS('
SELECT m.name as name,l.id_lang as id_lang, id_manufacturer
FROM '._DB_PREFIX_.'manufacturer m , '._DB_PREFIX_.'lang l
WHERE l.id_lang = '.(int)$filter['id_lang'].' ');
foreach ($manufacturers as $manufacturer)
{
if (!isset($attribute_values_by_lang[$manufacturer['id_lang']]))
$attribute_values_by_lang[$manufacturer['id_lang']] = array();
if (!isset($attribute_values_by_lang[$manufacturer['id_lang']]['manufacturer']))
$attribute_values_by_lang[$manufacturer['id_lang']]['manufacturer'] = array();
$attribute_values_by_lang[$manufacturer['id_lang']]['manufacturer'][] = array('name' => $this->translateWord('Manufacturer', $manufacturer['id_lang']),
'id_name' => null, 'value' => $manufacturer['name'], 'id_value' => $manufacturer['id_manufacturer'],
'category_name' => $filter['link_rewrite'], 'type' => $filter['type']);
}
break;
case 'quantity':
$avaibility_list = array(
$this->translateWord('Not available', (int)$filter['id_lang']),
$this->translateWord('In stock', (int)$filter['id_lang'])
);
foreach ($avaibility_list as $key => $quantity)
$attribute_values_by_lang[$filter['id_lang']]['quantity'][] = array('name' => $this->translateWord('Availability', (int)$filter['id_lang']),
'id_name' => null, 'value' => $quantity, 'id_value' => $key, 'id_id_value' => 0,
'category_name' => $filter['link_rewrite'], 'type' => $filter['type']);
break;
case 'condition':
$condition_list = array(
'new' => $this->translateWord('New', (int)$filter['id_lang']),
'used' => $this->translateWord('Used', (int)$filter['id_lang']),
'refurbished' => $this->translateWord('Refurbished', (int)$filter['id_lang'])
);
foreach ($condition_list as $key => $condition)
$attribute_values_by_lang[$filter['id_lang']]['condition'][] = array('name' => $this->translateWord('Condition', (int)$filter['id_lang']),
'id_name' => null, 'value' => $condition, 'id_value' => $key,
'category_name' => $filter['link_rewrite'], 'type' => $filter['type']);
break;
}
// Foreach langs
foreach ($attribute_values_by_lang as $id_lang => $attribute_values)
{
// Foreach attributes generate a couple "/<attribute_name>_<atttribute_value>". For example: color_blue
foreach ($attribute_values as $attribute)
foreach ($attribute as $param)
{
$selected_filters = array();
$link = '/'.str_replace(Configuration::get('PS_ATTRIBUTE_ANCHOR_SEPARATOR'), '_', Tools::link_rewrite($param['name'])).Configuration::get('PS_ATTRIBUTE_ANCHOR_SEPARATOR').str_replace(Configuration::get('PS_ATTRIBUTE_ANCHOR_SEPARATOR'), '_', Tools::link_rewrite($param['value']));
$selected_filters[$param['type']] = array();
if (!isset($param['id_id_value']))
$param['id_id_value'] = $param['id_value'];
$selected_filters[$param['type']][$param['id_id_value']] = $param['id_value'];
$url_key = md5($link);
$id_layered_friendly_url = Db::getInstance()->getValue('SELECT id_layered_friendly_url
FROM `'._DB_PREFIX_.'layered_friendly_url` WHERE `id_lang` = '.$id_lang.' AND `url_key` = \''.$url_key.'\'');
if ($id_layered_friendly_url == false)
{
Db::getInstance()->AutoExecute(_DB_PREFIX_.'layered_friendly_url', array('url_key' => $url_key, 'data' => serialize($selected_filters), 'id_lang' => $id_lang), 'INSERT');
$id_layered_friendly_url = Db::getInstance()->Insert_ID();
}
}
}
if ($ajax)
return '{"result": 1}';
else
return 1;
}
public function translateWord($string, $id_lang )
{
static $_MODULES = array();
global $_MODULE;
$file = _PS_MODULE_DIR_.$this->name.'/translations/'.Language::getIsoById($id_lang).'.php';
if (!array_key_exists($id_lang, $_MODULES))
{
if (file_exists($file1 = _PS_MODULE_DIR_.$this->name.'/translations/'.Language::getIsoById($id_lang).'.php'))
{
include($file1);
$_MODULES[$id_lang] = $_MODULE;
}
elseif (file_exists($file2 = _PS_MODULE_DIR_.$this->name.'/'.Language::getIsoById($id_lang).'.php'))
{
include($file2);
$_MODULES[$id_lang] = $_MODULE;
}
else
return $string;
}
$string = str_replace('\'', '\\\'', $string);
// set array key to lowercase for 1.3 compatibility
$_MODULES[$id_lang] = array_change_key_case($_MODULES[$id_lang]);
$current_key = '<{'.strtolower( $this->name).'}'.strtolower(_THEME_NAME_).'>'.strtolower($this->name).'_'.md5($string);
$default_key = '<{'.strtolower( $this->name).'}prestashop>'.strtolower($this->name).'_'.md5($string);
if (isset($_MODULES[$id_lang][$current_key]))
$ret = stripslashes($_MODULES[$id_lang][$current_key]);
else if (isset($_MODULES[$id_lang][Tools::strtolower($current_key)]))
$ret = stripslashes($_MODULES[$id_lang][Tools::strtolower($current_key)]);
else if (isset($_MODULES[$id_lang][$default_key]))
$ret = stripslashes($_MODULES[$id_lang][$default_key]);
else if (isset($_MODULES[$id_lang][Tools::strtolower($default_key)]))
$ret = stripslashes($_MODULES[$id_lang][Tools::strtolower($default_key)]);
else
$ret = stripslashes($string);
return str_replace('"', '&quot;', $ret);
}
public function hookProductListAssign($params)
{
global $smarty;
if (!Configuration::getGlobalValue('PS_LAYERED_INDEXED'))
return;
// Inform the hook was executed
$params['hookExecuted'] = true;
// List of product to overrride categoryController
$params['catProducts'] = array();
$selected_filters = $this->getSelectedFilters();
$filter_block = $this->getFilterBlock($selected_filters);
$title = '';
if (is_array($filter_block['title_values']))
foreach ($filter_block['title_values'] as $key => $val)
$title .= ' '.$key.' '.implode('/', $val);
$smarty->assign('categoryNameComplement', $title);
$this->getProducts($selected_filters, $params['catProducts'], $params['nbProducts'], $p, $n, $pages_nb, $start, $stop, $range);
// Need a nofollow on the pagination links?
$smarty->assign('no_follow', $filter_block['no_follow']);
}
public function hookAfterSaveProduct($params)
{
if (!$params['id_product'])
return;
self::indexProductPrices((int)$params['id_product']);
$this->indexAttribute((int)$params['id_product']);
}
public function hookAfterSaveFeature($params)
{
if (!$params['id_feature'] || Tools::getValue('layered_indexable') === false)
return;
Db::getInstance()->execute('DELETE FROM '._DB_PREFIX_.'layered_indexable_feature WHERE id_feature = '.(int)$params['id_feature']);
Db::getInstance()->execute('INSERT INTO '._DB_PREFIX_.'layered_indexable_feature VALUES ('.(int)$params['id_feature'].', '.(int)Tools::getValue('layered_indexable').')');
Db::getInstance()->execute('DELETE FROM '._DB_PREFIX_.'layered_indexable_feature_lang_value WHERE id_feature = '.(int)$params['id_feature']); // don't care about the id_lang
foreach (Language::getLanguages(false) as $language)
{
// Data are validated by method "hookPostProcessFeature"
$id_lang = (int)$language['id_lang'];
Db::getInstance()->execute('INSERT INTO '._DB_PREFIX_.'layered_indexable_feature_lang_value
VALUES ('.(int)$params['id_feature'].', '.$id_lang.', \''.pSQL(Tools::link_rewrite(Tools::getValue('url_name_'.$id_lang))).'\',
\''.pSQL(Tools::getValue('meta_title_'.$id_lang), true).'\')');
}
}
public function hookAfterSaveFeatureValue($params)
{
if (!$params['id_feature_value'])
return;
Db::getInstance()->execute('DELETE FROM '._DB_PREFIX_.'layered_indexable_feature_value_lang_value WHERE id_feature_value = '.(int)$params['id_feature_value']); // don't care about the id_lang
foreach (Language::getLanguages(false) as $language)
{
// Data are validated by method "hookPostProcessFeatureValue"
$id_lang = (int)$language['id_lang'];
Db::getInstance()->execute('INSERT INTO '._DB_PREFIX_.'layered_indexable_feature_value_lang_value
VALUES ('.(int)$params['id_feature_value'].', '.$id_lang.', \''.pSQL(Tools::link_rewrite(Tools::getValue('url_name_'.$id_lang))).'\',
\''.pSQL(Tools::getValue('meta_title_'.$id_lang), true).'\')');
}
}
public function hookAfterDeleteFeatureValue($params)
{
if (!$params['id_feature_value'])
return;
Db::getInstance()->execute('DELETE FROM '._DB_PREFIX_.'layered_indexable_feature_value_lang_value WHERE id_feature_value = '.(int)$params['id_feature_value']);
}
public function hookPostProcessFeatureValue($params)
{
$this->hookPostProcessAttributeGroup($params);
}
public function hookFeatureValueForm($params)
{
$languages = Language::getLanguages(false);
$default_form_language = (int)$this->context->controller->default_form_language;
$lang_value = array();
$result = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS(
'SELECT url_name, meta_title, id_lang FROM '._DB_PREFIX_.'layered_indexable_feature_value_lang_value
WHERE id_feature_value = '.(int)$params['id_feature_value']);
if ($result)
foreach ($result as $data)
$lang_value[$data['id_lang']] = array('url_name' => $data['url_name'], 'meta_title' => $data['meta_title']);
$return = '<div class="form-group">
<label class="control-label col-lg-3">
<span class="label-tooltip" data-toggle="tooltip" data-html="true" title="" data-original-title="'.$this->l('Invalid characters:').' <>;=#{}_">
'.$this->l('URL:').'
</span>
</label>
<div class="col-lg-9">
<div class="form-group">';
foreach ($languages as $language)
{
$return .= '
<div class="translatable-field lang-'.$language['id_lang'].'" style="display: '.($language['id_lang'] == $default_form_language ? 'block' : 'none').';">
<div class="col-lg-9">
<input type="text" name="url_name_'.$language['id_lang'].'" value="'.Tools::safeOutput(@$lang_value[$language['id_lang']]['url_name'], true).'" />
</div>
<div class="col-lg-2">
<button type="button" class="btn btn-default dropdown-toggle" tabindex="-1" data-toggle="dropdown">
'.$language['iso_code'].'
<span class="caret"></span>
</button>
<ul class="dropdown-menu">';
foreach ($languages as $language)
$return .= '<li><a href="javascript:hideOtherLanguage('.$language['id_lang'].');" tabindex="-1">'.$language['name'].'</a></li>';
$return .= '</ul>
</div>
</div>';
}
$return .= '<div class="col-lg-9">
<p class="help-block">'.$this->l('Specific URL format in block layered generation').'</p>
</div>
</div>
</div>
</div>
<div class="form-group">
<label class="control-label col-lg-3">'.$this->l('Meta title:').' </label>
<div class="col-lg-9">
<div class="form-group">';
foreach ($languages as $language)
{
$return .= '
<div class="translatable-field lang-'.$language['id_lang'].'" style="display: '.($language['id_lang'] == $default_form_language ? 'block' : 'none').';">
<div class="col-lg-9">
<input type="text" name="meta_title_'.$language['id_lang'].'" value="'.Tools::safeOutput(@$lang_value[$language['id_lang']]['meta_title'], true).'" />
</div>
<div class="col-lg-2">
<button type="button" class="btn btn-default dropdown-toggle" tabindex="-1" data-toggle="dropdown">
'.$language['iso_code'].'
<span class="caret"></span>
</button>
<ul class="dropdown-menu">';
foreach ($languages as $language)
$return .= '<li><a href="javascript:hideOtherLanguage('.$language['id_lang'].');" tabindex="-1">'.$language['name'].'</a></li>';
$return .= '</ul>
</div>
</div>';
}
$return .= '<div class="col-lg-9">
<p class="help-block">'.$this->l('Specific format for meta title').'</p>
</div>
</div>
</div>
</div>';
return $return;
}
public function hookAfterSaveAttribute($params)
{
if (!$params['id_attribute'])
return;
Db::getInstance()->execute('DELETE FROM '._DB_PREFIX_.'layered_indexable_attribute_lang_value WHERE id_attribute = '.(int)$params['id_attribute']); // don't care about the id_lang
foreach (Language::getLanguages(false) as $language)
{
// Data are validated by method "hookPostProcessAttribute"
$id_lang = (int)$language['id_lang'];
Db::getInstance()->execute('INSERT INTO '._DB_PREFIX_.'layered_indexable_attribute_lang_value
VALUES ('.(int)$params['id_attribute'].', '.$id_lang.', \''.pSQL(Tools::link_rewrite(Tools::getValue('url_name_'.$id_lang))).'\',
\''.pSQL(Tools::getValue('meta_title_'.$id_lang), true).'\')');
}
}
public function hookAfterDeleteAttribute($params)
{
if (!$params['id_attribute'])
return;
Db::getInstance()->execute('DELETE FROM '._DB_PREFIX_.'layered_indexable_attribute_lang_value WHERE id_attribute = '.(int)$params['id_attribute']);
}
public function hookPostProcessAttribute($params)
{
$this->hookPostProcessAttributeGroup($params);
}
public function hookAttributeForm($params)
{
$languages = Language::getLanguages(false);
$default_form_language = (int)$this->context->controller->default_form_language;
$lang_value = array();
$result = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS(
'SELECT url_name, meta_title, id_lang FROM '._DB_PREFIX_.'layered_indexable_attribute_lang_value
WHERE id_attribute = '.(int)$params['id_attribute']);
if ($result)
foreach ($result as $data)
$lang_value[$data['id_lang']] = array('url_name' => $data['url_name'], 'meta_title' => $data['meta_title']);
$return = '<div class="form-group">
<label class="control-label col-lg-3">
<span class="label-tooltip" data-toggle="tooltip" data-html="true" title="" data-original-title="'.$this->l('Invalid characters:').' <>;=#{}_">
'.$this->l('URL:').'
</span>
</label>
<div class="col-lg-9">
<div class="form-group">';
foreach ($languages as $language)
{
$return .= '
<div class="translatable-field lang-'.$language['id_lang'].'" style="display: '.($language['id_lang'] == $default_form_language ? 'block' : 'none').';">
<div class="col-lg-9">
<input type="text" name="url_name_'.$language['id_lang'].'" value="'.Tools::safeOutput(@$lang_value[$language['id_lang']]['url_name'], true).'" />
</div>
<div class="col-lg-2">
<button type="button" class="btn btn-default dropdown-toggle" tabindex="-1" data-toggle="dropdown">
'.$language['iso_code'].'
<span class="caret"></span>
</button>
<ul class="dropdown-menu">';
foreach ($languages as $language)
$return .= '<li><a href="javascript:hideOtherLanguage('.$language['id_lang'].');" tabindex="-1">'.$language['name'].'</a></li>';
$return .= '</ul>
</div>
</div>';
}
$return .= '<div class="col-lg-9">
<p class="help-block">'.$this->l('Specific URL format in block layered generation').'</p>
</div>
</div>
</div>
</div>
<div class="form-group">
<label class="control-label col-lg-3">'.$this->l('Meta title:').' </label>
<div class="col-lg-9">
<div class="form-group">';
foreach ($languages as $language)
{
$return .= '
<div class="translatable-field lang-'.$language['id_lang'].'" style="display: '.($language['id_lang'] == $default_form_language ? 'block' : 'none').';">
<div class="col-lg-9">
<input type="text" name="meta_title_'.$language['id_lang'].'" value="'.Tools::safeOutput(@$lang_value[$language['id_lang']]['meta_title'], true).'" />
</div>
<div class="col-lg-2">
<button type="button" class="btn btn-default dropdown-toggle" tabindex="-1" data-toggle="dropdown">
'.$language['iso_code'].'
<span class="caret"></span>
</button>
<ul class="dropdown-menu">';
foreach ($languages as $language)
$return .= '<li><a href="javascript:hideOtherLanguage('.$language['id_lang'].');" tabindex="-1">'.$language['name'].'</a></li>';
$return .= '</ul>
</div>
</div>';
}
$return .= '<div class="col-lg-9">
<p class="help-block">'.$this->l('Specific format for meta title').'</p>
</div>
</div>
</div>
</div>';
return $return;
}
public function hookPostProcessFeature($params)
{
$this->hookPostProcessAttributeGroup($params);
}
public function hookAfterDeleteFeature($params)
{
if (!$params['id_feature'])
return;
Db::getInstance()->execute('DELETE FROM '._DB_PREFIX_.'layered_indexable_feature WHERE id_feature = '.(int)$params['id_feature']);
}
public function hookAfterSaveAttributeGroup($params)
{
if (!$params['id_attribute_group'] || Tools::getValue('layered_indexable') === false)
return;
Db::getInstance()->execute('DELETE FROM '._DB_PREFIX_.'layered_indexable_attribute_group WHERE id_attribute_group = '.(int)$params['id_attribute_group']);
Db::getInstance()->execute('INSERT INTO '._DB_PREFIX_.'layered_indexable_attribute_group VALUES ('.(int)$params['id_attribute_group'].', '.(int)Tools::getValue('layered_indexable').')');
Db::getInstance()->execute('DELETE FROM '._DB_PREFIX_.'layered_indexable_attribute_group_lang_value WHERE id_attribute_group = '.(int)$params['id_attribute_group']); // don't care about the id_lang
foreach (Language::getLanguages(false) as $language)
{
// Data are validated by method "hookPostProcessAttributeGroup"
$id_lang = (int)$language['id_lang'];
Db::getInstance()->execute('INSERT INTO '._DB_PREFIX_.'layered_indexable_attribute_group_lang_value
VALUES ('.(int)$params['id_attribute_group'].', '.$id_lang.', \''.pSQL(Tools::link_rewrite(Tools::getValue('url_name_'.$id_lang))).'\',
\''.pSQL(Tools::getValue('meta_title_'.$id_lang), true).'\')');
}
}
public function hookPostProcessAttributeGroup($params)
{
// Limit to one call
static $once = false;
if ($once)
return;
$once = true;
$errors = array();
foreach (Language::getLanguages(false) as $language)
{
$id_lang = $language['id_lang'];
if (Tools::getValue('url_name_'.$id_lang))
if (Tools::link_rewrite(Tools::getValue('url_name_'.$id_lang)) != strtolower( Tools::getValue('url_name_'.$id_lang)))
{
// Here use the reference "errors" to stop saving process
$params['errors'][] = Tools::displayError(sprintf($this->l('"%s" is not a valid url'), Tools::getValue('url_name_'.$id_lang)));
}
}
}
public function hookAfterDeleteAttributeGroup($params)
{
if (!$params['id_attribute_group'])
return;
Db::getInstance()->execute('DELETE FROM '._DB_PREFIX_.'layered_indexable_attribute_group WHERE id_attribute_group = '.(int)$params['id_attribute_group']);
Db::getInstance()->execute('DELETE FROM '._DB_PREFIX_.'layered_indexable_attribute_group_lang_value WHERE id_attribute_group = '.(int)$params['id_attribute_group']);
}
public function hookAttributeGroupForm($params)
{
$languages = Language::getLanguages(false);
$default_form_language = (int)$this->context->controller->default_form_language;
$indexable = Db::getInstance(_PS_USE_SQL_SLAVE_)->getValue('SELECT indexable FROM '._DB_PREFIX_.'layered_indexable_attribute_group
WHERE id_attribute_group = '.(int)$params['id_attribute_group']);
$lang_value = array();
$result = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS(
'SELECT url_name, meta_title, id_lang FROM '._DB_PREFIX_.'layered_indexable_attribute_group_lang_value
WHERE id_attribute_group = '.(int)$params['id_attribute_group']);
if ($result)
foreach ($result as $data)
$lang_value[$data['id_lang']] = array('url_name' => $data['url_name'], 'meta_title' => $data['meta_title']);
if ($indexable === false)
$on = true;
else
$on = (bool)$indexable;
$return = '<div class="form-group">
<label class="control-label col-lg-3">
<span class="label-tooltip" data-toggle="tooltip" data-html="true" title="" data-original-title="'.$this->l('Invalid characters:').' <>;=#{}_">
'.$this->l('URL:').'
</span>
</label>
<div class="col-lg-9">
<div class="form-group">';
foreach ($languages as $language)
{
$return .= '
<div class="translatable-field lang-'.$language['id_lang'].'" style="display: '.($language['id_lang'] == $default_form_language ? 'block' : 'none').';">
<div class="col-lg-9">
<input type="text" name="url_name_'.$language['id_lang'].'" value="'.Tools::safeOutput(@$lang_value[$language['id_lang']]['url_name'], true).'" />
</div>
<div class="col-lg-2">
<button type="button" class="btn btn-default dropdown-toggle" tabindex="-1" data-toggle="dropdown">
'.$language['iso_code'].'
<span class="caret"></span>
</button>
<ul class="dropdown-menu">';
foreach ($languages as $language)
$return .= '<li><a href="javascript:hideOtherLanguage('.$language['id_lang'].');" tabindex="-1">'.$language['name'].'</a></li>';
$return .= '</ul>
</div>
</div>';
}
$return .= '<div class="col-lg-9">
<p class="help-block">'.$this->l('Specific URL format in block layered generation').'</p>
</div>
</div>
</div>
</div>
<div class="form-group">
<label class="control-label col-lg-3">'.$this->l('Meta title:').' </label>
<div class="col-lg-9">
<div class="form-group">';
foreach ($languages as $language)
{
$return .= '
<div class="translatable-field lang-'.$language['id_lang'].'" style="display: '.($language['id_lang'] == $default_form_language ? 'block' : 'none').';">
<div class="col-lg-9">
<input type="text" name="meta_title_'.$language['id_lang'].'" value="'.Tools::safeOutput(@$lang_value[$language['id_lang']]['meta_title'], true).'" />
</div>
<div class="col-lg-2">
<button type="button" class="btn btn-default dropdown-toggle" tabindex="-1" data-toggle="dropdown">
'.$language['iso_code'].'
<span class="caret"></span>
</button>
<ul class="dropdown-menu">';
foreach ($languages as $language)
$return .= '<li><a href="javascript:hideOtherLanguage('.$language['id_lang'].');" tabindex="-1">'.$language['name'].'</a></li>';
$return .= '</ul>
</div>
</div>';
}
$return .= '<div class="col-lg-9">
<p class="help-block">'.$this->l('Specific format for meta title').'</p>
</div>
</div>
</div>
</div>
<div class="form-group">
<label class="control-label col-lg-3" for="">'.$this->l('Indexable:').'</label>
<div class="col-lg-9">
<div class="row">
<div class="input-group col-lg-2">
<span class="switch prestashop-switch">
<input type="radio" name="layered_indexable" id="indexable_on" value="1"'.(($on) ? ' checked="checked"' : '').'>
<label for="indexable_on" class="radioCheck">
<i class="icon-check-sign color_success"></i> '.$this->l('Yes').'
</label>
<input type="radio" name="layered_indexable" id="indexable_off" value="0"'.((!$on) ? ' checked="checked"' : '').'>
<label for="indexable_off" class="radioCheck">
<i class="icon-ban-circle color_danger"></i> '.$this->l('No').'
</label>
<a class="slide-button btn btn-default"></a>
</span>
</div>
</div>
</div>
<div class="col-lg-9 col-lg-push-3">
<p class="help-block">'.$this->l('Use this attribute in URL generated by the layered navigation module').'</p>
</div>
</div>';
return $return;
}
public function hookFeatureForm($params)
{
$languages = Language::getLanguages(false);
$default_form_language = (int)$this->context->controller->default_form_language;
$indexable = Db::getInstance(_PS_USE_SQL_SLAVE_)->getValue('SELECT indexable FROM '._DB_PREFIX_.'layered_indexable_feature WHERE id_feature = '.(int)$params['id_feature']);
$lang_value = array();
$result = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS(
'SELECT url_name, meta_title, id_lang FROM '._DB_PREFIX_.'layered_indexable_feature_lang_value
WHERE id_feature = '.(int)$params['id_feature']);
if ($result)
foreach ($result as $data)
$lang_value[$data['id_lang']] = array('url_name' => $data['url_name'], 'meta_title' => $data['meta_title']);
if ($indexable === false)
$on = true;
else
$on = (bool)$indexable;
$return = '<div class="form-group">
<label class="control-label col-lg-3">
<span class="label-tooltip" data-toggle="tooltip" data-html="true" title="" data-original-title="'.$this->l('Invalid characters:').' <>;=#{}_">
'.$this->l('URL:').'
</span>
</label>
<div class="col-lg-9">
<div class="form-group">';
foreach ($languages as $language)
{
$return .= '
<div class="translatable-field lang-'.$language['id_lang'].'" style="display: '.($language['id_lang'] == $default_form_language ? 'block' : 'none').';">
<div class="col-lg-9">
<input type="text" name="url_name_'.$language['id_lang'].'" value="'.Tools::safeOutput(@$lang_value[$language['id_lang']]['url_name'], true).'" />
</div>
<div class="col-lg-2">
<button type="button" class="btn btn-default dropdown-toggle" tabindex="-1" data-toggle="dropdown">
'.$language['iso_code'].'
<span class="caret"></span>
</button>
<ul class="dropdown-menu">';
foreach ($languages as $language)
$return .= '<li><a href="javascript:hideOtherLanguage('.$language['id_lang'].');" tabindex="-1">'.$language['name'].'</a></li>';
$return .= '</ul>
</div>
</div>';
}
$return .= '<div class="col-lg-9">
<p class="help-block">'.$this->l('Specific URL format in block layered generation').'</p>
</div>
</div>
</div>
</div>
<div class="form-group">
<label class="control-label col-lg-3">'.$this->l('Meta title:').' </label>
<div class="col-lg-9">
<div class="form-group">';
foreach ($languages as $language)
{
$return .= '
<div class="translatable-field lang-'.$language['id_lang'].'" style="display: '.($language['id_lang'] == $default_form_language ? 'block' : 'none').';">
<div class="col-lg-9">
<input type="text" name="meta_title_'.$language['id_lang'].'" value="'.Tools::safeOutput(@$lang_value[$language['id_lang']]['meta_title'], true).'" />
</div>
<div class="col-lg-2">
<button type="button" class="btn btn-default dropdown-toggle" tabindex="-1" data-toggle="dropdown">
'.$language['iso_code'].'
<span class="caret"></span>
</button>
<ul class="dropdown-menu">';
foreach ($languages as $language)
$return .= '<li><a href="javascript:hideOtherLanguage('.$language['id_lang'].');" tabindex="-1">'.$language['name'].'</a></li>';
$return .= '</ul>
</div>
</div>';
}
$return .= '<div class="col-lg-9">
<p class="help-block">'.$this->l('Specific format for meta title').'</p>
</div>
</div>
</div>
</div>
<div class="form-group">
<label class="control-label col-lg-3" for="">'.$this->l('Indexable:').'</label>
<div class="col-lg-9">
<div class="row">
<div class="input-group col-lg-2">
<span class="switch prestashop-switch">
<input type="radio" name="layered_indexable" id="indexable_on" value="1"'.(($on) ? ' checked="checked"' : '').'>
<label for="indexable_on" class="radioCheck">
<i class="icon-check-sign color_success"></i> '.$this->l('Yes').'
</label>
<input type="radio" name="layered_indexable" id="indexable_off" value="0"'.((!$on) ? ' checked="checked"' : '').'>
<label for="indexable_off" class="radioCheck">
<i class="icon-ban-circle color_danger"></i> '.$this->l('No').'
</label>
<a class="slide-button btn btn-default"></a>
</span>
</div>
</div>
</div>
<div class="col-lg-9 col-lg-push-3">
<p class="help-block">'.$this->l('Use this attribute in URL generated by the layered navigation module').'</p>
</div>
</div>';
return $return;
}
/*
* $cursor $cursor in order to restart indexing from the last state
*/
public static function fullPricesIndexProcess($cursor = 0, $ajax = false, $smart = false)
{
if ($cursor == 0 && !$smart)
self::installPriceIndexTable();
return self::indexPrices($cursor, true, $ajax, $smart);
}
/*
* $cursor $cursor in order to restart indexing from the last state
*/
public static function pricesIndexProcess($cursor = 0, $ajax = false)
{
return self::indexPrices($cursor, false, $ajax);
}
private static function indexPrices($cursor = null, $full = false, $ajax = false, $smart = false)
{
if ($full)
$nb_products = (int)Db::getInstance()->getValue('
SELECT count(DISTINCT p.`id_product`)
FROM '._DB_PREFIX_.'product p
INNER JOIN `'._DB_PREFIX_.'product_shop` ps
ON (ps.`id_product` = p.`id_product` AND ps.`active` = 1 AND ps.`visibility` IN ("both", "catalog"))');
else
$nb_products = (int)Db::getInstance()->getValue('
SELECT COUNT(DISTINCT p.`id_product`) FROM `'._DB_PREFIX_.'product` p
INNER JOIN `'._DB_PREFIX_.'product_shop` ps
ON (ps.`id_product` = p.`id_product` AND ps.`active` = 1 AND ps.`visibility` IN ("both", "catalog"))
LEFT JOIN `'._DB_PREFIX_.'layered_price_index` psi ON (psi.id_product = p.id_product)
WHERE psi.id_product IS NULL');
$max_executiontime = @ini_get('max_execution_time');
if ($max_executiontime > 5 || $max_executiontime <= 0)
$max_executiontime = 5;
$start_time = microtime(true);
do
{
$cursor = (int)self::indexPricesUnbreakable((int)$cursor, $full, $smart);
$time_elapsed = microtime(true) - $start_time;
}
while ($cursor < $nb_products && (Tools::getMemoryLimit()) > memory_get_peak_usage() && $time_elapsed < $max_executiontime);
if (($nb_products > 0 && !$full || $cursor < $nb_products && $full) && !$ajax)
{
$token = substr(Tools::encrypt('blocklayered/index'), 0, 10);
if (Tools::usingSecureMode())
$domain = Tools::getShopDomainSsl(true);
else
$domain = Tools::getShopDomain(true);
if (!Tools::file_get_contents($domain.__PS_BASE_URI__.'modules/blocklayered/blocklayered-price-indexer.php?token='.$token.'&cursor='.(int)$cursor.'&full='.(int)$full))
self::indexPrices((int)$cursor, (int)$full);
return $cursor;
}
if ($ajax && $nb_products > 0 && $cursor < $nb_products && $full)
return '{"cursor": '.$cursor.', "count": '.($nb_products - $cursor).'}';
else if ($ajax && $nb_products > 0 && !$full)
return '{"cursor": '.$cursor.', "count": '.($nb_products).'}';
else
{
Configuration::updateGlobalValue('PS_LAYERED_INDEXED', 1);
if ($ajax)
return '{"result": "ok"}';
else
return -1;
}
}
/*
* $cursor $cursor in order to restart indexing from the last state
*/
private static function indexPricesUnbreakable($cursor, $full = false, $smart = false)
{
static $length = 100; // Nb of products to index
if (is_null($cursor))
$cursor = 0;
if ($full)
$query = '
SELECT p.`id_product`
FROM `'._DB_PREFIX_.'product` p
INNER JOIN `'._DB_PREFIX_.'product_shop` ps
ON (ps.`id_product` = p.`id_product` AND ps.`active` = 1 AND ps.`visibility` IN ("both", "catalog"))
GROUP BY p.`id_product`
ORDER BY p.`id_product` LIMIT '.(int)$cursor.','.(int)$length;
else
$query = '
SELECT p.`id_product`
FROM `'._DB_PREFIX_.'product` p
INNER JOIN `'._DB_PREFIX_.'product_shop` ps
ON (ps.`id_product` = p.`id_product` AND ps.`active` = 1 AND ps.`visibility` IN ("both", "catalog"))
LEFT JOIN `'._DB_PREFIX_.'layered_price_index` psi ON (psi.id_product = p.id_product)
WHERE psi.id_product IS NULL
GROUP BY p.`id_product`
ORDER BY p.`id_product` LIMIT 0,'.(int)$length;
foreach (Db::getInstance()->executeS($query) as $product)
self::indexProductPrices((int)$product['id_product'], ($smart && $full));
return (int)($cursor + $length);
}
public static function indexProductPrices($id_product, $smart = true)
{
static $groups = null;
if (is_null($groups))
{
$groups = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS('SELECT id_group FROM `'._DB_PREFIX_.'group_reduction`');
if (!$groups)
$groups = array();
}
$shop_list = Shop::getShops(false, null, true);
foreach ($shop_list as $id_shop)
{
static $currency_list = null;
if (is_null($currency_list))
$currency_list = Currency::getCurrencies(false, 1, new Shop($id_shop));
$min_price = array();
$max_price = array();
if ($smart)
Db::getInstance()->execute('DELETE FROM `'._DB_PREFIX_.'layered_price_index` WHERE `id_product` = '.(int)$id_product.' AND `id_shop` = '.(int)$id_shop);
if (Configuration::get('PS_LAYERED_FILTER_PRICE_USETAX'))
$max_tax_rate = Db::getInstance()->getValue('
SELECT max(t.rate) max_rate
FROM `'._DB_PREFIX_.'product_shop` p
LEFT JOIN `'._DB_PREFIX_.'tax_rules_group` trg ON (trg.id_tax_rules_group = p.id_tax_rules_group AND p.id_shop = '.(int)$shop_list.')
LEFT JOIN `'._DB_PREFIX_.'tax_rule` tr ON (tr.id_tax_rules_group = trg.id_tax_rules_group)
LEFT JOIN `'._DB_PREFIX_.'tax` t ON (t.id_tax = tr.id_tax AND t.active = 1)
WHERE id_product = '.(int)$id_product.'
GROUP BY id_product');
else
$max_tax_rate = 0;
$product_min_prices = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS('
SELECT id_shop, id_currency, id_country, id_group, from_quantity
FROM `'._DB_PREFIX_.'specific_price`
WHERE id_product = '.(int)$id_product);
// Get min price
foreach ($currency_list as $currency)
{
$price = Product::priceCalculation($id_shop, (int)$id_product, null, null, null, null,
$currency['id_currency'], null, null, false, 6, false, true, true,
$specific_price_output, true);
if (!isset($max_price[$currency['id_currency']]))
$max_price[$currency['id_currency']] = 0;
if (!isset($min_price[$currency['id_currency']]))
$min_price[$currency['id_currency']] = null;
if ($price > $max_price[$currency['id_currency']])
$max_price[$currency['id_currency']] = $price;
if ($price == 0)
continue;
if (is_null($min_price[$currency['id_currency']]) || $price < $min_price[$currency['id_currency']])
$min_price[$currency['id_currency']] = $price;
}
foreach ($product_min_prices as $specific_price)
foreach ($currency_list as $currency)
{
if ($specific_price['id_currency'] && $specific_price['id_currency'] != $currency['id_currency'])
continue;
$price = Product::priceCalculation((($specific_price['id_shop'] == 0) ? null : (int)$specific_price['id_shop']), (int)$id_product,
null, (($specific_price['id_country'] == 0) ? null : $specific_price['id_country']), null, null,
$currency['id_currency'], (($specific_price['id_group'] == 0) ? null : $specific_price['id_group']),
$specific_price['from_quantity'], false, 6, false, true, true, $specific_price_output, true);
if (!isset($max_price[$currency['id_currency']]))
$max_price[$currency['id_currency']] = 0;
if (!isset($min_price[$currency['id_currency']]))
$min_price[$currency['id_currency']] = null;
if ($price > $max_price[$currency['id_currency']])
$max_price[$currency['id_currency']] = $price;
if ($price == 0)
continue;
if (is_null($min_price[$currency['id_currency']]) || $price < $min_price[$currency['id_currency']])
$min_price[$currency['id_currency']] = $price;
}
foreach ($groups as $group)
foreach ($currency_list as $currency)
{
$price = Product::priceCalculation(null, (int)$id_product, null, null, null, null, (int)$currency['id_currency'], (int)$group['id_group'],
null, false, 6, false, true, true, $specific_price_output, true);
if (!isset($max_price[$currency['id_currency']]))
$max_price[$currency['id_currency']] = 0;
if (!isset($min_price[$currency['id_currency']]))
$min_price[$currency['id_currency']] = null;
if ($price > $max_price[$currency['id_currency']])
$max_price[$currency['id_currency']] = $price;
if ($price == 0)
continue;
if (is_null($min_price[$currency['id_currency']]) || $price < $min_price[$currency['id_currency']])
$min_price[$currency['id_currency']] = $price;
}
$values = array();
foreach ($currency_list as $currency)
$values[] = '('.(int)$id_product.',
'.(int)$currency['id_currency'].',
'.$id_shop.',
'.(int)$min_price[$currency['id_currency']].',
'.(int)Tools::ps_round($max_price[$currency['id_currency']] * (100 + $max_tax_rate) / 100, 0).')';
Db::getInstance()->execute('
INSERT INTO `'._DB_PREFIX_.'layered_price_index` (id_product, id_currency, id_shop, price_min, price_max)
VALUES '.implode(',', $values).'
ON DUPLICATE KEY UPDATE id_product = id_product # avoid duplicate keys');
}
}
public function hookLeftColumn($params)
{
return $this->generateFiltersBlock($this->getSelectedFilters());
}
public function hookRightColumn($params)
{
return $this->hookLeftColumn($params);
}
public function hookDisplayBackOfficeHeader()
{
if (method_exists($this->context->controller, 'addJquery'))
{
if (version_compare(_PS_VERSION_, '1.6.0', '>=') === TRUE)
$this->context->controller->addCSS($this->_path.'views/css/blocklayered_bt.css');
else
$this->context->controller->addCSS($this->_path.'views/css/blocklayered.css');
}
}
public function hookHeader($params)
{
global $smarty, $cookie;
// No filters => module disable
if ($filter_block = $this->getFilterBlock($this->getSelectedFilters()))
if ($filter_block['nbr_filterBlocks'] == 0)
return false;
if (Tools::getValue('id_category', Tools::getValue('id_category_layered', 1)) == 1)
return;
$id_lang = (int)$cookie->id_lang;
$category = new Category((int)Tools::getValue('id_category'));
// Generate meta title and meta description
$category_title = (empty($category->meta_title[$id_lang]) ? $category->name[$id_lang] : $category->meta_title[$id_lang]);
$title = '';
$description = '';
$keywords = '';
if (is_array($filter_block['meta_values']))
foreach ($filter_block['meta_values'] as $key => $val)
{
if (!empty($val['title']))
$val['title'] = $val['title'].' ';
foreach ($val['values'] as $value)
{
$title .= $category_title.' '.$val['title'].$value.' - ';
$description .= $category_title.' '.$val['title'].$value.', ';
$keywords .= $val['title'].$value.', ';
}
}
// Title attributes (ex: <attr1> <value1>/<value2> - <attr2> <value1>)
$title = strtolower(rtrim(substr($title, 0, -3)));
// Title attributes (ex: <attr1> <value1>/<value2>, <attr2> <value1>)
$description = strtolower(rtrim(substr($description, 0, -2)));
// kewords attributes (ex: <attr1> <value1>, <attr1> <value2>, <attr2> <value1>)
$category_metas = Meta::getMetaTags($id_lang, 'category', $title);
if (!empty($title))
{
$smarty->assign('meta_title', ucfirst(substr($category_metas['meta_title'], 3)));
$smarty->assign('meta_description', $description.'. '.$category_metas['meta_description']);
}
else
$smarty->assign('meta_title', $category_metas['meta_title']);
$keywords = substr(strtolower($keywords), 0, 1000);
if (!empty($keywords))
$smarty->assign('meta_keywords', rtrim($category_title.', '.$keywords.', '.$category_metas['meta_keywords'], ', '));
$this->context->controller->addJS(($this->_path).'blocklayered.js');
$this->context->controller->addJS(_PS_JS_DIR_.'jquery/jquery-ui-1.8.10.custom.min.js');
$this->context->controller->addJQueryUI('ui.slider');
$this->context->controller->addCSS(_PS_CSS_DIR_.'jquery-ui-1.8.10.custom.css"');
$this->context->controller->addCSS(($this->_path).'blocklayered-15.css', 'all');
$this->context->controller->addJQueryPlugin('scrollTo');
$filters = $this->getSelectedFilters();
// Get non indexable attributes
$attribute_group_list = Db::getInstance()->executeS('SELECT id_attribute_group FROM '._DB_PREFIX_.'layered_indexable_attribute_group WHERE indexable = 0');
// Get non indexable features
$feature_list = Db::getInstance()->executeS('SELECT id_feature FROM '._DB_PREFIX_.'layered_indexable_feature WHERE indexable = 0');
$attributes = array();
$features = array();
$blacklist = array('weight', 'price');
if (!Configuration::get('PS_LAYERED_FILTER_INDEX_CDT'))
$blacklist[] = 'condition';
if (!Configuration::get('PS_LAYERED_FILTER_INDEX_QTY'))
$blacklist[] = 'quantity';
if (!Configuration::get('PS_LAYERED_FILTER_INDEX_MNF'))
$blacklist[] = 'manufacturer';
if (!Configuration::get('PS_LAYERED_FILTER_INDEX_CAT'))
$blacklist[] = 'category';
foreach ($filters as $type => $val)
{
switch ($type)
{
case 'id_attribute_group':
foreach ($val as $attr)
{
$attr_id = preg_replace('/_\d+$/', '', $attr);
if (in_array($attr_id, $attributes) || in_array(array('id_attribute_group' => $attr_id), $attribute_group_list))
{
$smarty->assign('nobots', true);
$smarty->assign('nofollow', true);
return;
}
$attributes[] = $attr_id;
}
break;
case 'id_feature':
foreach ($val as $feat)
{
$feat_id = preg_replace('/_\d+$/', '', $feat);
if (in_array($feat_id, $features) || in_array(array('id_feature' => $feat_id), $feature_list))
{
$smarty->assign('nobots', true);
$smarty->assign('nofollow', true);
return;
}
$features[] = $feat_id;
}
break;
default:
if (in_array($type, $blacklist))
{
if (count($val))
{
$smarty->assign('nobots', true);
$smarty->assign('nofollow', true);
return;
}
}
elseif (count($val) > 1)
{
$smarty->assign('nobots', true);
$smarty->assign('nofollow', true);
return;
}
break;
}
}
}
public function hookFooter($params)
{
// No filters => module disable
if ($filter_block = $this->getFilterBlock($this->getSelectedFilters()))
if ($filter_block['nbr_filterBlocks'] == 0)
return false;
if (Dispatcher::getInstance()->getController() == 'category')
return '
<script type="text/javascript">
//<![CDATA[
$(document).ready(function()
{
$(\'#selectProductSort\').unbind(\'change\').bind(\'change\', function()
{
reloadContent();
})
});
//]]>
</script>';
}
public function hookCategoryAddition($params)
{
$this->rebuildLayeredCache(array(), array((int)$params['category']->id));
}
public function hookCategoryUpdate($params)
{
/* The category status might (active, inactive) have changed, we have to update the layered cache table structure */
if (isset($params['category']) && !$params['category']->active)
$this->hookCategoryDeletion($params);
}
public function hookCategoryDeletion($params)
{
$layered_filter_list = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS('SELECT * FROM '._DB_PREFIX_.'layered_filter');
foreach ($layered_filter_list as $layered_filter)
{
$data = Tools::unSerialize($layered_filter['filters']);
if (in_array((int)$params['category']->id, $data['categories']))
{
unset($data['categories'][array_search((int)$params['category']->id, $data['categories'])]);
Db::getInstance()->execute('UPDATE `'._DB_PREFIX_.'layered_filter` SET `filters` = \''.pSQL(serialize($data)).'\' WHERE `id_layered_filter` = '.(int)$layered_filter['id_layered_filter'].'');
}
}
$this->buildLayeredCategories();
}
public function getContent()
{
global $cookie;
$html = '';
if (Tools::isSubmit('SubmitFilter'))
{
if (!Tools::getValue('layered_tpl_name'))
$html .= $this->displayError($this->l('Filter template name required (cannot be empty)'));
else
{
if (isset($_POST['id_layered_filter']) && $_POST['id_layered_filter'])
{
Db::getInstance()->execute('DELETE FROM '._DB_PREFIX_.'layered_filter WHERE id_layered_filter = '.(int)Tools::getValue('id_layered_filter'));
$this->buildLayeredCategories();
}
if (Tools::getValue('scope') == 1)
{
Db::getInstance()->execute('TRUNCATE TABLE '._DB_PREFIX_.'layered_filter');
$categories = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS('SELECT id_category FROM '._DB_PREFIX_.'category');
foreach ($categories as $category)
$_POST['categoryBox'][] = (int)$category['id_category'];
}
$id_layered_filter = (int)$_POST['id_layered_filter'];
if (!$id_layered_filter)
$id_layered_filter = (int)Db::getInstance()->Insert_ID();
$shop_list = array();
if (isset($_POST['checkBoxShopAsso_layered_filter']))
{
foreach ($_POST['checkBoxShopAsso_layered_filter'] as $id_shop => $row)
{
$assos[] = array('id_object' => (int)$id_layered_filter, 'id_shop' => (int)$id_shop);
$shop_list[] = (int)$id_shop;
}
}
else
$shop_list = array(Context::getContext()->shop->id);
if (count($_POST['categoryBox']))
{
/* Clean categoryBox before use */
if (isset($_POST['categoryBox']) && is_array($_POST['categoryBox']))
foreach ($_POST['categoryBox'] as &$category_box_tmp)
$category_box_tmp = (int)$category_box_tmp;
$filter_values = array();
foreach ($_POST['categoryBox'] as $idc)
$filter_values['categories'][] = (int)$idc;
$filter_values['shop_list'] = $shop_list;
$values = false;
foreach ($_POST['categoryBox'] as $id_category_layered)
{
foreach ($_POST as $key => $value)
if (substr($key, 0, 17) == 'layered_selection' && $value == 'on')
{
$values = true;
$type = 0;
$limit = 0;
if (Tools::getValue($key.'_filter_type'))
$type = Tools::getValue($key.'_filter_type');
if (Tools::getValue($key.'_filter_show_limit'))
$limit = Tools::getValue($key.'_filter_show_limit');
$filter_values[$key] = array(
'filter_type' => (int)$type,
'filter_show_limit' => (int)$limit
);
}
}
Db::getInstance()->execute('DELETE FROM '._DB_PREFIX_.'layered_filter_shop WHERE `id_layered_filter` = '.(int)$id_layered_filter);
if (isset($assos))
foreach ($assos as $asso)
Db::getInstance()->execute('INSERT INTO '._DB_PREFIX_.'layered_filter_shop (`id_layered_filter`, `id_shop`)
VALUES('.$id_layered_filter.', '.(int)$asso['id_shop'].')');
$values_to_insert = array(
'name' => pSQL(Tools::getValue('layered_tpl_name')),
'filters' => pSQL(serialize($filter_values)),
'n_categories' => (int)count($filter_values['categories']),
'date_add' => date('Y-m-d H:i:s'));
if (isset($_POST['id_layered_filter']) && $_POST['id_layered_filter'])
$values_to_insert['id_layered_filter'] = (int)Tools::getValue('id_layered_filter');
Db::getInstance()->autoExecute(_DB_PREFIX_.'layered_filter', $values_to_insert, 'INSERT');
$this->buildLayeredCategories();
$html .= $this->displayConfirmation($this->l('Your filter').' "'.Tools::safeOutput(Tools::getValue('layered_tpl_name')).'" '.
((isset($_POST['id_layered_filter']) && $_POST['id_layered_filter']) ? $this->l('was updated successfully.') : $this->l('was added successfully.')));
}
}
}
else if (Tools::isSubmit('submitLayeredSettings'))
{
Configuration::updateValue('PS_LAYERED_HIDE_0_VALUES', (int)Tools::getValue('ps_layered_hide_0_values'));
Configuration::updateValue('PS_LAYERED_SHOW_QTIES', (int)Tools::getValue('ps_layered_show_qties'));
Configuration::updateValue('PS_LAYERED_FULL_TREE', (int)Tools::getValue('ps_layered_full_tree'));
Configuration::updateValue('PS_LAYERED_FILTER_PRICE_USETAX', (int)Tools::getValue('ps_layered_filter_price_usetax'));
Configuration::updateValue('PS_LAYERED_FILTER_CATEGORY_DEPTH', (int)Tools::getValue('ps_layered_filter_category_depth'));
Configuration::updateValue('PS_LAYERED_FILTER_INDEX_QTY', (int)Tools::getValue('ps_layered_filter_index_availability'));
Configuration::updateValue('PS_LAYERED_FILTER_INDEX_CDT', (int)Tools::getValue('ps_layered_filter_index_condition'));
Configuration::updateValue('PS_LAYERED_FILTER_INDEX_MNF', (int)Tools::getValue('ps_layered_filter_index_manufacturer'));
Configuration::updateValue('PS_LAYERED_FILTER_INDEX_CAT', (int)Tools::getValue('ps_layered_filter_index_category'));
$html .= '
<div class="conf">'.$this->l('Settings saved successfully').'</div>';
}
else if (isset($_GET['deleteFilterTemplate']))
{
$layered_values = Db::getInstance(_PS_USE_SQL_SLAVE_)->getValue('
SELECT filters
FROM '._DB_PREFIX_.'layered_filter
WHERE id_layered_filter = '.(int)$_GET['id_layered_filter']);
if ($layered_values)
{
Db::getInstance()->execute('DELETE FROM '._DB_PREFIX_.'layered_filter WHERE id_layered_filter = '.(int)$_GET['id_layered_filter'].' LIMIT 1');
$this->buildLayeredCategories();
$html .= $this->displayConfirmation($this->l('Filter template deleted, categories updated (reverted to default Filter template).'));
}
else
$html .= $this->displayError($this->l('Filter template not found'));
}
$html .= '
<div id="ajax-message-ok" class="conf ajax-message alert alert-success" style="display: none">
<span class="message"></span>
</div>
<div id="ajax-message-ko" class="error ajax-message alert alert-danger" style="display: none">
<span class="message"></span>
</div>
<div class="panel">
<h3><i class="icon-cogs"></i> '.$this->l('Indexes and caches').'</h3>
<div id="indexing-warning" class="alert alert-warning" style="display: none">'.$this->l('Indexing is in progress. Please do not leave this page').'</div>';
if (!Configuration::getGlobalValue('PS_LAYERED_INDEXED'))
$html .= '
<script type="text/javascript">
$(document).ready(function() {
$(\'#url-indexer\').click();
$(\'#full-index\').click();
});
</script>';
$category_ist = array();
foreach (Db::getInstance()->executeS('SELECT id_category FROM `'._DB_PREFIX_.'category`') as $category)
if ($category['id_category'] != 1)
$category_ist[] = $category['id_category'];
$domain = Tools::getProtocol(Tools::usingSecureMode()).$_SERVER['HTTP_HOST'];
$html .= '<div class="row">
<a class="ajaxcall-recurcive btn btn-default"
href="'.$domain.__PS_BASE_URI__.'modules/blocklayered/blocklayered-price-indexer.php'.'?token='.substr(Tools::encrypt('blocklayered/index'), 0, 10).'">'.
$this->l('Index all missing prices').'</a>
<a class="ajaxcall-recurcive btn btn-default"
href="'.$domain.__PS_BASE_URI__.'modules/blocklayered/blocklayered-price-indexer.php'.'?token='.substr(Tools::encrypt('blocklayered/index'), 0, 10).'&full=1">'.
$this->l('Rebuild entire price index').'</a>
<a class="ajaxcall btn btn-default" id="attribute-indexer" rel="attribute"
href="'.$domain.__PS_BASE_URI__.'modules/blocklayered/blocklayered-attribute-indexer.php'.'?token='.substr(Tools::encrypt('blocklayered/index'), 0, 10).'">'.
$this->l('Build attribute index').'</a>
<a class="ajaxcall btn btn-default" id="url-indexer" rel="price"
href="'.$domain.__PS_BASE_URI__.'modules/blocklayered/blocklayered-url-indexer.php'.'?token='.substr(Tools::encrypt('blocklayered/index'), 0, 10).'&truncate=1">'.
$this->l('Build URL index').'</a>
<br /><br />
</div><div class="row">
<div class="alert alert-info">'.$this->l('You can set a cron job that will rebuild price index using the following URL:').'<br /><strong>'.
$domain.__PS_BASE_URI__.'modules/blocklayered/blocklayered-price-indexer.php'.'?token='.substr(Tools::encrypt('blocklayered/index'), 0, 10).'&full=1</strong>
</div></div><div class="row">
<div class="alert alert-info">'.$this->l('You can set a cron job that will rebuild URL index using the following URL:').'<br /><strong>'.
$domain.__PS_BASE_URI__.'modules/blocklayered/blocklayered-url-indexer.php'.'?token='.substr(Tools::encrypt('blocklayered/index'), 0, 10).'&truncate=1</strong>
</div></div><div class="row">
<div class="alert alert-info">'.$this->l('You can set a cron job that will rebuild attribute index using the following URL:').'<br /><strong>'.
$domain.__PS_BASE_URI__.'modules/blocklayered/blocklayered-attribute-indexer.php'.'?token='.substr(Tools::encrypt('blocklayered/index'), 0, 10).'</strong>
</div></div><div class="row">
'.$this->l('A nightly rebuild is recommended.').'
<script type="text/javascript">
$(\'.ajaxcall\').click(function() {
if (this.legend == undefined)
this.legend = $(this).html();
if (this.running == undefined)
this.running = false;
if (this.running == true)
return false;
$(\'.ajax-message\').hide();
this.running = true;
if (typeof(this.restartAllowed) == \'undefined\' || this.restartAllowed)
{
$(this).html(this.legend+\' '.addslashes($this->l('(in progress)')).'\');
$(\'#indexing-warning\').show();
}
this.restartAllowed = false;
var type = $(this).attr(\'rel\');
$.ajax({
url: this.href+\'&ajax=1\',
context: this,
dataType: \'json\',
cache: \'false\',
success: function(res)
{
this.running = false;
this.restartAllowed = true;
$(\'#indexing-warning\').hide();
$(this).html(this.legend);
if (type == \'price\')
$(\'#ajax-message-ok span\').html(\''.addslashes($this->l('URL indexation finished')).'\');
else
$(\'#ajax-message-ok span\').html(\''.addslashes($this->l('Attribute indexation finished')).'\');
$(\'#ajax-message-ok\').show();
return;
},
error: function(res)
{
this.restartAllowed = true;
$(\'#indexing-warning\').hide();
if (type == \'price\')
$(\'#ajax-message-ko span\').html(\''.addslashes($this->l('URL indexation failed')).'\');
else
$(\'#ajax-message-ko span\').html(\''.addslashes($this->l('Attribute indexation failed')).'\');
$(\'#ajax-message-ko\').show();
$(this).html(this.legend);
this.running = false;
}
});
return false;
});
$(\'.ajaxcall-recurcive\').each(function(it, elm) {
$(elm).click(function() {
if (this.cursor == undefined)
this.cursor = 0;
if (this.legend == undefined)
this.legend = $(this).html();
if (this.running == undefined)
this.running = false;
if (this.running == true)
return false;
$(\'.ajax-message\').hide();
this.running = true;
if (typeof(this.restartAllowed) == \'undefined\' || this.restartAllowed)
{
$(this).html(this.legend+\' '.addslashes($this->l('(in progress)')).'\');
$(\'#indexing-warning\').show();
}
this.restartAllowed = false;
$.ajax({
url: this.href+\'&ajax=1&cursor=\'+this.cursor,
context: this,
dataType: \'json\',
cache: \'false\',
success: function(res)
{
this.running = false;
if (res.result)
{
this.cursor = 0;
$(\'#indexing-warning\').hide();
$(this).html(this.legend);
$(\'#ajax-message-ok span\').html(\''.addslashes($this->l('Price indexation finished')).'\');
$(\'#ajax-message-ok\').show();
return;
}
this.cursor = parseInt(res.cursor);
$(this).html(this.legend+\' '.addslashes($this->l('(in progress, %s products price to index)')).'\'.replace(\'%s\', res.count));
$(this).click();
},
error: function(res)
{
this.restartAllowed = true;
$(\'#indexing-warning\').hide();
$(\'#ajax-message-ko span\').html(\''.addslashes($this->l('Price indexation failed')).'\');
$(\'#ajax-message-ko\').show();
$(this).html(this.legend);
this.cursor = 0;
this.running = false;
}
});
return false;
});
});
</script>
</div>
</div>
<div class="panel">
<h3><i class="icon-cogs"></i> '.$this->l('Existing filter templates').'</h3>';
$filters_templates = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS('SELECT * FROM '._DB_PREFIX_.'layered_filter ORDER BY date_add DESC');
if (count($filters_templates))
{
$html .= '<p>'.sprintf($this->l('%s filter templates are configured:'), count($filters_templates)).'</p>
<table id="table-filter-templates" class="table">
<thead>
<tr>
<th class="fixed-width-xs"><span class="title_box">'.$this->l('ID').'</span></th>
<th><span class="title_box text-left">'.$this->l('Name').'</span></th>
<th class="fixed-width-sm"><span class="title_box">'.$this->l('Categories').'</span></th>
<th><span class="title_box">'.$this->l('Created on').'</span></th>
<th><span class="title_box text-right">'.$this->l('Actions').'</span></th>
</tr>
</thead>';
foreach ($filters_templates as $filters_template)
{
/* Clean request URI first */
$_SERVER['REQUEST_URI'] = preg_replace('/&deleteFilterTemplate=[0-9]*&id_layered_filter=[0-9]*/', '', $_SERVER['REQUEST_URI']);
$html .= '
<tr>
<td>'.(int)$filters_template['id_layered_filter'].'</td>
<td class="text-left">'.$filters_template['name'].'</td>
<td>'.(int)$filters_template['n_categories'].'</td>
<td>'.Tools::displayDate($filters_template['date_add'],null , true).'</td>
<td class="text-right">
<div class="btn-group-action">
<div class="btn-group">
<a href="#" class="btn btn-default" onclick="return updElements('.($filters_template['n_categories'] ? 0 : 1).', '.(int)$filters_template['id_layered_filter'].');">
<i class="icon-pencil"></i> '.$this->l('Edit').'
</a>
<button class="btn btn-default dropdown-toggle" data-toggle="dropdown">
<span class="caret"></span>&nbsp;
</button>
<ul class="dropdown-menu">
<li>
<a href="'.Tools::safeOutput($_SERVER['REQUEST_URI']).'&deleteFilterTemplate=1&id_layered_filter='.(int)$filters_template['id_layered_filter'].'"
onclick="return confirm(\''.addslashes(sprintf($this->l('Delete filter template #%d?'), (int)$filters_template['id_layered_filter'])).'\');">
<i class="icon-trash"></i> '.$this->l('Delete').'
</a>
</li>
</ul>
</div>
</div>
</td>
</tr>';
}
$html .= '
</table>';
}
else
$html .= $this->l('No filter template found.');
$html .= '
</div>
<div class="panel">
<h3><i class="icon-cogs"></i> '.$this->l('Build your own filter template').'</h3>
<form action="'.Tools::safeOutput($_SERVER['REQUEST_URI']).'" method="post" onsubmit="return checkForm();">';
$html .= '
<h4>'.$this->l('Step 1/3 - Select categories').'</h4>
<hr />
<div class="form-horizontal">
<div class="form-group">
<label class="control-label col-lg-3">'.$this->l('Use this template for:').'</label>
<div class="col-lg-9">
<p class="radio">
<input type="radio" id="scope_1" name="scope" value="1" onclick="$(\'#error-treeview\').hide(); $(\'#layered-step-2\').show(); updElements(1, 0);" />
<label for="scope_1">'.$this->l('All categories').'</label>
</p>
<p class="radio">
<input type="radio" id="scope_2" name="scope" value="2" class="layered-category-selection" onclick="$(\'label a#inline\').click(); $(\'#layered-step-2\').show();" />
<label for="scope_2"><a id="inline" href="#layered-categories-selection">'.sprintf($this->l('Specific categories (%s selected)'), '<span id="layered-cat-counter"></span>').'</a></label>
</p>
</div>
</div>';
$shops = Shop::getShops(true, null, true);
if (count($shops) > 1)
{
$helper = new HelperForm();
$helper->id = null;
$helper->table = 'layered_filter';
$helper->identifier = 'id_layered_filter';
if (Shop::isFeatureActive())
{
$html .= '<div class="form-group">
<label class="control-label col-lg-3">'.$this->l('Choose shop association:').'</label>
<div class="col-lg-9">'.$helper->renderAssoShop().'</div>
</div>';
}
}
$html .= '</div>
<div id="error-treeview" class="alert alert-danger" style="display: none;">
'.$this->l('Please select at least one specific category or select "All categories".').'
</div>
<div style="display: none;">
<div id="layered-categories-selection" style="padding: 10px; text-align: left;">
<h2>'.$this->l('Categories using this template').'</h2>
<ol style="padding-left: 20px;">
<li>'.$this->l('Select one ore more category using this filter template').'</li>
<li>'.$this->l('Press "Save this selection" or close the window to save').'</li>
</ol>';
$selected_cat = array();
// Translations are not automatic for the moment ;)
if (Shop::getContext() == Shop::CONTEXT_SHOP)
{
$root_category = Category::getRootCategory();
$root_category = array('id_category' => $root_category->id_category, 'name' => $root_category->name);
}
else
$root_category = array('id_category' => '0', 'name' => $this->l('Root'));
$helper = new Helper();
$html .= $helper->renderCategoryTree(null, $selected_cat, 'categoryBox', false, false, array(), true);
$html .= '
<br />
<button type="button" class="btn btn-default" onclick="$.fancybox.close();">'.$this->l('Save this selection').'</button>
</div>
</div>
<div id="layered-step-2" class="row" style="display:none">
<h4>'.$this->l('Step 2/3 - Select filters').'</h4>
<hr />
<div id="layered_container">
<div id="layered_container_left" class="col-lg-6">
<h4><strong>'.$this->l('Selected filters').' <span id="num_sel_filters">(0)</span></strong></h4>
<p id="no-filters">'.$this->l('No filters selected yet.').'</p>
<ul id="selected_filters" style="height: 200px; overflow-y: auto;"></ul>
</div>
<div id="layered-ajax-refresh" class="col-lg-6">
'.$this->ajaxCallBackOffice().'
</div>
</div>';
$this->context->controller->addJQueryPlugin('fancybox');
$this->context->controller->addJQueryUI('ui.sortable');
$this->context->controller->addJQueryUI('ui.draggable');
$this->context->controller->addJQueryUI('effects.transfer');
$id_lang = Context::getContext()->cookie->id_lang;
$html .= '
<script type="text/javascript">
function updLayCounters(showAlert)
{
$(\'#num_sel_filters\').html(\'(\'+$(\'ul#selected_filters\').find(\'li\').length+\')\');
$(\'#num_avail_filters\').html(\'(\'+$(\'#layered_container_right ul\').find(\'li\').length+\')\');
if ($(\'ul#selected_filters\').find(\'li\').length >= 1)
{
$(\'#layered-step-3\').show();
$(\'#layered-step-3 .alert\').hide();
}
else
{
if (showAlert)
$(\'#layered-step-3\').show();
else
$(\'#layered-step-3\').hide();
$(\'#layered-step-3 .alert\').show();
}
}
function updPositions()
{
$(\'#layered_container_left li\').each(function(idx) {
$(this).find(\'span.position\').html(parseInt(1+idx)+\'. \');
});
}
function updCatCounter()
{
$(\'#layered-cat-counter\').html($(\'#categories-treeview\').find(\'input:checked\').length);
$(\'#layered-cat-counter\').show();
}
function updHeight()
{
$(\'#layered_container_left\').css(\'height\', 30+(1+$(\'#layered_container_left\').find(\'li\').length)*34);
$(\'#layered_container_right\').css(\'height\', 30+(1+$(\'#layered_container_right\').find(\'li\').length)*34);
}
function updElements(all, id_layered_filter)
{
if ($(\'#error-treeview\').is(\':hidden\'))
$(\'#layered-step-2\').show();
else
$(\'#layered-step-2\').hide();
$(\'#layered-ajax-refresh\').css(\'background-color\', \'black\');
$(\'#layered-ajax-refresh\').css(\'opacity\', \'0.2\');
$(\'#layered-ajax-refresh\').html(\'<div style="margin: 0 auto; padding: 10px; text-align: center;">\'
+\'<img src="../img/admin/ajax-loader-big.gif" alt="" /><br /><p style="color: white;">'.addslashes($this->l('Loading...')).'</p></div>\');
$.ajax(
{
type: \'POST\',
url: \''.__PS_BASE_URI__.'\' + \'modules/blocklayered/blocklayered-ajax-back.php?admin_dir='.base64_encode(_PS_ADMIN_DIR_).'\',
data: \'layered_token='.substr(Tools::encrypt('blocklayered/index'), 0, 10).'&id_lang='.$id_lang.'&\'
+(all ? \'\' : $(\'input[name="categoryBox[]"]\').serialize()+\'&\')
+(id_layered_filter ? \'id_layered_filter=\'+parseInt(id_layered_filter) : \'\')
+\'&base_folder='.urlencode(_PS_ADMIN_DIR_).'\',
success: function(result)
{
$(\'#layered-ajax-refresh\').css(\'background-color\', \'transparent\');
$(\'#layered-ajax-refresh\').css(\'opacity\', \'1\');
$(\'#layered-ajax-refresh\').html(result);
$(\'#layered_container_right li input\').each(function() {
if ($(\'#layered_container_left\').find(\'input[id="\'+$(this).attr(\'id\')+\'"]\').length > 0)
$(this).parent().remove();
});
updHeight();
updLayCounters(true);
}
});
return false;
}
function checkForm()
{
if ($(\'#layered_tpl_name\').val() == \'\')
{
$(\'#error-filter-name\').show();
return false;
}
else if ($(\'#scope_1\').attr(\'checked\') && $(\'#n_existing\').val() > 0)
if (!confirm(\''.addslashes($this->l('You selected -All categories-, all existing filter templates will be deleted, OK?')).'\'))
return false;
return true;
}
function launch()
{
$(\'#layered_container input\').live(\'click\', function ()
{
if ($(this).parent().hasClass(\'layered_right\'))
{
$(\'p#no-filters\').hide();
$(this).parent().css(\'background\', \'url("../img/jquery-ui/ui-bg_glass_100_fdf5ce_1x400.png") repeat-x scroll 50% 50% #FDF5CE\');
$(this).parent().removeClass(\'layered_right\');
$(this).parent().addClass(\'layered_left\');
$(this).effect(\'transfer\', { to: $(\'#layered_container_left ul#selected_filters\') }, 300, function() {
$(this).parent().appendTo(\'ul#selected_filters\');
updLayCounters(false);
updHeight();
updPositions();
});
}
else
{
$(this).parent().css(\'background\', \'url("../img/jquery-ui/ui-bg_glass_100_f6f6f6_1x400.png") repeat-x scroll 50% 50% #F6F6F6\');
$(this).effect(\'transfer\', { to: $(\'#layered_container_right ul#all_filters\') }, 300, function() {
$(this).parent().removeClass(\'layered_left\');
$(this).parent().addClass(\'layered_right\');
$(this).parent().appendTo(\'ul#all_filters\');
updLayCounters(true);
updHeight();
updPositions();
if ($(\'#layered_container_left ul\').length == 0)
$(\'p#no-filters\').show();
});
}
enableSortable();
});
$(\'label a#inline\').fancybox({
\'hideOnContentClick\': false,
\'beforeClose\': function() {
lock_treeview_hidding = false;
$(\'#categories-treeview\').parent().parent().hide();
updCatCounter();
if ($(\'#categories-treeview\').find(\'input:checked\').length == 0)
$(\'#error-treeview\').show();
else
$(\'#error-treeview\').hide();
updElements(0, 0);
},
\'afterLoad\': function() {
lock_treeview_hidding = true;
$(\'#categories-treeview\').parent().parent().show();
if($($(\'#categories-treeview li\')[0]).attr(\'cleaned\'))
return;
if($($(\'#categories-treeview li\')[0]).attr(\'cleaned\', true))
$($(\'#categories-treeview li\')[0]).removeClass(\'static\');
$($(\'#categories-treeview li span\')[0]).trigger(\'click\');
$($(\'#categories-treeview li\')[0]).children(\'div\').remove();
$($(\'#categories-treeview li\')[0]).removeClass(\'collapsable lastCollapsable\');
$(\'.hitarea\').live(\'click\', function(it)
{
$(this).parent().find(\'> .category_label\').click();
});
}
});
updHeight();
updLayCounters(false);
updPositions();
updCatCounter();
enableSortable();
}
function enableSortable()
{
$(function() {
$(\'ul#selected_filters\').sortable({
axis: \'y\',
update: function() { updPositions(); },
placeholder: \'ui-state-highlight\'
});
$(\'ul#selected_filters\').disableSelection();
});
}
$(document).ready(function() {
launch();
});
</script>
</div>
<div class="clearfix">&nbsp;</div>
<div id="layered-step-3" class="row">
<div id="error-filter-name" class="alert alert-danger">'.$this->l('Filter template name required (cannot be empty)').'</div>
<h4>'.$this->l('Step 3/3 - Name your template').'</h4>
<hr />
<p>'.$this->l('Template name:').' <input type="text" id="layered_tpl_name" onkeyup="if ($(this).val() != \'\')
{ $(\'#error-filter-name\').hide(); } else { $(\'#error-filter-name\').show(); }" name="layered_tpl_name" maxlength="64" value="'.sprintf($this->l('My template %s'), date('Y-m-d')).'"
style="width: 200px; font-size: 11px;" /> <span style="font-size: 10px; font-style: italic;">('.$this->l('only as a reminder').')</span></p>
<div class="alert alert-warning">'.$this->l('No filters selected, the blocklayered will be disable for the categories seleted.').'</div>
<button type="submit" class="btn btn-default" name="SubmitFilter"><i class="icon-save"></i> '.$this->l('Save this filter template').'</button>
</div>
<input type="hidden" name="id_layered_filter" id="id_layered_filter" value="0" />
<input type="hidden" name="n_existing" id="n_existing" value="'.(int)count($filters_templates).'" />
</form>
</div>
<div class="panel">
<h3><i class="icon-cogs"></i> '.$this->l('Configuration').'</h3>
<form action="'.Tools::safeOutput($_SERVER['REQUEST_URI']).'" method="post" class="form-horizontal">
<div class="form-group">
<label class="col-lg-3 control-label">'.$this->l('Hide filter values with no product is matching').'</label>
<div class="col-lg-9">
<div class="row">
<div class="input-group col-lg-2">
<span class="switch prestashop-switch">
<input type="radio" name="ps_layered_hide_0_values" id="ps_layered_hide_0_values_on" value="1"'.(Configuration::get('PS_LAYERED_HIDE_0_VALUES') ? ' checked="checked"' : '').'>
<label for="ps_layered_hide_0_values_on" class="radioCheck">
<i class="icon-check-sign color_success"></i> '.$this->l('Yes').'
</label>
<input type="radio" name="ps_layered_hide_0_values" id="ps_layered_hide_0_values_off" value="0"'.(!Configuration::get('PS_LAYERED_HIDE_0_VALUES') ? ' checked="checked"' : '').'>
<label for="ps_layered_hide_0_values_off" class="radioCheck">
<i class="icon-ban-circle color_danger"></i> '.$this->l('No').'
</label>
<a class="slide-button btn btn-default"></a>
</span>
</div>
</div>
</div>
</div>
<div class="form-group">
<label class="col-lg-3 control-label">'.$this->l('Show the number of matching products').'</label>
<div class="col-lg-9">
<div class="row">
<div class="input-group col-lg-2">
<span class="switch prestashop-switch">
<input type="radio" name="ps_layered_show_qties" id="ps_layered_show_qties_on" value="1"'.(Configuration::get('PS_LAYERED_SHOW_QTIES') ? ' checked="checked"' : '').'>
<label for="ps_layered_show_qties_on" class="radioCheck">
<i class="icon-check-sign color_success"></i> '.$this->l('Yes').'
</label>
<input type="radio" name="ps_layered_show_qties" id="ps_layered_show_qties_off" value="0"'.(!Configuration::get('PS_LAYERED_SHOW_QTIES') ? ' checked="checked"' : '').'>
<label for="ps_layered_show_qties_off" class="radioCheck">
<i class="icon-ban-circle color_danger"></i> '.$this->l('No').'
</label>
<a class="slide-button btn btn-default"></a>
</span>
</div>
</div>
</div>
</div>
<div class="form-group">
<label class="col-lg-3 control-label">'.$this->l('Show products from subcategories').'</label>
<div class="col-lg-9">
<div class="row">
<div class="input-group col-lg-2">
<span class="switch prestashop-switch">
<input type="radio" name="ps_layered_full_tree" id="ps_layered_full_tree_on" value="1"'.(Configuration::get('PS_LAYERED_FULL_TREE') ? ' checked="checked"' : '').'>
<label for="ps_layered_hide_0_values_on" class="radioCheck">
<i class="icon-check-sign color_success"></i> '.$this->l('Yes').'
</label>
<input type="radio" name="ps_layered_full_tree" id="ps_layered_full_tree_off" value="0"'.(!Configuration::get('PS_LAYERED_FULL_TREE') ? ' checked="checked"' : '').'>
<label for="ps_layered_full_tree_off" class="radioCheck">
<i class="icon-ban-circle color_danger"></i> '.$this->l('No').'
</label>
<a class="slide-button btn btn-default"></a>
</span>
</div>
</div>
</div>
</div>
<div class="form-group">
<label class="col-lg-3 control-label">'.$this->l('Category filter depth (0 for no limits, 1 by default)').'</label>
<div class="col-lg-9">
<input type="text" name="ps_layered_filter_category_depth" value="'.((Configuration::get('PS_LAYERED_FILTER_CATEGORY_DEPTH') !== false) ? Configuration::get('PS_LAYERED_FILTER_CATEGORY_DEPTH') : 1).'" />
</div>
</div>
<div class="form-group">
<label class="col-lg-3 control-label">'.$this->l('Use tax to filter price').'</label>
<div class="col-lg-9">
<div class="row">
<div class="input-group col-lg-2">
<span class="switch prestashop-switch">
<input type="radio" name="ps_layered_filter_price_usetax" id="ps_layered_filter_price_usetax_on" value="1"'.(Configuration::get('PS_LAYERED_FILTER_PRICE_USETAX') ? ' checked="checked"' : '').'>
<label for="ps_layered_filter_price_usetax_on" class="radioCheck">
<i class="icon-check-sign color_success"></i> '.$this->l('Yes').'
</label>
<input type="radio" name="ps_layered_filter_price_usetax" id="ps_layered_filter_price_usetax_off" value="0"'.(!Configuration::get('PS_LAYERED_FILTER_PRICE_USETAX') ? ' checked="checked"' : '').'>
<label for="pps_layered_filter_price_usetax_off" class="radioCheck">
<i class="icon-ban-circle color_danger"></i> '.$this->l('No').'
</label>
<a class="slide-button btn btn-default"></a>
</span>
</div>
</div>
</div>
</div>
<div class="form-group">
<label class="col-lg-3 control-label">'.$this->l('Allow indexing robots (google, yahoo, bing, ...) to use condition filter').'</label>
<div class="col-lg-9">
<div class="row">
<div class="input-group col-lg-2">
<span class="switch prestashop-switch">
<input type="radio" name="ps_layered_filter_index_condition" id="ps_layered_filter_index_condition_on" value="1"'.(Configuration::get('PS_LAYERED_FILTER_INDEX_CDT') ? ' checked="checked"' : '').'>
<label for="ps_layered_filter_index_condition_on" class="radioCheck">
<i class="icon-check-sign color_success"></i> '.$this->l('Yes').'
</label>
<input type="radio" name="ps_layered_filter_index_condition" id="ps_layered_filter_index_condition_off" value="0"'.(!Configuration::get('PS_LAYERED_FILTER_INDEX_CDT') ? ' checked="checked"' : '').'>
<label for="ps_layered_filter_index_condition_off" class="radioCheck">
<i class="icon-ban-circle color_danger"></i> '.$this->l('No').'
</label>
<a class="slide-button btn btn-default"></a>
</span>
</div>
</div>
</div>
</div>
<div class="form-group">
<label class="col-lg-3 control-label">'.$this->l('Allow indexing robots (google, yahoo, bing, ...) to use availability filter').'</label>
<div class="col-lg-9">
<div class="row">
<div class="input-group col-lg-2">
<span class="switch prestashop-switch">
<input type="radio" name="ps_layered_filter_index_availability" id="ps_layered_filter_index_availability_on" value="1"'.(Configuration::get('PS_LAYERED_FILTER_INDEX_QTY') ? ' checked="checked"' : '').'>
<label for="ps_layered_filter_index_availability_on" class="radioCheck">
<i class="icon-check-sign color_success"></i> '.$this->l('Yes').'
</label>
<input type="radio" name="ps_layered_filter_index_availability" id="ps_layered_filter_index_availability_off" value="0"'.(!Configuration::get('PS_LAYERED_FILTER_INDEX_QTY') ? ' checked="checked"' : '').'>
<label for="ps_layered_filter_index_availability_off" class="radioCheck">
<i class="icon-ban-circle color_danger"></i> '.$this->l('No').'
</label>
<a class="slide-button btn btn-default"></a>
</span>
</div>
</div>
</div>
</div>
<div class="form-group">
<label class="col-lg-3 control-label">'.$this->l('Allow indexing robots (google, yahoo, bing, ...) to use manufacturer filter').'</label>
<div class="col-lg-9">
<div class="row">
<div class="input-group col-lg-2">
<span class="switch prestashop-switch">
<input type="radio" name="ps_layered_filter_index_manufacturer" id="ps_layered_filter_index_manufacturer_on" value="1"'.(Configuration::get('PS_LAYERED_FILTER_INDEX_MNF') ? ' checked="checked"' : '').'>
<label for="ps_layered_filter_index_manufacturer_on" class="radioCheck">
<i class="icon-check-sign color_success"></i> '.$this->l('Yes').'
</label>
<input type="radio" name="ps_layered_filter_index_manufacturer" id="ps_layered_filter_index_manufacturer_off" value="0"'.(!Configuration::get('PS_LAYERED_FILTER_INDEX_MNF') ? ' checked="checked"' : '').'>
<label for="ps_layered_filter_index_manufacturer_off" class="radioCheck">
<i class="icon-ban-circle color_danger"></i> '.$this->l('No').'
</label>
<a class="slide-button btn btn-default"></a>
</span>
</div>
</div>
</div>
</div>
<div class="form-group">
<label class="col-lg-3 control-label">'.$this->l('Allow indexing robots (google, yahoo, bing, ...) to use category filter').'</label>
<div class="col-lg-9">
<div class="row">
<div class="input-group col-lg-2">
<span class="switch prestashop-switch">
<input type="radio" name="ps_layered_filter_index_category" id="ps_layered_filter_index_category_on" value="1"'.(Configuration::get('PS_LAYERED_FILTER_INDEX_CAT') ? ' checked="checked"' : '').'>
<label for="ps_layered_filter_index_category_on" class="radioCheck">
<i class="icon-check-sign color_success"></i> '.$this->l('Yes').'
</label>
<input type="radio" name="ps_layered_filter_index_category" id="ps_layered_filter_index_category_off" value="0"'.(!Configuration::get('PS_LAYERED_FILTER_INDEX_CAT') ? ' checked="checked"' : '').'>
<label for="ps_layered_filter_index_category_off" class="radioCheck">
<i class="icon-ban-circle color_danger"></i> '.$this->l('No').'
</label>
<a class="slide-button btn btn-default"></a>
</span>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-lg-9 col-lg-offset-3">
<button type="submit" class="btn btn-default" name="submitLayeredSettings"><i class="icon-save"></i> '.$this->l('Save configuration').'</button>
</div>
</div>
</form>
</div>';
return $html;
}
private function getSelectedFilters()
{
$id_parent = (int)Tools::getValue('id_category', Tools::getValue('id_category_layered', 1));
if ($id_parent == 1)
return;
// Force attributes selection (by url '.../2-mycategory/color-blue' or by get parameter 'selected_filters')
if (strpos($_SERVER['SCRIPT_FILENAME'], 'blocklayered-ajax.php') === false || Tools::getValue('selected_filters') !== false)
{
if (Tools::getValue('selected_filters'))
$url = Tools::getValue('selected_filters');
else
$url = preg_replace('/\/(?:\w*)\/(?:[0-9]+[-\w]*)([^\?]*)\??.*/', '$1', Tools::safeOutput($_SERVER['REQUEST_URI'], true));
$url_attributes = explode('/', ltrim($url, '/'));
$selected_filters = array('category' => array());
if (!empty($url_attributes))
{
foreach ($url_attributes as $url_attribute)
{
$url_parameters = explode(Configuration::get('PS_ATTRIBUTE_ANCHOR_SEPARATOR'), $url_attribute);
$attribute_name = array_shift($url_parameters);
if ($attribute_name == 'page')
$this->page = (int)$url_parameters[0];
else if (in_array($attribute_name, array('price', 'weight')))
$selected_filters[$attribute_name] = array($url_parameters[0], $url_parameters[1]);
else
{
foreach ($url_parameters as $url_parameter)
{
$data = Db::getInstance()->getValue('SELECT data FROM `'._DB_PREFIX_.'layered_friendly_url` WHERE `url_key` = \''.md5('/'.$attribute_name.Configuration::get('PS_ATTRIBUTE_ANCHOR_SEPARATOR').$url_parameter).'\'');
if ($data)
foreach (Tools::unSerialize($data) as $key_params => $params)
{
if (!isset($selected_filters[$key_params]))
$selected_filters[$key_params] = array();
foreach ($params as $key_param => $param)
{
if (!isset($selected_filters[$key_params][$key_param]))
$selected_filters[$key_params][$key_param] = array();
$selected_filters[$key_params][$key_param] = $param;
}
}
}
}
}
return $selected_filters;
}
}
/* Analyze all the filters selected by the user and store them into a tab */
$selected_filters = array('category' => array(), 'manufacturer' => array(), 'quantity' => array(), 'condition' => array());
foreach ($_GET as $key => $value)
if (substr($key, 0, 8) == 'layered_')
{
preg_match('/^(.*)_([0-9]+|new|used|refurbished|slider)$/', substr($key, 8, strlen($key) - 8), $res);
if (isset($res[1]))
{
$tmp_tab = explode('_', $value);
$value = $tmp_tab[0];
$id_key = false;
if (isset($tmp_tab[1]))
$id_key = $tmp_tab[1];
if ($res[1] == 'condition' && in_array($value, array('new', 'used', 'refurbished')))
$selected_filters['condition'][] = $value;
else if ($res[1] == 'quantity' && (!$value || $value == 1))
$selected_filters['quantity'][] = $value;
else if (in_array($res[1], array('category', 'manufacturer')))
{
if (!isset($selected_filters[$res[1].($id_key ? '_'.$id_key : '')]))
$selected_filters[$res[1].($id_key ? '_'.$id_key : '')] = array();
$selected_filters[$res[1].($id_key ? '_'.$id_key : '')][] = (int)$value;
}
else if (in_array($res[1], array('id_attribute_group', 'id_feature')))
{
if (!isset($selected_filters[$res[1]]))
$selected_filters[$res[1]] = array();
$selected_filters[$res[1]][(int)$value] = $id_key.'_'.(int)$value;
}
else if ($res[1] == 'weight')
$selected_filters[$res[1]] = $tmp_tab;
else if ($res[1] == 'price')
$selected_filters[$res[1]] = $tmp_tab;
}
}
return $selected_filters;
}
public function getProductByFilters($selected_filters = array())
{
global $cookie;
if (!empty($this->products))
return $this->products;
/* If the current category isn't defined or if it's homepage, we have nothing to display */
$id_parent = (int)Tools::getValue('id_category', Tools::getValue('id_category_layered', 1));
if ($id_parent == 1)
return false;
$alias_where = 'p';
if (version_compare(_PS_VERSION_,'1.5','>'))
$alias_where = 'product_shop';
$query_filters_where = ' AND '.$alias_where.'.`active` = 1 AND '.$alias_where.'.`visibility` IN ("both", "catalog")';
$query_filters_from = '';
$parent = new Category((int)$id_parent);
if (!count($selected_filters['category']))
{
if (Configuration::get('PS_LAYERED_FULL_TREE'))
$query_filters_from .= ' INNER JOIN '._DB_PREFIX_.'category_product cp
ON p.id_product = cp.id_product
INNER JOIN '._DB_PREFIX_.'category c ON (c.id_category = cp.id_category AND
'.(Configuration::get('PS_LAYERED_FULL_TREE') ? 'c.nleft >= '.(int)$parent->nleft.'
AND c.nright <= '.(int)$parent->nright : 'c.id_category = '.(int)$id_parent).'
AND c.active = 1)';
else
$query_filters_from .= ' INNER JOIN '._DB_PREFIX_.'category_product cp
ON p.id_product = cp.id_product
INNER JOIN '._DB_PREFIX_.'category c ON (c.id_category = cp.id_category
AND c.id_category = '.(int)$id_parent.'
AND c.active = 1)';
}
foreach ($selected_filters as $key => $filter_values)
{
if (!count($filter_values))
continue;
preg_match('/^(.*[^_0-9])/', $key, $res);
$key = $res[1];
switch ($key)
{
case 'id_feature':
$sub_queries = array();
foreach ($filter_values as $filter_value)
{
$filter_value_array = explode('_', $filter_value);
if (!isset($sub_queries[$filter_value_array[0]]))
$sub_queries[$filter_value_array[0]] = array();
$sub_queries[$filter_value_array[0]][] = 'fp.`id_feature_value` = '.(int)$filter_value_array[1];
}
foreach ($sub_queries as $sub_query)
{
$query_filters_where .= ' AND p.id_product IN (SELECT `id_product` FROM `'._DB_PREFIX_.'feature_product` fp WHERE ';
$query_filters_where .= implode(' OR ', $sub_query).') ';
}
break;
case 'id_attribute_group':
$sub_queries = array();
foreach ($filter_values as $filter_value)
{
$filter_value_array = explode('_', $filter_value);
if (!isset($sub_queries[$filter_value_array[0]]))
$sub_queries[$filter_value_array[0]] = array();
$sub_queries[$filter_value_array[0]][] = 'pac.`id_attribute` = '.(int)$filter_value_array[1];
}
foreach ($sub_queries as $sub_query)
{
$query_filters_where .= ' AND p.id_product IN (SELECT pa.`id_product`
FROM `'._DB_PREFIX_.'product_attribute_combination` pac
LEFT JOIN `'._DB_PREFIX_.'product_attribute` pa
ON (pa.`id_product_attribute` = pac.`id_product_attribute`)'.
Shop::addSqlAssociation('product_attribute', 'pa').'
WHERE '.implode(' OR ', $sub_query).') ';
}
break;
case 'category':
$query_filters_where .= ' AND p.id_product IN (SELECT id_product FROM '._DB_PREFIX_.'category_product cp WHERE ';
foreach ($selected_filters['category'] as $id_category)
$query_filters_where .= 'cp.`id_category` = '.(int)$id_category.' OR ';
$query_filters_where = rtrim($query_filters_where, 'OR ').')';
break;
case 'quantity':
if (count($selected_filters['quantity']) == 2)
break;
$query_filters_where .= ' AND sa.quantity '.(!$selected_filters['quantity'][0] ? '<=' : '>').' 0 ';
$query_filters_from .= 'LEFT JOIN `'._DB_PREFIX_.'stock_available` sa ON (sa.id_product = p.id_product AND sa.id_shop = '.(int)Context::getContext()->shop->id.') ';
break;
case 'manufacturer':
$query_filters_where .= ' AND p.id_manufacturer IN ('.implode($selected_filters['manufacturer'], ',').')';
break;
case 'condition':
if (count($selected_filters['condition']) == 3)
break;
$query_filters_where .= ' AND '.$alias_where.'.condition IN (';
foreach ($selected_filters['condition'] as $cond)
$query_filters_where .= '\''.$cond.'\',';
$query_filters_where = rtrim($query_filters_where, ',').')';
break;
case 'weight':
if ($selected_filters['weight'][0] != 0 || $selected_filters['weight'][1] != 0)
$query_filters_where .= ' AND p.`weight` BETWEEN '.(float)($selected_filters['weight'][0] - 0.001).' AND '.(float)($selected_filters['weight'][1] + 0.001);
case 'price':
if (isset($selected_filters['price']))
{
if ($selected_filters['price'][0] !== '' || $selected_filters['price'][1] !== '')
{
$price_filter = array();
$price_filter['min'] = (float)($selected_filters['price'][0]);
$price_filter['max'] = (float)($selected_filters['price'][1]);
}
}
else
$price_filter = false;
break;
}
}
$id_currency = (int)Context::getContext()->currency->id;
$price_filter_query_in = ''; // All products with price range between price filters limits
$price_filter_query_out = ''; // All products with a price filters limit on it price range
if (isset($price_filter) && $price_filter)
{
$price_filter_query_in = 'INNER JOIN `'._DB_PREFIX_.'layered_price_index` psi
ON
(
psi.price_min >= '.(int)$price_filter['min'].'
AND psi.price_max <= '.(int)$price_filter['max'].'
AND psi.`id_product` = p.`id_product`
AND psi.`id_currency` = '.$id_currency.'
)';
$price_filter_query_out = 'INNER JOIN `'._DB_PREFIX_.'layered_price_index` psi
ON
((psi.price_min < '.(int)$price_filter['min'].' AND psi.price_max > '.(int)$price_filter['min'].')
OR
(psi.price_max > '.(int)$price_filter['max'].' AND psi.price_min < '.(int)$price_filter['max'].'))
AND psi.`id_product` = p.`id_product`
AND psi.`id_currency` = '.$id_currency;
}
$query_filters_from .= Shop::addSqlAssociation('product', 'p');
$all_products_out = self::query('
SELECT p.`id_product` id_product
FROM `'._DB_PREFIX_.'product` p
'.$price_filter_query_out.'
'.$query_filters_from.'
WHERE 1 '.$query_filters_where.' GROUP BY id_product');
$all_products_in = self::query('
SELECT p.`id_product` id_product
FROM `'._DB_PREFIX_.'product` p
'.$price_filter_query_in.'
'.$query_filters_from.'
WHERE 1 '.$query_filters_where.' GROUP BY id_product');
$product_id_list = array();
while ($product = DB::getInstance()->nextRow($all_products_in))
$product_id_list[] = (int)$product['id_product'];
while ($product = DB::getInstance()->nextRow($all_products_out))
if (isset($price_filter) && $price_filter)
{
$price = (int)Product::getPriceStatic($product['id_product'], Configuration::get('PS_LAYERED_FILTER_PRICE_USETAX')); // Cast to int because we don't care about cents
if ($price < $price_filter['min'] || $price > $price_filter['max'])
continue;
$product_id_list[] = (int)$product['id_product'];
}
$this->nbr_products = count($product_id_list);
if ($this->nbr_products == 0)
$this->products = array();
else
{
$n = (int)Tools::getValue('n', Configuration::get('PS_PRODUCTS_PER_PAGE'));
$nb_day_new_product = (Validate::isUnsignedInt(Configuration::get('PS_NB_DAYS_NEW_PRODUCT')) ? Configuration::get('PS_NB_DAYS_NEW_PRODUCT') : 20);
$this->products = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS('
SELECT
p.*,
'.($alias_where == 'p' ? '' : 'product_shop.*,' ).'
'.$alias_where.'.id_category_default,
pl.*,
MAX(image_shop.`id_image`) id_image,
il.legend,
m.name manufacturer_name,
DATEDIFF('.$alias_where.'.`date_add`, DATE_SUB(NOW(), INTERVAL '.(int)$nb_day_new_product.' DAY)) > 0 AS new,
stock.out_of_stock, IFNULL(stock.quantity, 0) as quantity
FROM `'._DB_PREFIX_.'category_product` cp
LEFT JOIN '._DB_PREFIX_.'category c ON (c.id_category = cp.id_category)
LEFT JOIN `'._DB_PREFIX_.'product` p ON p.`id_product` = cp.`id_product`
'.Shop::addSqlAssociation('product', 'p').'
'.Product::sqlStock('p', null, false, Context::getContext()->shop).'
LEFT JOIN '._DB_PREFIX_.'product_lang pl ON (pl.id_product = p.id_product'.Shop::addSqlRestrictionOnLang('pl').' AND pl.id_lang = '.(int)$cookie->id_lang.')
LEFT JOIN `'._DB_PREFIX_.'image` i ON (i.`id_product` = p.`id_product`)'.
Shop::addSqlAssociation('image', 'i', false, 'image_shop.cover=1').'
LEFT JOIN `'._DB_PREFIX_.'image_lang` il ON (image_shop.`id_image` = il.`id_image` AND il.`id_lang` = '.(int)$cookie->id_lang.')
LEFT JOIN '._DB_PREFIX_.'manufacturer m ON (m.id_manufacturer = p.id_manufacturer)
WHERE '.$alias_where.'.`active` = 1 AND '.$alias_where.'.`visibility` IN ("both", "catalog")
AND '.(Configuration::get('PS_LAYERED_FULL_TREE') ? 'c.nleft >= '.(int)$parent->nleft.' AND c.nright <= '.(int)$parent->nright : 'c.id_category = '.(int)$id_parent).'
AND c.active = 1
AND p.id_product IN ('.implode(',', $product_id_list).')
GROUP BY product_shop.id_product
ORDER BY '.Tools::getProductsOrder('by', Tools::getValue('orderby'), true).' '.Tools::getProductsOrder('way', Tools::getValue('orderway')).
' LIMIT '.(((int)$this->page - 1) * $n.','.$n));
}
if (Tools::getProductsOrder('by', Tools::getValue('orderby'), true) == 'p.price')
Tools::orderbyPrice($this->products, Tools::getProductsOrder('way', Tools::getValue('orderway')));
return $this->products;
}
private static function query($sql_query)
{
return Db::getInstance(_PS_USE_SQL_SLAVE_)->query($sql_query);
}
public function getFilterBlock($selected_filters = array())
{
global $cookie;
static $cache = null;
$id_lang = Context::getContext()->language->id;
$currency = Context::getContext()->currency;
$id_shop = (int) Context::getContext()->shop->id;
$alias = 'product_shop';
if (is_array($cache))
return $cache;
$id_parent = (int)Tools::getValue('id_category', Tools::getValue('id_category_layered', 1));
if ($id_parent == 1)
return;
$parent = new Category((int)$id_parent, $id_lang);
/* Get the filters for the current category */
$filters = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS('SELECT * FROM '._DB_PREFIX_.'layered_category
WHERE id_category = '.(int)$id_parent.'
AND id_shop = '.$id_shop.'
GROUP BY `type`, id_value ORDER BY position ASC');
// Remove all empty selected filters
foreach ($selected_filters as $key => $value)
switch ($key)
{
case 'price':
case 'weight':
if ($value[0] === '' && $value[1] === '')
unset($selected_filters[$key]);
break;
default:
if ($value == '')
unset($selected_filters[$key]);
break;
}
$filter_blocks = array();
foreach ($filters as $filter)
{
$sql_query = array('select' => '', 'from' => '', 'join' => '', 'where' => '', 'group' => '', 'second_query' => '');
switch ($filter['type'])
{
// conditions + quantities + weight + price
case 'price':
case 'weight':
case 'condition':
case 'quantity':
$sql_query['select'] = 'SELECT p.`id_product`, product_shop.`condition`, p.`id_manufacturer`, sa.`quantity`, p.`weight` ';
$sql_query['from'] = '
FROM '._DB_PREFIX_.'product p ';
$sql_query['join'] = '
INNER JOIN '._DB_PREFIX_.'category_product cp ON (cp.id_product = p.id_product)
INNER JOIN '._DB_PREFIX_.'category c ON (c.id_category = cp.id_category AND
'.(Configuration::get('PS_LAYERED_FULL_TREE') ? 'c.nleft >= '.(int)$parent->nleft.'
AND c.nright <= '.(int)$parent->nright : 'c.id_category = '.(int)$id_parent).'
AND c.active = 1) ';
$sql_query['join'] .= 'LEFT JOIN `'._DB_PREFIX_.'stock_available` sa
ON (sa.id_product = p.id_product AND sa.id_shop = '.(int)$this->context->shop->id.') ';
$sql_query['where'] = 'WHERE product_shop.`active` = 1 AND product_shop.`visibility` IN ("both", "catalog") ';
$sql_query['group'] = ' GROUP BY p.id_product ';
break;
case 'manufacturer':
$sql_query['select'] = 'SELECT m.name, COUNT(DISTINCT p.id_product) nbr, m.id_manufacturer ';
$sql_query['from'] = '
FROM `'._DB_PREFIX_.'category_product` cp
INNER JOIN `'._DB_PREFIX_.'category` c ON (c.id_category = cp.id_category)
INNER JOIN '._DB_PREFIX_.'product p ON (p.id_product = cp.id_product)
INNER JOIN '._DB_PREFIX_.'manufacturer m ON (m.id_manufacturer = p.id_manufacturer) ';
$sql_query['where'] = 'WHERE
'.(Configuration::get('PS_LAYERED_FULL_TREE') ? 'c.nleft >= '.(int)$parent->nleft.'
AND c.nright <= '.(int)$parent->nright : 'c.id_category = '.(int)$id_parent).'
AND c.active = 1
AND '.$alias.'.active = 1 AND '.$alias.'.`visibility` IN ("both", "catalog")';
$sql_query['group'] = ' GROUP BY p.id_manufacturer ORDER BY m.name';
if (!Configuration::get('PS_LAYERED_HIDE_0_VALUES'))
{
$sql_query['second_query'] = '
SELECT m.name, 0 nbr, m.id_manufacturer
FROM `'._DB_PREFIX_.'category_product` cp'.
Shop::addSqlAssociation('product', 'cp').'
INNER JOIN `'._DB_PREFIX_.'category` c ON (c.id_category = cp.id_category)
INNER JOIN '._DB_PREFIX_.'product p ON (p.id_product = cp.id_product)
INNER JOIN '._DB_PREFIX_.'manufacturer m ON (m.id_manufacturer = p.id_manufacturer)
WHERE '.(Configuration::get('PS_LAYERED_FULL_TREE') ? 'c.nleft >= '.(int)$parent->nleft.'
AND c.nright <= '.(int)$parent->nright : 'c.id_category = '.(int)$id_parent).'
AND c.active = 1
AND '.$alias.'.active = 1 AND '.$alias.'.`visibility` IN ("both", "catalog")
GROUP BY p.id_manufacturer ORDER BY m.name';
}
break;
case 'id_attribute_group':// attribute group
$sql_query['select'] = '
SELECT COUNT(DISTINCT p.id_product) nbr, lpa.id_attribute_group,
a.color, al.name attribute_name, agl.public_name attribute_group_name , lpa.id_attribute, ag.is_color_group,
liagl.url_name name_url_name, liagl.meta_title name_meta_title, lial.url_name value_url_name, lial.meta_title value_meta_title';
$sql_query['from'] = '
FROM '._DB_PREFIX_.'layered_product_attribute lpa
INNER JOIN '._DB_PREFIX_.'attribute a
ON a.id_attribute = lpa.id_attribute
INNER JOIN '._DB_PREFIX_.'attribute_lang al
ON al.id_attribute = a.id_attribute
AND al.id_lang = '.(int)$id_lang.'
INNER JOIN '._DB_PREFIX_.'product as p
ON p.id_product = lpa.id_product
INNER JOIN '._DB_PREFIX_.'attribute_group ag
ON ag.id_attribute_group = lpa.id_attribute_group
INNER JOIN '._DB_PREFIX_.'attribute_group_lang agl
ON agl.id_attribute_group = lpa.id_attribute_group
AND agl.id_lang = '.(int)$id_lang.'
LEFT JOIN '._DB_PREFIX_.'layered_indexable_attribute_group_lang_value liagl
ON (liagl.id_attribute_group = lpa.id_attribute_group AND liagl.id_lang = '.(int)$id_lang.')
LEFT JOIN '._DB_PREFIX_.'layered_indexable_attribute_lang_value lial
ON (lial.id_attribute = lpa.id_attribute AND lial.id_lang = '.(int)$id_lang.') ';
$sql_query['where'] = 'WHERE a.id_attribute_group = '.(int)$filter['id_value'];
$sql_query['where'] .= ' AND lpa.`id_shop` = '.(int)Context::getContext()->shop->id;
$sql_query['where'] .= ' AND '.$alias.'.active = 1 AND '.$alias.'.`visibility` IN ("both", "catalog")
AND p.id_product IN (
SELECT id_product
FROM '._DB_PREFIX_.'category_product cp
INNER JOIN '._DB_PREFIX_.'category c ON (c.id_category = cp.id_category AND
'.(Configuration::get('PS_LAYERED_FULL_TREE') ? 'c.nleft >= '.(int)$parent->nleft.'
AND c.nright <= '.(int)$parent->nright : 'c.id_category = '.(int)$id_parent).'
AND c.active = 1)
) ';
$sql_query['group'] = '
GROUP BY lpa.id_attribute
ORDER BY ag.`position` ASC, a.`position` ASC';
if (!Configuration::get('PS_LAYERED_HIDE_0_VALUES'))
{
$sql_query['second_query'] = '
SELECT 0 nbr, lpa.id_attribute_group,
a.color, al.name attribute_name, agl.public_name attribute_group_name , lpa.id_attribute, ag.is_color_group,
liagl.url_name name_url_name, liagl.meta_title name_meta_title, lial.url_name value_url_name, lial.meta_title value_meta_title
FROM '._DB_PREFIX_.'layered_product_attribute lpa'.
Shop::addSqlAssociation('product', 'lpa').'
INNER JOIN '._DB_PREFIX_.'attribute a
ON a.id_attribute = lpa.id_attribute
INNER JOIN '._DB_PREFIX_.'attribute_lang al
ON al.id_attribute = a.id_attribute AND al.id_lang = '.(int)$id_lang.'
INNER JOIN '._DB_PREFIX_.'product as p
ON p.id_product = lpa.id_product
INNER JOIN '._DB_PREFIX_.'attribute_group ag
ON ag.id_attribute_group = lpa.id_attribute_group
INNER JOIN '._DB_PREFIX_.'attribute_group_lang agl
ON agl.id_attribute_group = lpa.id_attribute_group
AND agl.id_lang = '.(int)$id_lang.'
LEFT JOIN '._DB_PREFIX_.'layered_indexable_attribute_group_lang_value liagl
ON (liagl.id_attribute_group = lpa.id_attribute_group AND liagl.id_lang = '.(int)$id_lang.')
LEFT JOIN '._DB_PREFIX_.'layered_indexable_attribute_lang_value lial
ON (lial.id_attribute = lpa.id_attribute AND lial.id_lang = '.(int)$id_lang.')
WHERE '.$alias.'.active = 1 AND '.$alias.'.`visibility` IN ("both", "catalog")
AND a.id_attribute_group = '.(int)$filter['id_value'].'
AND lpa.`id_shop` = '.(int)Context::getContext()->shop->id.'
GROUP BY lpa.id_attribute
ORDER BY id_attribute_group, id_attribute';
}
break;
case 'id_feature':
$sql_query['select'] = 'SELECT fl.name feature_name, fp.id_feature, fv.id_feature_value, fvl.value,
COUNT(DISTINCT p.id_product) nbr,
lifl.url_name name_url_name, lifl.meta_title name_meta_title, lifvl.url_name value_url_name, lifvl.meta_title value_meta_title ';
$sql_query['from'] = '
FROM '._DB_PREFIX_.'feature_product fp
INNER JOIN '._DB_PREFIX_.'product p ON (p.id_product = fp.id_product)
LEFT JOIN '._DB_PREFIX_.'feature_lang fl ON (fl.id_feature = fp.id_feature AND fl.id_lang = '.$id_lang.')
INNER JOIN '._DB_PREFIX_.'feature_value fv ON (fv.id_feature_value = fp.id_feature_value AND (fv.custom IS NULL OR fv.custom = 0))
LEFT JOIN '._DB_PREFIX_.'feature_value_lang fvl ON (fvl.id_feature_value = fp.id_feature_value AND fvl.id_lang = '.$id_lang.')
LEFT JOIN '._DB_PREFIX_.'layered_indexable_feature_lang_value lifl
ON (lifl.id_feature = fp.id_feature AND lifl.id_lang = '.$id_lang.')
LEFT JOIN '._DB_PREFIX_.'layered_indexable_feature_value_lang_value lifvl
ON (lifvl.id_feature_value = fp.id_feature_value AND lifvl.id_lang = '.$id_lang.') ';
$sql_query['where'] = 'WHERE '.$alias.'.`active` = 1 AND '.$alias.'.`visibility` IN ("both", "catalog")
AND fp.id_feature = '.(int)$filter['id_value'].'
AND p.id_product IN (
SELECT id_product
FROM '._DB_PREFIX_.'category_product cp
INNER JOIN '._DB_PREFIX_.'category c ON (c.id_category = cp.id_category AND
'.(Configuration::get('PS_LAYERED_FULL_TREE') ? 'c.nleft >= '.(int)$parent->nleft.'
AND c.nright <= '.(int)$parent->nright : 'c.id_category = '.(int)$id_parent).'
AND c.active = 1)) ';
$sql_query['group'] = 'GROUP BY fv.id_feature_value ';
if (!Configuration::get('PS_LAYERED_HIDE_0_VALUES'))
{
$sql_query['second_query'] = '
SELECT fl.name feature_name, fp.id_feature, fv.id_feature_value, fvl.value,
0 nbr,
lifl.url_name name_url_name, lifl.meta_title name_meta_title, lifvl.url_name value_url_name, lifvl.meta_title value_meta_title
FROM '._DB_PREFIX_.'feature_product fp'.
Shop::addSqlAssociation('product', 'fp').'
INNER JOIN '._DB_PREFIX_.'product p ON (p.id_product = fp.id_product)
LEFT JOIN '._DB_PREFIX_.'feature_lang fl ON (fl.id_feature = fp.id_feature AND fl.id_lang = '.(int)$id_lang.')
INNER JOIN '._DB_PREFIX_.'feature_value fv ON (fv.id_feature_value = fp.id_feature_value AND (fv.custom IS NULL OR fv.custom = 0))
LEFT JOIN '._DB_PREFIX_.'feature_value_lang fvl ON (fvl.id_feature_value = fp.id_feature_value AND fvl.id_lang = '.(int)$id_lang.')
LEFT JOIN '._DB_PREFIX_.'layered_indexable_feature_lang_value lifl
ON (lifl.id_feature = fp.id_feature AND lifl.id_lang = '.(int)$id_lang.')
LEFT JOIN '._DB_PREFIX_.'layered_indexable_feature_value_lang_value lifvl
ON (lifvl.id_feature_value = fp.id_feature_value AND lifvl.id_lang = '.(int)$id_lang.')
WHERE '.$alias.'.`active` = 1 AND '.$alias.'.`visibility` IN ("both", "catalog")
AND fp.id_feature = '.(int)$filter['id_value'].'
GROUP BY fv.id_feature_value';
}
break;
case 'category':
$depth = Configuration::get('PS_LAYERED_FILTER_CATEGORY_DEPTH');
if ($depth === false)
$depth = 1;
$sql_query['select'] = '
SELECT c.id_category, c.id_parent, cl.name, (SELECT count(DISTINCT p.id_product) # ';
$sql_query['from'] = '
FROM '._DB_PREFIX_.'category_product cp
LEFT JOIN '._DB_PREFIX_.'product p ON (p.id_product = cp.id_product) ';
$sql_query['where'] = '
WHERE cp.id_category = c.id_category
AND '.$alias.'.active = 1 AND '.$alias.'.`visibility` IN ("both", "catalog")';
$sql_query['group'] = ') count_products
FROM '._DB_PREFIX_.'category c
LEFT JOIN '._DB_PREFIX_.'category_lang cl ON (cl.id_category = c.id_category AND cl.`id_shop` = '.(int)Context::getContext()->shop->id.' and cl.id_lang = '.$id_lang.')
WHERE c.nleft > '.(int)$parent->nleft.'
AND c.nright < '.(int)$parent->nright.'
'.($depth ? 'AND c.level_depth <= '.($parent->level_depth+(int)$depth) : '').'
GROUP BY c.id_category ORDER BY c.nleft, c.position';
}
foreach ($filters as $filter_tmp)
{
$method_name = 'get'.ucfirst($filter_tmp['type']).'FilterSubQuery';
if (method_exists('BlockLayered', $method_name) &&
(!in_array($filter['type'], array('price', 'weight')) && $filter['type'] != $filter_tmp['type'] || $filter['type'] == $filter_tmp['type']))
{
if ($filter['type'] == $filter_tmp['type'] && $filter['id_value'] == $filter_tmp['id_value'])
$sub_query_filter = self::$method_name(array(), true);
else
{
if (!is_null($filter_tmp['id_value']))
$selected_filters_cleaned = $this->cleanFilterByIdValue(@$selected_filters[$filter_tmp['type']], $filter_tmp['id_value']);
else
$selected_filters_cleaned = @$selected_filters[$filter_tmp['type']];
$sub_query_filter = self::$method_name($selected_filters_cleaned, $filter['type'] == $filter_tmp['type']);
}
foreach ($sub_query_filter as $key => $value)
$sql_query[$key] .= $value;
}
}
$products = false;
if (!empty($sql_query['from']))
{
$sql_query['from'] .= Shop::addSqlAssociation('product', 'p');
$products = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS($sql_query['select']."\n".$sql_query['from']."\n".$sql_query['join']."\n".$sql_query['where']."\n".$sql_query['group']);
}
foreach ($filters as $filter_tmp)
{
$method_name = 'filterProductsBy'.ucfirst($filter_tmp['type']);
if (method_exists('BlockLayered', $method_name) &&
(!in_array($filter['type'], array('price', 'weight')) && $filter['type'] != $filter_tmp['type'] || $filter['type'] == $filter_tmp['type']))
if ($filter['type'] == $filter_tmp['type'])
$products = self::$method_name(array(), $products);
else
$products = self::$method_name(@$selected_filters[$filter_tmp['type']], $products);
}
if (!empty($sql_query['second_query']))
{
$res = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS($sql_query['second_query']);
if ($res)
$products = array_merge($products, $res);
}
switch ($filter['type'])
{
case 'price':
$price_array = array(
'type_lite' => 'price',
'type' => 'price',
'id_key' => 0,
'name' => $this->l('Price'),
'slider' => true,
'max' => '0',
'min' => null,
'values' => array ('1' => 0),
'unit' => $currency->sign,
'format' => $currency->format,
'filter_show_limit' => $filter['filter_show_limit'],
'filter_type' => $filter['filter_type']
);
if (isset($products) && $products)
foreach ($products as $product)
{
if (is_null($price_array['min']))
{
$price_array['min'] = $product['price_min'];
$price_array['values'][0] = $product['price_min'];
}
else if ($price_array['min'] > $product['price_min'])
{
$price_array['min'] = $product['price_min'];
$price_array['values'][0] = $product['price_min'];
}
if ($price_array['max'] < $product['price_max'])
{
$price_array['max'] = $product['price_max'];
$price_array['values'][1] = $product['price_max'];
}
}
if ($price_array['max'] != $price_array['min'] && $price_array['min'] != null)
{
if ($filter['filter_type'] == 2)
{
$price_array['list_of_values'] = array();
$nbr_of_value = $filter['filter_show_limit'];
if ($nbr_of_value < 2)
$nbr_of_value = 4;
$delta = ($price_array['max'] - $price_array['min']) / $nbr_of_value;
$current_step = $price_array['min'];
for ($i = 0; $i < $nbr_of_value; $i++)
$price_array['list_of_values'][] = array(
(int)($price_array['min'] + $i * $delta),
(int)($price_array['min'] + ($i + 1) * $delta)
);
}
if (isset($selected_filters['price']) && isset($selected_filters['price'][0])
&& isset($selected_filters['price'][1]))
{
$price_array['values'][0] = $selected_filters['price'][0];
$price_array['values'][1] = $selected_filters['price'][1];
}
$filter_blocks[] = $price_array;
}
break;
case 'weight':
$weight_array = array(
'type_lite' => 'weight',
'type' => 'weight',
'id_key' => 0,
'name' => $this->l('Weight'),
'slider' => true,
'max' => '0',
'min' => null,
'values' => array ('1' => 0),
'unit' => Configuration::get('PS_WEIGHT_UNIT'),
'format' => 5, // Ex: xxxxx kg
'filter_show_limit' => $filter['filter_show_limit'],
'filter_type' => $filter['filter_type']
);
if (isset($products) && $products)
foreach ($products as $product)
{
if (is_null($weight_array['min']))
{
$weight_array['min'] = $product['weight'];
$weight_array['values'][0] = $product['weight'];
}
else if ($weight_array['min'] > $product['weight'])
{
$weight_array['min'] = $product['weight'];
$weight_array['values'][0] = $product['weight'];
}
if ($weight_array['max'] < $product['weight'])
{
$weight_array['max'] = $product['weight'];
$weight_array['values'][1] = $product['weight'];
}
}
if ($weight_array['max'] != $weight_array['min'] && $weight_array['min'] != null)
{
if (isset($selected_filters['weight']) && isset($selected_filters['weight'][0])
&& isset($selected_filters['weight'][1]))
{
$weight_array['values'][0] = $selected_filters['weight'][0];
$weight_array['values'][1] = $selected_filters['weight'][1];
}
$filter_blocks[] = $weight_array;
}
break;
case 'condition':
$condition_array = array(
'new' => array('name' => $this->l('New'),'nbr' => 0),
'used' => array('name' => $this->l('Used'), 'nbr' => 0),
'refurbished' => array('name' => $this->l('Refurbished'),
'nbr' => 0)
);
if (isset($products) && $products)
foreach ($products as $product)
if (isset($selected_filters['condition']) && in_array($product['condition'], $selected_filters['condition']))
$condition_array[$product['condition']]['checked'] = true;
foreach ($condition_array as $key => $condition)
if (isset($selected_filters['condition']) && in_array($key, $selected_filters['condition']))
$condition_array[$key]['checked'] = true;
if (isset($products) && $products)
foreach ($products as $product)
if (isset($condition_array[$product['condition']]))
$condition_array[$product['condition']]['nbr']++;
$filter_blocks[] = array(
'type_lite' => 'condition',
'type' => 'condition',
'id_key' => 0,
'name' => $this->l('Condition'),
'values' => $condition_array,
'filter_show_limit' => $filter['filter_show_limit'],
'filter_type' => $filter['filter_type']
);
break;
case 'quantity':
$quantity_array = array (
0 => array('name' => $this->l('Not available'), 'nbr' => 0),
1 => array('name' => $this->l('In stock'),
'nbr' => 0));
foreach ($quantity_array as $key => $quantity)
if (isset($selected_filters['quantity']) && in_array($key, $selected_filters['quantity']))
$quantity_array[$key]['checked'] = true;
if (isset($products) && $products)
foreach ($products as $product)
$quantity_array[(int)($product['quantity'] > 0)]['nbr']++;
$filter_blocks[] = array(
'type_lite' => 'quantity',
'type' => 'quantity',
'id_key' => 0,
'name' => $this->l('Availability'),
'values' => $quantity_array,
'filter_show_limit' => $filter['filter_show_limit'],
'filter_type' => $filter['filter_type']
);
break;
case 'manufacturer':
if (isset($products) && $products)
{
$manufaturers_array = array();
foreach ($products as $manufacturer)
{
if (!isset($manufaturers_array[$manufacturer['id_manufacturer']]))
$manufaturers_array[$manufacturer['id_manufacturer']] = array('name' => $manufacturer['name'], 'nbr' => $manufacturer['nbr']);
if (isset($selected_filters['manufacturer']) && in_array((int)$manufacturer['id_manufacturer'], $selected_filters['manufacturer']))
$manufaturers_array[$manufacturer['id_manufacturer']]['checked'] = true;
}
$filter_blocks[] = array(
'type_lite' => 'manufacturer',
'type' => 'manufacturer',
'id_key' => 0,
'name' => $this->l('Manufacturer'),
'values' => $manufaturers_array,
'filter_show_limit' => $filter['filter_show_limit'],
'filter_type' => $filter['filter_type']
);
}
break;
case 'id_attribute_group':
$attributes_array = array();
if (isset($products) && $products)
{
foreach ($products as $attributes)
{
if (!isset($attributes_array[$attributes['id_attribute_group']]))
$attributes_array[$attributes['id_attribute_group']] = array (
'type_lite' => 'id_attribute_group',
'type' => 'id_attribute_group',
'id_key' => (int)$attributes['id_attribute_group'],
'name' => $attributes['attribute_group_name'],
'is_color_group' => (bool)$attributes['is_color_group'],
'values' => array(),
'url_name' => $attributes['name_url_name'],
'meta_title' => $attributes['name_meta_title'],
'filter_show_limit' => $filter['filter_show_limit'],
'filter_type' => $filter['filter_type']
);
if (!isset($attributes_array[$attributes['id_attribute_group']]['values'][$attributes['id_attribute']]))
$attributes_array[$attributes['id_attribute_group']]['values'][$attributes['id_attribute']] = array(
'color' => $attributes['color'],
'name' => $attributes['attribute_name'],
'nbr' => (int)$attributes['nbr'],
'url_name' => $attributes['value_url_name'],
'meta_title' => $attributes['value_meta_title']
);
if (isset($selected_filters['id_attribute_group'][$attributes['id_attribute']]))
$attributes_array[$attributes['id_attribute_group']]['values'][$attributes['id_attribute']]['checked'] = true;
}
$filter_blocks = array_merge($filter_blocks, $attributes_array);
}
break;
case 'id_feature':
$feature_array = array();
if (isset($products) && $products)
{
foreach ($products as $feature)
{
if (!isset($feature_array[$feature['id_feature']]))
$feature_array[$feature['id_feature']] = array(
'type_lite' => 'id_feature',
'type' => 'id_feature',
'id_key' => (int)$feature['id_feature'],
'values' => array(),
'name' => $feature['feature_name'],
'url_name' => $feature['name_url_name'],
'meta_title' => $feature['name_meta_title'],
'filter_show_limit' => $filter['filter_show_limit'],
'filter_type' => $filter['filter_type']
);
if (!isset($feature_array[$feature['id_feature']]['values'][$feature['id_feature_value']]))
$feature_array[$feature['id_feature']]['values'][$feature['id_feature_value']] = array(
'nbr' => (int)$feature['nbr'],
'name' => $feature['value'],
'url_name' => $feature['value_url_name'],
'meta_title' => $feature['value_meta_title']
);
if (isset($selected_filters['id_feature'][$feature['id_feature_value']]))
$feature_array[$feature['id_feature']]['values'][$feature['id_feature_value']]['checked'] = true;
}
//Natural sort
foreach ($feature_array as $key => $value)
{
$temp = array();
foreach ($feature_array[$key]['values'] as $keyint => $valueint)
$temp[$keyint] = $valueint['name'];
natcasesort($temp);
$temp2 = array();
foreach ($temp as $keytemp => $valuetemp)
$temp2[$keytemp] = $feature_array[$key]['values'][$keytemp];
$feature_array[$key]['values'] = $temp2;
}
$filter_blocks = array_merge($filter_blocks, $feature_array);
}
break;
case 'category':
$tmp_array = array();
if (isset($products) && $products)
{
$categories_with_products_count = 0;
foreach ($products as $category)
{
$tmp_array[$category['id_category']] = array(
'name' => $category['name'],
'nbr' => (int)$category['count_products']
);
if ((int)$category['count_products'])
$categories_with_products_count++;
if (isset($selected_filters['category']) && in_array($category['id_category'], $selected_filters['category']))
$tmp_array[$category['id_category']]['checked'] = true;
}
if ($categories_with_products_count || !Configuration::get('PS_LAYERED_HIDE_0_VALUES'))
$filter_blocks[] = array (
'type_lite' => 'category',
'type' => 'category',
'id_key' => 0, 'name' => $this->l('Categories'),
'values' => $tmp_array,
'filter_show_limit' => $filter['filter_show_limit'],
'filter_type' => $filter['filter_type']
);
}
break;
}
}
// All non indexable attribute and feature
$non_indexable = array();
// Get all non indexable attribute groups
foreach (Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS('
SELECT public_name
FROM `'._DB_PREFIX_.'attribute_group_lang` agl
LEFT JOIN `'._DB_PREFIX_.'layered_indexable_attribute_group` liag
ON liag.id_attribute_group = agl.id_attribute_group
WHERE indexable IS NULL OR indexable = 0
AND id_lang = '.$id_lang) as $attribute)
$non_indexable[] = Tools::link_rewrite($attribute['public_name']);
// Get all non indexable features
foreach (Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS('
SELECT name
FROM `'._DB_PREFIX_.'feature_lang` fl
LEFT JOIN `'._DB_PREFIX_.'layered_indexable_feature` lif
ON lif.id_feature = fl.id_feature
WHERE indexable IS NULL OR indexable = 0
AND id_lang = '.$id_lang) as $attribute)
$non_indexable[] = Tools::link_rewrite($attribute['name']);
//generate SEO link
$param_selected = '';
$param_product_url = '';
$option_checked_array = array();
$param_group_selected_array = array();
$title_values = array();
$meta_values = array();
//get filters checked by group
foreach ($filter_blocks as $type_filter)
{
$filter_name = (!empty($type_filter['url_name']) ? $type_filter['url_name'] : $type_filter['name']);
$filter_meta = (!empty($type_filter['meta_title']) ? $type_filter['meta_title'] : '');
$attr_key = $type_filter['type'].'_'.$type_filter['id_key'];
$param_group_selected = '';
foreach ($type_filter['values'] as $key => $value)
{
if (is_array($value) && array_key_exists('checked', $value ))
{
$value_name = !empty($value['url_name']) ? $value['url_name'] : $value['name'];
$value_meta = !empty($value['meta_title']) ? $value['meta_title'] : $value['name'];
$param_group_selected .= Configuration::get('PS_ATTRIBUTE_ANCHOR_SEPARATOR').str_replace(Configuration::get('PS_ATTRIBUTE_ANCHOR_SEPARATOR'), '_', Tools::link_rewrite($value_name));
$param_group_selected_array[Tools::link_rewrite($filter_name)][] = Tools::link_rewrite($value_name);
if (!isset($title_values[$filter_name]))
$title_values[$filter_name] = array();
$title_values[$filter_name][] = $value_name;
if (!isset($meta_values[$attr_key]))
$meta_values[$attr_key] = array('title' => $filter_meta, 'values' => array());
$meta_values[$attr_key]['values'][] = $value_meta;
}
else
$param_group_selected_array[Tools::link_rewrite($filter_name)][] = array();
}
if (!empty($param_group_selected))
{
$param_selected .= '/'.str_replace(Configuration::get('PS_ATTRIBUTE_ANCHOR_SEPARATOR'), '_', Tools::link_rewrite($filter_name)).$param_group_selected;
$option_checked_array[Tools::link_rewrite($filter_name)] = $param_group_selected;
}
// select only attribute and group attribute to display an unique product combination link
if (!empty($param_group_selected) && $type_filter['type'] == 'id_attribute_group')
$param_product_url .= '/'.str_replace(Configuration::get('PS_ATTRIBUTE_ANCHOR_SEPARATOR'), '_', Tools::link_rewrite($filter_name)).$param_group_selected;
}
if ($this->page > 1)
$param_selected .= '/page-'.$this->page;
$blacklist = array('weight', 'price');
if (!Configuration::get('PS_LAYERED_FILTER_INDEX_CDT'))
$blacklist[] = 'condition';
if (!Configuration::get('PS_LAYERED_FILTER_INDEX_QTY'))
$blacklist[] = 'quantity';
if (!Configuration::get('PS_LAYERED_FILTER_INDEX_MNF'))
$blacklist[] = 'manufacturer';
if (!Configuration::get('PS_LAYERED_FILTER_INDEX_CAT'))
$blacklist[] = 'category';
$global_nofollow = false;
foreach ($filter_blocks as &$type_filter)
{
$filter_name = (!empty($type_filter['url_name']) ? $type_filter['url_name'] : $type_filter['name']);
if (count($type_filter) > 0 && !isset($type_filter['slider']))
{
foreach ($type_filter['values'] as $key => $values)
{
$nofollow = false;
if (!empty($values['checked']) && in_array($type_filter['type'], $blacklist))
$global_nofollow = true;
$option_checked_clone_array = $option_checked_array;
// If not filters checked, add parameter
$value_name = !empty($values['url_name']) ? $values['url_name'] : $values['name'];
if (!in_array(Tools::link_rewrite($value_name), $param_group_selected_array[Tools::link_rewrite($filter_name)]))
{
// Update parameter filter checked before
if (array_key_exists(Tools::link_rewrite($filter_name), $option_checked_array))
{
$option_checked_clone_array[Tools::link_rewrite($filter_name)] = $option_checked_clone_array[Tools::link_rewrite($filter_name)].Configuration::get('PS_ATTRIBUTE_ANCHOR_SEPARATOR').str_replace(Configuration::get('PS_ATTRIBUTE_ANCHOR_SEPARATOR'), '_', Tools::link_rewrite($value_name));
$nofollow = true;
}
else
$option_checked_clone_array[Tools::link_rewrite($filter_name)] = Configuration::get('PS_ATTRIBUTE_ANCHOR_SEPARATOR').str_replace(Configuration::get('PS_ATTRIBUTE_ANCHOR_SEPARATOR'), '_', Tools::link_rewrite($value_name));
}
else
{
// Remove selected parameters
$option_checked_clone_array[Tools::link_rewrite($filter_name)] = str_replace(Configuration::get('PS_ATTRIBUTE_ANCHOR_SEPARATOR').str_replace(Configuration::get('PS_ATTRIBUTE_ANCHOR_SEPARATOR'), '_', Tools::link_rewrite($value_name)), '', $option_checked_clone_array[Tools::link_rewrite($filter_name)]);
if (empty($option_checked_clone_array[Tools::link_rewrite($filter_name)]))
unset($option_checked_clone_array[Tools::link_rewrite($filter_name)]);
}
$parameters = '';
ksort($option_checked_clone_array); // Order parameters
foreach ($option_checked_clone_array as $key_group => $value_group)
$parameters .= '/'.str_replace(Configuration::get('PS_ATTRIBUTE_ANCHOR_SEPARATOR'), '_', $key_group).$value_group;
// Check if there is an non indexable attribute or feature in the url
foreach ($non_indexable as $value)
if (strpos($parameters, '/'.$value) !== false)
$nofollow = true;
$type_filter['values'][$key]['link'] = Context::getContext()->link->getCategoryLink($parent, null, null, ltrim($parameters, '/'));
$type_filter['values'][$key]['rel'] = ($nofollow) ? 'nofollow' : '';
}
}
}
$n_filters = 0;
if (isset($selected_filters['price']))
if ($price_array['min'] == $selected_filters['price'][0] && $price_array['max'] == $selected_filters['price'][1])
unset($selected_filters['price']);
if (isset($selected_filters['weight']))
if ($weight_array['min'] == $selected_filters['weight'][0] && $weight_array['max'] == $selected_filters['weight'][1])
unset($selected_filters['weight']);
foreach ($selected_filters as $filters)
$n_filters += count($filters);
$cache = array(
'layered_show_qties' => (int)Configuration::get('PS_LAYERED_SHOW_QTIES'),
'id_category_layered' => (int)$id_parent,
'selected_filters' => $selected_filters,
'n_filters' => (int)$n_filters,
'nbr_filterBlocks' => count($filter_blocks),
'filters' => $filter_blocks,
'title_values' => $title_values,
'meta_values' => $meta_values,
'current_friendly_url' => $param_selected,
'param_product_url' => $param_product_url,
'no_follow' => (!empty($param_selected) || $global_nofollow)
);
return $cache;
}
public function cleanFilterByIdValue($attributes, $id_value)
{
$selected_filters = array();
if (is_array($attributes))
foreach ($attributes as $attribute)
{
$attribute_data = explode('_', $attribute);
if ($attribute_data[0] == $id_value)
$selected_filters[] = $attribute_data[1];
}
return $selected_filters;
}
public function generateFiltersBlock($selected_filters)
{
global $smarty;
if ($filter_block = $this->getFilterBlock($selected_filters))
{
if ($filter_block['nbr_filterBlocks'] == 0)
return false;
$smarty->assign($filter_block);
$smarty->assign('hide_0_values', Configuration::get('PS_LAYERED_HIDE_0_VALUES'));
return $this->display(__FILE__, 'blocklayered.tpl');
}
else
return false;
}
private static function getPriceFilterSubQuery($filter_value)
{
$id_currency = (int)Context::getContext()->currency->id;
if (isset($filter_value) && $filter_value)
{
$price_filter_query = '
INNER JOIN `'._DB_PREFIX_.'layered_price_index` psi ON (psi.id_product = p.id_product AND psi.id_currency = '.(int)$id_currency.'
AND psi.price_min <= '.(int)$filter_value[1].' AND psi.price_max >= '.(int)$filter_value[0].') ';
}
else
{
$price_filter_query = '
INNER JOIN `'._DB_PREFIX_.'layered_price_index` psi
ON (psi.id_product = p.id_product AND psi.id_currency = '.(int)$id_currency.') ';
}
return array('join' => $price_filter_query, 'select' => ', psi.price_min, psi.price_max');
}
private static function filterProductsByPrice($filter_value, $product_collection)
{
if (empty($filter_value))
return $product_collection;
foreach ($product_collection as $key => $product)
{
if (isset($filter_value) && $filter_value && isset($product['price_min']) && isset($product['id_product'])
&& ((int)$filter_value[0] > $product['price_min'] || (int)$filter_value[1] < $product['price_max']))
{
$price = Product::getPriceStatic($product['id_product'], Configuration::get('PS_LAYERED_FILTER_PRICE_USETAX'));
if ($price < $filter_value[0] || $price > $filter_value[1])
continue;
unset($product_collection[$key]);
}
}
return $product_collection;
}
private static function getWeightFilterSubQuery($filter_value, $ignore_join)
{
if (isset($filter_value) && $filter_value)
if ($filter_value[0] != 0 || $filter_value[1] != 0)
return array('where' => ' AND p.`weight` BETWEEN '.(float)($filter_value[0] - 0.001).' AND '.(float)($filter_value[1] + 0.001).' ');
return array();
}
private static function getId_featureFilterSubQuery($filter_value, $ignore_join)
{
if (empty($filter_value))
return array();
$query_filters = ' AND p.id_product IN (SELECT id_product FROM '._DB_PREFIX_.'feature_product fp WHERE ';
foreach ($filter_value as $filter_val)
$query_filters .= 'fp.`id_feature_value` = '.(int)$filter_val.' OR ';
$query_filters = rtrim($query_filters, 'OR ').') ';
return array('where' => $query_filters);
}
private static function getId_attribute_groupFilterSubQuery($filter_value, $ignore_join)
{
if (empty($filter_value))
return array();
$query_filters = '
AND p.id_product IN (SELECT pa.`id_product`
FROM `'._DB_PREFIX_.'product_attribute_combination` pac
LEFT JOIN `'._DB_PREFIX_.'product_attribute` pa ON (pa.`id_product_attribute` = pac.`id_product_attribute`)
WHERE ';
foreach ($filter_value as $filter_val)
$query_filters .= 'pac.`id_attribute` = '.(int)$filter_val.' OR ';
$query_filters = rtrim($query_filters, 'OR ').') ';
return array('where' => $query_filters);
}
private static function getCategoryFilterSubQuery($filter_value, $ignore_join)
{
if (empty($filter_value))
return array();
$query_filters_join = '';
$query_filters_where = ' AND p.id_product IN (SELECT id_product FROM '._DB_PREFIX_.'category_product cp WHERE ';
foreach ($filter_value as $id_category)
$query_filters_where .= 'cp.`id_category` = '.(int)$id_category.' OR ';
$query_filters_where = rtrim($query_filters_where, 'OR ').') ';
return array('where' => $query_filters_where, 'join' => $query_filters_join);
}
private static function getQuantityFilterSubQuery($filter_value, $ignore_join)
{
if (count($filter_value) == 2 || empty($filter_value))
return array();
$query_filters_join = '';
$query_filters = ' AND sav.quantity '.(!$filter_value[0] ? '<=' : '>').' 0 ';
$query_filters_join = 'LEFT JOIN `'._DB_PREFIX_.'stock_available` sav ON (sav.id_product = p.id_product AND sav.id_shop = '.(int)Context::getContext()->shop->id.') ';
return array('where' => $query_filters, 'join' => $query_filters_join);
}
private static function getManufacturerFilterSubQuery($filter_value, $ignore_join)
{
if (empty($filter_value))
$query_filters = '';
else
{
array_walk($filter_value, create_function('&$id_manufacturer', '$id_manufacturer = (int)$id_manufacturer;'));
$query_filters = ' AND p.id_manufacturer IN ('.implode($filter_value, ',').')';
}
if ($ignore_join)
return array('where' => $query_filters, 'select' => ', m.name');
else
return array('where' => $query_filters, 'select' => ', m.name', 'join' => 'LEFT JOIN `'._DB_PREFIX_.'manufacturer` m ON (m.id_manufacturer = p.id_manufacturer) ');
}
private static function getConditionFilterSubQuery($filter_value, $ignore_join)
{
if (count($filter_value) == 3 || empty($filter_value))
return array();
$query_filters = ' AND product_shop.condition IN (';
foreach ($filter_value as $cond)
$query_filters .= '\''.$cond.'\',';
$query_filters = rtrim($query_filters, ',').') ';
return array('where' => $query_filters);
}
public function ajaxCallBackOffice($category_box = array(), $id_layered_filter = null)
{
global $cookie;
if (!empty($id_layered_filter))
{
$layered_filter = Db::getInstance(_PS_USE_SQL_SLAVE_)->getRow('SELECT * FROM '._DB_PREFIX_.'layered_filter WHERE id_layered_filter = '.(int)$id_layered_filter);
if ($layered_filter && isset($layered_filter['filters']) && !empty($layered_filter['filters']))
$layered_values = Tools::unSerialize($layered_filter['filters']);
if (isset($layered_values['categories']) && count($layered_values['categories']))
foreach ($layered_values['categories'] as $id_category)
$category_box[] = (int)$id_category;
}
/* Clean categoryBox before use */
if (isset($category_box) && is_array($category_box))
foreach ($category_box as &$value)
$value = (int)$value;
else
$category_box = array();
$attribute_groups = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS('
SELECT ag.id_attribute_group, ag.is_color_group, agl.name, COUNT(DISTINCT(a.id_attribute)) n
FROM '._DB_PREFIX_.'attribute_group ag
LEFT JOIN '._DB_PREFIX_.'attribute_group_lang agl ON (agl.id_attribute_group = ag.id_attribute_group)
LEFT JOIN '._DB_PREFIX_.'attribute a ON (a.id_attribute_group = ag.id_attribute_group)
'.(count($category_box) ? '
LEFT JOIN '._DB_PREFIX_.'product_attribute_combination pac ON (pac.id_attribute = a.id_attribute)
LEFT JOIN '._DB_PREFIX_.'product_attribute pa ON (pa.id_product_attribute = pac.id_product_attribute)'.
Shop::addSqlAssociation('product_attribute', 'pa').'
LEFT JOIN '._DB_PREFIX_.'category_product cp ON (cp.id_product = pa.id_product)' : '').'
WHERE agl.id_lang = '.(int)$cookie->id_lang.
(count($category_box) ? ' AND cp.id_category IN ('.implode(',', $category_box).')' : '').'
GROUP BY ag.id_attribute_group');
$features = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS('
SELECT fl.id_feature, fl.name, COUNT(DISTINCT(fv.id_feature_value)) n
FROM '._DB_PREFIX_.'feature_lang fl
LEFT JOIN '._DB_PREFIX_.'feature_value fv ON (fv.id_feature = fl.id_feature)
'.(count($category_box) ? '
LEFT JOIN '._DB_PREFIX_.'feature_product fp ON (fp.id_feature = fv.id_feature)
LEFT JOIN '._DB_PREFIX_.'category_product cp ON (cp.id_product = fp.id_product)' : '').'
WHERE (fv.custom IS NULL OR fv.custom = 0) AND fl.id_lang = '.(int)$cookie->id_lang.
(count($category_box) ? ' AND cp.id_category IN ('.implode(',', $category_box).')' : '').'
GROUP BY fl.id_feature');
$n_elements = count($attribute_groups) + count($features) + 4;
if ($n_elements > 20)
$n_elements = 20;
$html = '
<div id="layered_container_right" style="height: 200px; overflow-y: auto;">
<h4><strong>'.$this->l('Available filters').' <span id="num_avail_filters">(0)</span></strong></h4>
<ul id="all_filters"></ul>
<ul>
<li class="ui-state-default layered_right">
<input type="checkbox" id="layered_selection_subcategories" name="layered_selection_subcategories" />
<span class="position"></span>'.$this->l('Sub-categories filter').'
<select class="filter_show_limit" name="layered_selection_subcategories_filter_show_limit">
<option value="0">'.$this->l('No limit').'</option>
<option value="4">4</option>
<option value="5">5</option>
<option value="10">10</option>
<option value="20">20</option>
</select>
<select class="filter_type" name="layered_selection_subcategories_filter_type">
<option value="0">'.$this->l('Checkbox').'</option>
<option value="1">'.$this->l('Radio button').'</option>
<option value="2">'.$this->l('Drop-down list').'</option>
</select>
</li>
</ul>
<ul>
<li class="ui-state-default layered_right">
<span class="ui-icon ui-icon-arrowthick-2-n-s"></span>
<input type="checkbox" id="layered_selection_stock" name="layered_selection_stock" /> <span class="position"></span>'.$this->l('Product stock filter').'
<select class="filter_show_limit" name="layered_selection_stock_filter_show_limit">
<option value="0">'.$this->l('No limit').'</option>
<option value="4">4</option>
<option value="5">5</option>
<option value="10">10</option>
<option value="20">20</option>
</select>
<select class="filter_type" name="layered_selection_stock_filter_type">
<option value="0">'.$this->l('Checkbox').'</option>
<option value="1">'.$this->l('Radio button').'</option>
<option value="2">'.$this->l('Drop-down list').'</option>
</select>
</li>
</ul>
<ul>
<li class="ui-state-default layered_right">
<span class="ui-icon ui-icon-arrowthick-2-n-s"></span>
<input type="checkbox" id="layered_selection_condition" name="layered_selection_condition" />
<span class="position"></span>'.$this->l('Product condition filter').'
<select class="filter_show_limit" name="layered_selection_condition_filter_show_limit">
<option value="0">'.$this->l('No limit').'</option>
<option value="4">4</option>
<option value="5">5</option>
<option value="10">10</option>
<option value="20">20</option>
</select>
<select class="filter_type" name="layered_selection_condition_filter_type">
<option value="0">'.$this->l('Checkbox').'</option>
<option value="1">'.$this->l('Radio button').'</option>
<option value="2">'.$this->l('Drop-down list').'</option>
</select>
</li>
</ul>
<ul>
<li class="ui-state-default layered_right">
<span class="ui-icon ui-icon-arrowthick-2-n-s"></span>
<input type="checkbox" id="layered_selection_manufacturer" name="layered_selection_manufacturer" />
<span class="position"></span>'.$this->l('Product manufacturer filter').'
<select class="filter_show_limit" name="layered_selection_manufacturer_filter_show_limit">
<option value="0">'.$this->l('No limit').'</option>
<option value="4">4</option>
<option value="5">5</option>
<option value="10">10</option>
<option value="20">20</option>
</select>
<select class="filter_type" name="layered_selection_manufacturer_filter_type">
<option value="0">'.$this->l('Checkbox').'</option>
<option value="1">'.$this->l('Radio button').'</option>
<option value="2">'.$this->l('Drop-down list').'</option>
</select>
</li>
</ul>
<ul>
<li class="ui-state-default layered_right">
<span class="ui-icon ui-icon-arrowthick-2-n-s"></span>
<input type="checkbox" id="layered_selection_weight_slider" name="layered_selection_weight_slider" />
<span class="position"></span>'.$this->l('Product weight filter (slider)').'
<select class="filter_show_limit" name="layered_selection_weight_slider_filter_show_limit">
<option value="0">'.$this->l('No limit').'</option>
<option value="4">4</option>
<option value="5">5</option>
<option value="10">10</option>
<option value="20">20</option>
</select>
<select class="filter_type" name="layered_selection_weight_slider_filter_type">
<option value="0">'.$this->l('Slider').'</option>
<option value="1">'.$this->l('Inputs area').'</option>
<option value="2">'.$this->l('List of values').'</option>
</select>
</li>
</ul>
<ul>
<li class="ui-state-default layered_right">
<span class="ui-icon ui-icon-arrowthick-2-n-s"></span>
<input type="checkbox" id="layered_selection_price_slider" name="layered_selection_price_slider" />
<span class="position"></span>'.$this->l('Product price filter (slider)').'
<select class="filter_show_limit" name="layered_selection_price_slider_filter_show_limit">
<option value="0">'.$this->l('No limit').'</option>
<option value="4">4</option>
<option value="5">5</option>
<option value="10">10</option>
<option value="20">20</option>
</select>
<select class="filter_type" name="layered_selection_price_slider_filter_type">
<option value="0">'.$this->l('Slider').'</option>
<option value="1">'.$this->l('Inputs area').'</option>
<option value="2">'.$this->l('List of values').'</option>
</select>
</li>
</ul>';
if (count($attribute_groups))
{
$html .= '<ul>';
foreach ($attribute_groups as $attribute_group)
$html .= '
<li class="ui-state-default layered_right">
<span class="ui-icon ui-icon-arrowthick-2-n-s"></span>
<input type="checkbox" id="layered_selection_ag_'.(int)$attribute_group['id_attribute_group'].'" name="layered_selection_ag_'.(int)$attribute_group['id_attribute_group'].'" />
<span class="position"></span>
'.($attribute_group['n'] > 1 ? sprintf($this->l('Attribute group: %1$s (%2$d attributes)'), $attribute_group['name'], $attribute_group['n']) : sprintf($this->l('Attribute group: %1$s (%2$d attribute)'), $attribute_group['name'], $attribute_group['n'])).')'.
($attribute_group['is_color_group'] ? ' <img src="../img/admin/color_swatch.png" alt="" title="'.$this->l('This group will allow user to select a color').'" />' : '').'
<select class="filter_show_limit" name="layered_selection_ag_'.(int)$attribute_group['id_attribute_group'].'_filter_show_limit">
<option value="0">'.$this->l('No limit').'</option>
<option value="4">4</option>
<option value="5">5</option>
<option value="10">10</option>
<option value="20">20</option>
</select>
<select class="filter_type" name="layered_selection_ag_'.(int)$attribute_group['id_attribute_group'].'_filter_type">
<option value="0">'.$this->l('Checkbox').'</option>
<option value="1">'.$this->l('Radio button').'</option>
<option value="2">'.$this->l('Drop-down list').'</option>
</select>
</li>';
$html .= '</ul>';
}
if (count($features))
{
$html .= '<ul>';
foreach ($features as $feature)
$html .= '
<li class="ui-state-default layered_right">
<span class="ui-icon ui-icon-arrowthick-2-n-s"></span>
<input type="checkbox" id="layered_selection_feat_'.(int)$feature['id_feature'].'" name="layered_selection_feat_'.(int)$feature['id_feature'].'" />
<span class="position"></span>
'.($feature['n'] > 1 ? sprintf($this->l('Feature: %1$s (%2$d values)'), $feature['name'], $feature['n']) : sprintf($this->l('Feature: %1$s (%2$d value)'), $feature['name'], $feature['n'])).')
<select class="filter_show_limit" name="layered_selection_feat_'.(int)$feature['id_feature'].'_filter_show_limit">
<option value="0">'.$this->l('No limit').'</option>
<option value="4">4</option>
<option value="5">5</option>
<option value="10">10</option>
<option value="20">20</option>
</select>
<select class="filter_type" name="layered_selection_feat_'.(int)$feature['id_feature'].'_filter_type">
<option value="0">'.$this->l('Checkbox').'</option>
<option value="1">'.$this->l('Radio button').'</option>
<option value="2">'.$this->l('Drop-down list').'</option>
</select>
</li>';
$html .= '</ul>';
}
$html .= '
</div>';
if (isset($layered_values))
{
$html .= '
<script type="text/javascript">
$(document).ready(function()
{
$(\'#selected_filters li\').remove();
';
foreach ($layered_values as $key => $layered_value)
if ($key != 'categories' && $key != 'shop_list')
$html .= '
$(\'#'.$key.'\').click();
$(\'select[name='.$key.'_filter_type]\').val('.$layered_value['filter_type'].');
$(\'select[name='.$key.'_filter_show_limit]\').val('.$layered_value['filter_show_limit'].');
';
if (isset($layered_values['categories']) && count($layered_values['categories']))
{
$html .= '
function expandCategories(categories, iteration, id_category, init) {
if (categories[iteration])
{
category = $(\'#categories-treeview\').find(\'input[name="categoryBox[]"][value=\'+categories[iteration]+\']\');
if (category.length)
{
if (category.parent().hasClass(\'expandable\'))
{
$(\'#\'+categories[iteration]+\' .hitarea\').click();
}
if (parseInt(categories[iteration]) == parseInt(id_category))
{
$(\'#layered-cat-counter\').html(parseInt($(\'#layered-cat-counter\').html()) + 1);
if ($(\'#categories-treeview\').find(\'input[name="categoryBox[]"][value=\'+id_category+\']:checked\').length == 0)
{
$(\'#categories-treeview\').find(\'input[name="categoryBox[]"][value=\'+id_category+\']\').click();
clickOnCategoryBox($(\'#categories-treeview\').find(\'input[name="categoryBox[]"][value=\'+id_category+\']\'));
}
collapseAllCategories();
}
}
else {
setTimeout(function() { expandCategories(categories, iteration, id_category, false); }, 20 );
return;
}
$(\'#categories-treeview\').parent().parent().show();
expandCategories(categories, iteration+1, id_category);
if (typeof(lock_treeview_hidding) == \'undefined\' || !lock_treeview_hidding)
$(\'#categories-treeview\').parent().parent().hide();
}
}
$(\'#layered-cat-counter\').html(0);
$(\'.nb_sub_cat_selected\').hide();
$(\'#categories-treeview\').find(\'input[name="categoryBox[]"]:checked\').each(function(i, it) {
$(it).click();
updateNbSubCategorySelected($(it), false);
});';
foreach ($layered_values['categories'] as $id_category) {
if ($id_category != 1) // @todo do we need to use the root of the current shop ?
{
$category = new Category($id_category);
$parent_list = array_reverse($category->getParentsCategories());
}
else
$parent_list = array(array('id_category' => 1));
$html .= 'var categories = [];
';
foreach ($parent_list as $parent)
{
$html .= '
categories.push('.(int)$parent['id_category'].');';
}
$html .= '
expandCategories(categories, 0, '.(int)$id_category.', false);';
}
$html .= '
updCatCounter();
$(\'#scope_1\').attr(\'checked\', \'\');
$(\'#scope_2\').attr(\'checked\', \'checked\');
';
}
else
$html .= '
$(\'#scope_2\').attr(\'checked\', \'\');
$(\'#scope_1\').attr(\'checked\', \'checked\');
';
$html .= '
$(\'#layered_tpl_name\').val(\''.addslashes($layered_filter['name']).'\');
$(\'#id_layered_filter\').val(\''.(int)$layered_filter['id_layered_filter'].'\');
';
$html .= '
});
</script>';
}
if (!empty($id_layered_filter))
{
if (Shop::isFeatureActive() && Shop::getContext() != Shop::CONTEXT_ALL)
{
$shops = Shop::getShops(true, null, true);
if (count($shops) > 1)
{
$helper = new HelperForm();
$helper->id = (int)$id_layered_filter;
$helper->table = 'layered_filter';
$helper->identifier = 'id_layered_filter';
$helper->base_folder = Tools::getValue('base_folder').'/themes/default/template/helpers/form/';
$html .= '
<div id="shop_association_ajax">'.$helper->renderAssoShop(false, Tools::getValue('base_folder').'/themes/default/template/helpers/tree/').'</div>
<script type="text/javascript">
$(document).ready(function() {
$(\'#shop_association\').html($(\'#shop_association_ajax\').html());
$(\'#shop_association_ajax\').remove();
// Initialize checkbox
$(\'.input_shop\').each(function(k, v) {
check_shop_group_status($(v).val());
check_all_shop();
});
});
</script>';
}
}
}
return $html;
}
public function ajaxCall()
{
global $smarty, $cookie;
$selected_filters = $this->getSelectedFilters();
$this->getProducts($selected_filters, $products, $nb_products, $p, $n, $pages_nb, $start, $stop, $range);
// Add pagination variable
$nArray = (int)Configuration::get('PS_PRODUCTS_PER_PAGE') != 10 ? array((int)Configuration::get('PS_PRODUCTS_PER_PAGE'), 10, 20, 50) : array(10, 20, 50);
// Clean duplicate values
$nArray = array_unique($nArray);
asort($nArray);
$smarty->assign(
array(
'homeSize' => Image::getSize(ImageType::getFormatedName('home')),
'nb_products' => $nb_products,
'category' => new Category(Tools::getValue('id_category_layered', 1), (int)$cookie->id_lang),
'pages_nb' => (int)$pages_nb,
'p' => (int)$p,
'n' => (int)$n,
'range' => (int)$range,
'start' => (int)$start,
'stop' => (int)$stop,
'n_array' => ((int)Configuration::get('PS_PRODUCTS_PER_PAGE') != 10) ? array((int)Configuration::get('PS_PRODUCTS_PER_PAGE'), 10, 20, 50) : array(10, 20, 50),
'comparator_max_item' => (int)(Configuration::get('PS_COMPARATOR_MAX_ITEM')),
'products' => $products,
'products_per_page' => (int)Configuration::get('PS_PRODUCTS_PER_PAGE'),
'static_token' => Tools::getToken(false),
'page_name' => 'category',
'nArray' => $nArray,
)
);
// Prevent bug with old template where category.tpl contain the title of the category and category-count.tpl do not exists
if (file_exists(_PS_THEME_DIR_.'category-count.tpl'))
$category_count = $smarty->fetch(_PS_THEME_DIR_.'category-count.tpl');
else
$category_count = '';
if ($nb_products == 0)
$product_list = $this->display(__FILE__, 'blocklayered-no-products.tpl');
else
$product_list = $smarty->fetch(_PS_THEME_DIR_.'product-list.tpl');
/* We are sending an array in jSon to the .js controller, it will update both the filters and the products zones */
return Tools::jsonEncode(array(
'filtersBlock' => utf8_encode($this->generateFiltersBlock($selected_filters)),
'productList' => utf8_encode($product_list),
'pagination' => $smarty->fetch(_PS_THEME_DIR_.'pagination.tpl'),
'categoryCount' => $category_count));
}
public function getProducts($selected_filters, &$products, &$nb_products, &$p, &$n, &$pages_nb, &$start, &$stop, &$range)
{
global $cookie;
$products = $this->getProductByFilters($selected_filters);
$products = Product::getProductsProperties((int)$cookie->id_lang, $products);
$nb_products = $this->nbr_products;
$range = 2; /* how many pages around page selected */
$n = (int)Tools::getValue('n', Configuration::get('PS_PRODUCTS_PER_PAGE'));
$p = $this->page;
if ($p < 0)
$p = 0;
if ($p > ($nb_products / $n))
$p = ceil($nb_products / $n);
$pages_nb = ceil($nb_products / (int)($n));
$start = (int)($p - $range);
if ($start < 1)
$start = 1;
$stop = (int)($p + $range);
if ($stop > $pages_nb)
$stop = (int)($pages_nb);
}
public function rebuildLayeredStructure()
{
@set_time_limit(0);
/* Set memory limit to 128M only if current is lower */
$memory_limit = @ini_get('memory_limit');
if (substr($memory_limit, -1) != 'G' && ((substr($memory_limit, -1) == 'M' && substr($memory_limit, 0, -1) < 128) || is_numeric($memory_limit) && (intval($memory_limit) < 131072)))
@ini_set('memory_limit', '128M');
/* Delete and re-create the layered categories table */
Db::getInstance()->execute('DROP TABLE IF EXISTS '._DB_PREFIX_.'layered_category');
Db::getInstance()->execute('
CREATE TABLE IF NOT EXISTS `'._DB_PREFIX_.'layered_category` (
`id_layered_category` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`id_shop` INT(11) UNSIGNED NOT NULL,
`id_category` INT(10) UNSIGNED NOT NULL,
`id_value` INT(10) UNSIGNED NULL DEFAULT \'0\',
`type` ENUM(\'category\',\'id_feature\',\'id_attribute_group\',\'quantity\',\'condition\',\'manufacturer\',\'weight\',\'price\') NOT NULL,
`position` INT(10) UNSIGNED NOT NULL,
`filter_type` int(10) UNSIGNED NOT NULL DEFAULT 0,
`filter_show_limit` int(10) UNSIGNED NOT NULL DEFAULT 0,
PRIMARY KEY (`id_layered_category`),
KEY `id_category` (`id_category`,`type`)
) ENGINE='._MYSQL_ENGINE_.' DEFAULT CHARSET=latin1 AUTO_INCREMENT=1;'); /* MyISAM + latin1 = Smaller/faster */
Db::getInstance()->execute('
CREATE TABLE IF NOT EXISTS `'._DB_PREFIX_.'layered_filter` (
`id_layered_filter` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
`name` VARCHAR(64) NOT NULL,
`filters` TEXT NULL,
`n_categories` INT(10) UNSIGNED NOT NULL,
`date_add` DATETIME NOT NULL
) ENGINE='._MYSQL_ENGINE_.' DEFAULT CHARSET=utf8;');
Db::getInstance()->execute('
CREATE TABLE IF NOT EXISTS `'._DB_PREFIX_.'layered_filter_shop` (
`id_layered_filter` INT(10) UNSIGNED NOT NULL,
`id_shop` INT(11) UNSIGNED NOT NULL,
PRIMARY KEY (`id_layered_filter`, `id_shop`),
KEY `id_shop` (`id_shop`)
) ENGINE='._MYSQL_ENGINE_.' DEFAULT CHARSET=utf8;');
}
public function rebuildLayeredCache($products_ids = array(), $categories_ids = array())
{
@set_time_limit(0);
$filter_data = array('categories' => array());
/* Set memory limit to 128M only if current is lower */
$memory_limit = @ini_get('memory_limit');
if (substr($memory_limit, -1) != 'G' && ((substr($memory_limit, -1) == 'M' && substr($memory_limit, 0, -1) < 128) || is_numeric($memory_limit) && (intval($memory_limit) < 131072)))
@ini_set('memory_limit', '128M');
$db = Db::getInstance(_PS_USE_SQL_SLAVE_);
$n_categories = array();
$done_categories = array();
$alias = 'p';
$join_product_attribute = $join_product = '';
$alias = 'product_shop';
$join_product = Shop::addSqlAssociation('product', 'p');
$join_product_attribute = Shop::addSqlAssociation('product_attribute', 'pa');
$attribute_groups = self::query('
SELECT a.id_attribute, a.id_attribute_group
FROM '._DB_PREFIX_.'attribute a
LEFT JOIN '._DB_PREFIX_.'product_attribute_combination pac ON (pac.id_attribute = a.id_attribute)
LEFT JOIN '._DB_PREFIX_.'product_attribute pa ON (pa.id_product_attribute = pac.id_product_attribute)
LEFT JOIN '._DB_PREFIX_.'product p ON (p.id_product = pa.id_product)
'.$join_product.$join_product_attribute.'
LEFT JOIN '._DB_PREFIX_.'category_product cp ON (cp.id_product = p.id_product)
LEFT JOIN '._DB_PREFIX_.'category c ON (c.id_category = cp.id_category)
WHERE c.active = 1'.
(count($categories_ids) ? ' AND cp.id_category IN ('.implode(',', $categories_ids).')' : '').'
AND '.$alias.'.active = 1 AND '.$alias.'.`visibility` IN ("both", "catalog")
'.(count($products_ids) ? 'AND p.id_product IN ('.implode(',', $products_ids).')' : ''));
$attribute_groups_by_id = array();
while ($row = $db->nextRow($attribute_groups))
$attribute_groups_by_id[(int)$row['id_attribute']] = (int)$row['id_attribute_group'];
$features = self::query('
SELECT fv.id_feature_value, fv.id_feature
FROM '._DB_PREFIX_.'feature_value fv
LEFT JOIN '._DB_PREFIX_.'feature_product fp ON (fp.id_feature_value = fv.id_feature_value)
LEFT JOIN '._DB_PREFIX_.'product p ON (p.id_product = fp.id_product)
'.$join_product.'
LEFT JOIN '._DB_PREFIX_.'category_product cp ON (cp.id_product = p.id_product)
LEFT JOIN '._DB_PREFIX_.'category c ON (c.id_category = cp.id_category)
WHERE (fv.custom IS NULL OR fv.custom = 0) AND c.active = 1'.(count($categories_ids) ? ' AND cp.id_category IN ('.implode(',', $categories_ids).')' : '').'
AND '.$alias.'.active = 1 AND '.$alias.'.`visibility` IN ("both", "catalog") '.(count($products_ids) ? 'AND p.id_product IN ('.implode(',', $products_ids).')' : ''));
$features_by_id = array();
while ($row = $db->nextRow($features))
$features_by_id[(int)$row['id_feature_value']] = (int)$row['id_feature'];
$result = self::query('
SELECT p.id_product,
GROUP_CONCAT(DISTINCT fv.id_feature_value) features,
GROUP_CONCAT(DISTINCT cp.id_category) categories,
GROUP_CONCAT(DISTINCT pac.id_attribute) attributes
FROM '._DB_PREFIX_.'product p
LEFT JOIN '._DB_PREFIX_.'category_product cp ON (cp.id_product = p.id_product)
LEFT JOIN '._DB_PREFIX_.'category c ON (c.id_category = cp.id_category)
LEFT JOIN '._DB_PREFIX_.'feature_product fp ON (fp.id_product = p.id_product)
LEFT JOIN '._DB_PREFIX_.'feature_value fv ON (fv.id_feature_value = fp.id_feature_value)
LEFT JOIN '._DB_PREFIX_.'product_attribute pa ON (pa.id_product = p.id_product)
'.$join_product.$join_product_attribute.'
LEFT JOIN '._DB_PREFIX_.'product_attribute_combination pac ON (pac.id_product_attribute = pa.id_product_attribute)
WHERE c.active = 1'.(count($categories_ids) ? ' AND cp.id_category IN ('.implode(',', $categories_ids).')' : '').'
AND '.$alias.'.active = 1 AND '.$alias.'.`visibility` IN ("both", "catalog")
'.(count($products_ids) ? 'AND p.id_product IN ('.implode(',', $products_ids).')' : '').
' AND (fv.custom IS NULL OR fv.custom = 0)
GROUP BY p.id_product');
$shop_list = Shop::getShops(false, null, true);
$to_insert = false;
while ($product = $db->nextRow($result))
{
$a = $c = $f = array();
if (!empty($product['attributes']))
$a = array_flip(explode(',', $product['attributes']));
if (!empty($product['categories']))
$c = array_flip(explode(',', $product['categories']));
if (!empty($product['features']))
$f = array_flip(explode(',', $product['features']));
$filter_data['shop_list'] = $shop_list;
foreach ($shop_list as $id_shop)
{
foreach ($c as $id_category => $category)
{
if (!in_array($id_category, $filter_data['categories']))
$filter_data['categories'][] = $id_category;
if (!isset($n_categories[(int)$id_category]))
$n_categories[(int)$id_category] = 1;
if (!isset($done_categories[(int)$id_category]['cat']))
{
$filter_data['layered_selection_subcategories'] = array('filter_type' => 0, 'filter_show_limit' => 0);
$done_categories[(int)$id_category]['cat'] = true;
$to_insert = true;
}
foreach ($a as $k_attribute => $attribute)
if (!isset($done_categories[(int)$id_category]['a'.(int)$attribute_groups_by_id[(int)$k_attribute]]))
{
$filter_data['layered_selection_ag_'.(int)$attribute_groups_by_id[(int)$k_attribute]] = array('filter_type' => 0, 'filter_show_limit' => 0);
$done_categories[(int)$id_category]['a'.(int)$attribute_groups_by_id[(int)$k_attribute]] = true;
$to_insert = true;
}
foreach ($f as $k_feature => $feature)
if (!isset($done_categories[(int)$id_category]['f'.(int)$features_by_id[(int)$k_feature]]))
{
$filter_data['layered_selection_feat_'.(int)$features_by_id[(int)$k_feature]] = array('filter_type' => 0, 'filter_show_limit' => 0);
$done_categories[(int)$id_category]['f'.(int)$features_by_id[(int)$k_feature]] = true;
$to_insert = true;
}
if (!isset($done_categories[(int)$id_category]['q']))
{
$filter_data['layered_selection_stock'] = array('filter_type' => 0, 'filter_show_limit' => 0);
$done_categories[(int)$id_category]['q'] = true;
$to_insert = true;
}
if (!isset($done_categories[(int)$id_category]['m']))
{
$filter_data['layered_selection_manufacturer'] = array('filter_type' => 0, 'filter_show_limit' => 0);
$done_categories[(int)$id_category]['m'] = true;
$to_insert = true;
}
if (!isset($done_categories[(int)$id_category]['c']))
{
$filter_data['layered_selection_condition'] = array('filter_type' => 0, 'filter_show_limit' => 0);
$done_categories[(int)$id_category]['c'] = true;
$to_insert = true;
}
if (!isset($done_categories[(int)$id_category]['w']))
{
$filter_data['layered_selection_weight_slider'] = array('filter_type' => 0, 'filter_show_limit' => 0);
$done_categories[(int)$id_category]['w'] = true;
$to_insert = true;
}
if (!isset($done_categories[(int)$id_category]['p']))
{
$filter_data['layered_selection_price_slider'] = array('filter_type' => 0, 'filter_show_limit' => 0);
$done_categories[(int)$id_category]['p'] = true;
$to_insert = true;
}
}
}
}
if ($to_insert)
{
Db::getInstance()->execute('INSERT INTO '._DB_PREFIX_.'layered_filter(name, filters, n_categories, date_add)
VALUES (\''.sprintf($this->l('My template %s'), date('Y-m-d')).'\', \''.pSQL(serialize($filter_data)).'\', '.count($filter_data['categories']).', NOW())');
$last_id = Db::getInstance()->Insert_ID();
Db::getInstance()->execute('DELETE FROM '._DB_PREFIX_.'layered_filter_shop WHERE `id_layered_filter` = '.$last_id);
foreach ($shop_list as $id_shop)
Db::getInstance()->execute('INSERT INTO '._DB_PREFIX_.'layered_filter_shop (`id_layered_filter`, `id_shop`)
VALUES('.$last_id.', '.(int)$id_shop.')');
$this->buildLayeredCategories();
}
}
public function buildLayeredCategories()
{
// Get all filter template
$res = Db::getInstance()->executeS('SELECT * FROM '._DB_PREFIX_.'layered_filter ORDER BY date_add DESC');
$categories = array();
// Remove all from layered_category
Db::getInstance()->execute('TRUNCATE '._DB_PREFIX_.'layered_category');
if (!count($res)) // No filters templates defined, nothing else to do
return true;
$sql_to_insert = 'INSERT INTO '._DB_PREFIX_.'layered_category (id_category, id_shop, id_value, type, position, filter_show_limit, filter_type) VALUES ';
$values = false;
foreach ($res as $filter_template)
{
$data = Tools::unSerialize($filter_template['filters']);
foreach ($data['categories'] as $id_category)
{
$n = 0;
if (!in_array($id_category, $categories)) // Last definition, erase preivious categories defined
{
$categories[] = $id_category;
foreach ($data as $key => $value)
if (substr($key, 0, 17) == 'layered_selection')
{
$values = true;
$type = $value['filter_type'];
$limit = $value['filter_show_limit'];
$n++;
foreach ($data['shop_list'] as $id_shop)
{
if ($key == 'layered_selection_stock')
$sql_to_insert .= '('.(int)$id_category.', '.(int)$id_shop.', NULL,\'quantity\','.(int)$n.', '.(int)$limit.', '.(int)$type.'),';
else if ($key == 'layered_selection_subcategories')
$sql_to_insert .= '('.(int)$id_category.', '.(int)$id_shop.', NULL,\'category\','.(int)$n.', '.(int)$limit.', '.(int)$type.'),';
else if ($key == 'layered_selection_condition')
$sql_to_insert .= '('.(int)$id_category.', '.(int)$id_shop.', NULL,\'condition\','.(int)$n.', '.(int)$limit.', '.(int)$type.'),';
else if ($key == 'layered_selection_weight_slider')
$sql_to_insert .= '('.(int)$id_category.', '.(int)$id_shop.', NULL,\'weight\','.(int)$n.', '.(int)$limit.', '.(int)$type.'),';
else if ($key == 'layered_selection_price_slider')
$sql_to_insert .= '('.(int)$id_category.', '.(int)$id_shop.', NULL,\'price\','.(int)$n.', '.(int)$limit.', '.(int)$type.'),';
else if ($key == 'layered_selection_manufacturer')
$sql_to_insert .= '('.(int)$id_category.', '.(int)$id_shop.', NULL,\'manufacturer\','.(int)$n.', '.(int)$limit.', '.(int)$type.'),';
else if (substr($key, 0, 21) == 'layered_selection_ag_')
$sql_to_insert .= '('.(int)$id_category.', '.(int)$id_shop.', '.(int)str_replace('layered_selection_ag_', '', $key).',
\'id_attribute_group\','.(int)$n.', '.(int)$limit.', '.(int)$type.'),';
else if (substr($key, 0, 23) == 'layered_selection_feat_')
$sql_to_insert .= '('.(int)$id_category.', '.(int)$id_shop.', '.(int)str_replace('layered_selection_feat_', '', $key).',
\'id_feature\','.(int)$n.', '.(int)$limit.', '.(int)$type.'),';
}
}
}
}
}
if ($values)
Db::getInstance()->execute(rtrim($sql_to_insert, ','));
}
}