Files
PrestaShop/modules/blocklayered/blocklayered.php
rMalie 7c85e104de // Merge -> revision 8734
git-svn-id: http://dev.prestashop.com/svn/v1/branches/1.5.x@8740 b9a71923-0436-4b27-9f14-aed3839534dd
2011-09-23 14:06:47 +00:00

2958 lines
131 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
/*
* 2007-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$
* @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::fullIndexProcess();
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');
Db::getInstance()->Execute('DROP TABLE IF EXISTS '._DB_PREFIX_.'layered_indexable_feature_lang');
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`');
Db::getInstance()->Execute('
CREATE TABLE `'._DB_PREFIX_.'layered_indexable_attribute_group_lang` (
`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`');
Db::getInstance()->Execute('
CREATE TABLE `'._DB_PREFIX_.'layered_indexable_attribute_lang` (
`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`');
Db::getInstance()->Execute('DROP TABLE IF EXISTS `'._DB_PREFIX_.'layered_indexable_feature_lang`');
Db::getInstance()->Execute('
CREATE TABLE `'._DB_PREFIX_.'layered_indexable_feature_lang` (
`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`');
Db::getInstance()->Execute('
CREATE TABLE `'._DB_PREFIX_.'layered_indexable_feature_value_lang` (
`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()
{
Db::getInstance()->execute('TRUNCATE '._DB_PREFIX_.'layered_product_attribute');
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
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');
$attributeValues = 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 )
ORDER BY position ASC');
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 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 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($attributeValues[$attribute['id_lang']]))
$attributeValues[$attribute['id_lang']] = array();
if (!isset($attributeValues[$attribute['id_lang']][$filter['id_category']]))
$attributeValues[$attribute['id_lang']][$filter['id_category']] = array();
if (!isset($attributeValues[$attribute['id_lang']][$filter['id_category']]['c'.$attribute['id_name']]))
$attributeValues[$attribute['id_lang']][$filter['id_category']]['c'.$attribute['id_name']] = array();
$attributeValues[$attribute['id_lang']][$filter['id_category']]['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 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 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($attributeValues[$feature['id_lang']]))
$attributeValues[$feature['id_lang']] = array();
if (!isset($attributeValues[$feature['id_lang']][$filter['id_category']]))
$attributeValues[$feature['id_lang']][$filter['id_category']] = array();
if (!isset($attributeValues[$feature['id_lang']][$filter['id_category']]['f'.$feature['id_name']]))
$attributeValues[$feature['id_lang']][$filter['id_category']]['f'.$feature['id_name']] = array();
$attributeValues[$feature['id_lang']][$filter['id_category']]['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($attribute['value_url_name']) ? $attribute['value_url_name'] : $feature['value']), '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 nleft > (SELECT nleft FROM '._DB_PREFIX_.'category WHERE id_category = '.$filter['id_category'].')
AND nright < (SELECT nright FROM '._DB_PREFIX_.'category WHERE id_category = '.$filter['id_category'].')
AND cl.id_lang = '.(int)$filter['id_lang'].' ');
foreach($categories as $category)
{
if (!isset($attributeValues[$category['id_lang']]))
$attributeValues[$category['id_lang']] = array();
if (!isset($attributeValues[$category['id_lang']][$filter['id_category']]))
$attributeValues[$category['id_lang']][$filter['id_category']] = array();
if (!isset($attributeValues[$category['id_lang']][$filter['id_category']]['category']))
$attributeValues[$category['id_lang']][$filter['id_category']]['category'] = array();
$attributeValues[$category['id_lang']][$filter['id_category']]['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($attributeValues[$manufacturer['id_lang']]))
$attributeValues[$manufacturer['id_lang']] = array();
if (!isset($attributeValues[$manufacturer['id_lang']][$filter['id_category']]))
$attributeValues[$manufacturer['id_lang']][$filter['id_category']] = array();
if (!isset($attributeValues[$manufacturer['id_lang']][$filter['id_category']]['manufacturer']))
$attributeValues[$manufacturer['id_lang']][$filter['id_category']]['manufacturer'] = array();
$attributeValues[$manufacturer['id_lang']][$filter['id_category']]['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':
foreach (array (0 => $this->translateWord('Not available',(int)$filter['id_lang']), 1 => $this->translateWord('In stock', (int)$filter['id_lang']))
as $key => $quantity)
$attributeValues[$filter['id_lang']][$filter['id_category']]['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':
foreach (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']))
as $key => $condition)
$attributeValues[$filter['id_lang']][$filter['id_category']]['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
$attributeValuesKeys = array_keys($attributeValues);
foreach($attributeValues as $id_lang => $attributesByCategoriesByLang)
{
// Foreach categories
foreach($attributesByCategoriesByLang as $id_category => $attributesByCategory)
foreach($attributesByCategory 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]);
elseif (isset($_MODULES[$id_lang][Tools::strtolower($currentKey)]))
$ret = stripslashes($_MODULES[$id_lang][Tools::strtolower($currentKey)]);
elseif (isset($_MODULES[$id_lang][$defaultKey]))
$ret = stripslashes($_MODULES[$id_lang][$defaultKey]);
elseif (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;
$params['hookExecuted'] = true;
$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);
//test nofollow link
$smarty->assign('no_follow', $filterBlock['nofollow']);
}
public function hookAfterSaveProduct($params)
{
if (!$params['id_product'])
return;
self::indexProduct((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 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
VALUES ('.(int)$params['id_feature'].', '.$id_lang.', \''.Tools::link_rewrite(Tools::getValue('url_name_'.$id_lang)).'\',
\''.pSQL(htmlspecialchars(Tools::getValue('meta_title_'.$id_lang))).'\')');
}
}
public function hookAfterSaveFeatureValue($params)
{
if (!$params['id_feature_value'])
return;
Db::getInstance()->Execute('DELETE FROM '._DB_PREFIX_.'layered_indexable_feature_value_lang 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
VALUES ('.(int)$params['id_feature_value'].', '.$id_lang.', \''.Tools::link_rewrite(Tools::getValue('url_name_'.$id_lang)).'\',
\''.pSQL(htmlspecialchars(Tools::getValue('meta_title_'.$id_lang))).'\')');
}
}
public function hookAfterDeleteFeatureValue($params)
{
if (!$params['id_feature_value'])
return;
Db::getInstance()->Execute('DELETE FROM '._DB_PREFIX_.'layered_indexable_feature_value_lang 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
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="'.htmlspecialchars(@$langValue[$language['id_lang']]['url_name']).'" />
<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="'.htmlspecialchars(@$langValue[$language['id_lang']]['meta_title']).'" />
<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 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
VALUES ('.(int)$params['id_attribute'].', '.$id_lang.', \''.Tools::link_rewrite(Tools::getValue('url_name_'.$id_lang)).'\',
\''.pSQL(htmlspecialchars(Tools::getValue('meta_title_'.$id_lang))).'\')');
}
}
public function hookAfterDeleteAttribute($params)
{
if (!$params['id_attribute'])
return;
Db::getInstance()->Execute('DELETE FROM '._DB_PREFIX_.'layered_indexable_attribute_lang 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
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="'.htmlspecialchars(@$langValue[$language['id_lang']]['url_name']).'" />
<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="'.htmlspecialchars(@$langValue[$language['id_lang']]['meta_title']).'" />
<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 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
VALUES ('.(int)$params['id_attribute_group'].', '.$id_lang.', \''.Tools::link_rewrite(Tools::getValue('url_name_'.$id_lang)).'\',
\''.pSQL(htmlspecialchars(Tools::getValue('meta_title_'.$id_lang))).'\')');
}
}
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 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
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="'.htmlspecialchars(@$langValue[$language['id_lang']]['url_name']).'" />
<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="'.htmlspecialchars(@$langValue[$language['id_lang']]['meta_title']).'" />
<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
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="'.htmlspecialchars(@$langValue[$language['id_lang']]['url_name']).'" />
<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="'.htmlspecialchars(@$langValue[$language['id_lang']]['meta_title']).'" />
<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 fullIndexProcess($cursor = 0, $ajax = false, $smart = false)
{
if ($cursor == 0 AND !$smart)
self::_installPriceIndexTable();
return self::_indexer($cursor, true, $ajax, $smart);
}
/*
* $cursor $cursor in order to restart indexing from the last state
*/
public static function indexProcess($cursor = 0, $ajax = false)
{
return self::_indexer($cursor, false, $ajax);
}
private static function _indexer($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::_index((int)$cursor, $full, $smart);
$timeElapsed = microtime(true) - $startTime;
}
while ($cursor < $nbProducts AND (Tools::getMemoryLimit()) > memory_get_peak_usage() AND $timeElapsed < $maxExecutionTime);
if (($nbProducts > 0 AND !$full OR $cursor < $nbProducts AND $full) AND !$ajax)
{
if (!Tools::file_get_contents(Tools::getProtocol().Tools::getHttpHost().'/modules/blocklayered/blocklayered-indexer.php?token='.substr(Tools::encrypt('blocklayered/index'), 0, 10).'&cursor='.(int)$cursor.'&full='.(int)$full))
self::_indexer((int)$cursor, (int)$full);
return $cursor;
}
if ($ajax AND $nbProducts > 0 AND $cursor < $nbProducts AND $full)
return '{"cursor": '.$cursor.', "count": '.($nbProducts - $cursor).'}';
elseif ($ajax AND $nbProducts > 0 AND !$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 _index($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::indexProduct((int)$product['id_product'], ($smart AND $full));
return (int)($cursor + $length);
}
public static function indexProduct($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']]) OR $price < $minPrice[$currency['id_currency']])
$minPrice[$currency['id_currency']] = $price;
}
foreach ($productMinPrices as $specificPrice)
foreach ($currencyList as $currency)
{
if ($specificPrice['id_currency'] AND $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']]) OR $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']]) OR $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']].')';
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;
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
$filterBlock = self::getFilterBlock($this->getSelectedFilters());
$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');
}
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']) AND $_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 (sizeof($_POST['categoryBox']))
{
/* Clean categoryBox before use */
if (isset($_POST['categoryBox']) AND 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(',', $_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' AND $value == 'on')
{
$filterValues[$key] = $value;
$n++;
if ($key == 'layered_selection_stock')
$sqlToInsert .= '('.(int)$id_category_layered.',NULL,\'quantity\','.(int)$n.'),';
elseif ($key == 'layered_selection_subcategories')
$sqlToInsert .= '('.(int)$id_category_layered.',NULL,\'category\','.(int)$n.'),';
elseif ($key == 'layered_selection_condition')
$sqlToInsert .= '('.(int)$id_category_layered.',NULL,\'condition\','.(int)$n.'),';
elseif ($key == 'layered_selection_weight_slider')
$sqlToInsert .= '('.(int)$id_category_layered.',NULL,\'weight\','.(int)$n.'),';
elseif ($key == 'layered_selection_price_slider')
$sqlToInsert .= '('.(int)$id_category_layered.',NULL,\'price\','.(int)$n.'),';
elseif ($key == 'layered_selection_manufacturer')
$sqlToInsert .= '('.(int)$id_category_layered.',NULL,\'manufacturer\','.(int)$n.'),';
elseif (substr($key, 0, 21) == 'layered_selection_ag_')
$sqlToInsert .= '('.(int)$id_category_layered.','.(int)str_replace('layered_selection_ag_', '', $key).',\'id_attribute_group\','.(int)$n.'),';
elseif (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)sizeof($filterValues['categories']), 'date_add' => date('Y-m-d H:i:s'));
if (isset($_POST['id_layered_filter']) AND $_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::getValue('layered_tpl_name').'" '.((isset($_POST['id_layered_filter']) AND $_POST['id_layered_filter']) ? $this->l('was updated successfully.') : $this->l('was added successfully.')).'</div>';
}
}
}
elseif (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>';
}
elseif (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-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-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-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+\' '.$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(\''.$this->l('Url indexation finished').'\');
$(\'#ajax-message-ok\').show();
return;
},
error: function(res)
{
this.restartAllowed = true;
$(\'#indexing-warning\').hide();
$(\'#ajax-message-ko span\').html(\''.$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+\' '.$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(\''.$this->l('Product indexation finished').'\');
$(\'#ajax-message-ok\').show();
return;
}
this.cursor = parseInt(res.cursor);
$(this).html(this.legend+\' '.$this->l('(in progress, %s products to index)').'\'.replace(\'%s\', res.count));
$(this).click();
},
error: function(res)
{
this.restartAllowed = true;
$(\'#indexing-warning\').hide();
$(\'#ajax-message-ko span\').html(\''.$this->l('Product 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 (sizeof($filtersTemplates))
{
$html .= '<p>'.sizeof($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="'.$_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="'.$_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;">'.$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() {
$(\'#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\');
}
});
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)sizeof($filtersTemplates).'" />
</form>
</fieldset><br />
<fieldset class="width2">
<legend><img src="../img/admin/cog.gif" alt="" /> '.$this->l('Configuration').'</legend>
<form action="'.$_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::htmlentitiesUTF8($_SERVER['REQUEST_URI']));
$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' AND in_array($value, array('new', 'used', 'refurbished')))
$selectedFilters['condition'][] = $value;
elseif ($res[1] == 'quantity' AND (!$value OR $value == 1))
$selectedFilters['quantity'][] = $value;
elseif (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;
}
elseif (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;
}
elseif ($res[1] == 'weight')
$selectedFilters[$res[1]] = $tmpTab;
elseif ($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 (!sizeof($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 (!sizeof($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 (sizeof($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 (sizeof($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 OR $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 OR $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) AND $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) AND $priceFilter)
{
$price = Product::getPriceStatic($product['id_product']);
if ($price < $priceFilter['min'] OR $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] == '' AND $value[1] == '' OR $value[0] == 0 AND $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_.'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.')
LEFT JOIN '._DB_PREFIX_.'layered_indexable_attribute_group_lang 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 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'].' ';
$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
LEFT JOIN `'._DB_PREFIX_.'category_product` cp ON (cp.`id_product` = fp.`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.')
INNER JOIN '._DB_PREFIX_.'product p ON (p.id_product = cp.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 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 lifvl
ON (lifvl.id_feature_value = fp.id_feature_value AND lifvl.id_lang = '.(int)$cookie->id_lang.') ';
$sqlQuery['where'] = 'WHERE p.`active` = 1 ';
$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'])
$subQueryFilter = self::$methodName(array(), $filter['type'] == $filterTmp['type']);
else
$subQueryFilter = self::$methodName(@$selectedFilters[$filterTmp['type']], $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) AND $products)
foreach ($products as $product)
{
if (is_null($priceArray['min']))
{
$priceArray['min'] = $product['price_min'];
$priceArray['values'][0] = $product['price_min'];
}
elseif ($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']) AND isset($selectedFilters['price'][0])
AND 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) AND $products)
foreach ($products as $product)
{
if (is_null($weightArray['min']))
{
$weightArray['min'] = $product['weight'];
$weightArray['values'][0] = $product['weight'];
}
elseif ($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']) AND isset($selectedFilters['weight'][0])
AND 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) AND $products)
foreach ($products as $product)
if (isset($selectedFilters['condition']) AND in_array($product['condition'], $selectedFilters['condition']))
$conditionArray[$product['condition']]['checked'] = true;
foreach ($conditionArray as $key => $condition)
if (isset($selectedFilters['condition']) AND in_array($key, $selectedFilters['condition']))
$conditionArray[$key]['checked'] = true;
if (isset($products) AND $products)
foreach ($products as $product)
$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']) AND in_array($key, $selectedFilters['quantity']))
$quantityArray[$key]['checked'] = true;
if (isset($products) AND $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) AND $products)
{
$manufaturersArray = array();
foreach ($products as $manufacturer)
{
$manufaturersArray[$manufacturer['id_manufacturer']] = array('name' => $manufacturer['name'], 'nbr' => $manufacturer['nbr']);
if (isset($selectedFilters['manufacturer']) AND 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) AND $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) AND $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) AND $products)
{
foreach ($products as $category)
{
$tmpArray[$category['id_category']] = array('name' => $category['name'], 'nbr' => (int)$category['count_products']);
if (isset($selectedFilters['category']) AND 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) AND 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 AND !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 += sizeof($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' => sizeof($filterBlocks), 'filters' => $filterBlocks,
'title_values' => $titleValues, 'current_friendly_url' => htmlentities($paramSelected), 'nofollow' => !empty($paramSelected) || $nofollow);
return $cache;
}
public function generateFiltersBlock($selectedFilters)
{
global $smarty;
if($filterBlock = $this->getFilterBlock($selectedFilters))
{
$smarty->assign($this->getFilterBlock($selectedFilters));
return $this->display(__FILE__, 'blocklayered.tpl');
}
else
return false;
}
private static function getPriceFilterSubQuery($filterValue)
{
$idCurrency = (int)Currency::getCurrent()->id;
$priceFilterQuery = '';
if (isset($filterValue) AND $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) AND $filterValue AND isset($product['price_min']) AND isset($product['id_product'])
AND ((int)$filterValue[0] > $product['price_min'] OR (int)$filterValue[1] < $product['price_max']))
{
$price = Product::getPriceStatic($product['id_product']);
if ($price < $filterValue[0] OR $price > $filterValue[1])
continue;
unset($productCollection[$key]);
}
}
return $productCollection;
}
private static function getWeightFilterSubQuery($filterValue, $ignoreJoin)
{
if (isset($filterValue) AND $filterValue)
if ($filterValue[0] != 0 OR $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 (sizeof($filterValue) == 2 OR 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 (sizeof($filterValue) == 3 OR 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 AND isset($layeredFilter['filters']) AND !empty($layeredFilter['filters']))
$layeredValues = unserialize($layeredFilter['filters']);
if (isset($layeredValues['categories']) AND sizeof($layeredValues['categories']))
foreach ($layeredValues['categories'] as $id_category)
$categoryBox[] = (int)$id_category;
}
/* Clean categoryBox before use */
if (isset($categoryBox) AND 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)
'.(sizeof($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.
(sizeof($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)
'.(sizeof($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.
(sizeof($categoryBox) ? ' AND cp.id_category IN ('.implode(',', $categoryBox).')' : '').'
GROUP BY fl.id_feature');
$nElements = sizeof($attributeGroups) + sizeof($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 (sizeof($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 (sizeof($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']) AND sizeof($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 = '';
/* 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(_PS_THEME_DIR_.'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' AND ((substr($memory_limit,-1) == 'M' AND substr($memory_limit,0,-1) < 128) OR is_numeric($memory_limit) AND (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' AND ((substr($memory_limit,-1) == 'M' AND substr($memory_limit,0,-1) < 128) OR is_numeric($memory_limit) AND (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'.(sizeof($categoriesIds) ? ' AND cp.id_category IN ('.implode(',', $categoriesIds).')' : '').' AND p.active = 1'.(sizeof($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'.(sizeof($categoriesIds) ? ' AND cp.id_category IN ('.implode(',', $categoriesIds).')' : '').' AND p.active = 1'.(sizeof($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'.(sizeof($categoriesIds) ? ' AND cp.id_category IN ('.implode(',', $categoriesIds).')' : '').' AND p.active = 1'.(sizeof($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, ','));
}
}
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 OR !sizeof($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]] AND (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'] AND (float)$product['price_max'] <= (float)$max)
$productsToKeep[] = (int)$k;
elseif ((float)$product['price_min'] < (float)$max AND (float)$product['price_max'] > (float)$max
OR (float)$product['price_min'] < (float)$min AND (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 AND $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;
}
}