bebeboutik/modules/blocklayered/blocklayered.php

3099 lines
133 KiB
PHP
Raw Normal View History

2016-01-04 12:49:26 +01:00
<?php
/*
* 2007-2011 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-2011 PrestaShop SA
* @version Release: $Revision: 9298 $
* @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;
public function __construct()
{
$this->name = 'blocklayered';
$this->tab = 'front_office_features';
$this->version = 1.4;
$this->author = 'PrestaShop';
$this->need_instance = 0;
parent::__construct();
$this->displayName = $this->l('Layered navigation block');
$this->description = $this->l('Displays a block with layered navigation filters.');
}
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'))
{
Configuration::updateValue('PS_LAYERED_HIDE_0_VALUES', 0);
Configuration::updateValue('PS_LAYERED_SHOW_QTIES', 1);
$this->rebuildLayeredStructure();
$this->rebuildLayeredCache();
self::installPriceIndexTable();
$this->installFriendlyUrlTable();
$this->installIndexableAttributeTable();
$this->installProductAttributeTable();
$this->indexUrl();
$this->indexAttribute();
if (Db::getInstance()->getValue('SELECT count(*) FROM `'._DB_PREFIX_.'product`') < 10000) // Lock price indexation if too many products
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_INDEXED');
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_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,
`price_min` INT NOT NULL, `price_max` INT NOT NULL,
PRIMARY KEY (`id_product`, `id_currency`), INDEX `id_currency` (`id_currency`),
INDEX `price_min` (`price_min`), INDEX `price_max` (`price_max`)) ENGINE = '._MYSQL_ENGINE_);
}
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_);
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_);
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_);
// 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_);
// 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_);
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_);
// 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_);
}
/**
*
* 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",
KEY `id_attribute` (`id_attribute`)
) ENGINE= '._MYSQL_ENGINE_);
}
/**
*
* 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`)
SELECT pac.id_attribute, pa.id_product, ag.id_attribute_group
FROM '._DB_PREFIX_.'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');
return 1;
}
/*
* Url indexation
*/
public function indexUrl($ajax = false, $truncate = true)
{
if ($truncate)
Db::getInstance()->execute('TRUNCATE '._DB_PREFIX_.'layered_friendly_url');
$attributeValuesByLang = 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($attributeValuesByLang[$attribute['id_lang']]))
$attributeValuesByLang[$attribute['id_lang']] = array();
if (!isset($attributeValuesByLang[$attribute['id_lang']]['c'.$attribute['id_name']]))
$attributeValuesByLang[$attribute['id_lang']]['c'.$attribute['id_name']] = array();
$attributeValuesByLang[$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($attributeValuesByLang[$feature['id_lang']]))
$attributeValuesByLang[$feature['id_lang']] = array();
if (!isset($attributeValuesByLang[$feature['id_lang']]['f'.$feature['id_name']]))
$attributeValuesByLang[$feature['id_lang']]['f'.$feature['id_name']] = array();
$attributeValuesByLang[$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($attributeValuesByLang[$category['id_lang']]))
$attributeValuesByLang[$category['id_lang']] = array();
if (!isset($attributeValuesByLang[$category['id_lang']]['category']))
$attributeValuesByLang[$category['id_lang']]['category'] = array();
$attributeValuesByLang[$category['id_lang']]['category'][] = array('name' => $this->l('Categories'),
'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($attributeValuesByLang[$manufacturer['id_lang']]))
$attributeValuesByLang[$manufacturer['id_lang']] = array();
if (!isset($attributeValuesByLang[$manufacturer['id_lang']]['manufacturer']))
$attributeValuesByLang[$manufacturer['id_lang']]['manufacturer'] = array();
$attributeValuesByLang[$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)
$attributeValuesByLang[$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)
$attributeValuesByLang[$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 ($attributeValuesByLang as $id_lang => $attributeValues)
{
// Foreach attributes generate a couple "/<attribute_name>_<atttribute_value>". For example: color_blue
foreach ($attributeValues as $attribute)
foreach ($attribute as $param)
{
$selectedFilters = array();
$link = '/'.str_replace('-', '_', Tools::link_rewrite($param['name'])).'-'.str_replace('-', '_', Tools::link_rewrite($param['value']));
$selectedFilters[$param['type']] = array();
if (!isset($param['id_id_value']))
$param['id_id_value'] = $param['id_value'];
$selectedFilters[$param['type']][$param['id_id_value']] = $param['id_value'];
$urlKey = md5($link);
$idLayeredFriendlyUrl = Db::getInstance()->getValue('SELECT id_layered_friendly_url
FROM `'._DB_PREFIX_.'layered_friendly_url` WHERE `id_lang` = '.$id_lang.' AND `url_key` = \''.$urlKey.'\'');
if ($idLayeredFriendlyUrl == false)
{
Db::getInstance()->AutoExecute(_DB_PREFIX_.'layered_friendly_url', array('url_key' => $urlKey, 'data' => serialize($selectedFilters), 'id_lang' => $id_lang), 'INSERT');
$idLayeredFriendlyUrl = 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.'/'.Language::getIsoById($id_lang).'.php';
if (!array_key_exists($id_lang, $_MODULES))
{
if (!file_exists($file))
return $string;
include($file);
$_MODULES[$id_lang] = $_MODULE;
}
$string = str_replace('\'', '\\\'', $string);
// set array key to lowercase for 1.3 compatibility
$_MODULES[$id_lang] = array_change_key_case($_MODULES[$id_lang]);
$currentKey = '<{'.strtolower( $this->name).'}'.strtolower(_THEME_NAME_).'>'.strtolower($this->name).'_'.md5($string);
$defaultKey = '<{'.strtolower( $this->name).'}prestashop>'.strtolower($this->name).'_'.md5($string);
if (isset($_MODULES[$id_lang][$currentKey]))
$ret = stripslashes($_MODULES[$id_lang][$currentKey]);
else if (isset($_MODULES[$id_lang][Tools::strtolower($currentKey)]))
$ret = stripslashes($_MODULES[$id_lang][Tools::strtolower($currentKey)]);
else if (isset($_MODULES[$id_lang][$defaultKey]))
$ret = stripslashes($_MODULES[$id_lang][$defaultKey]);
else if (isset($_MODULES[$id_lang][Tools::strtolower($defaultKey)]))
$ret = stripslashes($_MODULES[$id_lang][Tools::strtolower($defaultKey)]);
else
$ret = stripslashes($string);
return str_replace('"', '&quot;', $ret);
}
public function hookProductListAssign($params)
{
global $smarty;
if (!Configuration::get('PS_LAYERED_INDEXED'))
return;
// Inform the hook was executed
$params['hookExecuted'] = true;
// List of product to overrride categoryController
$params['catProducts'] = array();
$selectedFilters = $this->getSelectedFilters();
$filterBlock = self::getFilterBlock($selectedFilters);
$title = '';
if (is_array($filterBlock['title_values']))
foreach ($filterBlock['title_values'] as $key => $val)
$title .= ' '.$key.' '.implode('/', $val);
$smarty->assign('categoryNameComplement', $title);
$this->getProducts($selectedFilters, $params['catProducts'], $params['nbProducts'], $p, $n, $pages_nb, $start, $stop, $range);
// Need a nofollow on the pagination links?
$smarty->assign('no_follow', $filterBlock['nofollow']);
}
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::safeOutput(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::safeOutput(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)(Configuration::get('PS_LANG_DEFAULT'));
$langValue = 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)
$langValue[$data['id_lang']] = array('url_name' => $data['url_name'], 'meta_title' => $data['meta_title']);
$return = '<div class="clear"></div>
<label>'.$this->l('Url:').'</label>
<div class="margin-form">
<script type="text/javascript">
flag_fields += \'¤url_name¤meta_title\';
</script>';
foreach ($languages as $language)
$return .= '
<div id="url_name_'.$language['id_lang'].'" style="display: '.($language['id_lang'] == $default_form_language ? 'block' : 'none').'; float: left;">
<input size="33" type="text" name="url_name_'.$language['id_lang'].'" value="'.Tools::safeOutput(@$langValue[$language['id_lang']]['url_name'], true).'" />
<span class="hint" name="help_box">'.$this->l('Invalid characters:').' <>;=#{}_<span class="hint-pointer">&nbsp;</span></span>
<p style="clear: both">'.$this->l('Specific format in url block layered generation').'</p>
</div>';
$return .= $this->displayFlags($languages, $default_form_language, 'flag_fields', 'url_name', true, true);
$return .= '
<div class="clear"></div>
</div>
<label>'.$this->l('Meta title:').' </label>
<div class="margin-form">';
foreach ($languages as $language)
$return .= '
<div id="meta_title_'.$language['id_lang'].'" style="display: '.($language['id_lang'] == $default_form_language ? 'block' : 'none').'; float: left;">
<input size="33" type="text" name="meta_title_'.$language['id_lang'].'" value="'.Tools::safeOutput(@$langValue[$language['id_lang']]['meta_title'], true).'" />
<p style="clear: both">'.$this->l('Specific format for meta title').'</p>
</div>';
$return .= $this->displayFlags($languages, $default_form_language, 'flag_fields', 'meta_title', true, true);
$return .= '
<div class="clear"></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::safeOutput(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)(Configuration::get('PS_LANG_DEFAULT'));
$langValue = 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)
$langValue[$data['id_lang']] = array('url_name' => $data['url_name'], 'meta_title' => $data['meta_title']);
$return = '<div class="clear"></div>
<label>'.$this->l('Url:').'</label>
<div class="margin-form">
<script type="text/javascript">
flag_fields += \'¤url_name¤meta_title\';
</script>';
foreach ($languages as $language)
$return .= '
<div id="url_name_'.$language['id_lang'].'" style="display: '.($language['id_lang'] == $default_form_language ? 'block' : 'none').'; float: left;">
<input size="33" type="text" name="url_name_'.$language['id_lang'].'" value="'.Tools::safeOutput(@$langValue[$language['id_lang']]['url_name'], true).'" />
<span class="hint" name="help_box">'.$this->l('Invalid characters:').' <>;=#{}_<span class="hint-pointer">&nbsp;</span></span>
<p style="clear: both">'.$this->l('Specific format in url block layered generation').'</p>
</div>';
$return .= $this->displayFlags($languages, $default_form_language, 'flag_fields', 'url_name', true, true);
$return .= '
<div class="clear"></div>
</div>
<label>'.$this->l('Meta title:').' </label>
<div class="margin-form">';
foreach ($languages as $language)
$return .= '
<div id="meta_title_'.$language['id_lang'].'" style="display: '.($language['id_lang'] == $default_form_language ? 'block' : 'none').'; float: left;">
<input size="33" type="text" name="meta_title_'.$language['id_lang'].'" value="'.Tools::safeOutput(@$langValue[$language['id_lang']]['meta_title'], true).'" />
<p style="clear: both">'.$this->l('Specific format for meta title').'</p>
</div>';
$return .= $this->displayFlags($languages, $default_form_language, 'flag_fields', 'meta_title', true, true);
$return .= '
<div class="clear"></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::safeOutput(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)(Configuration::get('PS_LANG_DEFAULT'));
$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']);
$langValue = 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)
$langValue[$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="clear"></div>
<label>'.$this->l('Url:').'</label>
<div class="margin-form">
<script type="text/javascript">
flag_fields += \'¤url_name¤meta_title\';
</script>';
foreach ($languages as $language)
$return .= '
<div id="url_name_'.$language['id_lang'].'" style="display: '.($language['id_lang'] == $default_form_language ? 'block' : 'none').'; float: left;">
<input size="33" type="text" name="url_name_'.$language['id_lang'].'" value="'.Tools::safeOutput(@$langValue[$language['id_lang']]['url_name'], true).'" />
<span class="hint" name="help_box">'.$this->l('Invalid characters:').' <>;=#{}_<span class="hint-pointer">&nbsp;</span></span>
<p style="clear: both">'.$this->l('Specific format in url block layered generation').'</p>
</div>';
$return .= $this->displayFlags($languages, $default_form_language, 'flag_fields', 'url_name', true, true);
$return .= '
<div class="clear"></div>
</div>
<label>'.$this->l('Meta title:').' </label>
<div class="margin-form">';
foreach ($languages as $language)
$return .= '
<div id="meta_title_'.$language['id_lang'].'" style="display: '.($language['id_lang'] == $default_form_language ? 'block' : 'none').'; float: left;">
<input size="33" type="text" name="meta_title_'.$language['id_lang'].'" value="'.Tools::safeOutput(@$langValue[$language['id_lang']]['meta_title'], true).'" />
<p style="clear: both">'.$this->l('Specific format for meta title').'</p>
</div>';
$return .= $this->displayFlags($languages, $default_form_language, 'flag_fields', 'meta_title', true, true);
$return .= '
<div class="clear"></div>
</div>
<label>'.$this->l('Indexable:').' </label>
<div class="margin-form">
<input type="radio" '.(($on) ? 'checked="checked"' : '').' value="1" id="indexable_on" name="layered_indexable">
<label for="indexable_on" class="t"><img title="Yes" alt="Enabled" src="../img/admin/enabled.gif"></label>
<input type="radio" '.((!$on) ? 'checked="checked"' : '').' value="0" id="indexable_off" name="layered_indexable">
<label for="indexable_off" class="t"><img title="No" alt="Disabled" src="../img/admin/disabled.gif"></label>
<p>'.$this->l('Use this attribute in url generated by the module block layered navigation').'</p>
</div>';
return $return;
}
public function hookFeatureForm($params)
{
$languages = Language::getLanguages(false);
$default_form_language = (int)(Configuration::get('PS_LANG_DEFAULT'));
$indexable = Db::getInstance(_PS_USE_SQL_SLAVE_)->getValue('SELECT indexable FROM '._DB_PREFIX_.'layered_indexable_feature WHERE id_feature = '.(int)$params['id_feature']);
$langValue = 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)
$langValue[$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="clear"></div>
<label>'.$this->l('Url:').'</label>
<div class="margin-form">
<script type="text/javascript">
flag_fields += \'¤url_name¤meta_title\';
</script>';
foreach ($languages as $language)
$return .= '
<div id="url_name_'.$language['id_lang'].'" style="display: '.($language['id_lang'] == $default_form_language ? 'block' : 'none').'; float: left;">
<input size="33" type="text" name="url_name_'.$language['id_lang'].'" value="'.Tools::safeOutput(@$langValue[$language['id_lang']]['url_name'], true).'" />
<span class="hint" name="help_box">'.$this->l('Invalid characters:').' <>;=#{}_<span class="hint-pointer">&nbsp;</span></span>
<p style="clear: both">'.$this->l('Specific format in url block layered generation').'</p>
</div>';
$return .= $this->displayFlags($languages, $default_form_language, 'flag_fields', 'url_name', true, true);
$return .= '
<div class="clear"></div>
</div>
<label>'.$this->l('Meta title:').' </label>
<div class="margin-form">';
foreach ($languages as $language)
$return .= '
<div id="meta_title_'.$language['id_lang'].'" style="display: '.($language['id_lang'] == $default_form_language ? 'block' : 'none').'; float: left;">
<input size="33" type="text" name="meta_title_'.$language['id_lang'].'" value="'.Tools::safeOutput(@$langValue[$language['id_lang']]['meta_title'], true).'" />
<p style="clear: both">'.$this->l('Specific format for meta title').'</p>
</div>';
$return .= $this->displayFlags($languages, $default_form_language, 'flag_fields', 'meta_title', true, true);
$return .= '
<div class="clear"></div>
</div>
<label>'.$this->l('Indexable:').' </label>
<div class="margin-form">
<input type="radio" '.(($on) ? 'checked="checked"' : '').' value="1" id="indexable_on" name="layered_indexable">
<label for="indexable_on" class="t"><img title="Yes" alt="Enabled" src="../img/admin/enabled.gif"></label>
<input type="radio" '.((!$on) ? 'checked="checked"' : '').' value="0" id="indexable_off" name="layered_indexable">
<label for="indexable_off" class="t"><img title="No" alt="Disabled" src="../img/admin/disabled.gif"></label>
<p>'.$this->l('Use this attribute in url generated by the module block layered navigation').'</p>
</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)
$nbProducts = (int)Db::getInstance()->getValue('SELECT count(*) FROM '._DB_PREFIX_.'product WHERE `active` = 1');
else
$nbProducts = (int)Db::getInstance()->getValue(
'SELECT COUNT(*) FROM `'._DB_PREFIX_.'product` p
LEFT JOIN `'._DB_PREFIX_.'layered_price_index` psi ON (psi.id_product = p.id_product)
WHERE `active` = 1 AND psi.id_product IS NULL');
$maxExecutionTime = @ini_get('max_execution_time');
if ($maxExecutionTime > 5 || $maxExecutionTime <= 0)
$maxExecutionTime = 5;
$startTime = microtime(true);
do
{
$cursor = (int)self::indexPricesUnbreakable((int)$cursor, $full, $smart);
$timeElapsed = microtime(true) - $startTime;
}
while ($cursor < $nbProducts && (Tools::getMemoryLimit()) > memory_get_peak_usage() && $timeElapsed < $maxExecutionTime);
if (($nbProducts > 0 && !$full || $cursor < $nbProducts && $full) && !$ajax)
{
$token = substr(Tools::encrypt('blocklayered/index'), 0, 10);
if (!Tools::file_get_contents(Tools::getProtocol().Tools::getHttpHost().__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 && $nbProducts > 0 && $cursor < $nbProducts && $full)
return '{"cursor": '.$cursor.', "count": '.($nbProducts - $cursor).'}';
else if ($ajax && $nbProducts > 0 && !$full)
return '{"cursor": '.$cursor.', "count": '.($nbProducts).'}';
else
{
Configuration::updateValue('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 id_product
FROM `'._DB_PREFIX_.'product`
WHERE `active` = 1
ORDER by id_product LIMIT '.(int)$cursor.','.(int)$length;
else
$query = '
SELECT p.id_product
FROM `'._DB_PREFIX_.'product` p
LEFT JOIN `'._DB_PREFIX_.'layered_price_index` psi ON (psi.id_product = p.id_product)
WHERE `active` = 1 AND psi.id_product is null
ORDER by 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($idProduct, $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();
}
static $currencyList = null;
if (is_null($currencyList))
$currencyList = Currency::getCurrencies();
$minPrice = array();
$maxPrice = array();
if ($smart)
Db::getInstance()->execute('DELETE FROM `'._DB_PREFIX_.'layered_price_index` WHERE `id_product` = '.(int)$idProduct);
$maxTaxRate = Db::getInstance()->getValue('
SELECT max(t.rate) max_rate
FROM `'._DB_PREFIX_.'product` p
LEFT JOIN `'._DB_PREFIX_.'tax_rules_group` trg ON (trg.id_tax_rules_group = p.id_tax_rules_group)
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)$idProduct.'
GROUP BY id_product');
$productMinPrices = 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)$idProduct);
// Get min price
foreach ($currencyList as $currency)
{
$price = Product::priceCalculation(null, (int)$idProduct, null, null, null, null,
$currency['id_currency'], null, null, false, true, false, true, true,
$specificPriceOutput, true);
if (!isset($maxPrice[$currency['id_currency']]))
$maxPrice[$currency['id_currency']] = 0;
if (!isset($minPrice[$currency['id_currency']]))
$minPrice[$currency['id_currency']] = null;
if ($price > $maxPrice[$currency['id_currency']])
$maxPrice[$currency['id_currency']] = $price;
if ($price == 0)
continue;
if (is_null($minPrice[$currency['id_currency']]) || $price < $minPrice[$currency['id_currency']])
$minPrice[$currency['id_currency']] = $price;
}
foreach ($productMinPrices as $specificPrice)
foreach ($currencyList as $currency)
{
if ($specificPrice['id_currency'] && $specificPrice['id_currency'] != $currency['id_currency'])
continue;
$price = Product::priceCalculation((($specificPrice['id_shop'] == 0) ? null : (int)$specificPrice['id_shop']), (int)$idProduct,
null, (($specificPrice['id_country'] == 0) ? null : $specificPrice['id_country']), null, null,
$currency['id_currency'], (($specificPrice['id_group'] == 0) ? null : $specificPrice['id_group']),
$specificPrice['from_quantity'], false, true, false, true, true, $specificPriceOutput, true);
if (!isset($maxPrice[$currency['id_currency']]))
$maxPrice[$currency['id_currency']] = 0;
if (!isset($minPrice[$currency['id_currency']]))
$minPrice[$currency['id_currency']] = null;
if ($price > $maxPrice[$currency['id_currency']])
$maxPrice[$currency['id_currency']] = $price;
if ($price == 0)
continue;
if (is_null($minPrice[$currency['id_currency']]) || $price < $minPrice[$currency['id_currency']])
$minPrice[$currency['id_currency']] = $price;
}
foreach ($groups as $group)
foreach ($currencyList as $currency)
{
$price = Product::priceCalculation(null, (int)$idProduct, null, null, null, null, (int)$currency['id_currency'], (int)$group['id_group'],
null, false, true, false, true, true, $specificPriceOutput, true);
if (!isset($maxPrice[$currency['id_currency']]))
$maxPrice[$currency['id_currency']] = 0;
if (!isset($minPrice[$currency['id_currency']]))
$minPrice[$currency['id_currency']] = null;
if ($price > $maxPrice[$currency['id_currency']])
$maxPrice[$currency['id_currency']] = $price;
if ($price == 0)
continue;
if (is_null($minPrice[$currency['id_currency']]) || $price < $minPrice[$currency['id_currency']])
$minPrice[$currency['id_currency']] = $price;
}
$values = array();
foreach ($currencyList as $currency)
$values[] = '('.(int)$idProduct.', '.(int)$currency['id_currency'].',
'.(int)$minPrice[$currency['id_currency']].', '.(int)($maxPrice[$currency['id_currency']] * (100 + $maxTaxRate) / 100).')';
Db::getInstance()->Execute('
INSERT INTO `'._DB_PREFIX_.'layered_price_index` (id_product, id_currency, 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 hookHeader($params)
{
global $smarty, $cookie;
// No filters => module disable
if ($filterBlock = $this->getFilterBlock($this->getSelectedFilters()))
if ($filterBlock['nbr_filterBlocks'] == 0)
return false;
if (Tools::getValue('id_category', Tools::getValue('id_category_layered', 1)) == 1)
return;
$idLang = (int)$cookie->id_lang;
$category = new Category((int)Tools::getValue('id_category'));
$categoryMetas = Tools::getMetaTags($idLang, '');
$categoryTitle = (empty($category->meta_title[$idLang]) ? $category->name[$idLang] : $category->meta_title[$idLang]);
// Generate meta title and meta description
$title = '';
if (is_array($filterBlock['title_values']))
foreach ($filterBlock['title_values'] as $key => $val)
$title .= $key.' '.implode('/', $val).' ';
$title = rtrim($title, ' ');
if (!empty($title))
{
$smarty->assign('meta_title', ucfirst(strtolower(preg_replace('/^'.$categoryTitle.'/', $categoryTitle.' '.$title, $categoryMetas['meta_title']))));
$smarty->assign('meta_description', rtrim($categoryTitle.' '.$title.' '.$categoryMetas['meta_description'], ' '));
}
else
$smarty->assign('meta_title', ucfirst(strtolower($categoryMetas['meta_title'])));
$metaKeyWordsComplement = substr(str_replace(' ', ', ', strtolower($title)), 1000);
if (!empty($metaKeyWordsComplement))
$smarty->assign('meta_keywords', rtrim($categoryTitle.', '.$metaKeyWordsComplement.', '.$categoryMetas['meta_keywords'], ', '));
Tools::addJS(($this->_path).'blocklayered.js');
Tools::addJS(_PS_JS_DIR_.'jquery/jquery-ui-1.8.10.custom.min.js');
Tools::addCSS(_PS_CSS_DIR_.'jquery-ui-1.8.10.custom.css', 'all');
Tools::addCSS(($this->_path).'blocklayered.css', 'all');
Tools::addJS(_PS_JS_DIR_.'jquery/jquery.scrollTo-1.4.2-min.js');
}
public function hookFooter($params)
{
if (basename($_SERVER['PHP_SELF']) == 'category.php')
return '
<script type="text/javascript">
//<![CDATA[
$(document).ready(function()
{
$(\'#selectPrductSort\').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 (!$params['category']->active)
$this->hookCategoryDeletion($params);
}
public function hookCategoryDeletion($params)
{
Db::getInstance()->Execute('DELETE FROM '._DB_PREFIX_.'layered_category WHERE id_category = '.(int)$params['category']->id);
}
public function getContent()
{
global $cookie;
$html = '';
if (Tools::isSubmit('SubmitFilter'))
{
if (!Tools::getValue('layered_tpl_name'))
$html .= '
<div class="error">
<span style="float:right">
<a href="" id="hideError"><img src="../img/admin/close.png" alt="X"></a>
</span>
<img src="../img/admin/error2.png">'.$this->l('Filter template name required (cannot be empty)').'
</div>';
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'));
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'];
}
if (count($_POST['categoryBox']))
{
/* Clean categoryBox before use */
if (isset($_POST['categoryBox']) && is_array($_POST['categoryBox']))
foreach ($_POST['categoryBox'] as &$categoryBoxTmp)
$categoryBoxTmp = (int)$categoryBoxTmp;
Db::getInstance()->Execute('DELETE FROM '._DB_PREFIX_.'layered_category WHERE id_category IN ('.implode(',', array_map('intval', $_POST['categoryBox'])).')');
$filterValues = array();
foreach ($_POST['categoryBox'] as $idc)
$filterValues['categories'][] = (int)$idc;
$sqlToInsert = 'INSERT INTO '._DB_PREFIX_.'layered_category (id_category, id_value, type, position) VALUES ';
foreach ($_POST['categoryBox'] as $id_category_layered)
{
$n = 0;
foreach ($_POST as $key => $value)
if (substr($key, 0, 17) == 'layered_selection' && $value == 'on')
{
$filterValues[$key] = $value;
$n++;
if ($key == 'layered_selection_stock')
$sqlToInsert .= '('.(int)$id_category_layered.',NULL,\'quantity\','.(int)$n.'),';
else if ($key == 'layered_selection_subcategories')
$sqlToInsert .= '('.(int)$id_category_layered.',NULL,\'category\','.(int)$n.'),';
else if ($key == 'layered_selection_condition')
$sqlToInsert .= '('.(int)$id_category_layered.',NULL,\'condition\','.(int)$n.'),';
else if ($key == 'layered_selection_weight_slider')
$sqlToInsert .= '('.(int)$id_category_layered.',NULL,\'weight\','.(int)$n.'),';
else if ($key == 'layered_selection_price_slider')
$sqlToInsert .= '('.(int)$id_category_layered.',NULL,\'price\','.(int)$n.'),';
else if ($key == 'layered_selection_manufacturer')
$sqlToInsert .= '('.(int)$id_category_layered.',NULL,\'manufacturer\','.(int)$n.'),';
else if (substr($key, 0, 21) == 'layered_selection_ag_')
$sqlToInsert .= '('.(int)$id_category_layered.','.(int)str_replace('layered_selection_ag_', '', $key).',\'id_attribute_group\','.(int)$n.'),';
else if (substr($key, 0, 23) == 'layered_selection_feat_')
$sqlToInsert .= '('.(int)$id_category_layered.','.(int)str_replace('layered_selection_feat_', '', $key).',\'id_feature\','.(int)$n.'),';
}
}
Db::getInstance()->Execute(rtrim($sqlToInsert, ','));
$valuesToInsert = array(
'name' => pSQL(Tools::getValue('layered_tpl_name')),
'filters' => pSQL(serialize($filterValues)),
'n_categories' => (int)count($filterValues['categories']),
'date_add' => date('Y-m-d H:i:s'));
if (isset($_POST['id_layered_filter']) && $_POST['id_layered_filter'])
$valuesToInsert['id_layered_filter'] = (int)Tools::getValue('id_layered_filter');
Db::getInstance()->AutoExecute(_DB_PREFIX_.'layered_filter', $valuesToInsert, 'INSERT');
echo '<div class="conf"><img src="../img/admin/ok2.png" alt="" />
'.$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.')).'</div>';
}
}
}
else if (Tools::isSubmit('submitLayeredSettings'))
{
Configuration::updateValue('PS_LAYERED_HIDE_0_VALUES', Tools::getValue('ps_layered_hide_0_values'));
Configuration::updateValue('PS_LAYERED_SHOW_QTIES', Tools::getValue('ps_layered_show_qties'));
$html .= '
<div class="conf">
<img src="../img/admin/ok2.png" alt="" /> '.$this->l('Settings saved successfully').'
</div>';
}
else if (isset($_GET['deleteFilterTemplate']))
{
$layeredValues = Db::getInstance(_PS_USE_SQL_SLAVE_)->getValue('
SELECT filters
FROM '._DB_PREFIX_.'layered_filter
WHERE id_layered_filter = '.(int)$_GET['id_layered_filter']);
if ($layeredValues)
{
Db::getInstance()->Execute('DELETE FROM '._DB_PREFIX_.'layered_filter WHERE id_layered_filter = '.(int)$_GET['id_layered_filter'].' LIMIT 1');
$html .= '
<div class="conf">
<img src="../img/admin/ok2.png" alt="" /> '.$this->l('Filters template deleted, categories updated (reverted to default Filters template).').'
</div>';
}
else
{
$html .= '
<div class="error">
<img src="../img/admin/error.png" alt="" title="" /> '.$this->l('Filters template not found').'
</div>';
}
}
$html .= '
<div id="ajax-message-ok" class="conf ajax-message" style="display: none">
<img alt="" src="../img/admin/ok2.png"><span class="message"></span>
</div>
<div id="ajax-message-ko" class="error ajax-message" style="display: none">
<img src="../img/admin/error2.png" alt="" /><span class="message"></span>
</div>
<h2>'.$this->l('Layered navigation').'</h2>
<fieldset class="width4">
<legend><img src="../img/admin/cog.gif" alt="" />'.$this->l('Indexes and caches').'</legend>
<span id="indexing-warning" style="display: none; color:red; font-weight: bold">'.$this->l('Indexing are in progress. Please don\'t leave this page').'<br/><br/></span>';
if (!Configuration::get('PS_LAYERED_INDEXED'))
$html .= '
<script type="text/javascript">
$(document).ready(function() {
$(\'#url-indexer\').click();
$(\'#full-index\').click();
});
</script>';
$categoryList = array();
foreach (Db::getInstance()->ExecuteS('SELECT id_category FROM `'._DB_PREFIX_.'category`') as $category)
if ($category['id_category'] != 1)
$categoryList[] = $category['id_category'];
$html .= '
<a class="bold ajaxcall-recurcive"
style="width: 250px; text-align:center;display:block;border:1px solid #aaa;text-decoration:none;background-color:#fafafa;color:#123456;margin:2px;padding:2px"
href="'.Tools::getProtocol().Tools::getHttpHost().__PS_BASE_URI__.'modules/blocklayered/blocklayered-price-indexer.php'.'?token='.substr(Tools::encrypt('blocklayered/index'), 0, 10).'">'.
$this->l('Index all missing prices').'</a>
<br />
<a class="bold ajaxcall-recurcive"
style="width: 250px; text-align:center;display:block;border:1px solid #aaa;text-decoration:none;background-color:#fafafa;color:#123456;margin:2px;padding:2px" id="full-index"
href="'.Tools::getProtocol().Tools::getHttpHost().__PS_BASE_URI__.'modules/blocklayered/blocklayered-price-indexer.php'.'?token='.substr(Tools::encrypt('blocklayered/index'), 0, 10).'&full=1">'.
$this->l('Re-build entire price index').'</a>
<br />
<a class="bold ajaxcall" id="attribute-indexer"
style="width: 250px; text-align:center;display:block;border:1px solid #aaa;text-decoration:none;background-color:#fafafa;color:#123456;margin:2px;padding:2px" id="full-index"
href="'.Tools::getProtocol().Tools::getHttpHost().__PS_BASE_URI__.'modules/blocklayered/blocklayered-attribute-indexer.php'.'?token='.substr(Tools::encrypt('blocklayered/index'), 0, 10).'">'.
$this->l('Build attribute index').'</a>
<br />
<a class="bold ajaxcall" id="url-indexer"
style="width: 250px; text-align:center;display:block;border:1px solid #aaa;text-decoration:none;background-color:#fafafa;color:#123456;margin:2px;padding:2px" id="full-index"
href="'.Tools::getProtocol().Tools::getHttpHost().__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 />
'.$this->l('You can set a cron job that will re-build price index using the following URL:').'<br /><b>'.
Tools::getProtocol().Tools::getHttpHost().__PS_BASE_URI__.'modules/blocklayered/blocklayered-price-indexer.php'.'?token='.substr(Tools::encrypt('blocklayered/index'), 0, 10).'&full=1</b>
<br />
'.$this->l('You can set a cron job that will re-build url index using the following URL:').'<br /><b>'.
Tools::getProtocol().Tools::getHttpHost().__PS_BASE_URI__.'modules/blocklayered/blocklayered-url-indexer.php'.'?token='.substr(Tools::encrypt('blocklayered/index'), 0, 10).'&truncate=1</b>
<br />
'.$this->l('You can set a cron job that will re-build attribute index using the following URL:').'<br /><b>'.
Tools::getProtocol().Tools::getHttpHost().__PS_BASE_URI__.'modules/blocklayered/blocklayered-attribute-indexer.php'.'?token='.substr(Tools::encrypt('blocklayered/index'), 0, 10).'</b>
<br /><br />
'.$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;
$.ajax({
url: this.href+\'&ajax=1\',
context: this,
dataType: \'json\',
success: function(res)
{
this.running = false;
this.restartAllowed = true;
$(\'#indexing-warning\').hide();
$(this).html(this.legend);
$(\'#ajax-message-ok span\').html(\''.addslashes($this->l('Url indexation finished')).'\');
$(\'#ajax-message-ok\').show();
return;
},
error: function(res)
{
this.restartAllowed = true;
$(\'#indexing-warning\').hide();
$(\'#ajax-message-ko span\').html(\''.addslashes($this->l('Url 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\',
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>
</fieldset>
<br />
<fieldset class="width4">
<legend><img src="../img/admin/cog.gif" alt="" />'.$this->l('Existing filters templates').'</legend>';
$filtersTemplates = Db::getInstance(_PS_USE_SQL_SLAVE_)->ExecuteS('SELECT * FROM '._DB_PREFIX_.'layered_filter ORDER BY date_add DESC');
if (count($filtersTemplates))
{
$html .= '<p>'.count($filtersTemplates).' '.$this->l('filters templates are configured:').'</p>
<table id="table-filter-templates" class="table" style="width: 700px;">
<tr>
<th>'.$this->l('ID').'</th>
<th>'.$this->l('Name').'</th>
<th>'.$this->l('Categories').'</th>
<th>'.$this->l('Created on').'</th>
<th>'.$this->l('Actions').'</th>
</tr>';
foreach ($filtersTemplates as $filtersTemplate)
{
/* Clean request URI first */
$_SERVER['REQUEST_URI'] = preg_replace('/&deleteFilterTemplate=[0-9]*&id_layered_filter=[0-9]*/', '', $_SERVER['REQUEST_URI']);
$html .= '
<tr>
<td>'.(int)$filtersTemplate['id_layered_filter'].'</td>
<td style="text-align: left; padding-left: 10px; width: 270px;">'.$filtersTemplate['name'].'</td>
<td style="text-align: center;">'.(int)$filtersTemplate['n_categories'].'</td>
<td>'.Tools::displayDate($filtersTemplate['date_add'], (int)$cookie->id_lang, true).'</td>
<td>
<a href="#" onclick="updElements('.($filtersTemplate['n_categories'] ? 0 : 1).', '.(int)$filtersTemplate['id_layered_filter'].');">
<img src="../img/admin/edit.gif" alt="" title="'.$this->l('Edit').'" /></a>
<a href="'.Tools::safeOutput($_SERVER['REQUEST_URI']).'&deleteFilterTemplate=1&id_layered_filter='.(int)$filtersTemplate['id_layered_filter'].'"
onclick="return confirm(\''.addslashes($this->l('Delete filter template #')).(int)$filtersTemplate['id_layered_filter'].$this->l('?').'\');">
<img src="../img/admin/delete.gif" alt="" title="'.$this->l('Delete').'" /></a>
</td>
</tr>';
}
$html .= '
</table>';
}
else
$html .= $this->l('No filter template found.');
$html .= '
</fieldset><br />
<fieldset class="width4">
<legend><img src="../img/admin/cog.gif" alt="" />'.$this->l('Build your own filters template').'</legend>
<link rel="stylesheet" href="'._PS_CSS_DIR_.'jquery-ui-1.8.10.custom.css" />
<style type="text/css">
#error-filter-name { display: none; }
#layered_container_left ul, #layered_container_right ul { list-style-type: none; padding-left: 0px; }
.ui-effects-transfer { border: 1px solid #CCC; }
.ui-state-highlight { height: 1.5em; line-height: 1.2em; }
ul#selected_filters, #layered_container_right ul { list-style-type: none; margin: 0; padding: 0; }
ul#selected_filters li, #layered_container_right ul li { width: 326px; font-size: 11px; padding: 8px 9px 7px 20px; height: 14px; margin-bottom: 5px; }
ul#selected_filters li span.ui-icon { position: absolute; margin-top: -2px; margin-left: -18px; }
#layered_container_right ul li span { display: none; }
#layered_container_right ul li { padding-left: 8px; }
#layered_container_left ul li { cursor: move; }
#layered-cat-counter { display: none; }
#layered-step-2, #layered-step-3 { display: none; }
#table-filter-templates tr th, #table-filter-templates tr td { text-align: center; }
</style>
<form action="'.Tools::safeOutput($_SERVER['REQUEST_URI']).'" method="post" onsubmit="return checkForm();">';
$html .= '
<h2>'.$this->l('Step 1/3 - Select categories').'</h2>
<p style="margin-top: 20px;">'.$this->l('Use this template for:').'
<input type="radio" id="scope_1" name="scope" value="1" style="margin-left: 15px;" onclick="$(\'#error-treeview\').hide(); $(\'#layered-step-2\').show(); updElements(1, 0);" />
<label for="scope_1" style="float: none;">'.$this->l('All categories').'</label>
<input type="radio" id="scope_2" name="scope" value="2" style="margin-left: 15px;" onclick="$(\'label a#inline\').click(); $(\'#layered-step-2\').show();" />
<label for="scope_2" style="float: none;"><a id="inline" href="#layered-categories-selection" style="text-decoration: underline;">'.$this->l('Specific').'</a>
'.$this->l('categories').' (<span id="layered-cat-counter"></span> '.$this->l('selected').')</label>
</p>
<div id="error-treeview" class="error" style="display: none;">
<img src="../img/admin/error2.png" alt="" /> '.$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>';
$trads = array();
$selectedCat = array();
foreach (Helper::$translationsKeysForAdminCategorieTree as $key)
$trads[$key] = $this->l($key);
$html .= Helper::renderAdminCategorieTree($trads, $selectedCat, 'categoryBox');
$html .= '
<br />
<center><input type="button" class="button" value="'.$this->l('Save this selection').'" onclick="$.fancybox.close();" /></center>
</div>
</div>
<div id="layered-step-2">
<hr size="1" noshade />
<h2>'.$this->l('Step 2/3 - Select filters').'</h2>
<div id="layered_container">
<div id="layered_container_left" style="width: 360px; float: left; height: 200px; overflow-y: auto;">
<h3>'.$this->l('Selected filters').' <span id="num_sel_filters">(0)</span></h3>
<p id="no-filters">'.$this->l('No filters selected yet.').'</p>
<ul id="selected_filters"></ul>
</div>
<div id="layered-ajax-refresh">
'.$this->ajaxCallBackOffice().'
</div>
</div>
<div class="clear"></div>
<hr size="1" noshade />
<script type="text/javascript" src="'.__PS_BASE_URI__.'js/jquery/jquery-ui-1.8.10.custom.min.js"></script>
<script type="text/javascript" src="'.__PS_BASE_URI__.'js/jquery/jquery.fancybox-1.3.4.js"></script>
<link type="text/css" rel="stylesheet" href="'.__PS_BASE_URI__.'css/jquery.fancybox-1.3.4.css" />
<script type="text/javascript">
function updLayCounters()
{
$(\'#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();
else
$(\'#layered-step-3\').hide();
}
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: \'GET\',
url: \''.__PS_BASE_URI__.'\' + \'modules/blocklayered/blocklayered-ajax-back.php\',
data: \'layered_token='.substr(Tools::encrypt('blocklayered/index'), 0, 10).'&\'
+(all ? \'\' : $(\'input[name="categoryBox[]"]\').serialize()+\'&\')
+(id_layered_filter ? \'id_layered_filter=\'+parseInt(id_layered_filter) : \'\'),
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();
}
});
}
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();
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();
updHeight();
updPositions();
if ($(\'#layered_container_left ul\').length == 0)
$(\'p#no-filters\').show();
});
}
enableSortable();
});
$(\'label a#inline\').fancybox({
\'hideOnContentClick\': false,
\'onClosed\': function() {
updCatCounter();
if ($(\'#categories-treeview\').find(\'input:checked\').length == 0)
$(\'#error-treeview\').show(500);
else
$(\'#error-treeview\').hide(500);
updElements(0, 0);
},
\'onComplete\': function() {
if($(\'#categories-treeview li#1\').attr(\'cleaned\'))
return;
if($(\'#categories-treeview li#1\').attr(\'cleaned\', true))
$(\'#categories-treeview li#1\').removeClass(\'static\');
$(\'#categories-treeview li#1 span\').trigger(\'click\');
$(\'#categories-treeview li#1\').children(\'div\').remove();
$(\'#categories-treeview li#1\').
removeClass(\'collapsable lastCollapsable\').
addClass(\'last static\');
$(\'.hitarea\').click(function(it)
{
$(this).parent().find(\'> .category_label\').click();
});
}
});
updHeight();
updLayCounters();
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 id="layered-step-3">
<div id="error-filter-name" class="error">
<img src="../img/admin/error.png" alt="" title="" />'.$this->l('Errors:').'
<ul>
<li>'.$this->l('Filter template name required (cannot be empty)').'</li>
</ul>
</div>
<h2>'.$this->l('Step 3/3 - Name your template').'</h2>
<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="'.$this->l('My template').' '.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>
<hr size="1" noshade />
<br />
<center><input type="submit" class="button" name="SubmitFilter" value="'.$this->l('Save this filter template').'" /></center>
</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($filtersTemplates).'" />
</form>
</fieldset><br />
<fieldset class="width2">
<legend><img src="../img/admin/cog.gif" alt="" /> '.$this->l('Configuration').'</legend>
<form action="'.Tools::safeOutput($_SERVER['REQUEST_URI']).'" method="post">
<table border="0" style="font-size: 11px; width: 100%; margin: 0 auto;" class="table">
<tr>
<th style="text-align: center;">'.$this->l('Option').'</th>
<th style="text-align: center;">'.$this->l('Value').'</th>
</tr>
<tr>
<td style="text-align: right;">'.$this->l('Hide filter values with no product is matching').'</td>
<td>
<img src="../img/admin/enabled.gif" alt="'.$this->l('Yes').'" title="'.$this->l('Yes').'" />
'.$this->l('Yes').' <input type="radio" name="ps_layered_hide_0_values" value="1" '.(Configuration::get('PS_LAYERED_HIDE_0_VALUES') ? 'checked="checked"' : '').' />
<img src="../img/admin/disabled.gif" alt="'.$this->l('No').'" title="'.$this->l('No').'" style="margin-left: 10px;" />
'.$this->l('No').' <input type="radio" name="ps_layered_hide_0_values" value="0" '.(!Configuration::get('PS_LAYERED_HIDE_0_VALUES') ? 'checked="checked"' : '').' />
</td>
</tr>
<tr>
<td style="text-align: right;">'.$this->l('Show the number of matching products').'</td>
<td>
<img src="../img/admin/enabled.gif" alt="'.$this->l('Yes').'" title="'.$this->l('Yes').'" />
'.$this->l('Yes').' <input type="radio" name="ps_layered_show_qties" value="1" '.(Configuration::get('PS_LAYERED_SHOW_QTIES') ? 'checked="checked"' : '').' />
<img src="../img/admin/disabled.gif" alt="'.$this->l('No').'" title="'.$this->l('No').'" style="margin-left: 10px;" />
'.$this->l('No').' <input type="radio" name="ps_layered_show_qties" value="0" '.(!Configuration::get('PS_LAYERED_SHOW_QTIES') ? 'checked="checked"' : '').' />
</td>
</tr>
</table>
<p style="text-align: center;"><input type="submit" class="button" name="submitLayeredSettings" value="'.$this->l('Save configuration').'" /></p>
</form>
</fieldset>';
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));
$urlAttributes = explode('/', $url);
array_shift($urlAttributes);
$selectedFilters = array('category' => array());
if (!empty($urlAttributes))
{
foreach ($urlAttributes as $urlAttribute)
{
$urlParameters = explode('-', $urlAttribute);
$attributeName = array_shift($urlParameters);
if (in_array($attributeName, array('price', 'weight')))
$selectedFilters[$attributeName] = array($urlParameters[0], $urlParameters[1]);
else
{
foreach ($urlParameters as $urlParameter)
{
$data = Db::getInstance()->getValue('SELECT data FROM `'._DB_PREFIX_.'layered_friendly_url` WHERE `url_key` = \''.md5('/'.$attributeName.'-'.$urlParameter).'\'');
if ($data)
foreach (unserialize($data) as $keyParams => $params)
{
if (!isset($selectedFilters[$keyParams]))
$selectedFilters[$keyParams] = array();
foreach ($params as $keyParam => $param)
{
if (!isset($selectedFilters[$keyParams][$keyParam]))
$selectedFilters[$keyParams][$keyParam] = array();
$selectedFilters[$keyParams][$keyParam] = $param;
}
}
}
}
}
return $selectedFilters;
}
}
/* Analyze all the filters selected by the user and store them into a tab */
$selectedFilters = 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]))
{
$tmpTab = explode('_', $value);
$value = $tmpTab[0];
$id_key = false;
if (isset($tmpTab[1]))
$id_key = $tmpTab[1];
if ($res[1] == 'condition' && in_array($value, array('new', 'used', 'refurbished')))
$selectedFilters['condition'][] = $value;
else if ($res[1] == 'quantity' && (!$value || $value == 1))
$selectedFilters['quantity'][] = $value;
else if (in_array($res[1], array('category', 'manufacturer')))
{
if (!isset($selectedFilters[$res[1].($id_key ? '_'.$id_key : '')]))
$selectedFilters[$res[1].($id_key ? '_'.$id_key : '')] = array();
$selectedFilters[$res[1].($id_key ? '_'.$id_key : '')][] = (int)$value;
}
else if (in_array($res[1], array('id_attribute_group', 'id_feature')))
{
if (!isset($selectedFilters[$res[1]]))
$selectedFilters[$res[1]] = array();
$selectedFilters[$res[1]][(int)$value] = $id_key.'_'.(int)$value;
}
else if ($res[1] == 'weight')
$selectedFilters[$res[1]] = $tmpTab;
else if ($res[1] == 'price')
$selectedFilters[$res[1]] = $tmpTab;
}
}
return $selectedFilters;
}
public function getProductByFilters($selectedFilters = 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;
$queryFiltersWhere = ' AND p.active = 1';
$queryFiltersFrom = '';
$parent = new Category((int)$id_parent);
if (!count($selectedFilters['category']))
$queryFiltersFrom .= ' 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.nleft >= '.(int)$parent->nleft.' AND c.nright <= '.(int)$parent->nright.')';
foreach ($selectedFilters as $key => $filterValues)
{
if (!count($filterValues))
continue;
preg_match('/^(.*[^_0-9])/', $key, $res);
$key = $res[1];
switch ($key)
{
case 'id_feature':
$subQueries = array();
foreach ($filterValues as $filterValue)
{
$filterValueArray = explode('_', $filterValue);
if (!isset($subQueries[$filterValueArray[0]]))
$subQueries[$filterValueArray[0]] = array();
$subQueries[$filterValueArray[0]][] = 'fp.`id_feature_value` = '.(int)$filterValueArray[1];
}
foreach ($subQueries as $subQuery)
{
$queryFiltersWhere .= ' AND p.id_product IN (SELECT `id_product` FROM `'._DB_PREFIX_.'feature_product` fp WHERE ';
$queryFiltersWhere .= implode(' OR ', $subQuery).') ';
}
break;
case 'id_attribute_group':
$subQueries = array();
foreach ($filterValues as $filterValue)
{
$filterValueArray = explode('_', $filterValue);
if (!isset($subQueries[$filterValueArray[0]]))
$subQueries[$filterValueArray[0]] = array();
$subQueries[$filterValueArray[0]][] = 'pac.`id_attribute` = '.(int)$filterValueArray[1];
}
foreach ($subQueries as $subQuery)
{
$queryFiltersWhere .= ' 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 ';
$queryFiltersWhere .= implode(' OR ', $subQuery).') ';
}
break;
case 'category':
$queryFiltersWhere .= ' AND p.id_product IN (SELECT id_product FROM '._DB_PREFIX_.'category_product cp WHERE ';
foreach ($selectedFilters['category'] as $id_category)
$queryFiltersWhere .= 'cp.`id_category` = '.(int)$id_category.' OR ';
$queryFiltersWhere = rtrim($queryFiltersWhere, 'OR ').')';
break;
case 'quantity':
if (count($selectedFilters['quantity']) == 2)
break;
$queryFiltersWhere .= ' AND p.quantity '.(!$selectedFilters['quantity'][0] ? '=' : '>').' 0';
break;
case 'manufacturer':
$queryFiltersWhere .= ' AND p.id_manufacturer IN ('.implode($selectedFilters['manufacturer'], ',').')';
break;
case 'condition':
if (count($selectedFilters['condition']) == 3)
break;
$queryFiltersWhere .= ' AND p.condition IN (';
foreach ($selectedFilters['condition'] as $cond)
$queryFiltersWhere .= '\''.$cond.'\',';
$queryFiltersWhere = rtrim($queryFiltersWhere, ',').')';
break;
case 'weight':
if ($selectedFilters['weight'][0] != 0 || $selectedFilters['weight'][1] != 0)
$queryFiltersWhere .= ' AND p.`weight` BETWEEN '.(float)($selectedFilters['weight'][0] - 0.001).' AND '.(float)($selectedFilters['weight'][1] + 0.001);
case 'price':
if (isset($selectedFilters['price']))
{
if ($selectedFilters['price'][0] != 0 || $selectedFilters['price'][1] != 0)
{
$priceFilter = array();
$priceFilter['min'] = (float)($selectedFilters['price'][0]);
$priceFilter['max'] = (float)($selectedFilters['price'][1]);
}
}
else
$priceFilter = false;
break;
}
}
$idCurrency = Currency::getCurrent()->id;
$priceFilterQueryIn = ''; // All products with price range between price filters limits
$priceFilterQueryOut = ''; // All products with a price filters limit on it price range
if (isset($priceFilter) && $priceFilter)
{
$priceFilterQueryIn = 'INNER JOIN `'._DB_PREFIX_.'layered_price_index` psi
ON psi.price_min >= '.(int)$priceFilter['min'].'
AND psi.price_max <= '.(int)$priceFilter['max'].'
AND psi.`id_product` = p.`id_product`
AND psi.`id_currency` = '.(int)$idCurrency;
$priceFilterQueryOut = 'INNER JOIN `'._DB_PREFIX_.'layered_price_index` psi
ON
((psi.price_min <= '.(int)$priceFilter['min'].' AND psi.price_max >= '.(int)$priceFilter['min'].')
OR
(psi.price_max >= '.(int)$priceFilter['max'].' AND psi.price_min <= '.(int)$priceFilter['max'].'))
AND psi.`id_product` = p.`id_product`
AND psi.`id_currency` = '.(int)$idCurrency;
}
$allProductsOut = Db::getInstance(_PS_USE_SQL_SLAVE_)->ExecuteS('
SELECT p.`id_product` id_product
FROM `'._DB_PREFIX_.'product` p
'.$priceFilterQueryOut.'
'.$queryFiltersFrom.'
WHERE 1 '.$queryFiltersWhere.' GROUP BY id_product', false);
$allProductsIn = Db::getInstance(_PS_USE_SQL_SLAVE_)->ExecuteS('
SELECT p.`id_product` id_product
FROM `'._DB_PREFIX_.'product` p
'.$priceFilterQueryIn.'
'.$queryFiltersFrom.'
WHERE 1 '.$queryFiltersWhere.' GROUP BY id_product', false);
$productIdList = array();
while ($product = DB::getInstance()->nextRow($allProductsIn))
$productIdList[] = (int)$product['id_product'];
while ($product = DB::getInstance()->nextRow($allProductsOut))
if (isset($priceFilter) && $priceFilter)
{
$price = (int)Product::getPriceStatic($product['id_product']); // Cast to int because we don't care about cents
if ($price < $priceFilter['min'] || $price > $priceFilter['max'])
continue;
$productIdList[] = (int)$product['id_product'];
}
$this->nbr_products = count($productIdList);
if ($this->nbr_products == 0)
$this->products = array();
else
{
$n = (int)Tools::getValue('n', Configuration::get('PS_PRODUCTS_PER_PAGE'));
$this->products = Db::getInstance(_PS_USE_SQL_SLAVE_)->ExecuteS('
SELECT p.id_product, p.on_sale, p.out_of_stock, p.available_for_order, p.quantity, p.minimal_quantity, p.id_category_default, p.customizable, p.show_price, p.`weight`,
p.ean13, pl.available_later, pl.description_short, pl.link_rewrite, pl.name, i.id_image, il.legend, m.name manufacturer_name, p.condition, p.id_manufacturer,
DATEDIFF(p.`date_add`,
DATE_SUB(NOW(), INTERVAL '.(Validate::isUnsignedInt(Configuration::get('PS_NB_DAYS_NEW_PRODUCT')) ? Configuration::get('PS_NB_DAYS_NEW_PRODUCT') : 20).' DAY)) > 0 AS new
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`
LEFT JOIN '._DB_PREFIX_.'product_lang pl ON (pl.id_product = p.id_product)
LEFT JOIN '._DB_PREFIX_.'image i ON (i.id_product = p.id_product AND i.cover = 1)
LEFT JOIN '._DB_PREFIX_.'image_lang il ON (i.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 p.`active` = 1 AND c.nleft >= '.(int)$parent->nleft.' AND c.nright <= '.(int)$parent->nright.' AND pl.id_lang = '.(int)$cookie->id_lang.'
AND p.id_product IN ('.implode(',', $productIdList).')'
.' GROUP BY p.id_product ORDER BY '.Tools::getProductsOrder('by', Tools::getValue('orderby'), true).' '.Tools::getProductsOrder('way', Tools::getValue('orderway')).
' LIMIT '.(((int)Tools::getValue('p', 1) - 1) * $n.','.$n));
}
return $this->products;
}
public function getFilterBlock($selectedFilters = array())
{
global $cookie;
static $cache = null;
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);
/* 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.'
GROUP BY `type`, id_value ORDER BY position ASC');
// Remove all empty selected filters
foreach ($selectedFilters as $key => $value)
switch ($key)
{
case 'price':
case 'weight':
if ($value[0] == '' && $value[1] == '' || $value[0] == 0 && $value[1] == 0)
unset($selectedFilters[$key]);
break;
default:
if ($value == '')
unset($selectedFilters[$key]);
break;
}
$filterBlocks = array();
foreach ($filters as $filter)
{
$sqlQuery = array('select' => '', 'from' => '', 'join' => '', 'where' => '', 'group' => '');
switch ($filter['type'])
{
// conditions + quantities + weight + price
case 'price':
case 'weight':
case 'condition':
case 'quantity':
$sqlQuery['select'] = '
SELECT p.`id_product`, p.`condition`, p.`id_manufacturer`, p.`quantity`, p.`weight`
';
$sqlQuery['from'] = '
FROM '._DB_PREFIX_.'product p ';
$sqlQuery['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 c.nleft >= '.(int)$parent->nleft.' AND c.nright <= '.(int)$parent->nright.') ';
$sqlQuery['where'] = 'WHERE p.`active` = 1 ';
break;
case 'manufacturer':
$sqlQuery['select'] = 'SELECT m.name, count(p.id_product) nbr, m.id_manufacturer ';
$sqlQuery['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 AND p.active = 1)
INNER JOIN '._DB_PREFIX_.'manufacturer m ON (m.id_manufacturer = p.id_manufacturer) ';
$sqlQuery['where'] = '
WHERE c.nleft >= '.(int)$parent->nleft.' AND c.nright <= '.(int)$parent->nright.' ';
$sqlQuery['group'] = ' GROUP BY p.id_manufacturer ';
break;
case 'id_attribute_group':// attribute group
$sqlQuery['select'] = '
SELECT count(lpa.id_attribute) 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';
$sqlQuery['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)$cookie->id_lang.'
INNER JOIN '._DB_PREFIX_.'product as p
ON p.id_product = lpa.id_product
AND p.active = 1
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)$cookie->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)$cookie->id_lang.')
LEFT JOIN '._DB_PREFIX_.'layered_indexable_attribute_lang_value lial
ON (lial.id_attribute = lpa.id_attribute AND lial.id_lang = '.(int)$cookie->id_lang.') ';
$sqlQuery['where'] = 'WHERE a.id_attribute_group = '.(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 c.nleft >= '.(int)$parent->nleft.' AND c.nright <= '.(int)$parent->nright.')) ';
$sqlQuery['group'] = '
GROUP BY lpa.id_attribute
ORDER BY id_attribute_group, id_attribute ';
break;
case 'id_feature':
$sqlQuery['select'] = 'SELECT fl.name feature_name, fp.id_feature, fv.id_feature_value, fvl.value,
count(fv.id_feature_value) 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 ';
$sqlQuery['from'] = '
FROM '._DB_PREFIX_.'feature_product fp
INNER JOIN '._DB_PREFIX_.'product p ON (p.id_product = fp.id_product AND p.active = 1)
LEFT JOIN '._DB_PREFIX_.'feature_lang fl ON (fl.id_feature = fp.id_feature AND fl.id_lang = '.(int)$cookie->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)$cookie->id_lang.')
LEFT JOIN '._DB_PREFIX_.'layered_indexable_feature_lang_value lifl
ON (lifl.id_feature = fp.id_feature AND lifl.id_lang = '.(int)$cookie->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)$cookie->id_lang.') ';
$sqlQuery['where'] = 'WHERE p.`active` = 1 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 c.nleft >= '.(int)$parent->nleft.' AND c.nright <= '.(int)$parent->nright.')) ';
$sqlQuery['group'] = 'GROUP BY fv.id_feature_value ';
break;
case 'category':
$sqlQuery['select'] = '
SELECT c.id_category, c.id_parent, cl.name, (SELECT count(*) # ';
$sqlQuery['from'] = '
FROM '._DB_PREFIX_.'category_product cp
LEFT JOIN '._DB_PREFIX_.'product p ON (p.id_product = cp.id_product) ';
$sqlQuery['where'] = '
WHERE cp.id_category = c.id_category ';
$sqlQuery['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_lang = '.(int)$cookie->id_lang.')
WHERE c.id_parent = '.(int)$id_parent.'
GROUP BY c.id_category ORDER BY level_depth';
}
foreach ($filters as $filterTmp)
{
$methodName = 'get'.ucfirst($filterTmp['type']).'FilterSubQuery';
if (method_exists('BlockLayered', $methodName) &&
(!in_array($filter['type'], array('price', 'weight')) && $filter['type'] != $filterTmp['type'] || $filter['type'] == $filterTmp['type']))
{
if ($filter['type'] == $filterTmp['type'] && $filter['id_value'] == $filterTmp['id_value'])
$subQueryFilter = self::$methodName(array(), true);
else
{
if (!is_null($filterTmp['id_value']))
$selected_filters_cleaned = $this->cleanFilterByIdValue(@$selectedFilters[$filterTmp['type']], $filterTmp['id_value']);
else
$selected_filters_cleaned = @$selectedFilters[$filterTmp['type']];
$subQueryFilter = self::$methodName($selected_filters_cleaned, $filter['type'] == $filterTmp['type']);
}
foreach ($subQueryFilter as $key => $value)
$sqlQuery[$key] .= $value;
}
}
$products = false;
if (!empty($sqlQuery['from']))
$products = Db::getInstance(_PS_USE_SQL_SLAVE_)->ExecuteS($sqlQuery['select']."\n".$sqlQuery['from']."\n".$sqlQuery['join']."\n".$sqlQuery['where']."\n".$sqlQuery['group']);
foreach ($filters as $filterTmp)
{
$methodName = 'filterProductsBy'.ucfirst($filterTmp['type']);
if (method_exists('BlockLayered', $methodName) &&
(!in_array($filter['type'], array('price', 'weight')) && $filter['type'] != $filterTmp['type'] || $filter['type'] == $filterTmp['type']))
if ($filter['type'] == $filterTmp['type'])
$products = self::$methodName(array(), $products);
else
$products = self::$methodName(@$selectedFilters[$filterTmp['type']], $products);
}
switch ($filter['type'])
{
case 'price':
$priceArray = 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::getCurrent()->sign);
if (isset($products) && $products)
foreach ($products as $product)
{
if (is_null($priceArray['min']))
{
$priceArray['min'] = $product['price_min'];
$priceArray['values'][0] = $product['price_min'];
}
else if ($priceArray['min'] > $product['price_min'])
{
$priceArray['min'] = $product['price_min'];
$priceArray['values'][0] = $product['price_min'];
}
if ($priceArray['max'] < $product['price_max'])
{
$priceArray['max'] = $product['price_max'];
$priceArray['values'][1] = $product['price_max'];
}
}
if ($priceArray['max'] != $priceArray['min'] && $priceArray['min'] != null)
{
if (isset($selectedFilters['price']) && isset($selectedFilters['price'][0])
&& isset($selectedFilters['price'][1]))
{
$priceArray['values'][0] = $selectedFilters['price'][0];
$priceArray['values'][1] = $selectedFilters['price'][1];
}
$filterBlocks[] = $priceArray;
}
break;
case 'weight':
$weightArray = 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'));
if (isset($products) && $products)
foreach ($products as $product)
{
if (is_null($weightArray['min']))
{
$weightArray['min'] = $product['weight'];
$weightArray['values'][0] = $product['weight'];
}
else if ($weightArray['min'] > $product['weight'])
{
$weightArray['min'] = $product['weight'];
$weightArray['values'][0] = $product['weight'];
}
if ($weightArray['max'] < $product['weight'])
{
$weightArray['max'] = $product['weight'];
$weightArray['values'][1] = $product['weight'];
}
}
if ($weightArray['max'] != $weightArray['min'] && $weightArray['min'] != null)
{
if (isset($selectedFilters['weight']) && isset($selectedFilters['weight'][0])
&& isset($selectedFilters['weight'][1]))
{
$weightArray['values'][0] = $selectedFilters['weight'][0];
$weightArray['values'][1] = $selectedFilters['weight'][1];
}
$filterBlocks[] = $weightArray;
}
break;
case 'condition':
$conditionArray = 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($selectedFilters['condition']) && in_array($product['condition'], $selectedFilters['condition']))
$conditionArray[$product['condition']]['checked'] = true;
foreach ($conditionArray as $key => $condition)
if (isset($selectedFilters['condition']) && in_array($key, $selectedFilters['condition']))
$conditionArray[$key]['checked'] = true;
if (isset($products) && $products)
foreach ($products as $product)
if (isset($conditionArray[$product['condition']]))
$conditionArray[$product['condition']]['nbr']++;
$filterBlocks[] = array('type_lite' => 'condition', 'type' => 'condition', 'id_key' => 0, 'name' => $this->l('Condition'), 'values' => $conditionArray);
break;
case 'quantity':
$quantityArray = array (0 => array('name' => $this->l('Not available'), 'nbr' => 0), 1 => array('name' => $this->l('In stock'), 'nbr' => 0));
foreach ($quantityArray as $key => $quantity)
if (isset($selectedFilters['quantity']) && in_array($key, $selectedFilters['quantity']))
$quantityArray[$key]['checked'] = true;
if (isset($products) && $products)
foreach ($products as $product)
$quantityArray[(int)($product['quantity'] > 0)]['nbr']++;
$filterBlocks[] = array('type_lite' => 'quantity', 'type' => 'quantity', 'id_key' => 0, 'name' => $this->l('Availability'), 'values' => $quantityArray);
break;
case 'manufacturer':
if (isset($products) && $products)
{
$manufaturersArray = array();
foreach ($products as $manufacturer)
{
$manufaturersArray[$manufacturer['id_manufacturer']] = array('name' => $manufacturer['name'], 'nbr' => $manufacturer['nbr']);
if (isset($selectedFilters['manufacturer']) && in_array((int)$manufacturer['id_manufacturer'], $selectedFilters['manufacturer']))
$manufaturersArray[$manufacturer['id_manufacturer']]['checked'] = true;
}
$filterBlocks[] = array('type_lite' => 'manufacturer', 'type' => 'manufacturer', 'id_key' => 0, 'name' => $this->l('Manufacturer'), 'values' => $manufaturersArray);
}
break;
case 'id_attribute_group':
$attributesArray = array();
if (isset($products) && $products)
{
foreach ($products as $attributes)
{
if (!isset($attributesArray[$attributes['id_attribute_group']]))
$attributesArray[$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']);
$attributesArray[$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($selectedFilters['id_attribute_group'][$attributes['id_attribute']]))
$attributesArray[$attributes['id_attribute_group']]['values'][$attributes['id_attribute']]['checked'] = true;
}
$filterBlocks = array_merge($filterBlocks, $attributesArray);
}
break;
case 'id_feature':
$featureArray = array();
if (isset($products) && $products)
{
foreach ($products as $feature)
{
if (!isset($featureArray[$feature['id_feature']]))
$featureArray[$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']);
$featureArray[$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($selectedFilters['id_feature'][$feature['id_feature_value']]))
$featureArray[$feature['id_feature']]['values'][$feature['id_feature_value']]['checked'] = true;
}
$filterBlocks = array_merge($filterBlocks, $featureArray);
}
break;
case 'category':
$tmpArray = array();
if (isset($products) && $products)
{
foreach ($products as $category)
{
$tmpArray[$category['id_category']] = array('name' => $category['name'], 'nbr' => (int)$category['count_products']);
if (isset($selectedFilters['category']) && in_array($category['id_category'], $selectedFilters['category']))
$tmpArray[$category['id_category']]['checked'] = true;
}
$filterBlocks[] = array ('type_lite' => 'category', 'type' => 'category', 'id_key' => 0, 'name' => $this->l('Categories'), 'values' => $tmpArray);
}
break;
}
}
// All non indexable attribute and feature
$nonIndexable = 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 = '.(int)$cookie->id_lang) as $attribute)
$nonIndexable[] = 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 = '.(int)$cookie->id_lang) as $attribute)
$nonIndexable[] = Tools::link_rewrite($attribute['name']);
//generate SEO link
$paramSelected = '';
$optionCheckedArray = array();
$paramGroupSelectedArray = array();
$titleValues = array();
$link = new Link();
$linkBase = $link->getCategoryLink($id_parent, Category::getLinkRewrite($id_parent, (int)($cookie->id_lang)), (int)($cookie->id_lang));
$filterBlockList = array();
//get filters checked by group
foreach ($filterBlocks as $typeFilter)
{
$filterName = (!empty($typeFilter['url_name']) ? $typeFilter['url_name'] : $typeFilter['name']);
$paramGroupSelected = '';
foreach ($typeFilter['values'] as $key => $value)
{
if (is_array($value) && array_key_exists('checked', $value ))
{
$valueName = !empty($value['url_name']) ? $value['url_name'] : $value['name'];
$paramGroupSelected .= '-'.str_replace('-', '_', Tools::link_rewrite($valueName));
$paramGroupSelectedArray[Tools::link_rewrite($filterName)][] = Tools::link_rewrite($valueName);
if (!isset($titleValues[$filterName]))
$titleValues[$filterName] = array();
$titleValues[$filterName][] = $valueName;
}
else
$paramGroupSelectedArray[Tools::link_rewrite($filterName)][] = array();
}
if (!empty($paramGroupSelected))
{
$paramSelected .= '/'.str_replace('-', '_', Tools::link_rewrite($filterName)).$paramGroupSelected;
$optionCheckedArray[Tools::link_rewrite($filterName)] = $paramGroupSelected;
}
}
$blackList = array('weight','price');
$nofollow = false;
foreach ($filterBlocks as &$typeFilter)
{
$filterName = (!empty($typeFilter['url_name']) ? $typeFilter['url_name'] : $typeFilter['name']);
if (count($typeFilter) > 0 && !in_array($typeFilter['type'], $blackList))
{
foreach ($typeFilter['values'] as $key => $values)
{
$nofollow = false;
$optionCheckedCloneArray = $optionCheckedArray;
//if not filters checked, add parameter
$valueName = !empty($values['url_name']) ? $values['url_name'] : $values['name'];
if (!in_array(Tools::link_rewrite($valueName), $paramGroupSelectedArray[Tools::link_rewrite($filterName)]))
{
//update parameter filter checked before
if (array_key_exists(Tools::link_rewrite($filterName), $optionCheckedArray))
{
$optionCheckedCloneArray[Tools::link_rewrite($filterName)] = $optionCheckedCloneArray[Tools::link_rewrite($filterName)].'-'.str_replace('-', '_', Tools::link_rewrite($valueName));
$nofollow = true;
}
else
$optionCheckedCloneArray[Tools::link_rewrite($filterName)] = '-'.str_replace('-', '_', Tools::link_rewrite($valueName));
}
else
{
// Remove selected parameters
$optionCheckedCloneArray[Tools::link_rewrite($filterName)] = str_replace('-'.str_replace('-', '_', Tools::link_rewrite($valueName)), '', $optionCheckedCloneArray[Tools::link_rewrite($filterName)]);
if (empty($optionCheckedCloneArray[Tools::link_rewrite($filterName)]))
unset($optionCheckedCloneArray[Tools::link_rewrite($filterName)]);
}
$parameters = '';
foreach ($optionCheckedCloneArray as $keyGroup => $valueGroup)
$parameters .= '/'.str_replace('-', '_', $keyGroup).$valueGroup;
// Check if there is an non indexable attribute or feature in the url
foreach ($nonIndexable as $value)
if (strpos($parameters, '/'.$value) !== false)
$nofollow = true;
//write link by mode rewriting
if (!Configuration::get('PS_REWRITING_SETTINGS'))
$typeFilter['values'][$key]['link'] = $linkBase.'&selected_filters='.$parameters;
else
$typeFilter['values'][$key]['link'] = $linkBase.$parameters;
$typeFilter['values'][$key]['rel'] = ($nofollow) ? 'nofollow' : '';
}
}
}
$nFilters = 0;
if (isset($selectedFilters['price']))
if ($priceArray['min'] == $selectedFilters['price'][0] && $priceArray['max'] == $selectedFilters['price'][1])
unset($selectedFilters['price']);
if (isset($selectedFilters['weight']))
if ($weightArray['min'] == $selectedFilters['weight'][0] && $weightArray['max'] == $selectedFilters['weight'][1])
unset($selectedFilters['weight']);
foreach ($selectedFilters as $filters)
$nFilters += count($filters);
$cache = array('layered_show_qties' => (int)Configuration::get('PS_LAYERED_SHOW_QTIES'), 'id_category_layered' => (int)$id_parent,
'selected_filters' => $selectedFilters, 'n_filters' => (int)$nFilters, 'nbr_filterBlocks' => count($filterBlocks), 'filters' => $filterBlocks,
'title_values' => $titleValues, 'current_friendly_url' => htmlentities($paramSelected), 'nofollow' => !empty($paramSelected) || $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($selectedFilters)
{
global $smarty;
if ($filterBlock = $this->getFilterBlock($selectedFilters))
{
if ($filterBlock['nbr_filterBlocks'] == 0)
return false;
$smarty->assign($filterBlock);
return $this->display(__FILE__, 'blocklayered.tpl');
}
else
return false;
}
private static function getPriceFilterSubQuery($filterValue)
{
$idCurrency = (int)Currency::getCurrent()->id;
$priceFilterQuery = '';
if (isset($filterValue) && $filterValue)
{
$idCurrency = Currency::getCurrent()->id;
$priceFilterQuery = '
INNER JOIN `'._DB_PREFIX_.'layered_price_index` psi ON (psi.id_product = p.id_product AND psi.id_currency = '.(int)$idCurrency.'
AND psi.price_min <= '.(int)$filterValue[1].' AND psi.price_max >= '.(int)$filterValue[0].') ';
}
else
{
$idCurrency = Currency::getCurrent()->id;
$priceFilterQuery = '
INNER JOIN `'._DB_PREFIX_.'layered_price_index` psi
ON (psi.id_product = p.id_product AND psi.id_currency = '.(int)$idCurrency.') ';
}
return array('join' => $priceFilterQuery, 'select' => ', psi.price_min, psi.price_max');
}
private static function filterProductsByPrice($filterValue, $productCollection)
{
if (empty($filterValue))
return $productCollection;
foreach ($productCollection as $key => $product)
{
if (isset($filterValue) && $filterValue && isset($product['price_min']) && isset($product['id_product'])
&& ((int)$filterValue[0] > $product['price_min'] || (int)$filterValue[1] < $product['price_max']))
{
$price = Product::getPriceStatic($product['id_product']);
if ($price < $filterValue[0] || $price > $filterValue[1])
continue;
unset($productCollection[$key]);
}
}
return $productCollection;
}
private static function getWeightFilterSubQuery($filterValue, $ignoreJoin)
{
if (isset($filterValue) && $filterValue)
if ($filterValue[0] != 0 || $filterValue[1] != 0)
return array('where' => ' AND p.`weight` BETWEEN '.(float)($filterValue[0] - 0.001).' AND '.(float)($filterValue[1] + 0.001).' ');
return array();
}
private static function getFeatureFilterSubQuery($filterValue, $ignoreJoin)
{
if (empty($filterValue))
return array();
$queryFilters = ' AND p.id_product IN (SELECT id_product FROM '._DB_PREFIX_.'feature_product fp WHERE ';
foreach ($filterValue as $filterVal)
$queryFilters .= 'fp.`id_feature_value` = '.(int)$filterVal.' OR ';
$queryFilters = rtrim($queryFilters, 'OR ').') ';
return array('where' => $queryFilters);
}
private static function getId_attribute_groupFilterSubQuery($filterValue, $ignoreJoin)
{
if (empty($filterValue))
return array();
$queryFilters = '
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 ($filterValue as $filterVal)
$queryFilters .= 'pac.`id_attribute` = '.(int)$filterVal.' OR ';
$queryFilters = rtrim($queryFilters, 'OR ').') ';
return array('where' => $queryFilters);
}
private static function getCategoryFilterSubQuery($filterValue, $ignoreJoin)
{
if (empty($filterValue))
return array();
$queryFiltersJoin = '';
$queryFiltersWhere = ' AND p.id_product IN (SELECT id_product FROM '._DB_PREFIX_.'category_product cp WHERE ';
foreach ($filterValue as $id_category)
$queryFiltersWhere .= 'cp.`id_category` = '.(int)$id_category.' OR ';
$queryFiltersWhere = rtrim($queryFiltersWhere, 'OR ').') ';
return array('where' => $queryFiltersWhere, 'join' => $queryFiltersJoin);
}
private static function getQuantityFilterSubQuery($filterValue, $ignoreJoin)
{
if (count($filterValue) == 2 || empty($filterValue))
return array();
$queryFilters = ' AND p.quantity '.(!$filterValue[0] ? '=' : '>').' 0 ';
return array('where' => $queryFilters);
}
private static function getManufacturerFilterSubQuery($filterValue, $ignoreJoin)
{
if (empty($filterValue))
$queryFilters = '';
else
{
array_walk($filterValue, create_function('&$id_manufacturer', '$id_manufacturer = (int)$id_manufacturer;'));
$queryFilters = ' AND p.id_manufacturer IN ('.implode($filterValue, ',').')';
}
if ($ignoreJoin)
return array('where' => $queryFilters, 'select' => ', m.name');
else
return array('where' => $queryFilters, 'select' => ', m.name', 'join' => 'LEFT JOIN `'._DB_PREFIX_.'manufacturer` m ON (m.id_manufacturer = p.id_manufacturer) ');
}
private static function getConditionFilterSubQuery($filterValue, $ignoreJoin)
{
if (count($filterValue) == 3 || empty($filterValue))
return array();
$queryFilters = ' AND p.condition IN (';
foreach ($filterValue as $cond)
$queryFilters .= '\''.$cond.'\',';
$queryFilters = rtrim($queryFilters, ',').') ';
return array('where' => $queryFilters);
}
public function ajaxCallBackOffice($categoryBox = array(), $id_layered_filter = null)
{
global $cookie;
if (!empty($id_layered_filter))
{
$layeredFilter = Db::getInstance(_PS_USE_SQL_SLAVE_)->getRow('SELECT * FROM '._DB_PREFIX_.'layered_filter WHERE id_layered_filter = '.(int)$id_layered_filter);
if ($layeredFilter && isset($layeredFilter['filters']) && !empty($layeredFilter['filters']))
$layeredValues = unserialize($layeredFilter['filters']);
if (isset($layeredValues['categories']) && count($layeredValues['categories']))
foreach ($layeredValues['categories'] as $id_category)
$categoryBox[] = (int)$id_category;
}
/* Clean categoryBox before use */
if (isset($categoryBox) && is_array($categoryBox))
foreach ($categoryBox as &$value)
$value = (int)$value;
$attributeGroups = 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($categoryBox) ? '
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_.'category_product cp ON (cp.id_product = pa.id_product)' : '').'
WHERE agl.id_lang = '.(int)$cookie->id_lang.
(count($categoryBox) ? ' AND cp.id_category IN ('.implode(',', $categoryBox).')' : '').'
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($categoryBox) ? '
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($categoryBox) ? ' AND cp.id_category IN ('.implode(',', $categoryBox).')' : '').'
GROUP BY fl.id_feature');
$nElements = count($attributeGroups) + count($features) + 4;
if ($nElements > 20)
$nElements = 20;
$html = '
<div id="layered_container_right" style="width: 360px; float: left; margin-left: 20px; height: '.(int)(30 + $nElements * 38).'px; overflow-y: auto;">
<h3>'.$this->l('Available filters').' <span id="num_avail_filters">(0)</span></h3>
<ul id="all_filters"></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_subcategories" name="layered_selection_subcategories" />
<span class="position"></span>'.$this->l('Sub-categories filter').'
</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').'</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').'
</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').'
</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)').'
</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)').'
</li>
</ul>';
if (count($attributeGroups))
{
$html .= '<ul>';
foreach ($attributeGroups as $attributeGroup)
$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)$attributeGroup['id_attribute_group'].'" name="layered_selection_ag_'.(int)$attributeGroup['id_attribute_group'].'" />
<span class="position"></span>
'.$this->l('Attribute group:').' '.$attributeGroup['name'].' ('.(int)$attributeGroup['n'].' '.($attributeGroup['n'] > 1 ? $this->l('attributes') : $this->l('attribute')).')'.
($attributeGroup['is_color_group'] ? ' <img src="../img/admin/color_swatch.png" alt="" title="'.$this->l('This group will allow user to select a color').'" />' : '').'
</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>
'.$this->l('Feature:').' '.$feature['name'].' ('.(int)$feature['n'].' '.($feature['n'] > 1 ? $this->l('values') : $this->l('value')).')
</li>';
$html .= '</ul>';
}
$html .= '
</div>';
if (isset($layeredValues))
{
$html .= '
<script type="text/javascript">
//<![CDATA[
$(document).ready(function()
{
$(\'#selected_filters li\').remove();
';
foreach ($layeredValues as $key => $layeredValue)
if ($key != 'categories')
$html .= '$(\'#'.$key.'\').click();'."\n";
if (isset($layeredValues['categories']) && count($layeredValues['categories']))
{
foreach ($layeredValues['categories'] as $id_category)
$html .= '$(\'#categories-treeview\').find(\'input[name="categoryBox[]"][value='.(int)$id_category.']\').attr(\'checked\', \'checked\');'."\n";
$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($layeredFilter['name']).'\');
$(\'#id_layered_filter\').val(\''.(int)$layeredFilter['id_layered_filter'].'\')';
$html .= '
});
</script>';
}
return $html;
}
public function ajaxCall()
{
global $smarty;
$selectedFilters = $this->getSelectedFilters();
$this->getProducts($selectedFilters, $products, $nbProducts, $p, $n, $pages_nb, $start, $stop, $range);
$smarty->assign('nb_products', $nbProducts);
$smarty->assign('category', (object)array('id' => Tools::getValue('id_category_layered', 1)));
$pagination_infos = array('pages_nb' => (int)($pages_nb), 'p' => (int)$p, 'n' => (int)$n, 'range' => (int)$range, 'start' => (int)$start, 'stop' => (int)$stop,
'nArray' => $nArray = (int)Configuration::get('PS_PRODUCTS_PER_PAGE') != 10 ? array((int)Configuration::get('PS_PRODUCTS_PER_PAGE'), 10, 20, 50) : array(10, 20, 50));
$smarty->assign($pagination_infos);
$smarty->assign('comparator_max_item', (int)(Configuration::get('PS_COMPARATOR_MAX_ITEM')));
$smarty->assign('products', $products);
// 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'))
$categoryCount = $smarty->fetch(_PS_THEME_DIR_.'category-count.tpl');
else
$categoryCount = '';
if ($nbProducts == 0)
$product_list_tpl = 'blocklayered-no-products.tpl';
else
$product_list_tpl = _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' => $this->generateFiltersBlock($selectedFilters),
'productList' => $smarty->fetch($product_list_tpl),
'pagination' => $smarty->fetch(_PS_THEME_DIR_.'pagination.tpl'),
'categoryCount' => $categoryCount));
}
public function getProducts($selectedFilters, &$products, &$nbProducts, &$p, &$n, &$pages_nb, &$start, &$stop, &$range)
{
global $cookie;
$products = $this->getProductByFilters($selectedFilters);
$products = Product::getProductsProperties((int)$cookie->id_lang, $products);
$nbProducts = $this->nbr_products;
$range = 2; /* how many pages around page selected */
$n = (int)Tools::getValue('n', Configuration::get('PS_PRODUCTS_PER_PAGE'));
$p = Tools::getValue('p', 1);
if ($p < 0)
$p = 0;
if ($p > ($nbProducts / $n))
$p = ceil($nbProducts / $n);
$pages_nb = ceil($nbProducts / (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_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,
PRIMARY KEY (`id_layered_category`),
KEY `id_category` (`id_category`,`type`)
) ENGINE=MyISAM 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)');
}
public function rebuildLayeredCache($productsIds = array(), $categoriesIds = array())
{
@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');
$db = Db::getInstance(_PS_USE_SQL_SLAVE_);
$nCategories = array();
$doneCategories = array();
$attributeGroups = Db::getInstance(_PS_USE_SQL_SLAVE_)->ExecuteS('
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)
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($categoriesIds) ? ' AND cp.id_category IN ('.implode(',', $categoriesIds).')' : '').' AND p.active = 1'.(count($productsIds) ? '
AND p.id_product IN ('.implode(',', $productsIds).')' : ''), false);
$attributeGroupsById = array();
while ($row = $db->nextRow($attributeGroups))
$attributeGroupsById[(int)$row['id_attribute']] = (int)$row['id_attribute_group'];
$features = Db::getInstance(_PS_USE_SQL_SLAVE_)->ExecuteS('
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)
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($categoriesIds) ? ' AND cp.id_category IN ('.implode(',', $categoriesIds).')' : '').'
AND p.active = 1'.(count($productsIds) ? ' AND p.id_product IN ('.implode(',', $productsIds).')' : ''), false);
$featuresById = array();
while ($row = $db->nextRow($features))
$featuresById[(int)$row['id_feature_value']] = (int)$row['id_feature'];
$result = $db->ExecuteS('
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)
LEFT JOIN '._DB_PREFIX_.'product_attribute_combination pac ON (pac.id_product_attribute = pa.id_product_attribute)
WHERE c.active = 1'.(count($categoriesIds) ? ' AND cp.id_category IN ('.implode(',', $categoriesIds).')' : '').' AND p.active = 1'.(count($productsIds) ? '
AND p.id_product IN ('.implode(',', $productsIds).')' : '').' AND (fv.custom IS NULL OR fv.custom = 0)
GROUP BY p.id_product', 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']));
$queryCategory = 'INSERT INTO '._DB_PREFIX_.'layered_category (id_category, id_value, type, position) VALUES ';
$toInsert = false;
foreach ($c as $id_category => $category)
{
if (!isset($nCategories[(int)$id_category]))
$nCategories[(int)$id_category] = 1;
if (!isset($doneCategories[(int)$id_category]['cat']))
{
$doneCategories[(int)$id_category]['cat'] = true;
$queryCategory .= '('.(int)$id_category.',NULL,\'category\','.(int)$nCategories[(int)$id_category]++.'),';
$toInsert = true;
}
foreach ($a as $kAttribute => $attribute)
if (!isset($doneCategories[(int)$id_category]['a'.(int)$attributeGroupsById[(int)$kAttribute]]))
{
$doneCategories[(int)$id_category]['a'.(int)$attributeGroupsById[(int)$kAttribute]] = true;
$queryCategory .= '('.(int)$id_category.','.(int)$attributeGroupsById[(int)$kAttribute].',\'id_attribute_group\','.(int)$nCategories[(int)$id_category]++.'),';
$toInsert = true;
}
foreach ($f as $kFeature => $feature)
if (!isset($doneCategories[(int)$id_category]['f'.(int)$featuresById[(int)$kFeature]]))
{
$doneCategories[(int)$id_category]['f'.(int)$featuresById[(int)$kFeature]] = true;
$queryCategory .= '('.(int)$id_category.','.(int)$featuresById[(int)$kFeature].',\'id_feature\','.(int)$nCategories[(int)$id_category]++.'),';
$toInsert = true;
}
if (!isset($doneCategories[(int)$id_category]['q']))
{
$doneCategories[(int)$id_category]['q'] = true;
$queryCategory .= '('.(int)$id_category.',NULL,\'quantity\','.(int)$nCategories[(int)$id_category]++.'),';
$toInsert = true;
}
if (!isset($doneCategories[(int)$id_category]['m']))
{
$doneCategories[(int)$id_category]['m'] = true;
$queryCategory .= '('.(int)$id_category.',NULL,\'manufacturer\','.(int)$nCategories[(int)$id_category]++.'),';
$toInsert = true;
}
if (!isset($doneCategories[(int)$id_category]['c']))
{
$doneCategories[(int)$id_category]['c'] = true;
$queryCategory .= '('.(int)$id_category.',NULL,\'condition\','.(int)$nCategories[(int)$id_category]++.'),';
$toInsert = true;
}
if (!isset($doneCategories[(int)$id_category]['w']))
{
$doneCategories[(int)$id_category]['w'] = true;
$queryCategory .= '('.(int)$id_category.',NULL,\'weight\','.(int)$nCategories[(int)$id_category]++.'),';
$toInsert = true;
}
if (!isset($doneCategories[(int)$id_category]['p']))
{
$doneCategories[(int)$id_category]['p'] = true;
$queryCategory .= '('.(int)$id_category.',NULL,\'price\','.(int)$nCategories[(int)$id_category]++.'),';
$toInsert = true;
}
}
if ($toInsert)
Db::getInstance()->Execute(rtrim($queryCategory, ','));
}
}
public function filterProducts($products, $selectedFilters, $excludeType = false)
{
static $priceStatics = array();
$productsToKeep = array();
$filterByLetter = array('id_attribute_group' => 'a', 'id_feature' => 'f', 'category' => 'c', 'manufacturer' => 'id_manufacturer',
'quantity' => 'quantity', 'condition' => 'condition', 'weight' => 'weight', 'price' => 'price');
foreach ($selectedFilters as $type => $filters)
{
if ($type == $excludeType || !count($filters))
continue;
else
{
$type = preg_match('/^(.*[^_0-9])/', $type, $res);
$type = $res[1];
switch ($type)
{
case 'category':
foreach ($products as $k => $product)
if ($filter = Tools::getValue('id_category_layered'))
$productsToKeep[] = (int)$k;
//don't break me
case 'id_attribute_group':
case 'id_feature':
foreach ($products as $k => $product)
foreach ($filters as $filter)
if (in_array($filter, $product[$filterByLetter[$type]]))
$productsToKeep[] = (int)$k;
break;
case 'manufacturer':
case 'condition':
case 'quantity':
foreach ($products as $k => $product)
foreach ($filters as $filter)
if ($product[$filterByLetter[$type]] == $filter)
$productsToKeep[] = (int)$k;
break;
case 'weight':
$min = $filters[0];
$max = $filters[1];
foreach ($products as $k => $product)
if ((float)$min <= (float)$product[$filterByLetter[$type]] && (float)$product[$filterByLetter[$type]] <= (float)$max)
$productsToKeep[] = (int)$k;
break;
case 'price':
$min = $filters[0];
$max = $filters[1];
foreach ($products as $k => $product)
if ((float)$min <= (float)$product['price_min'] && (float)$product['price_max'] <= (float)$max)
$productsToKeep[] = (int)$k;
else if ((float)$product['price_min'] < (float)$max && (float)$product['price_max'] > (float)$max
|| (float)$product['price_min'] < (float)$min && (float)$product['price_max'] > (float)$min)
{
if (!isset($priceStatics[(int)$k]))
$priceStatics[(int)$k] = Product::getPriceStatic((int)$k);
$price = $priceStatics[(int)$k];
if ((float)$min <= $price && $price <= (float)$max)
$productsToKeep[] = (int)$k;
}
break;
}
foreach ($products as $k => $product)
if (!in_array($k, $productsToKeep))
unset($products[(int)$k]);
$productsToKeep = array();
}
}
return $products;
}
}