· 7 years ago · Oct 05, 2018, 06:24 PM
1<?php
2/*
3* 2007-2016 PrestaShop
4*
5* NOTICE OF LICENSE
6*
7* This source file is subject to the Academic Free License (AFL 3.0)
8* that is bundled with this package in the file LICENSE.txt.
9* It is also available through the world-wide-web at this URL:
10* http://opensource.org/licenses/afl-3.0.php
11* If you did not receive a copy of the license and are unable to
12* obtain it through the world-wide-web, please send an email
13* to license@prestashop.com so we can send you a copy immediately.
14*
15* DISCLAIMER
16*
17* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
18* versions in the future. If you wish to customize PrestaShop for your
19* needs please refer to http://www.prestashop.com for more information.
20*
21* @author PrestaShop SA <contact@prestashop.com>
22* @copyright 2007-2016 PrestaShop SA
23* @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0)
24* International Registred Trademark & Property of PrestaShop SA
25*/
26
27if (!defined('_PS_VERSION_'))
28 exit;
29
30class BlockLayered extends Module
31{
32 private $products;
33 private $nbr_products;
34 private $page = 1;
35
36 public function __construct()
37 {
38 $this->name = 'blocklayered';
39 $this->tab = 'front_office_features';
40 $this->version = '2.2.1';
41 $this->author = 'PrestaShop';
42 $this->need_instance = 0;
43 $this->bootstrap = true;
44
45 parent::__construct();
46
47 $this->displayName = $this->l('Layered navigation block');
48 $this->description = $this->l('Displays a block with layered navigation filters.');
49 $this->ps_versions_compliancy = array('min' => '1.6', 'max' => '1.6.99.99');
50
51 if ((int)Tools::getValue('p'))
52 $this->page = (int)Tools::getValue('p');
53 }
54
55 public function install()
56 {
57 if (parent::install() && $this->registerHook('header') && $this->registerHook('leftColumn')
58 && $this->registerHook('categoryAddition') && $this->registerHook('categoryUpdate') && $this->registerHook('attributeGroupForm')
59 && $this->registerHook('afterSaveAttributeGroup') && $this->registerHook('afterDeleteAttributeGroup') && $this->registerHook('featureForm')
60 && $this->registerHook('afterDeleteFeature') && $this->registerHook('afterSaveFeature') && $this->registerHook('categoryDeletion')
61 && $this->registerHook('afterSaveProduct') && $this->registerHook('productListAssign') && $this->registerHook('postProcessAttributeGroup')
62 && $this->registerHook('postProcessFeature') && $this->registerHook('featureValueForm') && $this->registerHook('postProcessFeatureValue')
63 && $this->registerHook('afterDeleteFeatureValue') && $this->registerHook('afterSaveFeatureValue') && $this->registerHook('attributeForm')
64 && $this->registerHook('postProcessAttribute') && $this->registerHook('afterDeleteAttribute') && $this->registerHook('afterSaveAttribute') && $this->registerHook('leftColumn'))
65 {
66
67 Configuration::updateValue('PS_LAYERED_HIDE_0_VALUES', 1);
68 Configuration::updateValue('PS_LAYERED_SHOW_QTIES', 1);
69 Configuration::updateValue('PS_LAYERED_FULL_TREE', 1);
70 Configuration::updateValue('PS_LAYERED_FILTER_PRICE_USETAX', 1);
71 Configuration::updateValue('PS_LAYERED_FILTER_CATEGORY_DEPTH', 1);
72 Configuration::updateValue('PS_LAYERED_FILTER_INDEX_QTY', 0);
73 Configuration::updateValue('PS_LAYERED_FILTER_INDEX_CDT', 0);
74 Configuration::updateValue('PS_LAYERED_FILTER_INDEX_MNF', 0);
75 Configuration::updateValue('PS_LAYERED_FILTER_INDEX_CAT', 0);
76 Configuration::updateValue('PS_ATTRIBUTE_ANCHOR_SEPARATOR', '-');
77 Configuration::updateValue('PS_LAYERED_FILTER_PRICE_ROUNDING', 1);
78
79 $this->rebuildLayeredStructure();
80 $this->buildLayeredCategories();
81
82 $products_count = Db::getInstance()->getValue('SELECT COUNT(*) FROM `'._DB_PREFIX_.'product`');
83
84 if ($products_count < 20000) // Lock template filter creation if too many products
85 $this->rebuildLayeredCache();
86
87 self::installPriceIndexTable();
88 $this->installFriendlyUrlTable();
89 $this->installIndexableAttributeTable();
90 $this->installProductAttributeTable();
91
92 if ($products_count < 5000) // Lock indexation if too many products
93 {
94 self::fullPricesIndexProcess();
95 $this->indexUrl();
96 $this->indexAttribute();
97 }
98
99 return true;
100 }
101 else
102 {
103 // Installation failed (or hook registration) => uninstall the module
104 $this->uninstall();
105 return false;
106 }
107 }
108
109 public function uninstall()
110 {
111 /* Delete all configurations */
112 Configuration::deleteByName('PS_LAYERED_HIDE_0_VALUES');
113 Configuration::deleteByName('PS_LAYERED_SHOW_QTIES');
114 Configuration::deleteByName('PS_LAYERED_FULL_TREE');
115 Configuration::deleteByName('PS_LAYERED_INDEXED');
116 Configuration::deleteByName('PS_LAYERED_FILTER_PRICE_USETAX');
117 Configuration::deleteByName('PS_LAYERED_FILTER_CATEGORY_DEPTH');
118 Configuration::deleteByName('PS_LAYERED_FILTER_INDEX_QTY');
119 Configuration::deleteByName('PS_LAYERED_FILTER_INDEX_CDT');
120 Configuration::deleteByName('PS_LAYERED_FILTER_INDEX_MNF');
121 Configuration::deleteByName('PS_LAYERED_FILTER_INDEX_CAT');
122 Configuration::deleteByName('PS_LAYERED_FILTER_PRICE_ROUNDING');
123
124 Db::getInstance()->execute('DROP TABLE IF EXISTS '._DB_PREFIX_.'layered_price_index');
125 Db::getInstance()->execute('DROP TABLE IF EXISTS '._DB_PREFIX_.'layered_friendly_url');
126 Db::getInstance()->execute('DROP TABLE IF EXISTS '._DB_PREFIX_.'layered_indexable_attribute_group');
127 Db::getInstance()->execute('DROP TABLE IF EXISTS '._DB_PREFIX_.'layered_indexable_feature');
128 Db::getInstance()->execute('DROP TABLE IF EXISTS '._DB_PREFIX_.'layered_indexable_attribute_lang_value');
129 Db::getInstance()->execute('DROP TABLE IF EXISTS '._DB_PREFIX_.'layered_indexable_attribute_group_lang_value');
130 Db::getInstance()->execute('DROP TABLE IF EXISTS '._DB_PREFIX_.'layered_indexable_feature_lang_value');
131 Db::getInstance()->execute('DROP TABLE IF EXISTS '._DB_PREFIX_.'layered_indexable_feature_value_lang_value');
132 Db::getInstance()->execute('DROP TABLE IF EXISTS '._DB_PREFIX_.'layered_category');
133 Db::getInstance()->execute('DROP TABLE IF EXISTS '._DB_PREFIX_.'layered_filter');
134 Db::getInstance()->execute('DROP TABLE IF EXISTS '._DB_PREFIX_.'layered_filter_shop');
135 Db::getInstance()->execute('DROP TABLE IF EXISTS '._DB_PREFIX_.'layered_product_attribute');
136 return parent::uninstall();
137 }
138
139 private static function installPriceIndexTable()
140 {
141 Db::getInstance()->execute('DROP TABLE IF EXISTS `'._DB_PREFIX_.'layered_price_index`');
142
143 Db::getInstance()->execute('
144 CREATE TABLE `'._DB_PREFIX_.'layered_price_index` (
145 `id_product` INT NOT NULL,
146 `id_currency` INT NOT NULL,
147 `id_shop` INT NOT NULL,
148 `price_min` INT NOT NULL,
149 `price_max` INT NOT NULL,
150 PRIMARY KEY (`id_product`, `id_currency`, `id_shop`),
151 INDEX `id_currency` (`id_currency`),
152 INDEX `price_min` (`price_min`), INDEX `price_max` (`price_max`)
153 ) ENGINE='._MYSQL_ENGINE_.' DEFAULT CHARSET=utf8;');
154 }
155
156 private function installFriendlyUrlTable()
157 {
158 Db::getInstance()->execute('DROP TABLE IF EXISTS `'._DB_PREFIX_.'layered_friendly_url`');
159 Db::getInstance()->execute('
160 CREATE TABLE `'._DB_PREFIX_.'layered_friendly_url` (
161 `id_layered_friendly_url` INT NOT NULL AUTO_INCREMENT,
162 `url_key` varchar(32) NOT NULL,
163 `data` varchar(200) NOT NULL,
164 `id_lang` INT NOT NULL,
165 PRIMARY KEY (`id_layered_friendly_url`),
166 INDEX `id_lang` (`id_lang`)
167 ) ENGINE='._MYSQL_ENGINE_.' DEFAULT CHARSET=utf8;');
168
169 Db::getInstance()->execute('CREATE INDEX `url_key` ON `'._DB_PREFIX_.'layered_friendly_url`(url_key(5))');
170 }
171
172 private function installIndexableAttributeTable()
173 {
174 // Attributes Groups
175 Db::getInstance()->execute('DROP TABLE IF EXISTS `'._DB_PREFIX_.'layered_indexable_attribute_group`');
176 Db::getInstance()->execute('
177 CREATE TABLE `'._DB_PREFIX_.'layered_indexable_attribute_group` (
178 `id_attribute_group` INT NOT NULL,
179 `indexable` BOOL NOT NULL DEFAULT 0,
180 PRIMARY KEY (`id_attribute_group`)
181 ) ENGINE='._MYSQL_ENGINE_.' DEFAULT CHARSET=utf8;');
182 Db::getInstance()->execute('
183 INSERT INTO `'._DB_PREFIX_.'layered_indexable_attribute_group`
184 SELECT id_attribute_group, 1 FROM `'._DB_PREFIX_.'attribute_group`');
185
186 Db::getInstance()->execute('DROP TABLE IF EXISTS `'._DB_PREFIX_.'layered_indexable_attribute_group_lang_value`');
187 Db::getInstance()->execute('
188 CREATE TABLE `'._DB_PREFIX_.'layered_indexable_attribute_group_lang_value` (
189 `id_attribute_group` INT NOT NULL,
190 `id_lang` INT NOT NULL,
191 `url_name` VARCHAR(128),
192 `meta_title` VARCHAR(128),
193 PRIMARY KEY (`id_attribute_group`, `id_lang`)
194 ) ENGINE='._MYSQL_ENGINE_.' DEFAULT CHARSET=utf8;');
195
196 // Attributes
197 Db::getInstance()->execute('DROP TABLE IF EXISTS `'._DB_PREFIX_.'layered_indexable_attribute_lang_value`');
198 Db::getInstance()->execute('
199 CREATE TABLE `'._DB_PREFIX_.'layered_indexable_attribute_lang_value` (
200 `id_attribute` INT NOT NULL,
201 `id_lang` INT NOT NULL,
202 `url_name` VARCHAR(128),
203 `meta_title` VARCHAR(128),
204 PRIMARY KEY (`id_attribute`, `id_lang`)
205 ) ENGINE='._MYSQL_ENGINE_.' DEFAULT CHARSET=utf8;');
206
207
208 // Features
209 Db::getInstance()->execute('DROP TABLE IF EXISTS `'._DB_PREFIX_.'layered_indexable_feature`');
210 Db::getInstance()->execute('
211 CREATE TABLE `'._DB_PREFIX_.'layered_indexable_feature` (
212 `id_feature` INT NOT NULL,
213 `indexable` BOOL NOT NULL DEFAULT 0,
214 PRIMARY KEY (`id_feature`)
215 ) ENGINE='._MYSQL_ENGINE_.' DEFAULT CHARSET=utf8;');
216
217 Db::getInstance()->execute('
218 INSERT INTO `'._DB_PREFIX_.'layered_indexable_feature`
219 SELECT id_feature, 1 FROM `'._DB_PREFIX_.'feature`');
220
221 Db::getInstance()->execute('DROP TABLE IF EXISTS `'._DB_PREFIX_.'layered_indexable_feature_lang_value`');
222 Db::getInstance()->execute('
223 CREATE TABLE `'._DB_PREFIX_.'layered_indexable_feature_lang_value` (
224 `id_feature` INT NOT NULL,
225 `id_lang` INT NOT NULL,
226 `url_name` VARCHAR(128) NOT NULL,
227 `meta_title` VARCHAR(128),
228 PRIMARY KEY (`id_feature`, `id_lang`)
229 ) ENGINE='._MYSQL_ENGINE_.' DEFAULT CHARSET=utf8;');
230
231 // Features values
232 Db::getInstance()->execute('DROP TABLE IF EXISTS `'._DB_PREFIX_.'layered_indexable_feature_value_lang_value`');
233 Db::getInstance()->execute('
234 CREATE TABLE `'._DB_PREFIX_.'layered_indexable_feature_value_lang_value` (
235 `id_feature_value` INT NOT NULL,
236 `id_lang` INT NOT NULL,
237 `url_name` VARCHAR(128),
238 `meta_title` VARCHAR(128),
239 PRIMARY KEY (`id_feature_value`, `id_lang`)
240 ) ENGINE='._MYSQL_ENGINE_.' DEFAULT CHARSET=utf8;');
241 }
242
243 /**
244 *
245 * create table product attribute
246 */
247 public function installProductAttributeTable()
248 {
249 Db::getInstance()->execute('DROP TABLE IF EXISTS `'._DB_PREFIX_.'layered_product_attribute`');
250 Db::getInstance()->execute('
251 CREATE TABLE `'._DB_PREFIX_.'layered_product_attribute` (
252 `id_attribute` int(10) unsigned NOT NULL,
253 `id_product` int(10) unsigned NOT NULL,
254 `id_attribute_group` int(10) unsigned NOT NULL DEFAULT "0",
255 `id_shop` int(10) unsigned NOT NULL DEFAULT "1",
256 PRIMARY KEY (`id_attribute`, `id_product`, `id_shop`),
257 UNIQUE KEY `id_attribute_group` (`id_attribute_group`,`id_attribute`,`id_product`, `id_shop`)
258 ) ENGINE='._MYSQL_ENGINE_.' DEFAULT CHARSET=utf8;');
259 }
260
261 //ATTRIBUTES GROUP
262 public function hookAfterSaveAttributeGroup($params)
263 {
264 if (!$params['id_attribute_group'] || Tools::getValue('layered_indexable') === false)
265 return;
266
267 Db::getInstance()->execute(
268 'DELETE FROM '._DB_PREFIX_.'layered_indexable_attribute_group
269 WHERE `id_attribute_group` = '.(int)$params['id_attribute_group']
270 );
271 Db::getInstance()->execute(
272 'DELETE FROM '._DB_PREFIX_.'layered_indexable_attribute_group_lang_value
273 WHERE `id_attribute_group` = '.(int)$params['id_attribute_group']
274 );
275
276 Db::getInstance()->execute(
277 'INSERT INTO '._DB_PREFIX_.'layered_indexable_attribute_group (`id_attribute_group`, `indexable`)
278 VALUES ('.(int)$params['id_attribute_group'].', '.(int)Tools::getValue('layered_indexable').')'
279 );
280
281
282 foreach (Language::getLanguages(false) as $language)
283 {
284 $seo_url = Tools::getValue('url_name_'.(int)$language['id_lang']);
285
286 if(empty($seo_url))
287 $seo_url = Tools::getValue('name_'.(int)$language['id_lang']);
288
289 Db::getInstance()->execute(
290 'INSERT INTO '._DB_PREFIX_.'layered_indexable_attribute_group_lang_value
291 (`id_attribute_group`, `id_lang`, `url_name`, `meta_title`)
292 VALUES (
293 '.(int)$params['id_attribute_group'].', '.(int)$language['id_lang'].',
294 \''.pSQL(Tools::link_rewrite($seo_url)).'\',
295 \''.pSQL(Tools::getValue('meta_title_'.(int)$language['id_lang']), true).'\'
296 )'
297 );
298 }
299 }
300
301 public function hookAfterDeleteAttributeGroup($params)
302 {
303 if (!$params['id_attribute_group'])
304 return;
305
306 Db::getInstance()->execute(
307 'DELETE FROM '._DB_PREFIX_.'layered_indexable_attribute_group
308 WHERE `id_attribute_group` = '.(int)$params['id_attribute_group']
309 );
310 Db::getInstance()->execute(
311 'DELETE FROM '._DB_PREFIX_.'layered_indexable_attribute_group_lang_value
312 WHERE `id_attribute_group` = '.(int)$params['id_attribute_group']
313 );
314 }
315
316 public function hookPostProcessAttributeGroup($params)
317 {
318 $errors = array();
319
320 foreach (Language::getLanguages(false) as $language)
321 {
322 $id_lang = $language['id_lang'];
323
324 if (Tools::getValue('url_name_'.$id_lang))
325 if (Tools::link_rewrite(Tools::getValue('url_name_'.$id_lang)) != strtolower(Tools::getValue('url_name_'.$id_lang)))
326 $params['errors'][] = Tools::displayError(sprintf($this->l('"%s" is not a valid url'),
327 Tools::getValue('url_name_'.$id_lang)));
328 }
329 }
330
331 public function hookAttributeGroupForm($params)
332 {
333 $values = array();
334 $is_indexable = Db::getInstance(_PS_USE_SQL_SLAVE_)->getValue(
335 'SELECT `indexable`
336 FROM '._DB_PREFIX_.'layered_indexable_attribute_group
337 WHERE `id_attribute_group` = '.(int)$params['id_attribute_group']
338 );
339
340 if ($is_indexable === false)
341 $is_indexable = true;
342
343 if ($result = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS(
344 'SELECT `url_name`, `meta_title`, `id_lang` FROM '._DB_PREFIX_.'layered_indexable_attribute_group_lang_value
345 WHERE `id_attribute_group` = '.(int)$params['id_attribute_group']
346 ))
347 foreach ($result as $data)
348 $values[$data['id_lang']] = array('url_name' => $data['url_name'], 'meta_title' => $data['meta_title']);
349
350 $this->context->smarty->assign(array(
351 'languages' => Language::getLanguages(false),
352 'default_form_language' => (int)$this->context->controller->default_form_language,
353 'values' => $values,
354 'is_indexable' =>(bool)$is_indexable
355 ));
356
357 if (version_compare(_PS_VERSION_, '1.6.0', '>=') === true)
358 return $this->display(__FILE__, 'attribute_group_form_1.6.tpl');
359 else
360 return $this->display(__FILE__, 'attribute_group_form.tpl');
361 }
362
363 //ATTRIBUTES
364 public function hookAfterSaveAttribute($params)
365 {
366 if (!$params['id_attribute'])
367 return;
368
369 Db::getInstance()->execute(
370 'DELETE FROM '._DB_PREFIX_.'layered_indexable_attribute_lang_value
371 WHERE `id_attribute` = '.(int)$params['id_attribute']
372 );
373
374 foreach (Language::getLanguages(false) as $language)
375 {
376 $seo_url = Tools::getValue('url_name_'.(int)$language['id_lang']);
377
378 if(empty($seo_url))
379 $seo_url = Tools::getValue('name_'.(int)$language['id_lang']);
380
381 Db::getInstance()->execute(
382 'INSERT INTO '._DB_PREFIX_.'layered_indexable_attribute_lang_value
383 (`id_attribute`, `id_lang`, `url_name`, `meta_title`)
384 VALUES (
385 '.(int)$params['id_attribute'].', '.(int)$language['id_lang'].',
386 \''.pSQL(Tools::link_rewrite($seo_url)).'\',
387 \''.pSQL(Tools::getValue('meta_title_'.(int)$language['id_lang']), true).'\'
388 )'
389 );
390 }
391 }
392
393 public function hookAfterDeleteAttribute($params)
394 {
395 if (!$params['id_attribute'])
396 return;
397
398 Db::getInstance()->execute(
399 'DELETE FROM '._DB_PREFIX_.'layered_indexable_attribute_lang_value
400 WHERE `id_attribute` = '.(int)$params['id_attribute']
401 );
402 }
403
404 public function hookPostProcessAttribute($params)
405 {
406 $errors = array();
407
408 foreach (Language::getLanguages(false) as $language)
409 {
410 $id_lang = $language['id_lang'];
411
412 if (Tools::getValue('url_name_'.$id_lang))
413 if (Tools::link_rewrite(Tools::getValue('url_name_'.$id_lang)) != strtolower(Tools::getValue('url_name_'.$id_lang)))
414 $params['errors'][] = Tools::displayError(sprintf($this->l('"%s" is not a valid url'),
415 Tools::getValue('url_name_'.$id_lang)));
416 }
417 }
418
419 public function hookAttributeForm($params)
420 {
421 $values = array();
422
423 if ($result = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS(
424 'SELECT `url_name`, `meta_title`, `id_lang`
425 FROM '._DB_PREFIX_.'layered_indexable_attribute_lang_value
426 WHERE `id_attribute` = '.(int)$params['id_attribute']
427 ))
428 foreach ($result as $data)
429 $values[$data['id_lang']] = array('url_name' => $data['url_name'], 'meta_title' => $data['meta_title']);
430
431 $this->context->smarty->assign(array(
432 'languages' => Language::getLanguages(false),
433 'default_form_language' => (int)$this->context->controller->default_form_language,
434 'values' => $values
435 ));
436
437 if (version_compare(_PS_VERSION_, '1.6.0', '>=') === true)
438 return $this->display(__FILE__, 'attribute_form_1.6.tpl');
439 else
440 return $this->display(__FILE__, 'attribute_form.tpl');
441 }
442
443 //FEATURES
444 public function hookAfterSaveFeature($params)
445 {
446 if (!$params['id_feature'] || Tools::getValue('layered_indexable') === false)
447 return;
448
449 Db::getInstance()->execute(
450 'DELETE FROM '._DB_PREFIX_.'layered_indexable_feature
451 WHERE `id_feature` = '.(int)$params['id_feature']
452 );
453 Db::getInstance()->execute(
454 'DELETE FROM '._DB_PREFIX_.'layered_indexable_feature_lang_value
455 WHERE `id_feature` = '.(int)$params['id_feature']
456 );
457
458 Db::getInstance()->execute(
459 'INSERT INTO '._DB_PREFIX_.'layered_indexable_feature
460 (`id_feature`, `indexable`)
461 VALUES ('.(int)$params['id_feature'].', '.(int)Tools::getValue('layered_indexable').')'
462 );
463
464 foreach (Language::getLanguages(false) as $language)
465 {
466 $seo_url = Tools::getValue('url_name_'.(int)$language['id_lang']);
467
468 if(empty($seo_url))
469 $seo_url = Tools::getValue('name_'.(int)$language['id_lang']);
470
471 Db::getInstance()->execute(
472 'INSERT INTO '._DB_PREFIX_.'layered_indexable_feature_lang_value
473 (`id_feature`, `id_lang`, `url_name`, `meta_title`)
474 VALUES (
475 '.(int)$params['id_feature'].', '.(int)$language['id_lang'].',
476 \''.pSQL(Tools::link_rewrite($seo_url)).'\',
477 \''.pSQL(Tools::getValue('meta_title_'.(int)$language['id_lang']), true).'\'
478 )'
479 );
480 }
481 }
482
483 public function hookAfterDeleteFeature($params)
484 {
485 if (!$params['id_feature'])
486 return;
487
488 Db::getInstance()->execute(
489 'DELETE FROM '._DB_PREFIX_.'layered_indexable_feature
490 WHERE `id_feature` = '.(int)$params['id_feature']
491 );
492 }
493
494 public function hookPostProcessFeature($params)
495 {
496 $errors = array();
497
498 foreach (Language::getLanguages(false) as $language)
499 {
500 $id_lang = $language['id_lang'];
501
502 if (Tools::getValue('url_name_'.$id_lang))
503 if (Tools::link_rewrite(Tools::getValue('url_name_'.$id_lang)) != strtolower(Tools::getValue('url_name_'.$id_lang)))
504 $params['errors'][] = Tools::displayError(sprintf($this->l('"%s" is not a valid url'),
505 Tools::getValue('url_name_'.$id_lang)));
506 }
507 }
508
509 public function hookFeatureForm($params)
510 {
511 $values = array();
512 $is_indexable = Db::getInstance(_PS_USE_SQL_SLAVE_)->getValue(
513 'SELECT `indexable`
514 FROM '._DB_PREFIX_.'layered_indexable_feature
515 WHERE `id_feature` = '.(int)$params['id_feature']
516 );
517
518 if ($is_indexable === false)
519 $is_indexable = true;
520
521 if ($result = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS(
522 'SELECT `url_name`, `meta_title`, `id_lang` FROM '._DB_PREFIX_.'layered_indexable_feature_lang_value
523 WHERE `id_feature` = '.(int)$params['id_feature']
524 ))
525 foreach ($result as $data)
526 $values[$data['id_lang']] = array('url_name' => $data['url_name'], 'meta_title' => $data['meta_title']);
527
528 $this->context->smarty->assign(array(
529 'languages' => Language::getLanguages(false),
530 'default_form_language' => (int)$this->context->controller->default_form_language,
531 'values' => $values,
532 'is_indexable' =>(bool)$is_indexable
533 ));
534
535 if (version_compare(_PS_VERSION_, '1.6.0', '>=') === true)
536 return $this->display(__FILE__, 'feature_form_1.6.tpl');
537 else
538 return $this->display(__FILE__, 'feature_form.tpl');
539 }
540
541 //FEATURES VALUE
542 public function hookAfterSaveFeatureValue($params)
543 {
544 if (!$params['id_feature_value'])
545 return;
546
547 //Removing all indexed language data for this attribute value id
548 Db::getInstance()->execute(
549 'DELETE FROM '._DB_PREFIX_.'layered_indexable_feature_value_lang_value
550 WHERE `id_feature_value` = '.(int)$params['id_feature_value']
551 );
552
553 foreach (Language::getLanguages(false) as $language)
554 {
555 $seo_url = Tools::getValue('url_name_'.(int)$language['id_lang']);
556
557 if(empty($seo_url))
558 $seo_url = Tools::getValue('name_'.(int)$language['id_lang']);
559
560 Db::getInstance()->execute(
561 'INSERT INTO '._DB_PREFIX_.'layered_indexable_feature_value_lang_value
562 (`id_feature_value`, `id_lang`, `url_name`, `meta_title`)
563 VALUES (
564 '.(int)$params['id_feature_value'].', '.(int)$language['id_lang'].',
565 \''.pSQL(Tools::link_rewrite($seo_url)).'\',
566 \''.pSQL(Tools::getValue('meta_title_'.(int)$language['id_lang']), true).'\'
567 )'
568 );
569 }
570 }
571
572 public function hookAfterDeleteFeatureValue($params)
573 {
574 if (!$params['id_feature_value'])
575 return;
576
577 Db::getInstance()->execute(
578 'DELETE FROM '._DB_PREFIX_.'layered_indexable_feature_value_lang_value
579 WHERE `id_feature_value` = '.(int)$params['id_feature_value']
580 );
581 }
582
583 public function hookPostProcessFeatureValue($params)
584 {
585 $errors = array();
586
587 foreach (Language::getLanguages(false) as $language)
588 {
589 $id_lang = $language['id_lang'];
590
591 if (Tools::getValue('url_name_'.$id_lang))
592 if (Tools::link_rewrite(Tools::getValue('url_name_'.$id_lang)) != strtolower(Tools::getValue('url_name_'.$id_lang)))
593 $params['errors'][] = Tools::displayError(sprintf($this->l('"%s" is not a valid url'),
594 Tools::getValue('url_name_'.$id_lang)));
595 }
596 }
597
598 public function hookFeatureValueForm($params)
599 {
600 $values = array();
601
602 if ($result = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS(
603 'SELECT `url_name`, `meta_title`, `id_lang`
604 FROM '._DB_PREFIX_.'layered_indexable_feature_value_lang_value
605 WHERE `id_feature_value` = '.(int)$params['id_feature_value']
606 ))
607 foreach ($result as $data)
608 $values[$data['id_lang']] = array('url_name' => $data['url_name'], 'meta_title' => $data['meta_title']);
609
610 $this->context->smarty->assign(array(
611 'languages' => Language::getLanguages(false),
612 'default_form_language' => (int)$this->context->controller->default_form_language,
613 'values' => $values
614 ));
615
616 if (version_compare(_PS_VERSION_, '1.6.0', '>=') === true)
617 return $this->display(__FILE__, 'feature_value_form_1.6.tpl');
618 else
619 return $this->display(__FILE__, 'feature_value_form.tpl');
620 }
621
622 public function hookProductListAssign($params)
623 {
624 if ((isset($this->context->controller->display_column_left) && !$this->context->controller->display_column_left)
625 && (isset($this->context->controller->display_column_right) && !$this->context->controller->display_column_right))
626 return false;
627
628 global $smarty;
629 if (!Configuration::getGlobalValue('PS_LAYERED_INDEXED'))
630 return;
631
632 $categories_count = Db::getInstance()->getValue('
633 SELECT COUNT(*)
634 FROM '._DB_PREFIX_.'layered_category
635 WHERE id_category = '.(int)Tools::getValue('id_category', Tools::getValue('id_category_layered', Configuration::get('PS_HOME_CATEGORY'))).'
636 AND id_shop = '.(int) Context::getContext()->shop->id
637 );
638
639 if ($categories_count == 0)
640 return;
641
642 // Inform the hook was executed
643 $params['hookExecuted'] = true;
644 // List of product to overrride categoryController
645 $params['catProducts'] = array();
646 $selected_filters = $this->getSelectedFilters();
647 $filter_block = $this->getFilterBlock($selected_filters);
648 $title = '';
649
650 if (is_array($filter_block['title_values']))
651 foreach ($filter_block['title_values'] as $key => $val)
652 $title .= ' > '.$key.' '.implode('/', $val);
653
654 $smarty->assign('categoryNameComplement', $title);
655 $this->getProducts($selected_filters, $params['catProducts'], $params['nbProducts'], $p, $n, $pages_nb, $start, $stop, $range);
656 // Need a nofollow on the pagination links?
657 $smarty->assign('no_follow', $filter_block['no_follow']);
658 }
659
660 public function hookAfterSaveProduct($params)
661 {
662 if (!$params['id_product'])
663 return;
664
665 self::indexProductPrices((int)$params['id_product']);
666 $this->indexAttribute((int)$params['id_product']);
667 }
668
669 public function hookLeftColumn($params)
670 {
671 return $this->generateFiltersBlock($this->getSelectedFilters());
672 }
673
674 public function hookRightColumn($params)
675 {
676 return $this->hookLeftColumn($params);
677 }
678
679 public function hookHeader($params)
680 {
681 if ((isset($this->context->controller->display_column_left) && !$this->context->controller->display_column_left)
682 && (isset($this->context->controller->display_column_right) && !$this->context->controller->display_column_right))
683 return false;
684
685 global $smarty, $cookie;
686
687 // No filters => module disable
688 if ($filter_block = $this->getFilterBlock($this->getSelectedFilters()))
689 if ($filter_block['nbr_filterBlocks'] == 0)
690 return false;
691
692 if (Tools::getValue('id_category', Tools::getValue('id_category_layered', Configuration::get('PS_HOME_CATEGORY'))) == Configuration::get('PS_HOME_CATEGORY'))
693 return;
694
695 $id_lang = (int)$cookie->id_lang;
696 $category = new Category((int)Tools::getValue('id_category'));
697
698 // Generate meta title and meta description
699 $category_title = (empty($category->meta_title[$id_lang]) ? $category->name[$id_lang] : $category->meta_title[$id_lang]);
700 $category_metas = Meta::getMetaTags($id_lang, 'category');
701 $title = '';
702 $keywords = '';
703
704 if (is_array($filter_block['title_values']))
705 foreach ($filter_block['title_values'] as $key => $val)
706 {
707 $title .= ' > '.$key.' '.implode('/', $val);
708 $keywords .= $key.' '.implode('/', $val).', ';
709 }
710
711 $title = $category_title.$title;
712
713 if (!empty($title))
714 $smarty->assign('meta_title', $title.' - '.Configuration::get('PS_SHOP_NAME'));
715 else
716 $smarty->assign('meta_title', $category_metas['meta_title']);
717
718 $smarty->assign('meta_description', $category_metas['meta_description']);
719
720 $keywords = substr(strtolower($keywords), 0, 1000);
721 if (!empty($keywords))
722 $smarty->assign('meta_keywords', rtrim($category_title.', '.$keywords.', '.$category_metas['meta_keywords'], ', '));
723
724
725 $this->context->controller->addJS(($this->_path).'blocklayered.js');
726 $this->context->controller->addJS(_PS_JS_DIR_.'jquery/jquery-ui-1.8.10.custom.min.js');
727 $this->context->controller->addJQueryUI('ui.slider');
728 $this->context->controller->addCSS(_PS_CSS_DIR_.'jquery-ui-1.8.10.custom.css');
729
730 if (version_compare(_PS_VERSION_, '1.6.0', '>=') === true)
731 $this->context->controller->addCSS(($this->_path).'blocklayered.css', 'all');
732 else
733 $this->context->controller->addCSS(($this->_path).'blocklayered-15.css', 'all');
734 $this->context->controller->addJQueryPlugin('scrollTo');
735
736 $filters = $this->getSelectedFilters();
737
738 // Get non indexable attributes
739 $attribute_group_list = Db::getInstance()->executeS('SELECT id_attribute_group FROM '._DB_PREFIX_.'layered_indexable_attribute_group WHERE indexable = 0');
740 // Get non indexable features
741 $feature_list = Db::getInstance()->executeS('SELECT id_feature FROM '._DB_PREFIX_.'layered_indexable_feature WHERE indexable = 0');
742
743 $attributes = array();
744 $features = array();
745
746 $blacklist = array('weight', 'price');
747 if (!Configuration::get('PS_LAYERED_FILTER_INDEX_CDT'))
748 $blacklist[] = 'condition';
749 if (!Configuration::get('PS_LAYERED_FILTER_INDEX_QTY'))
750 $blacklist[] = 'quantity';
751 if (!Configuration::get('PS_LAYERED_FILTER_INDEX_MNF'))
752 $blacklist[] = 'manufacturer';
753 if (!Configuration::get('PS_LAYERED_FILTER_INDEX_CAT'))
754 $blacklist[] = 'category';
755
756 foreach ($filters as $type => $val)
757 {
758 switch ($type)
759 {
760 case 'id_attribute_group':
761 foreach ($val as $attr)
762 {
763 $attr_id = preg_replace('/_\d+$/', '', $attr);
764 if (in_array($attr_id, $attributes) || in_array(array('id_attribute_group' => $attr_id), $attribute_group_list))
765 {
766 $smarty->assign('nobots', true);
767 $smarty->assign('nofollow', true);
768 return;
769 }
770 $attributes[] = $attr_id;
771 }
772 break;
773 case 'id_feature':
774 foreach ($val as $feat)
775 {
776 $feat_id = preg_replace('/_\d+$/', '', $feat);
777 if (in_array($feat_id, $features) || in_array(array('id_feature' => $feat_id), $feature_list))
778 {
779 $smarty->assign('nobots', true);
780 $smarty->assign('nofollow', true);
781 return;
782 }
783 $features[] = $feat_id;
784 }
785 break;
786 default:
787 if (in_array($type, $blacklist))
788 {
789 if (count($val))
790 {
791 $smarty->assign('nobots', true);
792 $smarty->assign('nofollow', true);
793 return;
794 }
795 }
796 elseif (count($val) > 1)
797 {
798 $smarty->assign('nobots', true);
799 $smarty->assign('nofollow', true);
800 return;
801 }
802 break;
803 }
804 }
805 }
806
807 public function hookFooter($params)
808 {
809 if ((isset($this->context->controller->display_column_left) && !$this->context->controller->display_column_left)
810 && (isset($this->context->controller->display_column_right) && !$this->context->controller->display_column_right))
811 return false;
812
813 // No filters => module disable
814 if ($filter_block = $this->getFilterBlock($this->getSelectedFilters()))
815 if ($filter_block['nbr_filterBlocks'] == 0)
816 return false;
817
818 if (Dispatcher::getInstance()->getController() == 'category')
819 $this->context->controller->addJS($this->_path.'blocklayered-footer.js');
820 }
821
822 public function hookCategoryAddition($params)
823 {
824 $this->rebuildLayeredCache(array(), array((int)$params['category']->id));
825 }
826
827 public function hookCategoryUpdate($params)
828 {
829 /* The category status might (active, inactive) have changed, we have to update the layered cache table structure */
830 if (isset($params['category']) && !$params['category']->active)
831 $this->hookCategoryDeletion($params);
832 }
833
834 public function hookCategoryDeletion($params)
835 {
836 $layered_filter_list = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS(
837 'SELECT * FROM '._DB_PREFIX_.'layered_filter'
838 );
839
840 foreach ($layered_filter_list as $layered_filter)
841 {
842 $data = Tools::unSerialize($layered_filter['filters']);
843
844 if (in_array((int)$params['category']->id, $data['categories']))
845 {
846 unset($data['categories'][array_search((int)$params['category']->id, $data['categories'])]);
847 Db::getInstance()->execute(
848 'UPDATE `'._DB_PREFIX_.'layered_filter`
849 SET `filters` = \''.pSQL(serialize($data)).'\'
850 WHERE `id_layered_filter` = '.(int)$layered_filter['id_layered_filter']
851 );
852 }
853 }
854
855 $this->buildLayeredCategories();
856 }
857
858 /*
859 * Generate data product attribute
860 */
861 public function indexAttribute($id_product = null)
862 {
863 if (is_null($id_product))
864 Db::getInstance()->execute('TRUNCATE '._DB_PREFIX_.'layered_product_attribute');
865 else
866 Db::getInstance()->execute('
867 DELETE FROM '._DB_PREFIX_.'layered_product_attribute
868 WHERE id_product = '.(int)$id_product
869 );
870
871 Db::getInstance()->execute('
872 INSERT INTO `'._DB_PREFIX_.'layered_product_attribute` (`id_attribute`, `id_product`, `id_attribute_group`, `id_shop`)
873 SELECT pac.id_attribute, pa.id_product, ag.id_attribute_group, product_attribute_shop.`id_shop`
874 FROM '._DB_PREFIX_.'product_attribute pa'.
875 Shop::addSqlAssociation('product_attribute', 'pa').'
876 INNER JOIN '._DB_PREFIX_.'product_attribute_combination pac ON pac.id_product_attribute = pa.id_product_attribute
877 INNER JOIN '._DB_PREFIX_.'attribute a ON (a.id_attribute = pac.id_attribute)
878 INNER JOIN '._DB_PREFIX_.'attribute_group ag ON ag.id_attribute_group = a.id_attribute_group
879 '.(is_null($id_product) ? '' : 'AND pa.id_product = '.(int)$id_product).'
880 GROUP BY a.id_attribute, pa.id_product , product_attribute_shop.`id_shop`'
881 );
882
883 return 1;
884 }
885
886 /*
887 * Url indexation
888 */
889 public function indexUrl($ajax = false, $truncate = true)
890 {
891 if ($truncate)
892 Db::getInstance()->execute('TRUNCATE '._DB_PREFIX_.'layered_friendly_url');
893
894 $attribute_values_by_lang = array();
895 $filters = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS('
896 SELECT lc.*, id_lang, name, link_rewrite, cl.id_category
897 FROM '._DB_PREFIX_.'layered_category lc
898 INNER JOIN '._DB_PREFIX_.'category_lang cl ON (cl.id_category = lc.id_category AND lc.id_category <> 1 )
899 GROUP BY type, id_value, id_lang'
900 );
901
902 if (!$filters)
903 return;
904
905 foreach ($filters as $filter)
906 switch ($filter['type'])
907 {
908 case 'id_attribute_group':
909 $attributes = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS('
910 SELECT agl.public_name name, a.id_attribute_group id_name, al.name value, a.id_attribute id_value, al.id_lang,
911 liagl.url_name name_url_name, lial.url_name value_url_name
912 FROM '._DB_PREFIX_.'attribute_group ag
913 INNER JOIN '._DB_PREFIX_.'attribute_group_lang agl ON (agl.id_attribute_group = ag.id_attribute_group)
914 INNER JOIN '._DB_PREFIX_.'attribute a ON (a.id_attribute_group = ag.id_attribute_group)
915 INNER JOIN '._DB_PREFIX_.'attribute_lang al ON (al.id_attribute = a.id_attribute)
916 LEFT JOIN '._DB_PREFIX_.'layered_indexable_attribute_group liag ON (liag.id_attribute_group = a.id_attribute_group)
917 LEFT JOIN '._DB_PREFIX_.'layered_indexable_attribute_group_lang_value liagl
918 ON (liagl.id_attribute_group = ag.id_attribute_group AND liagl.id_lang = '.(int)$filter['id_lang'].')
919 LEFT JOIN '._DB_PREFIX_.'layered_indexable_attribute_lang_value lial
920 ON (lial.id_attribute = a.id_attribute AND lial.id_lang = '.(int)$filter['id_lang'].')
921 WHERE a.id_attribute_group = '.(int)$filter['id_value'].' AND agl.id_lang = al.id_lang AND agl.id_lang = '.(int)$filter['id_lang']
922 );
923
924 foreach ($attributes as $attribute)
925 {
926 if (!isset($attribute_values_by_lang[$attribute['id_lang']]))
927 $attribute_values_by_lang[$attribute['id_lang']] = array();
928 if (!isset($attribute_values_by_lang[$attribute['id_lang']]['c'.$attribute['id_name']]))
929 $attribute_values_by_lang[$attribute['id_lang']]['c'.$attribute['id_name']] = array();
930 $attribute_values_by_lang[$attribute['id_lang']]['c'.$attribute['id_name']][] = array(
931 'name' => (!empty($attribute['name_url_name']) ? $attribute['name_url_name'] : $attribute['name']),
932 'id_name' => 'c'.$attribute['id_name'],
933 'value' => (!empty($attribute['value_url_name']) ? $attribute['value_url_name'] : $attribute['value']),
934 'id_value' => $attribute['id_name'].'_'.$attribute['id_value'],
935 'id_id_value' => $attribute['id_value'],
936 'category_name' => $filter['link_rewrite'],
937 'type' => $filter['type']);
938 }
939 break;
940
941 case 'id_feature':
942 $features = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS('
943 SELECT fl.name name, fl.id_feature id_name, fvl.id_feature_value id_value, fvl.value value, fl.id_lang, fl.id_lang,
944 lifl.url_name name_url_name, lifvl.url_name value_url_name
945 FROM '._DB_PREFIX_.'feature_lang fl
946 LEFT JOIN '._DB_PREFIX_.'layered_indexable_feature lif ON (lif.id_feature = fl.id_feature)
947 INNER JOIN '._DB_PREFIX_.'feature_value fv ON (fv.id_feature = fl.id_feature)
948 INNER JOIN '._DB_PREFIX_.'feature_value_lang fvl ON (fvl.id_feature_value = fv.id_feature_value)
949 LEFT JOIN '._DB_PREFIX_.'layered_indexable_feature_lang_value lifl
950 ON (lifl.id_feature = fl.id_feature AND lifl.id_lang = '.(int)$filter['id_lang'].')
951 LEFT JOIN '._DB_PREFIX_.'layered_indexable_feature_value_lang_value lifvl
952 ON (lifvl.id_feature_value = fvl.id_feature_value AND lifvl.id_lang = '.(int)$filter['id_lang'].')
953 WHERE fl.id_feature = '.(int)$filter['id_value'].' AND fvl.id_lang = fl.id_lang AND fvl.id_lang = '.(int)$filter['id_lang']
954 );
955
956 foreach ($features as $feature)
957 {
958 if (!isset($attribute_values_by_lang[$feature['id_lang']]))
959 $attribute_values_by_lang[$feature['id_lang']] = array();
960 if (!isset($attribute_values_by_lang[$feature['id_lang']]['f'.$feature['id_name']]))
961 $attribute_values_by_lang[$feature['id_lang']]['f'.$feature['id_name']] = array();
962 $attribute_values_by_lang[$feature['id_lang']]['f'.$feature['id_name']][] = array(
963 'name' => (!empty($feature['name_url_name']) ? $feature['name_url_name'] : $feature['name']),
964 'id_name' => 'f'.$feature['id_name'],
965 'value' => (!empty($feature['value_url_name']) ? $feature['value_url_name'] : $feature['value']),
966 'id_value' => $feature['id_name'].'_'.$feature['id_value'],
967 'id_id_value' => $feature['id_value'],
968 'category_name' => $filter['link_rewrite'],
969 'type' => $filter['type']);
970 }
971 break;
972
973 case 'category':
974 $categories = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS('
975 SELECT cl.name, cl.id_lang, c.id_category
976 FROM '._DB_PREFIX_.'category c
977 INNER JOIN '._DB_PREFIX_.'category_lang cl ON (c.id_category = cl.id_category)
978 WHERE cl.id_lang = '.(int)$filter['id_lang']
979 );
980
981 foreach ($categories as $category)
982 {
983 if (!isset($attribute_values_by_lang[$category['id_lang']]))
984 $attribute_values_by_lang[$category['id_lang']] = array();
985 if (!isset($attribute_values_by_lang[$category['id_lang']]['category']))
986 $attribute_values_by_lang[$category['id_lang']]['category'] = array();
987 $attribute_values_by_lang[$category['id_lang']]['category'][] = array('name' => $this->translateWord('Categories', $category['id_lang']),
988 'id_name' => null, 'value' => $category['name'], 'id_value' => $category['id_category'],
989 'category_name' => $filter['link_rewrite'], 'type' => $filter['type']);
990 }
991 break;
992
993 case 'manufacturer':
994 $manufacturers = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS('
995 SELECT m.name as name,l.id_lang as id_lang, id_manufacturer
996 FROM '._DB_PREFIX_.'manufacturer m , '._DB_PREFIX_.'lang l
997 WHERE l.id_lang = '.(int)$filter['id_lang']
998 );
999
1000 foreach ($manufacturers as $manufacturer)
1001 {
1002 if (!isset($attribute_values_by_lang[$manufacturer['id_lang']]))
1003 $attribute_values_by_lang[$manufacturer['id_lang']] = array();
1004 if (!isset($attribute_values_by_lang[$manufacturer['id_lang']]['manufacturer']))
1005 $attribute_values_by_lang[$manufacturer['id_lang']]['manufacturer'] = array();
1006 $attribute_values_by_lang[$manufacturer['id_lang']]['manufacturer'][] = array('name' => $this->translateWord('Manufacturer', $manufacturer['id_lang']),
1007 'id_name' => null, 'value' => $manufacturer['name'], 'id_value' => $manufacturer['id_manufacturer'],
1008 'category_name' => $filter['link_rewrite'], 'type' => $filter['type']);
1009 }
1010 break;
1011
1012 case 'quantity':
1013 $avaibility_list = array(
1014 $this->translateWord('Not available', (int)$filter['id_lang']),
1015 $this->translateWord('In stock', (int)$filter['id_lang'])
1016 );
1017 foreach ($avaibility_list as $key => $quantity)
1018 $attribute_values_by_lang[$filter['id_lang']]['quantity'][] = array('name' => $this->translateWord('Availability', (int)$filter['id_lang']),
1019 'id_name' => null, 'value' => $quantity, 'id_value' => $key, 'id_id_value' => 0,
1020 'category_name' => $filter['link_rewrite'], 'type' => $filter['type']);
1021 break;
1022
1023 case 'condition':
1024 $condition_list = array(
1025 'new' => $this->translateWord('New', (int)$filter['id_lang']),
1026 'used' => $this->translateWord('Used', (int)$filter['id_lang']),
1027 'refurbished' => $this->translateWord('Refurbished', (int)$filter['id_lang'])
1028 );
1029 foreach ($condition_list as $key => $condition)
1030 $attribute_values_by_lang[$filter['id_lang']]['condition'][] = array('name' => $this->translateWord('Condition', (int)$filter['id_lang']),
1031 'id_name' => null, 'value' => $condition, 'id_value' => $key,
1032 'category_name' => $filter['link_rewrite'], 'type' => $filter['type']);
1033 break;
1034 }
1035
1036 // Foreach langs
1037 foreach ($attribute_values_by_lang as $id_lang => $attribute_values)
1038 {
1039 // Foreach attributes generate a couple "/<attribute_name>_<atttribute_value>". For example: color_blue
1040 foreach ($attribute_values as $attribute)
1041 foreach ($attribute as $param)
1042 {
1043 $selected_filters = array();
1044 $link = '/'.str_replace($this->getAnchor(), '_', Tools::link_rewrite($param['name'])).$this->getAnchor().str_replace($this->getAnchor(), '_', Tools::link_rewrite($param['value']));
1045 $selected_filters[$param['type']] = array();
1046
1047 if (!isset($param['id_id_value']))
1048 $param['id_id_value'] = $param['id_value'];
1049
1050 $selected_filters[$param['type']][$param['id_id_value']] = $param['id_value'];
1051 $url_key = md5($link);
1052 $id_layered_friendly_url = Db::getInstance()->getValue('
1053 SELECT id_layered_friendly_url
1054 FROM `'._DB_PREFIX_.'layered_friendly_url` WHERE `id_lang` = '.$id_lang.' AND `url_key` = \''.$url_key.'\''
1055 );
1056
1057 if ($id_layered_friendly_url == false)
1058 {
1059 Db::getInstance()->insert('layered_friendly_url', array('url_key' => $url_key, 'data' => serialize($selected_filters), 'id_lang' => (int)$id_lang));
1060 $id_layered_friendly_url = Db::getInstance()->Insert_ID();
1061 }
1062 }
1063 }
1064
1065 if ($ajax)
1066 return '{"result": 1}';
1067 else
1068 return 1;
1069 }
1070
1071 /*
1072 * $cursor $cursor in order to restart indexing from the last state
1073 */
1074 public static function fullPricesIndexProcess($cursor = 0, $ajax = false, $smart = false)
1075 {
1076 if ($cursor == 0 && !$smart)
1077 self::installPriceIndexTable();
1078
1079 return self::indexPrices($cursor, true, $ajax, $smart);
1080 }
1081
1082 /*
1083 * $cursor $cursor in order to restart indexing from the last state
1084 */
1085 public static function pricesIndexProcess($cursor = 0, $ajax = false)
1086 {
1087 return self::indexPrices($cursor, false, $ajax);
1088 }
1089
1090 private static function indexPrices($cursor = null, $full = false, $ajax = false, $smart = false)
1091 {
1092 if ($full)
1093 $nb_products = (int)Db::getInstance()->getValue('
1094 SELECT count(DISTINCT p.`id_product`)
1095 FROM '._DB_PREFIX_.'product p
1096 INNER JOIN `'._DB_PREFIX_.'product_shop` ps
1097 ON (ps.`id_product` = p.`id_product` AND ps.`active` = 1 AND ps.`visibility` IN ("both", "catalog"))');
1098 else
1099 $nb_products = (int)Db::getInstance()->getValue('
1100 SELECT COUNT(DISTINCT p.`id_product`) FROM `'._DB_PREFIX_.'product` p
1101 INNER JOIN `'._DB_PREFIX_.'product_shop` ps
1102 ON (ps.`id_product` = p.`id_product` AND ps.`active` = 1 AND ps.`visibility` IN ("both", "catalog"))
1103 LEFT JOIN `'._DB_PREFIX_.'layered_price_index` psi ON (psi.id_product = p.id_product)
1104 WHERE psi.id_product IS NULL');
1105
1106 $max_executiontime = @ini_get('max_execution_time');
1107 if ($max_executiontime > 5 || $max_executiontime <= 0)
1108 $max_executiontime = 5;
1109
1110 $start_time = microtime(true);
1111
1112 if (function_exists('memory_get_peak_usage'))
1113 do
1114 {
1115 $cursor = (int)self::indexPricesUnbreakable((int)$cursor, $full, $smart);
1116 $time_elapsed = microtime(true) - $start_time;
1117 }
1118 while ($cursor < $nb_products && Tools::getMemoryLimit() > memory_get_peak_usage() && $time_elapsed < $max_executiontime);
1119 else
1120 do
1121 {
1122 $cursor = (int)self::indexPricesUnbreakable((int)$cursor, $full, $smart);
1123 $time_elapsed = microtime(true) - $start_time;
1124 }
1125 while ($cursor < $nb_products && $time_elapsed < $max_executiontime);
1126
1127 if (($nb_products > 0 && !$full || $cursor < $nb_products && $full) && !$ajax)
1128 {
1129 $token = substr(Tools::encrypt('blocklayered/index'), 0, 10);
1130 if (Tools::usingSecureMode())
1131 $domain = Tools::getShopDomainSsl(true);
1132 else
1133 $domain = Tools::getShopDomain(true);
1134
1135 if (!Tools::file_get_contents($domain.__PS_BASE_URI__.'modules/blocklayered/blocklayered-price-indexer.php?token='.$token.'&cursor='.(int)$cursor.'&full='.(int)$full))
1136 self::indexPrices((int)$cursor, (int)$full);
1137 return $cursor;
1138 }
1139 if ($ajax && $nb_products > 0 && $cursor < $nb_products && $full)
1140 return '{"cursor": '.$cursor.', "count": '.($nb_products - $cursor).'}';
1141 else if ($ajax && $nb_products > 0 && !$full)
1142 return '{"cursor": '.$cursor.', "count": '.($nb_products).'}';
1143 else
1144 {
1145 Configuration::updateGlobalValue('PS_LAYERED_INDEXED', 1);
1146
1147 if ($ajax)
1148 return '{"result": "ok"}';
1149 else
1150 return -1;
1151 }
1152 }
1153
1154 /*
1155 * $cursor $cursor in order to restart indexing from the last state
1156 */
1157 private static function indexPricesUnbreakable($cursor, $full = false, $smart = false)
1158 {
1159 static $length = 100; // Nb of products to index
1160
1161 if (is_null($cursor))
1162 $cursor = 0;
1163
1164 if ($full)
1165 $query = '
1166 SELECT p.`id_product`
1167 FROM `'._DB_PREFIX_.'product` p
1168 INNER JOIN `'._DB_PREFIX_.'product_shop` ps
1169 ON (ps.`id_product` = p.`id_product` AND ps.`active` = 1 AND ps.`visibility` IN ("both", "catalog"))
1170 GROUP BY p.`id_product`
1171 ORDER BY p.`id_product` LIMIT '.(int)$cursor.','.(int)$length;
1172 else
1173 $query = '
1174 SELECT p.`id_product`
1175 FROM `'._DB_PREFIX_.'product` p
1176 INNER JOIN `'._DB_PREFIX_.'product_shop` ps
1177 ON (ps.`id_product` = p.`id_product` AND ps.`active` = 1 AND ps.`visibility` IN ("both", "catalog"))
1178 LEFT JOIN `'._DB_PREFIX_.'layered_price_index` psi ON (psi.id_product = p.id_product)
1179 WHERE psi.id_product IS NULL
1180 GROUP BY p.`id_product`
1181 ORDER BY p.`id_product` LIMIT 0,'.(int)$length;
1182
1183 foreach (Db::getInstance()->executeS($query) as $product)
1184 self::indexProductPrices((int)$product['id_product'], ($smart && $full));
1185
1186 return (int)($cursor + $length);
1187 }
1188
1189 public static function indexProductPrices($id_product, $smart = true)
1190 {
1191 static $groups = null;
1192
1193 if (is_null($groups))
1194 {
1195 $groups = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS('SELECT id_group FROM `'._DB_PREFIX_.'group_reduction`');
1196 if (!$groups)
1197 $groups = array();
1198 }
1199
1200 $shop_list = Shop::getShops(false, null, true);
1201
1202 foreach ($shop_list as $id_shop)
1203 {
1204 static $currency_list = null;
1205
1206 if (is_null($currency_list))
1207 $currency_list = Currency::getCurrencies(false, 1, new Shop($id_shop));
1208
1209 $min_price = array();
1210 $max_price = array();
1211
1212 if ($smart)
1213 Db::getInstance()->execute('DELETE FROM `'._DB_PREFIX_.'layered_price_index` WHERE `id_product` = '.(int)$id_product.' AND `id_shop` = '.(int)$id_shop);
1214
1215 if (Configuration::get('PS_LAYERED_FILTER_PRICE_USETAX'))
1216 $max_tax_rate = Db::getInstance()->getValue('
1217 SELECT max(t.rate) max_rate
1218 FROM `'._DB_PREFIX_.'product_shop` p
1219 LEFT JOIN `'._DB_PREFIX_.'tax_rules_group` trg ON (trg.id_tax_rules_group = p.id_tax_rules_group AND p.id_shop = '.(int)$id_shop.')
1220 LEFT JOIN `'._DB_PREFIX_.'tax_rule` tr ON (tr.id_tax_rules_group = trg.id_tax_rules_group)
1221 LEFT JOIN `'._DB_PREFIX_.'tax` t ON (t.id_tax = tr.id_tax AND t.active = 1)
1222 WHERE id_product = '.(int)$id_product.'
1223 GROUP BY id_product');
1224 else
1225 $max_tax_rate = 0;
1226
1227 $product_min_prices = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS('
1228 SELECT id_shop, id_currency, id_country, id_group, from_quantity
1229 FROM `'._DB_PREFIX_.'specific_price`
1230 WHERE id_product = '.(int)$id_product);
1231
1232 // Get min price
1233 foreach ($currency_list as $currency)
1234 {
1235 $price = Product::priceCalculation($id_shop, (int)$id_product, null, null, null, null,
1236 $currency['id_currency'], null, null, false, 6, false, true, true,
1237 $specific_price_output, true);
1238
1239 if (!isset($max_price[$currency['id_currency']]))
1240 $max_price[$currency['id_currency']] = 0;
1241 if (!isset($min_price[$currency['id_currency']]))
1242 $min_price[$currency['id_currency']] = null;
1243 if ($price > $max_price[$currency['id_currency']])
1244 $max_price[$currency['id_currency']] = $price;
1245 if ($price == 0)
1246 continue;
1247 if (is_null($min_price[$currency['id_currency']]) || $price < $min_price[$currency['id_currency']])
1248 $min_price[$currency['id_currency']] = $price;
1249 }
1250
1251 foreach ($product_min_prices as $specific_price)
1252 foreach ($currency_list as $currency)
1253 {
1254 if ($specific_price['id_currency'] && $specific_price['id_currency'] != $currency['id_currency'])
1255 continue;
1256 $price = Product::priceCalculation((($specific_price['id_shop'] == 0) ? null : (int)$specific_price['id_shop']), (int)$id_product,
1257 null, (($specific_price['id_country'] == 0) ? null : $specific_price['id_country']), null, null,
1258 $currency['id_currency'], (($specific_price['id_group'] == 0) ? null : $specific_price['id_group']),
1259 $specific_price['from_quantity'], false, 6, false, true, true, $specific_price_output, true);
1260
1261 if (!isset($max_price[$currency['id_currency']]))
1262 $max_price[$currency['id_currency']] = 0;
1263 if (!isset($min_price[$currency['id_currency']]))
1264 $min_price[$currency['id_currency']] = null;
1265 if ($price > $max_price[$currency['id_currency']])
1266 $max_price[$currency['id_currency']] = $price;
1267 if ($price == 0)
1268 continue;
1269 if (is_null($min_price[$currency['id_currency']]) || $price < $min_price[$currency['id_currency']])
1270 $min_price[$currency['id_currency']] = $price;
1271 }
1272
1273 foreach ($groups as $group)
1274 foreach ($currency_list as $currency)
1275 {
1276 $price = Product::priceCalculation(null, (int)$id_product, null, null, null, null, (int)$currency['id_currency'], (int)$group['id_group'],
1277 null, false, 6, false, true, true, $specific_price_output, true);
1278
1279 if (!isset($max_price[$currency['id_currency']]))
1280 $max_price[$currency['id_currency']] = 0;
1281 if (!isset($min_price[$currency['id_currency']]))
1282 $min_price[$currency['id_currency']] = null;
1283 if ($price > $max_price[$currency['id_currency']])
1284 $max_price[$currency['id_currency']] = $price;
1285 if ($price == 0)
1286 continue;
1287 if (is_null($min_price[$currency['id_currency']]) || $price < $min_price[$currency['id_currency']])
1288 $min_price[$currency['id_currency']] = $price;
1289 }
1290
1291 $values = array();
1292 foreach ($currency_list as $currency)
1293 $values[] = '('.(int)$id_product.',
1294 '.(int)$currency['id_currency'].',
1295 '.$id_shop.',
1296 '.(int)$min_price[$currency['id_currency']].',
1297 '.(int)Tools::ps_round($max_price[$currency['id_currency']] * (100 + $max_tax_rate) / 100, 0).')';
1298
1299 Db::getInstance()->execute('
1300 INSERT INTO `'._DB_PREFIX_.'layered_price_index` (id_product, id_currency, id_shop, price_min, price_max)
1301 VALUES '.implode(',', $values).'
1302 ON DUPLICATE KEY UPDATE id_product = id_product # avoid duplicate keys');
1303 }
1304 }
1305
1306 public function translateWord($string, $id_lang )
1307 {
1308 static $_MODULES = array();
1309 global $_MODULE;
1310
1311 $file = _PS_MODULE_DIR_.$this->name.'/translations/'.Language::getIsoById($id_lang).'.php';
1312
1313 if (!array_key_exists($id_lang, $_MODULES))
1314 {
1315 if (file_exists($file1 = _PS_MODULE_DIR_.$this->name.'/translations/'.Language::getIsoById($id_lang).'.php'))
1316 {
1317 include($file1);
1318 $_MODULES[$id_lang] = $_MODULE;
1319 }
1320 elseif (file_exists($file2 = _PS_MODULE_DIR_.$this->name.'/'.Language::getIsoById($id_lang).'.php'))
1321 {
1322 include($file2);
1323 $_MODULES[$id_lang] = $_MODULE;
1324 }
1325 else
1326 return $string;
1327 }
1328
1329 $string = str_replace('\'', '\\\'', $string);
1330
1331 // set array key to lowercase for 1.3 compatibility
1332 $_MODULES[$id_lang] = array_change_key_case($_MODULES[$id_lang]);
1333 $current_key = '<{'.strtolower( $this->name).'}'.strtolower(_THEME_NAME_).'>'.strtolower($this->name).'_'.md5($string);
1334 $default_key = '<{'.strtolower( $this->name).'}prestashop>'.strtolower($this->name).'_'.md5($string);
1335
1336 if (isset($_MODULES[$id_lang][$current_key]))
1337 $ret = stripslashes($_MODULES[$id_lang][$current_key]);
1338 else if (isset($_MODULES[$id_lang][Tools::strtolower($current_key)]))
1339 $ret = stripslashes($_MODULES[$id_lang][Tools::strtolower($current_key)]);
1340 else if (isset($_MODULES[$id_lang][$default_key]))
1341 $ret = stripslashes($_MODULES[$id_lang][$default_key]);
1342 else if (isset($_MODULES[$id_lang][Tools::strtolower($default_key)]))
1343 $ret = stripslashes($_MODULES[$id_lang][Tools::strtolower($default_key)]);
1344 else
1345 $ret = stripslashes($string);
1346
1347 return str_replace('"', '"', $ret);
1348 }
1349
1350 public function getContent()
1351 {
1352 global $cookie;
1353 $message = '';
1354
1355 if (Tools::isSubmit('SubmitFilter'))
1356 {
1357 if (!Tools::getValue('layered_tpl_name'))
1358 $message = $this->displayError($this->l('Filter template name required (cannot be empty)'));
1359 elseif (!Tools::getValue('categoryBox'))
1360 $message = $this->displayError($this->l('You must select at least one category.'));
1361 else
1362 {
1363 if (Tools::getValue('id_layered_filter'))
1364 {
1365 Db::getInstance()->execute('
1366 DELETE FROM '._DB_PREFIX_.'layered_filter
1367 WHERE id_layered_filter = '.(int)Tools::getValue('id_layered_filter')
1368 );
1369 $this->buildLayeredCategories();
1370 }
1371
1372 if (Tools::getValue('scope') == 1)
1373 {
1374 Db::getInstance()->execute('TRUNCATE TABLE '._DB_PREFIX_.'layered_filter');
1375 $categories = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS('
1376 SELECT id_category
1377 FROM '._DB_PREFIX_.'category'
1378 );
1379
1380 foreach ($categories as $category)
1381 $_POST['categoryBox'][] = (int)$category['id_category'];
1382 }
1383
1384 $id_layered_filter = (int)Tools::getValue('id_layered_filter');
1385
1386 if (!$id_layered_filter)
1387 $id_layered_filter = (int)Db::getInstance()->Insert_ID();
1388
1389 $shop_list = array();
1390
1391 if (isset($_POST['checkBoxShopAsso_layered_filter']))
1392 {
1393 foreach ($_POST['checkBoxShopAsso_layered_filter'] as $id_shop => $row)
1394 {
1395 $assos[] = array('id_object' => (int)$id_layered_filter, 'id_shop' => (int)$id_shop);
1396 $shop_list[] = (int)$id_shop;
1397 }
1398 }
1399 else
1400 $shop_list = array(Context::getContext()->shop->id);
1401
1402 Db::getInstance()->execute('
1403 DELETE FROM '._DB_PREFIX_.'layered_filter_shop
1404 WHERE `id_layered_filter` = '.(int)$id_layered_filter
1405 );
1406
1407 if (count($_POST['categoryBox']))
1408 {
1409 /* Clean categoryBox before use */
1410 if (isset($_POST['categoryBox']) && is_array($_POST['categoryBox']))
1411 foreach ($_POST['categoryBox'] as &$category_box_tmp)
1412 $category_box_tmp = (int)$category_box_tmp;
1413
1414 $filter_values = array();
1415
1416 foreach ($_POST['categoryBox'] as $idc)
1417 $filter_values['categories'][] = (int)$idc;
1418
1419 $filter_values['shop_list'] = $shop_list;
1420 $values = false;
1421
1422 foreach ($_POST['categoryBox'] as $id_category_layered)
1423 {
1424 foreach ($_POST as $key => $value)
1425 if (substr($key, 0, 17) == 'layered_selection' && $value == 'on')
1426 {
1427 $values = true;
1428 $type = 0;
1429 $limit = 0;
1430
1431 if (Tools::getValue($key.'_filter_type'))
1432 $type = Tools::getValue($key.'_filter_type');
1433 if (Tools::getValue($key.'_filter_show_limit'))
1434 $limit = Tools::getValue($key.'_filter_show_limit');
1435
1436 $filter_values[$key] = array(
1437 'filter_type' => (int)$type,
1438 'filter_show_limit' => (int)$limit
1439 );
1440 }
1441 }
1442
1443 $values_to_insert = array(
1444 'name' => pSQL(Tools::getValue('layered_tpl_name')),
1445 'filters' => pSQL(serialize($filter_values)),
1446 'n_categories' => (int)count($filter_values['categories']),
1447 'date_add' => date('Y-m-d H:i:s'));
1448
1449 if (isset($_POST['id_layered_filter']) && $_POST['id_layered_filter'])
1450 $values_to_insert['id_layered_filter'] = (int)Tools::getValue('id_layered_filter');
1451
1452 Db::getInstance()->autoExecute(_DB_PREFIX_.'layered_filter', $values_to_insert, 'INSERT');
1453 $id_layered_filter = (int)Db::getInstance()->Insert_ID();
1454
1455 if (isset($assos))
1456 foreach ($assos as $asso)
1457 Db::getInstance()->execute('
1458 INSERT INTO '._DB_PREFIX_.'layered_filter_shop (`id_layered_filter`, `id_shop`)
1459 VALUES('.$id_layered_filter.', '.(int)$asso['id_shop'].')'
1460 );
1461
1462 $this->buildLayeredCategories();
1463 $message = $this->displayConfirmation($this->l('Your filter').' "'.Tools::safeOutput(Tools::getValue('layered_tpl_name')).'" '.
1464 ((isset($_POST['id_layered_filter']) && $_POST['id_layered_filter']) ? $this->l('was updated successfully.') : $this->l('was added successfully.')));
1465 }
1466 }
1467 }
1468 else if (Tools::isSubmit('submitLayeredSettings'))
1469 {
1470 Configuration::updateValue('PS_LAYERED_HIDE_0_VALUES', (int)Tools::getValue('ps_layered_hide_0_values'));
1471 Configuration::updateValue('PS_LAYERED_SHOW_QTIES', (int)Tools::getValue('ps_layered_show_qties'));
1472 Configuration::updateValue('PS_LAYERED_FULL_TREE', (int)Tools::getValue('ps_layered_full_tree'));
1473 Configuration::updateValue('PS_LAYERED_FILTER_PRICE_USETAX', (int)Tools::getValue('ps_layered_filter_price_usetax'));
1474 Configuration::updateValue('PS_LAYERED_FILTER_CATEGORY_DEPTH', (int)Tools::getValue('ps_layered_filter_category_depth'));
1475 Configuration::updateValue('PS_LAYERED_FILTER_INDEX_QTY', (int)Tools::getValue('ps_layered_filter_index_availability'));
1476 Configuration::updateValue('PS_LAYERED_FILTER_INDEX_CDT', (int)Tools::getValue('ps_layered_filter_index_condition'));
1477 Configuration::updateValue('PS_LAYERED_FILTER_INDEX_MNF', (int)Tools::getValue('ps_layered_filter_index_manufacturer'));
1478 Configuration::updateValue('PS_LAYERED_FILTER_INDEX_CAT', (int)Tools::getValue('ps_layered_filter_index_category'));
1479 Configuration::updateValue('PS_LAYERED_FILTER_PRICE_ROUNDING', (int)Tools::getValue('ps_layered_filter_price_rounding'));
1480
1481 if (version_compare(_PS_VERSION_, '1.6.0', '>=') === true)
1482 $message = '<div class="alert alert-success">'.$this->l('Settings saved successfully').'</div>';
1483 else
1484 $message = '<div class="conf">'.$this->l('Settings saved successfully').'</div>';
1485 }
1486 else if (Tools::getValue('deleteFilterTemplate'))
1487 {
1488 $layered_values = Db::getInstance(_PS_USE_SQL_SLAVE_)->getValue('
1489 SELECT filters
1490 FROM '._DB_PREFIX_.'layered_filter
1491 WHERE id_layered_filter = '.(int)Tools::getValue('id_layered_filter')
1492 );
1493
1494 if ($layered_values)
1495 {
1496 Db::getInstance()->execute('
1497 DELETE FROM '._DB_PREFIX_.'layered_filter
1498 WHERE id_layered_filter = '.(int)Tools::getValue('id_layered_filter').' LIMIT 1'
1499 );
1500 $this->buildLayeredCategories();
1501 $message = $this->displayConfirmation($this->l('Filter template deleted, categories updated (reverted to default Filter template).'));
1502 }
1503 else
1504 $message = $this->displayError($this->l('Filter template not found'));
1505 }
1506
1507 $category_box = array();
1508 $attribute_groups = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS('
1509 SELECT ag.id_attribute_group, ag.is_color_group, agl.name, COUNT(DISTINCT(a.id_attribute)) n
1510 FROM '._DB_PREFIX_.'attribute_group ag
1511 LEFT JOIN '._DB_PREFIX_.'attribute_group_lang agl ON (agl.id_attribute_group = ag.id_attribute_group)
1512 LEFT JOIN '._DB_PREFIX_.'attribute a ON (a.id_attribute_group = ag.id_attribute_group)
1513 WHERE agl.id_lang = '.(int)$cookie->id_lang.'
1514 GROUP BY ag.id_attribute_group'
1515 );
1516
1517 $features = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS('
1518 SELECT fl.id_feature, fl.name, COUNT(DISTINCT(fv.id_feature_value)) n
1519 FROM '._DB_PREFIX_.'feature_lang fl
1520 LEFT JOIN '._DB_PREFIX_.'feature_value fv ON (fv.id_feature = fl.id_feature)
1521 WHERE (fv.custom IS NULL OR fv.custom = 0) AND fl.id_lang = '.(int)$cookie->id_lang.'
1522 GROUP BY fl.id_feature'
1523 );
1524
1525 if (Shop::isFeatureActive() && count(Shop::getShops(true, null, true)) > 1)
1526 {
1527 $helper = new HelperForm();
1528 $helper->id = Tools::getValue('id_layered_filter', null);
1529 $helper->table = 'layered_filter';
1530 $helper->identifier = 'id_layered_filter';
1531 $this->context->smarty->assign('asso_shops', $helper->renderAssoShop());
1532 }
1533
1534 if (version_compare(_PS_VERSION_, '1.6.0', '>=') === true)
1535 {
1536 $tree_categories_helper = new HelperTreeCategories('categories-treeview');
1537 $tree_categories_helper->setRootCategory((Shop::getContext() == Shop::CONTEXT_SHOP ? Category::getRootCategory()->id_category : 0))
1538 ->setUseCheckBox(true);
1539 }
1540 else
1541 {
1542 if (Shop::getContext() == Shop::CONTEXT_SHOP)
1543 {
1544 $root_category = Category::getRootCategory();
1545 $root_category = array('id_category' => $root_category->id_category, 'name' => $root_category->name);
1546 }
1547 else
1548 $root_category = array('id_category' => '0', 'name' => $this->l('Root'));
1549
1550 $tree_categories_helper = new Helper();
1551 }
1552
1553 $module_url = Tools::getProtocol(Tools::usingSecureMode()).$_SERVER['HTTP_HOST'].$this->getPathUri();
1554
1555 if (method_exists($this->context->controller, 'addJquery'))
1556 {
1557 $this->context->controller->addJS($this->_path.'js/blocklayered_admin.js');
1558
1559 if (version_compare(_PS_VERSION_, '1.6.0.3', '>=') === true)
1560 $this->context->controller->addjqueryPlugin('sortable');
1561 elseif (version_compare(_PS_VERSION_, '1.6.0', '>=') === true)
1562 $this->context->controller->addJS(_PS_JS_DIR_.'jquery/plugins/jquery.sortable.js');
1563 else
1564 $this->context->controller->addJS($this->_path.'js/jquery.sortable.js');
1565 }
1566
1567 if (version_compare(_PS_VERSION_, '1.6.0', '>=') === true)
1568 $this->context->controller->addCSS($this->_path.'css/blocklayered_admin_1.6.css');
1569 else
1570 $this->context->controller->addCSS($this->_path.'css/blocklayered_admin.css');
1571
1572 if (Tools::getValue('add_new_filters_template'))
1573 {
1574 $this->context->smarty->assign(array(
1575 'current_url' => $this->context->link->getAdminLink('AdminModules').'&configure=blocklayered&tab_module=front_office_features&module_name=blocklayered',
1576 'uri' => $this->getPathUri(),
1577 'id_layered_filter' => 0,
1578 'template_name' => sprintf($this->l('My template - %s'), date('Y-m-d')),
1579 'attribute_groups' => $attribute_groups,
1580 'features' => $features,
1581 'total_filters' => 6+count($attribute_groups)+count($features)
1582 ));
1583
1584 if (version_compare(_PS_VERSION_, '1.6.0', '>=') === true)
1585 $this->context->smarty->assign('categories_tree', $tree_categories_helper->render());
1586 else
1587 $this->context->smarty->assign('categories_tree', $tree_categories_helper->renderCategoryTree(
1588 $root_category, array(), 'categoryBox'));
1589
1590 if (version_compare(_PS_VERSION_, '1.6.0', '>=') === true)
1591 return $this->display(__FILE__, 'views/templates/admin/add_1.6.tpl');
1592 else
1593 return $this->display(__FILE__, 'views/templates/admin/add.tpl');
1594 }
1595 else if (Tools::getValue('edit_filters_template'))
1596 {
1597 $template = Db::getInstance()->getRow('
1598 SELECT *
1599 FROM `'._DB_PREFIX_.'layered_filter`
1600 WHERE id_layered_filter = '.(int)Tools::getValue('id_layered_filter')
1601 );
1602
1603 $filters = Tools::unSerialize($template['filters']);
1604
1605 if (version_compare(_PS_VERSION_, '1.6.0', '>=') === true)
1606 {
1607 $tree_categories_helper->setSelectedCategories($filters['categories']);
1608 $this->context->smarty->assign('categories_tree', $tree_categories_helper->render());
1609 }
1610 else
1611 $this->context->smarty->assign('categories_tree',$tree_categories_helper->renderCategoryTree(
1612 $root_category, $filters['categories'], 'categoryBox'));
1613
1614 $select_shops = $filters['shop_list'];
1615 unset($filters['categories']);
1616 unset($filters['shop_list']);
1617
1618 $this->context->smarty->assign(array(
1619 'current_url' => $this->context->link->getAdminLink('AdminModules').'&configure=blocklayered&tab_module=front_office_features&module_name=blocklayered',
1620 'uri' => $this->getPathUri(),
1621 'id_layered_filter' => (int)Tools::getValue('id_layered_filter'),
1622 'template_name' => $template['name'],
1623 'attribute_groups' => $attribute_groups,
1624 'features' => $features,
1625 'filters' => Tools::jsonEncode($filters),
1626 'total_filters' => 6+count($attribute_groups)+count($features)
1627 ));
1628
1629 if (version_compare(_PS_VERSION_, '1.6.0', '>=') === true)
1630 return $this->display(__FILE__, 'views/templates/admin/add_1.6.tpl');
1631 else
1632 return $this->display(__FILE__, 'views/templates/admin/add.tpl');
1633 }
1634 else
1635 {
1636 $this->context->smarty->assign(array(
1637 'message' => $message,
1638 'uri' => $this->getPathUri(),
1639 'PS_LAYERED_INDEXED' => Configuration::getGlobalValue('PS_LAYERED_INDEXED'),
1640 'current_url' => Tools::safeOutput(preg_replace('/&deleteFilterTemplate=[0-9]*&id_layered_filter=[0-9]*/', '', $_SERVER['REQUEST_URI'])),
1641 'id_lang' => Context::getContext()->cookie->id_lang,
1642 'token' => substr(Tools::encrypt('blocklayered/index'), 0, 10),
1643 'base_folder' => urlencode(_PS_ADMIN_DIR_),
1644 'price_indexer_url' => $module_url.'blocklayered-price-indexer.php'.'?token='.substr(Tools::encrypt('blocklayered/index'), 0, 10),
1645 'full_price_indexer_url' => $module_url.'blocklayered-price-indexer.php'.'?token='.substr(Tools::encrypt('blocklayered/index'), 0, 10).'&full=1',
1646 'attribute_indexer_url' => $module_url.'blocklayered-attribute-indexer.php'.'?token='.substr(Tools::encrypt('blocklayered/index'), 0, 10),
1647 'url_indexer_url' => $module_url.'blocklayered-url-indexer.php'.'?token='.substr(Tools::encrypt('blocklayered/index'), 0, 10).'&truncate=1',
1648 'filters_templates' => Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS('SELECT * FROM '._DB_PREFIX_.'layered_filter ORDER BY date_add DESC'),
1649 'hide_values' => Configuration::get('PS_LAYERED_HIDE_0_VALUES'),
1650 'show_quantities' => Configuration::get('PS_LAYERED_SHOW_QTIES'),
1651 'full_tree' => Configuration::get('PS_LAYERED_FULL_TREE'),
1652 'category_depth' => Configuration::get('PS_LAYERED_FILTER_CATEGORY_DEPTH'),
1653 'price_use_tax' => (bool)Configuration::get('PS_LAYERED_FILTER_PRICE_USETAX'),
1654 'index_cdt' => Configuration::get('PS_LAYERED_FILTER_INDEX_CDT'),
1655 'index_qty' => Configuration::get('PS_LAYERED_FILTER_INDEX_QTY'),
1656 'index_mnf' => Configuration::get('PS_LAYERED_FILTER_INDEX_MNF'),
1657 'index_cat' => Configuration::get('PS_LAYERED_FILTER_INDEX_CAT'),
1658 'limit_warning' => $this->displayLimitPostWarning(21+count($attribute_groups)*3+count($features)*3),
1659 'price_use_rounding' => (bool)Configuration::get('PS_LAYERED_FILTER_PRICE_ROUNDING')
1660 ));
1661
1662 if (version_compare(_PS_VERSION_, '1.6.0', '>=') === true)
1663 return $this->display(__FILE__, 'views/templates/admin/view_1.6.tpl');
1664 else
1665 return $this->display(__FILE__, 'views/templates/admin/view.tpl');
1666 }
1667 }
1668
1669 public function displayLimitPostWarning($count)
1670 {
1671 $return = array();
1672 if ((ini_get('suhosin.post.max_vars') && ini_get('suhosin.post.max_vars') < $count) || (ini_get('suhosin.request.max_vars') && ini_get('suhosin.request.max_vars') < $count))
1673 {
1674 $return['error_type'] = 'suhosin';
1675 $return['post.max_vars'] = ini_get('suhosin.post.max_vars');
1676 $return['request.max_vars'] = ini_get('suhosin.request.max_vars');
1677 $return['needed_limit'] = $count + 100;
1678 }
1679 elseif (ini_get('max_input_vars') && ini_get('max_input_vars') < $count)
1680 {
1681 $return['error_type'] = 'conf';
1682 $return['max_input_vars'] = ini_get('max_input_vars');
1683 $return['needed_limit'] = $count + 100;
1684 }
1685 return $return;
1686 }
1687
1688 private function getSelectedFilters()
1689 {
1690 $home_category = Configuration::get('PS_HOME_CATEGORY');
1691 $id_parent = (int)Tools::getValue('id_category', Tools::getValue('id_category_layered', $home_category));
1692 if ($id_parent == $home_category)
1693 return;
1694
1695 // Force attributes selection (by url '.../2-mycategory/color-blue' or by get parameter 'selected_filters')
1696 if (strpos($_SERVER['SCRIPT_FILENAME'], 'blocklayered-ajax.php') === false || Tools::getValue('selected_filters') !== false)
1697 {
1698 if (Tools::getValue('selected_filters'))
1699 $url = Tools::getValue('selected_filters');
1700 else
1701 $url = preg_replace('/\/(?:\w*)\/(?:[0-9]+[-\w]*)([^\?]*)\??.*/', '$1', Tools::safeOutput($_SERVER['REQUEST_URI'], true));
1702
1703 $url_attributes = explode('/', ltrim($url, '/'));
1704 $selected_filters = array('category' => array($id_parent));
1705 if (!empty($url_attributes))
1706 {
1707 foreach ($url_attributes as $url_attribute)
1708 {
1709 /* Pagination uses - as separator, can be different from $this->getAnchor()*/
1710 if (strpos($url_attribute, 'page-') === 0)
1711 $url_attribute = str_replace('-', $this->getAnchor(), $url_attribute);
1712 $url_parameters = explode($this->getAnchor(), $url_attribute);
1713 $attribute_name = array_shift($url_parameters);
1714 if ($attribute_name == 'page')
1715 $this->page = (int)$url_parameters[0];
1716 else if (in_array($attribute_name, array('price', 'weight')))
1717 $selected_filters[$attribute_name] = array($this->filterVar($url_parameters[0]), $this->filterVar($url_parameters[1]));
1718 else
1719 {
1720 foreach ($url_parameters as $url_parameter)
1721 {
1722 $data = Db::getInstance()->getValue('SELECT data FROM `'._DB_PREFIX_.'layered_friendly_url` WHERE `url_key` = \''.md5('/'.$attribute_name.$this->getAnchor().$url_parameter).'\'');
1723 if ($data)
1724 foreach (Tools::unSerialize($data) as $key_params => $params)
1725 {
1726 if (!isset($selected_filters[$key_params]))
1727 $selected_filters[$key_params] = array();
1728 foreach ($params as $key_param => $param)
1729 {
1730 if (!isset($selected_filters[$key_params][$key_param]))
1731 $selected_filters[$key_params][$key_param] = array();
1732 $selected_filters[$key_params][$key_param] = $this->filterVar($param);
1733 }
1734 }
1735 }
1736 }
1737 }
1738 return $selected_filters;
1739 }
1740 }
1741
1742 /* Analyze all the filters selected by the user and store them into a tab */
1743 $selected_filters = array('category' => array(), 'manufacturer' => array(), 'quantity' => array(), 'condition' => array());
1744 foreach ($_GET as $key => $value) {
1745 if (substr($key, 0, 8) == 'layered_')
1746 {
1747 preg_match('/^(.*)_([0-9]+|new|used|refurbished|slider)$/', substr($key, 8, strlen($key) - 8), $res);
1748 if (isset($res[1]))
1749 {
1750 $tmp_tab = explode('_', $this->filterVar($value));
1751 $value = $this->filterVar($tmp_tab[0]);
1752 $id_key = false;
1753 if (isset($tmp_tab[1]))
1754 $id_key = $tmp_tab[1];
1755 if ($res[1] == 'condition' && in_array($value, array('new', 'used', 'refurbished')))
1756 $selected_filters['condition'][] = $value;
1757 else if ($res[1] == 'quantity' && (!$value || $value == 1))
1758 $selected_filters['quantity'][] = $value;
1759 else if (in_array($res[1], array('category', 'manufacturer')))
1760 {
1761 if (!isset($selected_filters[$res[1].($id_key ? '_'.$id_key : '')]))
1762 $selected_filters[$res[1].($id_key ? '_'.$id_key : '')] = array();
1763 $selected_filters[$res[1].($id_key ? '_'.$id_key : '')][] = (int)$value;
1764 }
1765 else if (in_array($res[1], array('id_attribute_group', 'id_feature')))
1766 {
1767 if (!isset($selected_filters[$res[1]]))
1768 $selected_filters[$res[1]] = array();
1769 $selected_filters[$res[1]][(int)$value] = $id_key.'_'.(int)$value;
1770 }
1771 else if ($res[1] == 'weight')
1772 $selected_filters[$res[1]] = $tmp_tab;
1773 else if ($res[1] == 'price')
1774 $selected_filters[$res[1]] = $tmp_tab;
1775 }
1776 }
1777 }
1778
1779 if (empty($selected_filters['category'])) {
1780 $selected_filters['category'][] = $id_parent;
1781 }
1782
1783 return $selected_filters;
1784 }
1785
1786 public function getProductByFilters($selected_filters = array())
1787 {
1788 global $cookie;
1789
1790 if (!empty($this->products))
1791 return $this->products;
1792
1793 $home_category = Configuration::get('PS_HOME_CATEGORY');
1794 /* If the current category isn't defined or if it's homepage, we have nothing to display */
1795 $id_parent = (int)Tools::getValue('id_category', Tools::getValue('id_category_layered', $home_category));
1796 if ($id_parent == $home_category)
1797 return false;
1798
1799 $alias_where = 'p';
1800 if (version_compare(_PS_VERSION_,'1.5','>'))
1801 $alias_where = 'product_shop';
1802
1803 $query_filters_where = ' AND '.$alias_where.'.`active` = 1 AND '.$alias_where.'.`visibility` IN ("both", "catalog")';
1804 $query_filters_from = '';
1805
1806 $parent = new Category((int)$id_parent);
1807
1808 foreach ($selected_filters as $key => $filter_values)
1809 {
1810 if (!count($filter_values))
1811 continue;
1812
1813 preg_match('/^(.*[^_0-9])/', $key, $res);
1814 $key = $res[1];
1815
1816 switch ($key)
1817 {
1818 case 'id_feature':
1819 $sub_queries = array();
1820 foreach ($filter_values as $filter_value)
1821 {
1822 $filter_value_array = explode('_', $filter_value);
1823 if (!isset($sub_queries[$filter_value_array[0]]))
1824 $sub_queries[$filter_value_array[0]] = array();
1825 $sub_queries[$filter_value_array[0]][] = 'fp.`id_feature_value` = '.(int)$filter_value_array[1];
1826 }
1827 foreach ($sub_queries as $sub_query)
1828 {
1829 $query_filters_where .= ' AND p.id_product IN (SELECT `id_product` FROM `'._DB_PREFIX_.'feature_product` fp WHERE ';
1830 $query_filters_where .= implode(' OR ', $sub_query).') ';
1831 }
1832 break;
1833
1834 case 'id_attribute_group':
1835 $sub_queries = array();
1836
1837
1838 foreach ($filter_values as $filter_value)
1839 {
1840 $filter_value_array = explode('_', $filter_value);
1841 if (!isset($sub_queries[$filter_value_array[0]]))
1842 $sub_queries[$filter_value_array[0]] = array();
1843 $sub_queries[$filter_value_array[0]][] = 'pac.`id_attribute` = '.(int)$filter_value_array[1];
1844 }
1845 foreach ($sub_queries as $sub_query)
1846 {
1847 $query_filters_where .= ' AND p.id_product IN (SELECT pa.`id_product`
1848 FROM `'._DB_PREFIX_.'product_attribute_combination` pac
1849 LEFT JOIN `'._DB_PREFIX_.'product_attribute` pa
1850 ON (pa.`id_product_attribute` = pac.`id_product_attribute`)'.
1851 Shop::addSqlAssociation('product_attribute', 'pa').'
1852 WHERE '.implode(' OR ', $sub_query).') ';
1853 }
1854 break;
1855
1856 case 'category':
1857 $query_filters_where .= ' AND p.id_product IN (SELECT id_product FROM '._DB_PREFIX_.'category_product cp WHERE ';
1858 foreach ($selected_filters['category'] as $id_category)
1859 $query_filters_where .= 'cp.`id_category` = '.(int)$id_category.' OR ';
1860 $query_filters_where = rtrim($query_filters_where, 'OR ').')';
1861 break;
1862
1863 case 'quantity':
1864 if (count($selected_filters['quantity']) == 2)
1865 break;
1866
1867 $query_filters_where .= ' AND sa.quantity '.(!$selected_filters['quantity'][0] ? '<=' : '>').' 0 ';
1868 $query_filters_from .= 'LEFT JOIN `'._DB_PREFIX_.'stock_available` sa ON (sa.id_product = p.id_product '.StockAvailable::addSqlShopRestriction(null, null, 'sa').') ';
1869 break;
1870
1871 case 'manufacturer':
1872 $query_filters_where .= ' AND p.id_manufacturer IN ('.implode($selected_filters['manufacturer'], ',').')';
1873 break;
1874
1875 case 'condition':
1876 if (count($selected_filters['condition']) == 3)
1877 break;
1878 $query_filters_where .= ' AND '.$alias_where.'.condition IN (';
1879 foreach ($selected_filters['condition'] as $cond)
1880 $query_filters_where .= '\''.pSQL($cond).'\',';
1881 $query_filters_where = rtrim($query_filters_where, ',').')';
1882 break;
1883
1884 case 'weight':
1885 if ($selected_filters['weight'][0] != 0 || $selected_filters['weight'][1] != 0)
1886 $query_filters_where .= ' AND p.`weight` BETWEEN '.(float)($selected_filters['weight'][0] - 0.001).' AND '.(float)($selected_filters['weight'][1] + 0.001);
1887 break;
1888
1889 case 'price':
1890 if (isset($selected_filters['price']))
1891 {
1892 if ($selected_filters['price'][0] !== '' || $selected_filters['price'][1] !== '')
1893 {
1894 $price_filter = array();
1895 $price_filter['min'] = (float)($selected_filters['price'][0]);
1896 $price_filter['max'] = (float)($selected_filters['price'][1]);
1897 }
1898 }
1899 else
1900 $price_filter = false;
1901 break;
1902 }
1903 }
1904
1905 $context = Context::getContext();
1906 $id_currency = (int)$context->currency->id;
1907
1908 $price_filter_query_in = ''; // All products with price range between price filters limits
1909 $price_filter_query_out = ''; // All products with a price filters limit on it price range
1910 if (isset($price_filter) && $price_filter)
1911 {
1912 $price_filter_query_in = 'INNER JOIN `'._DB_PREFIX_.'layered_price_index` psi
1913 ON
1914 (
1915 psi.price_min <= '.(int)$price_filter['max'].'
1916 AND psi.price_max >= '.(int)$price_filter['min'].'
1917 AND psi.`id_product` = p.`id_product`
1918 AND psi.`id_shop` = '.(int)$context->shop->id.'
1919 AND psi.`id_currency` = '.$id_currency.'
1920 )';
1921
1922 $price_filter_query_out = 'INNER JOIN `'._DB_PREFIX_.'layered_price_index` psi
1923 ON
1924 ((psi.price_min < '.(int)$price_filter['min'].' AND psi.price_max > '.(int)$price_filter['min'].')
1925 OR
1926 (psi.price_max > '.(int)$price_filter['max'].' AND psi.price_min < '.(int)$price_filter['max'].'))
1927 AND psi.`id_product` = p.`id_product`
1928 AND psi.`id_shop` = '.(int)$context->shop->id.'
1929 AND psi.`id_currency` = '.$id_currency;
1930 }
1931
1932 $query_filters_from .= Shop::addSqlAssociation('product', 'p');
1933
1934 Db::getInstance()->execute('DROP TEMPORARY TABLE IF EXISTS '._DB_PREFIX_.'cat_filter_restriction', false);
1935 if (empty($selected_filters['category']))
1936 {
1937 /* Create the table which contains all the id_product in a cat or a tree */
1938 Db::getInstance()->execute('CREATE TEMPORARY TABLE '._DB_PREFIX_.'cat_filter_restriction ENGINE=MEMORY
1939 SELECT cp.id_product, MIN(cp.position) position FROM '._DB_PREFIX_.'category c
1940 STRAIGHT_JOIN '._DB_PREFIX_.'category_product cp ON (c.id_category = cp.id_category AND
1941 '.(Configuration::get('PS_LAYERED_FULL_TREE') ? 'c.nleft >= '.(int)$parent->nleft.'
1942 AND c.nright <= '.(int)$parent->nright : 'c.id_category = '.(int)$id_parent).'
1943 AND c.active = 1)
1944 STRAIGHT_JOIN `'._DB_PREFIX_.'product` p ON (p.id_product=cp.id_product)
1945 '.$price_filter_query_in.'
1946 '.$query_filters_from.'
1947 WHERE 1 '.$query_filters_where.'
1948 GROUP BY cp.id_product ORDER BY position, id_product', false);
1949 } else {
1950 $categories = array_map('intval', $selected_filters['category']);
1951
1952 Db::getInstance()->execute('CREATE TEMPORARY TABLE '._DB_PREFIX_.'cat_filter_restriction ENGINE=MEMORY
1953 SELECT cp.id_product, MIN(cp.position) position FROM '._DB_PREFIX_.'category_product cp
1954 STRAIGHT_JOIN `'._DB_PREFIX_.'product` p ON (p.id_product=cp.id_product)
1955 '.$price_filter_query_in.'
1956 '.$query_filters_from.'
1957 WHERE cp.`id_category` IN ('.implode(',', $categories).') '.$query_filters_where.'
1958 GROUP BY cp.id_product ORDER BY position, id_product', false);
1959 }
1960 Db::getInstance()->execute('ALTER TABLE '._DB_PREFIX_.'cat_filter_restriction ADD PRIMARY KEY (id_product), ADD KEY (position, id_product) USING BTREE', false);
1961
1962 if (isset($price_filter) && $price_filter) {
1963 static $ps_layered_filter_price_usetax = null;
1964 static $ps_layered_filter_price_rounding = null;
1965
1966 if ($ps_layered_filter_price_usetax === null) {
1967 $ps_layered_filter_price_usetax = Configuration::get('PS_LAYERED_FILTER_PRICE_USETAX');
1968 }
1969
1970 if ($ps_layered_filter_price_rounding === null) {
1971 $ps_layered_filter_price_rounding = Configuration::get('PS_LAYERED_FILTER_PRICE_ROUNDING');
1972 }
1973
1974 if (empty($selected_filters['category'])) {
1975 $all_products_out = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS('
1976 SELECT p.`id_product` id_product
1977 FROM `'._DB_PREFIX_.'product` p JOIN '._DB_PREFIX_.'category_product cp USING (id_product)
1978 INNER JOIN '._DB_PREFIX_.'category c ON (c.id_category = cp.id_category AND
1979 '.(Configuration::get('PS_LAYERED_FULL_TREE') ? 'c.nleft >= '.(int)$parent->nleft.'
1980 AND c.nright <= '.(int)$parent->nright : 'c.id_category = '.(int)$id_parent).'
1981 AND c.active = 1)
1982 '.$price_filter_query_out.'
1983 '.$query_filters_from.'
1984 WHERE 1 '.$query_filters_where.' GROUP BY cp.id_product');
1985 } else {
1986 $all_products_out = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS('
1987 SELECT p.`id_product` id_product
1988 FROM `'._DB_PREFIX_.'product` p JOIN '._DB_PREFIX_.'category_product cp USING (id_product)
1989 '.$price_filter_query_out.'
1990 '.$query_filters_from.'
1991 WHERE cp.`id_category` IN ('.implode(',', $categories).') '.$query_filters_where.' GROUP BY cp.id_product');
1992 }
1993
1994 /* for this case, price could be out of range, so we need to compute the real price */
1995 foreach($all_products_out as $product) {
1996 $price = Product::getPriceStatic($product['id_product'], $ps_layered_filter_price_usetax);
1997 if ($ps_layered_filter_price_rounding) {
1998 $price = (int)$price;
1999 }
2000 if ($price < $price_filter['min'] || $price > $price_filter['max']) {
2001 // out of range price, exclude the product
2002 $product_id_delete_list[] = (int)$product['id_product'];
2003 }
2004 }
2005 if (!empty($product_id_delete_list)) {
2006 Db::getInstance()->execute('DELETE FROM '._DB_PREFIX_.'cat_filter_restriction WHERE id_product IN ('.implode(',', $product_id_delete_list).')', false);
2007 }
2008 }
2009 $this->nbr_products = Db::getInstance()->getValue('SELECT COUNT(*) FROM '._DB_PREFIX_.'cat_filter_restriction', false);
2010
2011 if ($this->nbr_products == 0)
2012 $this->products = array();
2013 else
2014 {
2015 $product_per_page = isset($this->context->cookie->nb_item_per_page) ? (int)$this->context->cookie->nb_item_per_page : Configuration::get('PS_PRODUCTS_PER_PAGE');
2016 $default_products_per_page = max(1, (int)Configuration::get('PS_PRODUCTS_PER_PAGE'));
2017 $n = $default_products_per_page;
2018 if (isset($this->context->cookie->nb_item_per_page)) {
2019 $n = (int)$this->context->cookie->nb_item_per_page;
2020 }
2021 if ((int)Tools::getValue('n')) {
2022 $n = (int)Tools::getValue('n');
2023 }
2024 $nb_day_new_product = (Validate::isUnsignedInt(Configuration::get('PS_NB_DAYS_NEW_PRODUCT')) ? Configuration::get('PS_NB_DAYS_NEW_PRODUCT') : 20);
2025
2026 if (version_compare(_PS_VERSION_, '1.6.1', '>=') === true)
2027 {
2028 $this->products = Db::getInstance()->executeS('
2029 SELECT
2030 p.*,
2031 '.($alias_where == 'p' ? '' : 'product_shop.*,' ).'
2032 '.$alias_where.'.id_category_default,
2033 pl.*,
2034 image_shop.`id_image` id_image,
2035 il.legend,
2036 m.name manufacturer_name,
2037 '.(Combination::isFeatureActive() ? 'product_attribute_shop.id_product_attribute id_product_attribute,' : '').'
2038 DATEDIFF('.$alias_where.'.`date_add`, DATE_SUB("'.date('Y-m-d').' 00:00:00", INTERVAL '.(int)$nb_day_new_product.' DAY)) > 0 AS new,
2039 stock.out_of_stock, IFNULL(stock.quantity, 0) as quantity'.(Combination::isFeatureActive() ? ', product_attribute_shop.minimal_quantity AS product_attribute_minimal_quantity' : '').'
2040 FROM '._DB_PREFIX_.'cat_filter_restriction cp
2041 LEFT JOIN `'._DB_PREFIX_.'product` p ON p.`id_product` = cp.`id_product`
2042 '.Shop::addSqlAssociation('product', 'p').
2043 (Combination::isFeatureActive() ?
2044 ' LEFT JOIN `'._DB_PREFIX_.'product_attribute_shop` product_attribute_shop
2045 ON (p.`id_product` = product_attribute_shop.`id_product` AND product_attribute_shop.`default_on` = 1 AND product_attribute_shop.id_shop='.(int)$context->shop->id.')':'').'
2046 LEFT JOIN '._DB_PREFIX_.'product_lang pl ON (pl.id_product = p.id_product'.Shop::addSqlRestrictionOnLang('pl').' AND pl.id_lang = '.(int)$cookie->id_lang.')
2047 LEFT JOIN `'._DB_PREFIX_.'image_shop` image_shop
2048 ON (image_shop.`id_product` = p.`id_product` AND image_shop.cover=1 AND image_shop.id_shop='.(int)$context->shop->id.')
2049 LEFT JOIN `'._DB_PREFIX_.'image_lang` il ON (image_shop.`id_image` = il.`id_image` AND il.`id_lang` = '.(int)$cookie->id_lang.')
2050 LEFT JOIN '._DB_PREFIX_.'manufacturer m ON (m.id_manufacturer = p.id_manufacturer)
2051 '.Product::sqlStock('p', 0).'
2052 WHERE '.$alias_where.'.`active` = 1 AND '.$alias_where.'.`visibility` IN ("both", "catalog")
2053 ORDER BY '.Tools::getProductsOrder('by', Tools::getValue('orderby'), true).' '.Tools::getProductsOrder('way', Tools::getValue('orderway')).' , cp.id_product'.
2054 ' LIMIT '.(((int)$this->page - 1) * $n.','.$n), true, false);
2055 }
2056 else
2057 {
2058 $this->products = Db::getInstance()->executeS('
2059 SELECT
2060 p.*,
2061 '.($alias_where == 'p' ? '' : 'product_shop.*,' ).'
2062 '.$alias_where.'.id_category_default,
2063 pl.*,
2064 MAX(image_shop.`id_image`) id_image,
2065 il.legend,
2066 m.name manufacturer_name,
2067 '.(Combination::isFeatureActive() ? 'MAX(product_attribute_shop.id_product_attribute) id_product_attribute,' : '').'
2068 DATEDIFF('.$alias_where.'.`date_add`, DATE_SUB("'.date('Y-m-d').' 00:00:00", INTERVAL '.(int)$nb_day_new_product.' DAY)) > 0 AS new,
2069 stock.out_of_stock, IFNULL(stock.quantity, 0) as quantity'.(Combination::isFeatureActive() ? ', MAX(product_attribute_shop.minimal_quantity) AS product_attribute_minimal_quantity' : '').'
2070 FROM '._DB_PREFIX_.'cat_filter_restriction cp
2071 LEFT JOIN `'._DB_PREFIX_.'product` p ON p.`id_product` = cp.`id_product`
2072 '.Shop::addSqlAssociation('product', 'p').
2073 (Combination::isFeatureActive() ?
2074 'LEFT JOIN `'._DB_PREFIX_.'product_attribute` pa ON (p.`id_product` = pa.`id_product`)
2075 '.Shop::addSqlAssociation('product_attribute', 'pa', false, 'product_attribute_shop.`default_on` = 1 AND product_attribute_shop.id_shop='.(int)$context->shop->id):'').'
2076 LEFT JOIN '._DB_PREFIX_.'product_lang pl ON (pl.id_product = p.id_product'.Shop::addSqlRestrictionOnLang('pl').' AND pl.id_lang = '.(int)$cookie->id_lang.')
2077 LEFT JOIN `'._DB_PREFIX_.'image` i ON (i.`id_product` = p.`id_product`)'.
2078 Shop::addSqlAssociation('image', 'i', false, 'image_shop.cover=1').'
2079 LEFT JOIN `'._DB_PREFIX_.'image_lang` il ON (image_shop.`id_image` = il.`id_image` AND il.`id_lang` = '.(int)$cookie->id_lang.')
2080 LEFT JOIN '._DB_PREFIX_.'manufacturer m ON (m.id_manufacturer = p.id_manufacturer)
2081 '.Product::sqlStock('p', 0).'
2082 WHERE '.$alias_where.'.`active` = 1 AND '.$alias_where.'.`visibility` IN ("both", "catalog")
2083 GROUP BY product_shop.id_product
2084 ORDER BY '.Tools::getProductsOrder('by', Tools::getValue('orderby'), true).' '.Tools::getProductsOrder('way', Tools::getValue('orderway')).' , cp.id_product'.
2085 ' LIMIT '.(((int)$this->page - 1) * $n.','.$n), true, false);
2086 }
2087 }
2088
2089 if (Tools::getProductsOrder('by', Tools::getValue('orderby'), true) == 'p.price')
2090 Tools::orderbyPrice($this->products, Tools::getProductsOrder('way', Tools::getValue('orderway')));
2091
2092 return $this->products;
2093 }
2094
2095 private static function query($sql_query)
2096 {
2097 return Db::getInstance(_PS_USE_SQL_SLAVE_)->query($sql_query);
2098 }
2099
2100 public function getFilterBlock($selected_filters = array())
2101 {
2102 global $cookie;
2103 static $cache = null;
2104
2105 $context = Context::getContext();
2106
2107 $id_lang = $context->language->id;
2108 $currency = $context->currency;
2109 $id_shop = (int) $context->shop->id;
2110 $alias = 'product_shop';
2111
2112 if (is_array($cache))
2113 return $cache;
2114
2115 $home_category = Configuration::get('PS_HOME_CATEGORY');
2116 $id_parent = (int)Tools::getValue('id_category', Tools::getValue('id_category_layered', $home_category));
2117 if ($id_parent == $home_category)
2118 return;
2119
2120 $parent = new Category((int)$id_parent, $id_lang);
2121
2122 /* Get the filters for the current category */
2123 $filters = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS('
2124 SELECT type, id_value, filter_show_limit, filter_type FROM '._DB_PREFIX_.'layered_category
2125 WHERE id_category = '.(int)$id_parent.'
2126 AND id_shop = '.$id_shop.'
2127 GROUP BY `type`, id_value ORDER BY position ASC'
2128 );
2129
2130 /* Create the table which contains all the id_product in a cat or a tree */
2131
2132 Db::getInstance()->execute('DROP TEMPORARY TABLE IF EXISTS '._DB_PREFIX_.'cat_restriction', false);
2133 Db::getInstance()->execute('CREATE TEMPORARY TABLE '._DB_PREFIX_.'cat_restriction ENGINE=MEMORY
2134 SELECT DISTINCT cp.id_product, p.id_manufacturer, product_shop.condition, p.weight FROM '._DB_PREFIX_.'category c
2135 STRAIGHT_JOIN '._DB_PREFIX_.'category_product cp ON (c.id_category = cp.id_category AND
2136 '.(Configuration::get('PS_LAYERED_FULL_TREE') ? 'c.nleft >= '.(int)$parent->nleft.'
2137 AND c.nright <= '.(int)$parent->nright : 'c.id_category = '.(int)$id_parent).'
2138 AND c.active = 1)
2139 STRAIGHT_JOIN '._DB_PREFIX_.'product_shop product_shop ON (product_shop.id_product = cp.id_product
2140 AND product_shop.id_shop = '.(int)$context->shop->id.')
2141 STRAIGHT_JOIN '._DB_PREFIX_.'product p ON (p.id_product=cp.id_product)
2142 WHERE product_shop.`active` = 1 AND product_shop.`visibility` IN ("both", "catalog")', false);
2143
2144
2145 Db::getInstance()->execute('ALTER TABLE '._DB_PREFIX_.'cat_restriction ADD PRIMARY KEY (id_product),
2146 ADD KEY `id_manufacturer` (`id_manufacturer`,`id_product`) USING BTREE,
2147 ADD KEY `condition` (`condition`,`id_product`) USING BTREE,
2148 ADD KEY `weight` (`weight`,`id_product`) USING BTREE', false);
2149
2150 // Remove all empty selected filters
2151 foreach ($selected_filters as $key => $value)
2152 switch ($key)
2153 {
2154 case 'price':
2155 case 'weight':
2156 if ($value[0] === '' && $value[1] === '')
2157 unset($selected_filters[$key]);
2158 break;
2159 default:
2160 if ($value == '')
2161 unset($selected_filters[$key]);
2162 break;
2163 }
2164
2165 $filter_blocks = array();
2166 foreach ($filters as $filter)
2167 {
2168 $sql_query = array('select' => '', 'from' => '', 'join' => '', 'where' => '', 'group' => '', 'second_query' => '');
2169 switch ($filter['type'])
2170 {
2171 case 'price':
2172 $sql_query['select'] = 'SELECT p.`id_product`, psi.price_min, psi.price_max ';
2173 // price slider is not filter dependent
2174 $sql_query['from'] = '
2175 FROM '._DB_PREFIX_.'cat_restriction p';
2176 $sql_query['join'] = 'INNER JOIN `'._DB_PREFIX_.'layered_price_index` psi
2177 ON (psi.id_product = p.id_product AND psi.id_currency = '.(int)$context->currency->id.' AND psi.id_shop='.(int)$context->shop->id.')';
2178 $sql_query['where'] = 'WHERE 1';
2179 break;
2180 case 'weight':
2181 $sql_query['select'] = 'SELECT p.`id_product`, p.`weight` ';
2182 // price slider is not filter dependent
2183 $sql_query['from'] = '
2184 FROM '._DB_PREFIX_.'cat_restriction p';
2185 $sql_query['where'] = 'WHERE 1';
2186 break;
2187 case 'condition':
2188 $sql_query['select'] = 'SELECT p.`id_product`, product_shop.`condition` ';
2189 $sql_query['from'] = '
2190 FROM '._DB_PREFIX_.'cat_restriction p';
2191 $sql_query['where'] = 'WHERE 1';
2192 $sql_query['from'] .= Shop::addSqlAssociation('product', 'p');
2193 break;
2194 case 'quantity':
2195 $sql_query['select'] = 'SELECT p.`id_product`, sa.`quantity`, sa.`out_of_stock` ';
2196
2197 $sql_query['from'] = '
2198 FROM '._DB_PREFIX_.'cat_restriction p';
2199
2200 $sql_query['join'] .= 'LEFT JOIN `'._DB_PREFIX_.'stock_available` sa
2201 ON (sa.id_product = p.id_product AND sa.id_product_attribute=0 '.StockAvailable::addSqlShopRestriction(null, null, 'sa').') ';
2202 $sql_query['where'] = 'WHERE 1';
2203 break;
2204
2205 case 'manufacturer':
2206 $sql_query['select'] = 'SELECT COUNT(DISTINCT p.id_product) nbr, m.id_manufacturer, m.name ';
2207 $sql_query['from'] = '
2208 FROM '._DB_PREFIX_.'cat_restriction p
2209 INNER JOIN '._DB_PREFIX_.'manufacturer m ON (m.id_manufacturer = p.id_manufacturer) ';
2210 $sql_query['where'] = 'WHERE 1';
2211 $sql_query['group'] = ' GROUP BY p.id_manufacturer ORDER BY m.name';
2212
2213 if (!Configuration::get('PS_LAYERED_HIDE_0_VALUES'))
2214 {
2215 $sql_query['second_query'] = '
2216 SELECT m.name, 0 nbr, m.id_manufacturer
2217
2218 FROM '._DB_PREFIX_.'cat_restriction p
2219 INNER JOIN '._DB_PREFIX_.'manufacturer m ON (m.id_manufacturer = p.id_manufacturer)
2220 WHERE 1
2221 GROUP BY p.id_manufacturer ORDER BY m.name';
2222 }
2223
2224 break;
2225 case 'id_attribute_group':// attribute group
2226 $sql_query['select'] = '
2227 SELECT COUNT(DISTINCT lpa.id_product) nbr, lpa.id_attribute_group,
2228 a.color, al.name attribute_name, agl.public_name attribute_group_name , lpa.id_attribute, ag.is_color_group,
2229 liagl.url_name name_url_name, liagl.meta_title name_meta_title, lial.url_name value_url_name, lial.meta_title value_meta_title';
2230 $sql_query['from'] = '
2231 FROM '._DB_PREFIX_.'layered_product_attribute lpa
2232 INNER JOIN '._DB_PREFIX_.'attribute a
2233 ON a.id_attribute = lpa.id_attribute
2234 INNER JOIN '._DB_PREFIX_.'attribute_lang al
2235 ON al.id_attribute = a.id_attribute
2236 AND al.id_lang = '.(int)$id_lang.'
2237 INNER JOIN '._DB_PREFIX_.'cat_restriction p
2238 ON p.id_product = lpa.id_product
2239 INNER JOIN '._DB_PREFIX_.'attribute_group ag
2240 ON ag.id_attribute_group = lpa.id_attribute_group
2241 INNER JOIN '._DB_PREFIX_.'attribute_group_lang agl
2242 ON agl.id_attribute_group = lpa.id_attribute_group
2243 AND agl.id_lang = '.(int)$id_lang.'
2244 LEFT JOIN '._DB_PREFIX_.'layered_indexable_attribute_group_lang_value liagl
2245 ON (liagl.id_attribute_group = lpa.id_attribute_group AND liagl.id_lang = '.(int)$id_lang.')
2246 LEFT JOIN '._DB_PREFIX_.'layered_indexable_attribute_lang_value lial
2247 ON (lial.id_attribute = lpa.id_attribute AND lial.id_lang = '.(int)$id_lang.') ';
2248
2249 $sql_query['where'] = 'WHERE lpa.id_attribute_group = '.(int)$filter['id_value'];
2250 $sql_query['where'] .= ' AND lpa.`id_shop` = '.(int)$context->shop->id;
2251 $sql_query['group'] = '
2252 GROUP BY lpa.id_attribute
2253 ORDER BY ag.`position` ASC, a.`position` ASC';
2254
2255 if (!Configuration::get('PS_LAYERED_HIDE_0_VALUES'))
2256 {
2257 $sql_query['second_query'] = '
2258 SELECT 0 nbr, lpa.id_attribute_group,
2259 a.color, al.name attribute_name, agl.public_name attribute_group_name , lpa.id_attribute, ag.is_color_group,
2260 liagl.url_name name_url_name, liagl.meta_title name_meta_title, lial.url_name value_url_name, lial.meta_title value_meta_title
2261 FROM '._DB_PREFIX_.'layered_product_attribute lpa'.
2262 Shop::addSqlAssociation('product', 'lpa').'
2263 INNER JOIN '._DB_PREFIX_.'attribute a
2264 ON a.id_attribute = lpa.id_attribute
2265 INNER JOIN '._DB_PREFIX_.'attribute_lang al
2266 ON al.id_attribute = a.id_attribute AND al.id_lang = '.(int)$id_lang.'
2267 INNER JOIN '._DB_PREFIX_.'product as p
2268 ON p.id_product = lpa.id_product
2269 INNER JOIN '._DB_PREFIX_.'attribute_group ag
2270 ON ag.id_attribute_group = lpa.id_attribute_group
2271 INNER JOIN '._DB_PREFIX_.'attribute_group_lang agl
2272 ON agl.id_attribute_group = lpa.id_attribute_group
2273 AND agl.id_lang = '.(int)$id_lang.'
2274 LEFT JOIN '._DB_PREFIX_.'layered_indexable_attribute_group_lang_value liagl
2275 ON (liagl.id_attribute_group = lpa.id_attribute_group AND liagl.id_lang = '.(int)$id_lang.')
2276 LEFT JOIN '._DB_PREFIX_.'layered_indexable_attribute_lang_value lial
2277 ON (lial.id_attribute = lpa.id_attribute AND lial.id_lang = '.(int)$id_lang.')
2278 WHERE lpa.id_attribute_group = '.(int)$filter['id_value'].'
2279 AND lpa.`id_shop` = '.(int)$context->shop->id.'
2280 GROUP BY lpa.id_attribute
2281 ORDER BY id_attribute_group, id_attribute';
2282 }
2283 break;
2284
2285 case 'id_feature':
2286 $sql_query['select'] = 'SELECT fl.name feature_name, fp.id_feature, fv.id_feature_value, fvl.value,
2287 COUNT(DISTINCT p.id_product) nbr,
2288 lifl.url_name name_url_name, lifl.meta_title name_meta_title, lifvl.url_name value_url_name, lifvl.meta_title value_meta_title ';
2289 $sql_query['from'] = '
2290 FROM '._DB_PREFIX_.'feature_product fp
2291 INNER JOIN '._DB_PREFIX_.'cat_restriction p
2292 ON p.id_product = fp.id_product
2293 LEFT JOIN '._DB_PREFIX_.'feature_lang fl ON (fl.id_feature = fp.id_feature AND fl.id_lang = '.$id_lang.')
2294 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))
2295 LEFT JOIN '._DB_PREFIX_.'feature_value_lang fvl ON (fvl.id_feature_value = fp.id_feature_value AND fvl.id_lang = '.$id_lang.')
2296 LEFT JOIN '._DB_PREFIX_.'layered_indexable_feature_lang_value lifl
2297 ON (lifl.id_feature = fp.id_feature AND lifl.id_lang = '.$id_lang.')
2298 LEFT JOIN '._DB_PREFIX_.'layered_indexable_feature_value_lang_value lifvl
2299 ON (lifvl.id_feature_value = fp.id_feature_value AND lifvl.id_lang = '.$id_lang.') ';
2300 $sql_query['where'] = 'WHERE fp.id_feature = '.(int)$filter['id_value'];
2301 $sql_query['group'] = 'GROUP BY fv.id_feature_value ';
2302
2303 if (!Configuration::get('PS_LAYERED_HIDE_0_VALUES'))
2304 {
2305 $sql_query['second_query'] = '
2306 SELECT fl.name feature_name, fp.id_feature, fv.id_feature_value, fvl.value,
2307 0 nbr,
2308 lifl.url_name name_url_name, lifl.meta_title name_meta_title, lifvl.url_name value_url_name, lifvl.meta_title value_meta_title
2309
2310 FROM '._DB_PREFIX_.'feature_product fp'.
2311 Shop::addSqlAssociation('product', 'fp').'
2312 INNER JOIN '._DB_PREFIX_.'product p ON (p.id_product = fp.id_product)
2313 LEFT JOIN '._DB_PREFIX_.'feature_lang fl ON (fl.id_feature = fp.id_feature AND fl.id_lang = '.(int)$id_lang.')
2314 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))
2315 LEFT JOIN '._DB_PREFIX_.'feature_value_lang fvl ON (fvl.id_feature_value = fp.id_feature_value AND fvl.id_lang = '.(int)$id_lang.')
2316 LEFT JOIN '._DB_PREFIX_.'layered_indexable_feature_lang_value lifl
2317 ON (lifl.id_feature = fp.id_feature AND lifl.id_lang = '.(int)$id_lang.')
2318 LEFT JOIN '._DB_PREFIX_.'layered_indexable_feature_value_lang_value lifvl
2319 ON (lifvl.id_feature_value = fp.id_feature_value AND lifvl.id_lang = '.(int)$id_lang.')
2320 WHERE fp.id_feature = '.(int)$filter['id_value'].'
2321 GROUP BY fv.id_feature_value';
2322 }
2323
2324 break;
2325
2326 case 'category':
2327 if (Group::isFeatureActive())
2328 $this->user_groups = ($this->context->customer->isLogged() ? $this->context->customer->getGroups() : array(Configuration::get('PS_UNIDENTIFIED_GROUP')));
2329
2330 $depth = Configuration::get('PS_LAYERED_FILTER_CATEGORY_DEPTH');
2331 if ($depth === false)
2332 $depth = 1;
2333
2334 $sql_query['select'] = '
2335 SELECT c.id_category, c.id_parent, cl.name, (SELECT count(DISTINCT p.id_product) # ';
2336 $sql_query['from'] = '
2337 FROM '._DB_PREFIX_.'category_product cp
2338 LEFT JOIN '._DB_PREFIX_.'product p ON (p.id_product = cp.id_product) ';
2339 $sql_query['where'] = '
2340 WHERE cp.id_category = c.id_category
2341 AND '.$alias.'.active = 1 AND '.$alias.'.`visibility` IN ("both", "catalog")';
2342 $sql_query['group'] = ') count_products
2343 FROM '._DB_PREFIX_.'category c
2344 LEFT JOIN '._DB_PREFIX_.'category_lang cl ON (cl.id_category = c.id_category AND cl.`id_shop` = '.(int)Context::getContext()->shop->id.' and cl.id_lang = '.(int)$id_lang.') ';
2345
2346 if (Group::isFeatureActive())
2347 $sql_query['group'] .= 'RIGHT JOIN '._DB_PREFIX_.'category_group cg ON (cg.id_category = c.id_category AND cg.`id_group` IN ('.implode(', ', $this->user_groups).')) ';
2348
2349 $sql_query['group'] .= 'WHERE c.nleft > '.(int)$parent->nleft.'
2350 AND c.nright < '.(int)$parent->nright.'
2351 '.($depth ? 'AND c.level_depth <= '.($parent->level_depth+(int)$depth) : '').'
2352 AND c.active = 1
2353 GROUP BY c.id_category ORDER BY c.nleft, c.position';
2354
2355 $sql_query['from'] .= Shop::addSqlAssociation('product', 'p');
2356 }
2357
2358 foreach ($filters as $filter_tmp)
2359 {
2360 $method_name = 'get'.ucfirst($filter_tmp['type']).'FilterSubQuery';
2361 if (method_exists('BlockLayered', $method_name) &&
2362 ($filter['type'] != 'price' && $filter['type'] != 'weight' && $filter['type'] != $filter_tmp['type'] || $filter['type'] == $filter_tmp['type']))
2363 {
2364 if ($filter['type'] == $filter_tmp['type'] && $filter['id_value'] == $filter_tmp['id_value'])
2365 $sub_query_filter = self::$method_name(array(), true);
2366 else
2367 {
2368 if (!is_null($filter_tmp['id_value']))
2369 $selected_filters_cleaned = $this->cleanFilterByIdValue(@$selected_filters[$filter_tmp['type']], $filter_tmp['id_value']);
2370 else
2371 $selected_filters_cleaned = @$selected_filters[$filter_tmp['type']];
2372 $sub_query_filter = self::$method_name($selected_filters_cleaned, $filter['type'] == $filter_tmp['type']);
2373 }
2374 foreach ($sub_query_filter as $key => $value)
2375 $sql_query[$key] .= $value;
2376 }
2377 }
2378
2379 $products = false;
2380 if (!empty($sql_query['from']))
2381 {
2382 $products = Db::getInstance()->executeS($sql_query['select']."\n".$sql_query['from']."\n".$sql_query['join']."\n".$sql_query['where']."\n".$sql_query['group'], true, false);
2383 }
2384
2385 // price & weight have slidebar, so it's ok to not complete recompute the product list
2386 if (!empty($selected_filters['price']) && $filter['type'] != 'price' && $filter['type'] != 'weight') {
2387 $products = self::filterProductsByPrice(@$selected_filters['price'], $products);
2388 }
2389
2390 if (!empty($sql_query['second_query']))
2391 {
2392 $res = Db::getInstance()->executeS($sql_query['second_query']);
2393 if ($res)
2394 $products = array_merge($products, $res);
2395 }
2396
2397 switch ($filter['type'])
2398 {
2399 case 'price':
2400 if ($this->showPriceFilter()) {
2401 $price_array = array(
2402 'type_lite' => 'price',
2403 'type' => 'price',
2404 'id_key' => 0,
2405 'name' => $this->l('Price'),
2406 'slider' => true,
2407 'max' => '0',
2408 'min' => null,
2409 'values' => array ('1' => 0),
2410 'unit' => $currency->sign,
2411 'format' => $currency->format,
2412 'filter_show_limit' => $filter['filter_show_limit'],
2413 'filter_type' => $filter['filter_type']
2414 );
2415 if (isset($products) && $products)
2416 foreach ($products as $product)
2417 {
2418 if (is_null($price_array['min']))
2419 {
2420 $price_array['min'] = $product['price_min'];
2421 $price_array['values'][0] = $product['price_min'];
2422 }
2423 else if ($price_array['min'] > $product['price_min'])
2424 {
2425 $price_array['min'] = $product['price_min'];
2426 $price_array['values'][0] = $product['price_min'];
2427 }
2428
2429 if ($price_array['max'] < $product['price_max'])
2430 {
2431 $price_array['max'] = $product['price_max'];
2432 $price_array['values'][1] = $product['price_max'];
2433 }
2434 }
2435
2436 if ($price_array['max'] != $price_array['min'] && $price_array['min'] != null)
2437 {
2438 if ($filter['filter_type'] == 2)
2439 {
2440 $price_array['list_of_values'] = array();
2441 $nbr_of_value = $filter['filter_show_limit'];
2442 if ($nbr_of_value < 2)
2443 $nbr_of_value = 4;
2444 $delta = ($price_array['max'] - $price_array['min']) / $nbr_of_value;
2445 $current_step = $price_array['min'];
2446 for ($i = 0; $i < $nbr_of_value; $i++)
2447 $price_array['list_of_values'][] = array(
2448 (int)($price_array['min'] + $i * $delta),
2449 (int)($price_array['min'] + ($i + 1) * $delta)
2450 );
2451 }
2452 if (isset($selected_filters['price']) && isset($selected_filters['price'][0])
2453 && isset($selected_filters['price'][1]))
2454 {
2455 $price_array['values'][0] = $selected_filters['price'][0];
2456 $price_array['values'][1] = $selected_filters['price'][1];
2457 }
2458 $filter_blocks[] = $price_array;
2459 }
2460 }
2461 break;
2462
2463 case 'weight':
2464 $weight_array = array(
2465 'type_lite' => 'weight',
2466 'type' => 'weight',
2467 'id_key' => 0,
2468 'name' => $this->l('Weight'),
2469 'slider' => true,
2470 'max' => '0',
2471 'min' => null,
2472 'values' => array ('1' => 0),
2473 'unit' => Configuration::get('PS_WEIGHT_UNIT'),
2474 'format' => 5, // Ex: xxxxx kg
2475 'filter_show_limit' => $filter['filter_show_limit'],
2476 'filter_type' => $filter['filter_type']
2477 );
2478 if (isset($products) && $products)
2479 foreach ($products as $product)
2480 {
2481 if (is_null($weight_array['min']))
2482 {
2483 $weight_array['min'] = $product['weight'];
2484 $weight_array['values'][0] = $product['weight'];
2485 }
2486 else if ($weight_array['min'] > $product['weight'])
2487 {
2488 $weight_array['min'] = $product['weight'];
2489 $weight_array['values'][0] = $product['weight'];
2490 }
2491
2492 if ($weight_array['max'] < $product['weight'])
2493 {
2494 $weight_array['max'] = $product['weight'];
2495 $weight_array['values'][1] = $product['weight'];
2496 }
2497 }
2498 if ($weight_array['max'] != $weight_array['min'] && $weight_array['min'] != null)
2499 {
2500 if (isset($selected_filters['weight']) && isset($selected_filters['weight'][0])
2501 && isset($selected_filters['weight'][1]))
2502 {
2503 $weight_array['values'][0] = $selected_filters['weight'][0];
2504 $weight_array['values'][1] = $selected_filters['weight'][1];
2505 }
2506 $filter_blocks[] = $weight_array;
2507 }
2508 break;
2509
2510 case 'condition':
2511 $condition_array = array(
2512 'new' => array('name' => $this->l('New'),'nbr' => 0),
2513 'used' => array('name' => $this->l('Used'), 'nbr' => 0),
2514 'refurbished' => array('name' => $this->l('Refurbished'),
2515 'nbr' => 0)
2516 );
2517 if (isset($products) && $products)
2518 foreach ($products as $product)
2519 if (isset($selected_filters['condition']) && in_array($product['condition'], $selected_filters['condition']))
2520 $condition_array[$product['condition']]['checked'] = true;
2521 foreach ($condition_array as $key => $condition)
2522 if (isset($selected_filters['condition']) && in_array($key, $selected_filters['condition']))
2523 $condition_array[$key]['checked'] = true;
2524 if (isset($products) && $products)
2525 foreach ($products as $product)
2526 if (isset($condition_array[$product['condition']]))
2527 $condition_array[$product['condition']]['nbr']++;
2528 $filter_blocks[] = array(
2529 'type_lite' => 'condition',
2530 'type' => 'condition',
2531 'id_key' => 0,
2532 'name' => $this->l('Condition'),
2533 'values' => $condition_array,
2534 'filter_show_limit' => $filter['filter_show_limit'],
2535 'filter_type' => $filter['filter_type']
2536 );
2537 break;
2538
2539 case 'quantity':
2540 $quantity_array = array (
2541 0 => array('name' => $this->l('Not available'), 'nbr' => 0),
2542 1 => array('name' => $this->l('In stock'), 'nbr' => 0)
2543 );
2544 foreach ($quantity_array as $key => $quantity)
2545 if (isset($selected_filters['quantity']) && in_array($key, $selected_filters['quantity']))
2546 $quantity_array[$key]['checked'] = true;
2547 if (isset($products) && $products)
2548 foreach ($products as $product)
2549 {
2550 //If oosp move all not available quantity to available quantity
2551 if ((int)$product['quantity'] > 0 || Product::isAvailableWhenOutOfStock($product['out_of_stock']))
2552 $quantity_array[1]['nbr']++;
2553 else
2554 $quantity_array[0]['nbr']++;
2555 }
2556
2557 $filter_blocks[] = array(
2558 'type_lite' => 'quantity',
2559 'type' => 'quantity',
2560 'id_key' => 0,
2561 'name' => $this->l('Availability'),
2562 'values' => $quantity_array,
2563 'filter_show_limit' => $filter['filter_show_limit'],
2564 'filter_type' => $filter['filter_type']
2565 );
2566
2567 break;
2568
2569 case 'manufacturer':
2570 if (isset($products) && $products)
2571 {
2572 $manufaturers_array = array();
2573 foreach ($products as $manufacturer)
2574 {
2575 if (!isset($manufaturers_array[$manufacturer['id_manufacturer']]))
2576 $manufaturers_array[$manufacturer['id_manufacturer']] = array('name' => $manufacturer['name'], 'nbr' => $manufacturer['nbr']);
2577 if (isset($selected_filters['manufacturer']) && in_array((int)$manufacturer['id_manufacturer'], $selected_filters['manufacturer']))
2578 $manufaturers_array[$manufacturer['id_manufacturer']]['checked'] = true;
2579 }
2580 $filter_blocks[] = array(
2581 'type_lite' => 'manufacturer',
2582 'type' => 'manufacturer',
2583 'id_key' => 0,
2584 'name' => $this->l('Manufacturer'),
2585 'values' => $manufaturers_array,
2586 'filter_show_limit' => $filter['filter_show_limit'],
2587 'filter_type' => $filter['filter_type']
2588 );
2589 }
2590 break;
2591
2592 case 'id_attribute_group':
2593 $attributes_array = array();
2594 if (isset($products) && $products)
2595 {
2596 foreach ($products as $attributes)
2597 {
2598 if (!isset($attributes_array[$attributes['id_attribute_group']]))
2599 $attributes_array[$attributes['id_attribute_group']] = array (
2600 'type_lite' => 'id_attribute_group',
2601 'type' => 'id_attribute_group',
2602 'id_key' => (int)$attributes['id_attribute_group'],
2603 'name' => $attributes['attribute_group_name'],
2604 'is_color_group' => (bool)$attributes['is_color_group'],
2605 'values' => array(),
2606 'url_name' => $attributes['name_url_name'],
2607 'meta_title' => $attributes['name_meta_title'],
2608 'filter_show_limit' => $filter['filter_show_limit'],
2609 'filter_type' => $filter['filter_type']
2610 );
2611
2612 if (!isset($attributes_array[$attributes['id_attribute_group']]['values'][$attributes['id_attribute']]))
2613 $attributes_array[$attributes['id_attribute_group']]['values'][$attributes['id_attribute']] = array(
2614 'color' => $attributes['color'],
2615 'name' => $attributes['attribute_name'],
2616 'nbr' => (int)$attributes['nbr'],
2617 'url_name' => $attributes['value_url_name'],
2618 'meta_title' => $attributes['value_meta_title']
2619 );
2620
2621 if (isset($selected_filters['id_attribute_group'][$attributes['id_attribute']]))
2622 $attributes_array[$attributes['id_attribute_group']]['values'][$attributes['id_attribute']]['checked'] = true;
2623 }
2624
2625 $filter_blocks = array_merge($filter_blocks, $attributes_array);
2626 }
2627 break;
2628 case 'id_feature':
2629 $feature_array = array();
2630 if (isset($products) && $products)
2631 {
2632 foreach ($products as $feature)
2633 {
2634 if (!isset($feature_array[$feature['id_feature']]))
2635 $feature_array[$feature['id_feature']] = array(
2636 'type_lite' => 'id_feature',
2637 'type' => 'id_feature',
2638 'id_key' => (int)$feature['id_feature'],
2639 'values' => array(),
2640 'name' => $feature['feature_name'],
2641 'url_name' => $feature['name_url_name'],
2642 'meta_title' => $feature['name_meta_title'],
2643 'filter_show_limit' => $filter['filter_show_limit'],
2644 'filter_type' => $filter['filter_type']
2645 );
2646
2647 if (!isset($feature_array[$feature['id_feature']]['values'][$feature['id_feature_value']]))
2648 $feature_array[$feature['id_feature']]['values'][$feature['id_feature_value']] = array(
2649 'nbr' => (int)$feature['nbr'],
2650 'name' => $feature['value'],
2651 'url_name' => $feature['value_url_name'],
2652 'meta_title' => $feature['value_meta_title']
2653 );
2654
2655 if (isset($selected_filters['id_feature'][$feature['id_feature_value']]))
2656 $feature_array[$feature['id_feature']]['values'][$feature['id_feature_value']]['checked'] = true;
2657 }
2658
2659 //Natural sort
2660 foreach ($feature_array as $key => $value)
2661 {
2662 $temp = array();
2663 foreach ($feature_array[$key]['values'] as $keyint => $valueint)
2664 $temp[$keyint] = $valueint['name'];
2665
2666 natcasesort($temp);
2667 $temp2 = array();
2668
2669 foreach ($temp as $keytemp => $valuetemp)
2670 $temp2[$keytemp] = $feature_array[$key]['values'][$keytemp];
2671
2672 $feature_array[$key]['values'] = $temp2;
2673 }
2674
2675 $filter_blocks = array_merge($filter_blocks, $feature_array);
2676 }
2677 break;
2678
2679 case 'category':
2680 $tmp_array = array();
2681 if (isset($products) && $products)
2682 {
2683 $categories_with_products_count = 0;
2684 foreach ($products as $category)
2685 {
2686 $tmp_array[$category['id_category']] = array(
2687 'name' => $category['name'],
2688 'nbr' => (int)$category['count_products']
2689 );
2690
2691 if ((int)$category['count_products'])
2692 $categories_with_products_count++;
2693
2694 if (isset($selected_filters['category']) && in_array($category['id_category'], $selected_filters['category']))
2695 $tmp_array[$category['id_category']]['checked'] = true;
2696 }
2697 if ($categories_with_products_count || !Configuration::get('PS_LAYERED_HIDE_0_VALUES'))
2698 $filter_blocks[] = array (
2699 'type_lite' => 'category',
2700 'type' => 'category',
2701 'id_key' => 0, 'name' => $this->l('Categories'),
2702 'values' => $tmp_array,
2703 'filter_show_limit' => $filter['filter_show_limit'],
2704 'filter_type' => $filter['filter_type']
2705 );
2706 }
2707 break;
2708 }
2709 }
2710
2711 // All non indexable attribute and feature
2712 $non_indexable = array();
2713
2714 // Get all non indexable attribute groups
2715 foreach (Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS('
2716 SELECT public_name
2717 FROM `'._DB_PREFIX_.'attribute_group_lang` agl
2718 LEFT JOIN `'._DB_PREFIX_.'layered_indexable_attribute_group` liag
2719 ON liag.id_attribute_group = agl.id_attribute_group
2720 WHERE indexable IS NULL OR indexable = 0
2721 AND id_lang = '.(int)$id_lang) as $attribute)
2722 $non_indexable[] = Tools::link_rewrite($attribute['public_name']);
2723
2724 // Get all non indexable features
2725 foreach (Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS('
2726 SELECT name
2727 FROM `'._DB_PREFIX_.'feature_lang` fl
2728 LEFT JOIN `'._DB_PREFIX_.'layered_indexable_feature` lif
2729 ON lif.id_feature = fl.id_feature
2730 WHERE indexable IS NULL OR indexable = 0
2731 AND id_lang = '.(int)$id_lang) as $attribute)
2732 $non_indexable[] = Tools::link_rewrite($attribute['name']);
2733
2734 //generate SEO link
2735 $param_selected = '';
2736 $param_product_url = '';
2737 $option_checked_array = array();
2738 $param_group_selected_array = array();
2739 $title_values = array();
2740 $meta_values = array();
2741
2742 //get filters checked by group
2743
2744 foreach ($filter_blocks as $type_filter)
2745 {
2746 $filter_name = (!empty($type_filter['url_name']) ? $type_filter['url_name'] : $type_filter['name']);
2747 $filter_meta = (!empty($type_filter['meta_title']) ? $type_filter['meta_title'] : $type_filter['name']);
2748 $attr_key = $type_filter['type'].'_'.$type_filter['id_key'];
2749
2750 $param_group_selected = '';
2751 $lower_filter = strtolower($type_filter['type']);
2752 $filter_name_rewritten = Tools::link_rewrite($filter_name);
2753
2754 if (($lower_filter == 'price' || $lower_filter == 'weight')
2755 && (float)$type_filter['values'][0] > (float)$type_filter['min']
2756 && (float)$type_filter['values'][1] > (float)$type_filter['max'])
2757 {
2758 $param_group_selected .= $this->getAnchor().str_replace($this->getAnchor(), '_', $type_filter['values'][0])
2759 .$this->getAnchor().str_replace($this->getAnchor(), '_', $type_filter['values'][1]);
2760 $param_group_selected_array[$filter_name_rewritten][] = $filter_name_rewritten;
2761
2762 if (!isset($title_values[$filter_meta]))
2763 $title_values[$filter_meta] = array();
2764 $title_values[$filter_meta][] = $filter_meta;
2765 if (!isset($meta_values[$attr_key]))
2766 $meta_values[$attr_key] = array('title' => $filter_meta, 'values' => array());
2767 $meta_values[$attr_key]['values'][] = $filter_meta;
2768 }
2769 else
2770 {
2771 foreach ($type_filter['values'] as $key => $value)
2772 {
2773 if (is_array($value) && array_key_exists('checked', $value ))
2774 {
2775 $value_name = !empty($value['url_name']) ? $value['url_name'] : $value['name'];
2776 $value_meta = !empty($value['meta_title']) ? $value['meta_title'] : $value['name'];
2777 $param_group_selected .= $this->getAnchor().str_replace($this->getAnchor(), '_', Tools::link_rewrite($value_name));
2778 $param_group_selected_array[$filter_name_rewritten][] = Tools::link_rewrite($value_name);
2779
2780 if (!isset($title_values[$filter_meta]))
2781 $title_values[$filter_meta] = array();
2782 $title_values[$filter_meta][] = $value_name;
2783 if (!isset($meta_values[$attr_key]))
2784 $meta_values[$attr_key] = array('title' => $filter_meta, 'values' => array());
2785 $meta_values[$attr_key]['values'][] = $value_meta;
2786 }
2787 else
2788 $param_group_selected_array[$filter_name_rewritten][] = array();
2789 }
2790 }
2791
2792 if (!empty($param_group_selected))
2793 {
2794 $param_selected .= '/'.str_replace($this->getAnchor(), '_', $filter_name_rewritten).$param_group_selected;
2795 $option_checked_array[$filter_name_rewritten] = $param_group_selected;
2796 }
2797 // select only attribute and group attribute to display an unique product combination link
2798 if (!empty($param_group_selected) && $type_filter['type'] == 'id_attribute_group')
2799 $param_product_url .= '/'.str_replace($this->getAnchor(), '_', $filter_name_rewritten).$param_group_selected;
2800
2801 }
2802
2803 if ($this->page > 1)
2804 $param_selected .= '/page-'.$this->page;
2805
2806 $blacklist = array('weight', 'price');
2807
2808 if (!Configuration::get('PS_LAYERED_FILTER_INDEX_CDT'))
2809 $blacklist[] = 'condition';
2810 if (!Configuration::get('PS_LAYERED_FILTER_INDEX_QTY'))
2811 $blacklist[] = 'quantity';
2812 if (!Configuration::get('PS_LAYERED_FILTER_INDEX_MNF'))
2813 $blacklist[] = 'manufacturer';
2814 if (!Configuration::get('PS_LAYERED_FILTER_INDEX_CAT'))
2815 $blacklist[] = 'category';
2816
2817 $global_nofollow = false;
2818 $categorie_link = Context::getContext()->link->getCategoryLink($parent, null, null);
2819
2820 foreach ($filter_blocks as &$type_filter)
2821 {
2822 $filter_name = (!empty($type_filter['url_name']) ? $type_filter['url_name'] : $type_filter['name']);
2823 $filter_link_rewrite = Tools::link_rewrite($filter_name);
2824
2825 if (count($type_filter) > 0 && !isset($type_filter['slider']))
2826 {
2827 foreach ($type_filter['values'] as $key => $values)
2828 {
2829 $nofollow = false;
2830 if (!empty($values['checked']) && in_array($type_filter['type'], $blacklist))
2831 $global_nofollow = true;
2832
2833 $option_checked_clone_array = $option_checked_array;
2834
2835 // If not filters checked, add parameter
2836 $value_name = !empty($values['url_name']) ? $values['url_name'] : $values['name'];
2837
2838 if (!in_array(Tools::link_rewrite($value_name), $param_group_selected_array[$filter_link_rewrite]))
2839 {
2840 // Update parameter filter checked before
2841 if (array_key_exists($filter_link_rewrite, $option_checked_array))
2842 {
2843 $option_checked_clone_array[$filter_link_rewrite] = $option_checked_clone_array[$filter_link_rewrite].$this->getAnchor().str_replace($this->getAnchor(), '_', Tools::link_rewrite($value_name));
2844
2845 if (in_array($type_filter['type'], $blacklist))
2846 $nofollow = true;
2847 }
2848 else
2849 $option_checked_clone_array[$filter_link_rewrite] = $this->getAnchor().str_replace($this->getAnchor(), '_', Tools::link_rewrite($value_name));
2850 }
2851 else
2852 {
2853 // Remove selected parameters
2854 $option_checked_clone_array[$filter_link_rewrite] = str_replace($this->getAnchor().str_replace($this->getAnchor(), '_', Tools::link_rewrite($value_name)), '', $option_checked_clone_array[$filter_link_rewrite]);
2855 if (empty($option_checked_clone_array[$filter_link_rewrite]))
2856 unset($option_checked_clone_array[$filter_link_rewrite]);
2857 }
2858 $parameters = '';
2859 ksort($option_checked_clone_array); // Order parameters
2860 foreach ($option_checked_clone_array as $key_group => $value_group)
2861 $parameters .= '/'.str_replace($this->getAnchor(), '_', $key_group).$value_group;
2862
2863 // Add nofollow if any blacklisted filters ins in parameters
2864 foreach ($filter_blocks as $filter)
2865 {
2866 $name = Tools::link_rewrite((!empty($filter['url_name']) ? $filter['url_name'] : $filter['name']));
2867 if (in_array($filter['type'], $blacklist) && strpos($parameters, $name.'-') !== false)
2868 $nofollow = true;
2869 }
2870
2871 // Check if there is an non indexable attribute or feature in the url
2872 foreach ($non_indexable as $value)
2873 if (strpos($parameters, '/'.$value) !== false)
2874 $nofollow = true;
2875
2876 $type_filter['values'][$key]['link'] = $categorie_link.'#'.ltrim($parameters, '/');
2877 $type_filter['values'][$key]['rel'] = ($nofollow) ? 'nofollow' : '';
2878 }
2879 }
2880 }
2881
2882 $n_filters = 0;
2883
2884 if (isset($selected_filters['price']))
2885 if ($price_array['min'] == $selected_filters['price'][0] && $price_array['max'] == $selected_filters['price'][1])
2886 unset($selected_filters['price']);
2887 if (isset($selected_filters['weight']))
2888 if ($weight_array['min'] == $selected_filters['weight'][0] && $weight_array['max'] == $selected_filters['weight'][1])
2889 unset($selected_filters['weight']);
2890
2891 foreach ($selected_filters as $filters)
2892 $n_filters += count($filters);
2893
2894 $cache = array(
2895 'layered_show_qties' => (int)Configuration::get('PS_LAYERED_SHOW_QTIES'),
2896 'id_category_layered' => (int)$id_parent,
2897 'selected_filters' => $selected_filters,
2898 'n_filters' => (int)$n_filters,
2899 'nbr_filterBlocks' => count($filter_blocks),
2900 'filters' => $filter_blocks,
2901 'title_values' => $title_values,
2902 'meta_values' => $meta_values,
2903 'current_friendly_url' => $param_selected,
2904 'param_product_url' => $param_product_url,
2905 'no_follow' => (!empty($param_selected) || $global_nofollow)
2906 );
2907
2908 return $cache;
2909 }
2910
2911 public function cleanFilterByIdValue($attributes, $id_value)
2912 {
2913 $selected_filters = array();
2914 if (is_array($attributes))
2915 foreach ($attributes as $attribute)
2916 {
2917 $attribute_data = explode('_', $attribute);
2918 if ($attribute_data[0] == $id_value)
2919 $selected_filters[] = $attribute_data[1];
2920 }
2921 return $selected_filters;
2922 }
2923
2924 public function generateFiltersBlock($selected_filters)
2925 {
2926 global $smarty;
2927 if ($filter_block = $this->getFilterBlock($selected_filters))
2928 {
2929 if ($filter_block['nbr_filterBlocks'] == 0)
2930 return false;
2931
2932 $translate = array();
2933 $translate['price'] = $this->l('price');
2934 $translate['weight'] = $this->l('weight');
2935
2936 $smarty->assign($filter_block);
2937 $smarty->assign(array(
2938 'hide_0_values' => Configuration::get('PS_LAYERED_HIDE_0_VALUES'),
2939 'blocklayeredSliderName' => $translate,
2940 'col_img_dir' => _PS_COL_IMG_DIR_
2941 ));
2942 return $this->display(__FILE__, 'blocklayered.tpl');
2943 }
2944 else
2945 return false;
2946 }
2947
2948 private static function getPriceFilterSubQuery($filter_value, $ignore_join = false)
2949 {
2950 $id_currency = (int)Context::getContext()->currency->id;
2951
2952 if (isset($filter_value) && $filter_value)
2953 {
2954 $price_filter_query = '
2955 INNER JOIN `'._DB_PREFIX_.'layered_price_index` psi ON (psi.id_product = p.id_product AND psi.id_currency = '.(int)$id_currency.'
2956 AND psi.price_min <= '.(int)$filter_value[1].' AND psi.price_max >= '.(int)$filter_value[0].' AND psi.id_shop='.(int)Context::getContext()->shop->id.') ';
2957 return array('join' => $price_filter_query);
2958 }
2959 return array();
2960 }
2961
2962 private static function filterProductsByPrice($filter_value, $product_collection)
2963 {
2964 static $ps_layered_filter_price_usetax = null;
2965 static $ps_layered_filter_price_rounding = null;
2966
2967 if (empty($filter_value))
2968 return $product_collection;
2969
2970 if ($ps_layered_filter_price_usetax === null) {
2971 $ps_layered_filter_price_usetax = Configuration::get('PS_LAYERED_FILTER_PRICE_USETAX');
2972 }
2973
2974 if ($ps_layered_filter_price_rounding === null) {
2975 $ps_layered_filter_price_rounding = Configuration::get('PS_LAYERED_FILTER_PRICE_ROUNDING');
2976 }
2977
2978 foreach ($product_collection as $key => $product)
2979 {
2980 if (isset($filter_value) && $filter_value && isset($product['price_min']) && isset($product['id_product'])
2981 && (($product['price_min'] < (int)$filter_value[0] && $product['price_max'] > (int)$filter_value[0])
2982 || ($product['price_max'] > (int)$filter_value[1] && $product['price_min'] < (int)$filter_value[1])))
2983 {
2984 $price = Product::getPriceStatic($product['id_product'], $ps_layered_filter_price_usetax);
2985 if ($ps_layered_filter_price_rounding) {
2986 $price = (int)$price;
2987 }
2988 if ($price < $filter_value[0] || $price > $filter_value[1]) {
2989 unset($product_collection[$key]);
2990 }
2991 }
2992 }
2993 return $product_collection;
2994 }
2995
2996 private static function getWeightFilterSubQuery($filter_value, $ignore_join = false)
2997 {
2998 if (isset($filter_value) && $filter_value) {
2999 if ($filter_value[0] != 0 || $filter_value[1] != 0) {
3000 return array('where' => ' AND p.`weight` BETWEEN '.(float)($filter_value[0] - 0.001).' AND '.(float)($filter_value[1] + 0.001).' ');
3001 }
3002 }
3003
3004 return array();
3005 }
3006
3007 private static function getId_featureFilterSubQuery($filter_value, $ignore_join = false)
3008 {
3009 if (empty($filter_value))
3010 return array();
3011 $query_filters = ' AND EXISTS (SELECT * FROM '._DB_PREFIX_.'feature_product fp WHERE fp.id_product = p.id_product AND ';
3012 foreach ($filter_value as $filter_val)
3013 $query_filters .= 'fp.`id_feature_value` = '.(int)$filter_val.' OR ';
3014 $query_filters = rtrim($query_filters, 'OR ').') ';
3015
3016 return array('where' => $query_filters);
3017 }
3018 private static function getId_attribute_groupFilterSubQuery($filter_value, $ignore_join = false)
3019 {
3020 if (empty($filter_value))
3021 return array();
3022 $query_filters = '
3023 AND EXISTS (SELECT *
3024 FROM `'._DB_PREFIX_.'product_attribute_combination` pac
3025 LEFT JOIN `'._DB_PREFIX_.'product_attribute` pa ON (pa.`id_product_attribute` = pac.`id_product_attribute`)
3026 WHERE pa.id_product = p.id_product AND ';
3027
3028 foreach ($filter_value as $filter_val)
3029 $query_filters .= 'pac.`id_attribute` = '.(int)$filter_val.' OR ';
3030 $query_filters = rtrim($query_filters, 'OR ').') ';
3031
3032 return array('where' => $query_filters);
3033 }
3034
3035 private static function getCategoryFilterSubQuery($filter_value, $ignore_join = false)
3036 {
3037 if (empty($filter_value))
3038 return array();
3039 $query_filters_where = ' AND EXISTS (SELECT * FROM '._DB_PREFIX_.'category_product cp WHERE id_product = p.id_product AND ';
3040 foreach ($filter_value as $id_category)
3041 $query_filters_where .= 'cp.`id_category` = '.(int)$id_category.' OR ';
3042 $query_filters_where = rtrim($query_filters_where, 'OR ').') ';
3043
3044 return array('where' => $query_filters_where);
3045 }
3046
3047 private static function getQuantityFilterSubQuery($filter_value, $ignore_join = false)
3048 {
3049 if (count($filter_value) == 2 || empty($filter_value))
3050 return array();
3051
3052 $query_filters_join = '';
3053
3054 $query_filters = ' AND sav.quantity '.(!$filter_value[0] ? '<=' : '>').' 0 ';
3055 $query_filters_join = 'LEFT JOIN `'._DB_PREFIX_.'stock_available` sav ON (sav.id_product = p.id_product AND sav.id_shop = '.(int)Context::getContext()->shop->id.') ';
3056
3057 return array('where' => $query_filters, 'join' => $query_filters_join);
3058 }
3059
3060 private static function getManufacturerFilterSubQuery($filter_value, $ignore_join = false)
3061 {
3062 if (empty($filter_value))
3063 $query_filters = '';
3064 else
3065 {
3066 array_walk($filter_value, create_function('&$id_manufacturer', '$id_manufacturer = (int)$id_manufacturer;'));
3067 $query_filters = ' AND p.id_manufacturer IN ('.implode($filter_value, ',').')';
3068 }
3069 if ($ignore_join)
3070 return array('where' => $query_filters);
3071 else
3072 return array('where' => $query_filters, 'join' => 'LEFT JOIN `'._DB_PREFIX_.'manufacturer` m ON (m.id_manufacturer = p.id_manufacturer) ');
3073 }
3074
3075 private static function getConditionFilterSubQuery($filter_value, $ignore_join = false)
3076 {
3077 if (count($filter_value) == 3 || empty($filter_value))
3078 return array();
3079
3080 $query_filters = ' AND p.condition IN (';
3081
3082 foreach ($filter_value as $cond)
3083 $query_filters .= '\''.$cond.'\',';
3084 $query_filters = rtrim($query_filters, ',').') ';
3085
3086 return array('where' => $query_filters);
3087 }
3088
3089 public function ajaxCall()
3090 {
3091 global $smarty, $cookie;
3092
3093 $selected_filters = $this->getSelectedFilters();
3094 $filter_block = $this->getFilterBlock($selected_filters);
3095 $this->getProducts($selected_filters, $products, $nb_products, $p, $n, $pages_nb, $start, $stop, $range);
3096 if (isset($selected_filters['id_attribute_group']) && $selected_filters['id_attribute_group']) {
3097 $this->switchProductImages($selected_filters, $products);
3098 }
3099
3100 // Add pagination variable
3101 $nArray = (int)Configuration::get('PS_PRODUCTS_PER_PAGE') != 10 ? array((int)Configuration::get('PS_PRODUCTS_PER_PAGE'), 10, 20, 50) : array(10, 20, 50);
3102 // Clean duplicate values
3103 $nArray = array_unique($nArray);
3104 asort($nArray);
3105
3106 Hook::exec(
3107 'actionProductListModifier',
3108 array(
3109 'nb_products' => &$nb_products,
3110 'cat_products' => &$products,
3111 )
3112 );
3113
3114 if (version_compare(_PS_VERSION_, '1.6.0', '>=') === true)
3115 $this->context->controller->addColorsToProductList($products);
3116
3117 $category = new Category(Tools::getValue('id_category_layered', Configuration::get('PS_HOME_CATEGORY')), (int)$cookie->id_lang);
3118
3119 // Generate meta title and meta description
3120 $category_title = (empty($category->meta_title) ? $category->name : $category->meta_title);
3121 $category_metas = Meta::getMetaTags((int)$cookie->id_lang, 'category');
3122 $title = '';
3123 $keywords = '';
3124
3125 if (is_array($filter_block['title_values']))
3126 foreach ($filter_block['title_values'] as $key => $val)
3127 {
3128 $title .= ' > '.$key.' '.implode('/', $val);
3129 $keywords .= $key.' '.implode('/', $val).', ';
3130 }
3131
3132 $title = $category_title.$title;
3133
3134 if (!empty($title))
3135 $meta_title = $title;
3136 else
3137 $meta_title = $category_metas['meta_title'];
3138
3139 $meta_description = $category_metas['meta_description'];
3140
3141 $keywords = Tools::substr(Tools::strtolower($keywords), 0, 1000);
3142 if (!empty($keywords))
3143 $meta_keywords = rtrim($category_title.', '.$keywords.', '.$category_metas['meta_keywords'], ', ');
3144
3145 $smarty->assign(
3146 array(
3147 'homeSize' => Image::getSize(ImageType::getFormatedName('home')),
3148 'nb_products' => $nb_products,
3149 'category' => $category,
3150 'pages_nb' => (int)$pages_nb,
3151 'p' => (int)$p,
3152 'n' => (int)$n,
3153 'range' => (int)$range,
3154 'start' => (int)$start,
3155 'stop' => (int)$stop,
3156 'n_array' => ((int)Configuration::get('PS_PRODUCTS_PER_PAGE') != 10) ? array((int)Configuration::get('PS_PRODUCTS_PER_PAGE'), 10, 20, 50) : array(10, 20, 50),
3157 'comparator_max_item' => (int)(Configuration::get('PS_COMPARATOR_MAX_ITEM')),
3158 'products' => $products,
3159 'products_per_page' => (int)Configuration::get('PS_PRODUCTS_PER_PAGE'),
3160 'static_token' => Tools::getToken(false),
3161 'page_name' => 'category',
3162 'nArray' => $nArray,
3163 'compareProducts' => CompareProduct::getCompareProducts((int)$this->context->cookie->id_compare)
3164 )
3165 );
3166
3167 // Prevent bug with old template where category.tpl contain the title of the category and category-count.tpl do not exists
3168 if (file_exists(_PS_THEME_DIR_.'category-count.tpl'))
3169 $category_count = $smarty->fetch(_PS_THEME_DIR_.'category-count.tpl');
3170 else
3171 $category_count = '';
3172
3173 if ($nb_products == 0)
3174 $product_list = $this->display(__FILE__, 'blocklayered-no-products.tpl');
3175 else
3176 $product_list = $smarty->fetch(_PS_THEME_DIR_.'product-list.tpl');
3177
3178 $vars = array(
3179 'filtersBlock' => utf8_encode($this->generateFiltersBlock($selected_filters)),
3180 'productList' => utf8_encode($product_list),
3181 'pagination' => $smarty->fetch(_PS_THEME_DIR_.'pagination.tpl'),
3182 'categoryCount' => $category_count,
3183 'meta_title' => $meta_title.' - '.Configuration::get('PS_SHOP_NAME'),
3184 'heading' => $meta_title,
3185 'meta_keywords' => isset($meta_keywords) ? $meta_keywords : null,
3186 'meta_description' => $meta_description,
3187 'current_friendly_url' => ((int)$n == (int)$nb_products) ? '#/show-all': '#'.$filter_block['current_friendly_url'],
3188 'filters' => $filter_block['filters'],
3189 'nbRenderedProducts' => (int)$nb_products,
3190 'nbAskedProducts' => (int)$n
3191 );
3192
3193 if (version_compare(_PS_VERSION_, '1.6.0', '>=') === true)
3194 $vars = array_merge($vars, array('pagination_bottom' => $smarty->assign('paginationId', 'bottom')
3195 ->fetch(_PS_THEME_DIR_.'pagination.tpl')));
3196 /* We are sending an array in jSon to the .js controller, it will update both the filters and the products zones */
3197 return Tools::jsonEncode($vars);
3198 }
3199
3200 public function getProducts($selected_filters, &$products, &$nb_products, &$p, &$n, &$pages_nb, &$start, &$stop, &$range)
3201 {
3202 global $cookie;
3203
3204 $products = $this->getProductByFilters($selected_filters);
3205 $products = Product::getProductsProperties((int)$cookie->id_lang, $products);
3206 $nb_products = $this->nbr_products;
3207 $range = 2; /* how many pages around page selected */
3208
3209 $product_per_page = isset($this->context->cookie->nb_item_per_page) ? (int)$this->context->cookie->nb_item_per_page : Configuration::get('PS_PRODUCTS_PER_PAGE');
3210 $n = (int)Tools::getValue('n', Configuration::get('PS_PRODUCTS_PER_PAGE'));
3211
3212 if ($n <= 0)
3213 $n = 1;
3214
3215 $p = $this->page;
3216
3217 if ($p < 0)
3218 $p = 0;
3219
3220 if ($p > ($nb_products / $n))
3221 $p = ceil($nb_products / $n);
3222 $pages_nb = ceil($nb_products / (int)($n));
3223
3224 $start = (int)($p - $range);
3225 if ($start < 1)
3226 $start = 1;
3227
3228 $stop = (int)($p + $range);
3229 if ($stop > $pages_nb)
3230 $stop = (int)($pages_nb);
3231
3232 foreach ($products as &$product)
3233 {
3234 if ($product['id_product_attribute'] && isset($product['product_attribute_minimal_quantity']))
3235 $product['minimal_quantity'] = $product['product_attribute_minimal_quantity'];
3236 }
3237 }
3238
3239 public function rebuildLayeredStructure()
3240 {
3241 @set_time_limit(0);
3242
3243 /* Set memory limit to 128M only if current is lower */
3244 $memory_limit = @ini_get('memory_limit');
3245 if (substr($memory_limit, -1) != 'G' && ((substr($memory_limit, -1) == 'M' && substr($memory_limit, 0, -1) < 128) || is_numeric($memory_limit) && (intval($memory_limit) < 131072)))
3246 @ini_set('memory_limit', '128M');
3247
3248 /* Delete and re-create the layered categories table */
3249 Db::getInstance()->execute('DROP TABLE IF EXISTS '._DB_PREFIX_.'layered_category');
3250 Db::getInstance()->execute('
3251 CREATE TABLE IF NOT EXISTS `'._DB_PREFIX_.'layered_category` (
3252 `id_layered_category` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
3253 `id_shop` INT(11) UNSIGNED NOT NULL,
3254 `id_category` INT(10) UNSIGNED NOT NULL,
3255 `id_value` INT(10) UNSIGNED NULL DEFAULT \'0\',
3256 `type` ENUM(\'category\',\'id_feature\',\'id_attribute_group\',\'quantity\',\'condition\',\'manufacturer\',\'weight\',\'price\') NOT NULL,
3257 `position` INT(10) UNSIGNED NOT NULL,
3258 `filter_type` int(10) UNSIGNED NOT NULL DEFAULT 0,
3259 `filter_show_limit` int(10) UNSIGNED NOT NULL DEFAULT 0,
3260 PRIMARY KEY (`id_layered_category`),
3261 KEY `id_category` (`id_category`,`type`)
3262 ) ENGINE='._MYSQL_ENGINE_.' DEFAULT CHARSET=latin1 AUTO_INCREMENT=1;'); /* MyISAM + latin1 = Smaller/faster */
3263
3264 Db::getInstance()->execute('
3265 CREATE TABLE IF NOT EXISTS `'._DB_PREFIX_.'layered_filter` (
3266 `id_layered_filter` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
3267 `name` VARCHAR(64) NOT NULL,
3268 `filters` TEXT NULL,
3269 `n_categories` INT(10) UNSIGNED NOT NULL,
3270 `date_add` DATETIME NOT NULL
3271 ) ENGINE='._MYSQL_ENGINE_.' DEFAULT CHARSET=utf8;');
3272
3273 Db::getInstance()->execute('
3274 CREATE TABLE IF NOT EXISTS `'._DB_PREFIX_.'layered_filter_shop` (
3275 `id_layered_filter` INT(10) UNSIGNED NOT NULL,
3276 `id_shop` INT(11) UNSIGNED NOT NULL,
3277 PRIMARY KEY (`id_layered_filter`, `id_shop`),
3278 KEY `id_shop` (`id_shop`)
3279 ) ENGINE='._MYSQL_ENGINE_.' DEFAULT CHARSET=utf8;');
3280 }
3281
3282 public function rebuildLayeredCache($products_ids = array(), $categories_ids = array())
3283 {
3284 @set_time_limit(0);
3285
3286 $filter_data = array('categories' => array());
3287
3288 /* Set memory limit to 128M only if current is lower */
3289 $memory_limit = @ini_get('memory_limit');
3290 if (substr($memory_limit, -1) != 'G' && ((substr($memory_limit, -1) == 'M' && substr($memory_limit, 0, -1) < 128) || is_numeric($memory_limit) && (intval($memory_limit) < 131072)))
3291 @ini_set('memory_limit', '128M');
3292
3293 $db = Db::getInstance(_PS_USE_SQL_SLAVE_);
3294 $n_categories = array();
3295 $done_categories = array();
3296 $alias = 'p';
3297 $join_product_attribute = $join_product = '';
3298
3299 $alias = 'product_shop';
3300 $join_product = Shop::addSqlAssociation('product', 'p');
3301 $join_product_attribute = Shop::addSqlAssociation('product_attribute', 'pa');
3302
3303
3304 $attribute_groups = self::query('
3305 SELECT a.id_attribute, a.id_attribute_group
3306 FROM '._DB_PREFIX_.'attribute a
3307 LEFT JOIN '._DB_PREFIX_.'product_attribute_combination pac ON (pac.id_attribute = a.id_attribute)
3308 LEFT JOIN '._DB_PREFIX_.'product_attribute pa ON (pa.id_product_attribute = pac.id_product_attribute)
3309 LEFT JOIN '._DB_PREFIX_.'product p ON (p.id_product = pa.id_product)
3310 '.$join_product.$join_product_attribute.'
3311 LEFT JOIN '._DB_PREFIX_.'category_product cp ON (cp.id_product = p.id_product)
3312 LEFT JOIN '._DB_PREFIX_.'category c ON (c.id_category = cp.id_category)
3313 WHERE c.active = 1'.
3314 (count($categories_ids) ? ' AND cp.id_category IN ('.implode(',', $categories_ids).')' : '').'
3315 AND '.$alias.'.active = 1 AND '.$alias.'.`visibility` IN ("both", "catalog")
3316 '.(count($products_ids) ? 'AND p.id_product IN ('.implode(',', $products_ids).')' : ''));
3317
3318 $attribute_groups_by_id = array();
3319 while ($row = $db->nextRow($attribute_groups))
3320 $attribute_groups_by_id[(int)$row['id_attribute']] = (int)$row['id_attribute_group'];
3321
3322 $features = self::query('
3323 SELECT fv.id_feature_value, fv.id_feature
3324 FROM '._DB_PREFIX_.'feature_value fv
3325 LEFT JOIN '._DB_PREFIX_.'feature_product fp ON (fp.id_feature_value = fv.id_feature_value)
3326 LEFT JOIN '._DB_PREFIX_.'product p ON (p.id_product = fp.id_product)
3327 '.$join_product.'
3328 LEFT JOIN '._DB_PREFIX_.'category_product cp ON (cp.id_product = p.id_product)
3329 LEFT JOIN '._DB_PREFIX_.'category c ON (c.id_category = cp.id_category)
3330 WHERE (fv.custom IS NULL OR fv.custom = 0) AND c.active = 1'.(count($categories_ids) ? ' AND cp.id_category IN ('.implode(',', $categories_ids).')' : '').'
3331 AND '.$alias.'.active = 1 AND '.$alias.'.`visibility` IN ("both", "catalog") '.(count($products_ids) ? 'AND p.id_product IN ('.implode(',', $products_ids).')' : ''));
3332
3333 $features_by_id = array();
3334 while ($row = $db->nextRow($features))
3335 $features_by_id[(int)$row['id_feature_value']] = (int)$row['id_feature'];
3336
3337 $result = self::query('
3338 SELECT p.id_product,
3339 GROUP_CONCAT(DISTINCT fv.id_feature_value) features,
3340 GROUP_CONCAT(DISTINCT cp.id_category) categories,
3341 GROUP_CONCAT(DISTINCT pac.id_attribute) attributes
3342 FROM '._DB_PREFIX_.'product p
3343 LEFT JOIN '._DB_PREFIX_.'category_product cp ON (cp.id_product = p.id_product)
3344 LEFT JOIN '._DB_PREFIX_.'category c ON (c.id_category = cp.id_category)
3345 LEFT JOIN '._DB_PREFIX_.'feature_product fp ON (fp.id_product = p.id_product)
3346 LEFT JOIN '._DB_PREFIX_.'feature_value fv ON (fv.id_feature_value = fp.id_feature_value)
3347 LEFT JOIN '._DB_PREFIX_.'product_attribute pa ON (pa.id_product = p.id_product)
3348 '.$join_product.$join_product_attribute.'
3349 LEFT JOIN '._DB_PREFIX_.'product_attribute_combination pac ON (pac.id_product_attribute = pa.id_product_attribute)
3350 WHERE c.active = 1'.(count($categories_ids) ? ' AND cp.id_category IN ('.implode(',', $categories_ids).')' : '').'
3351 AND '.$alias.'.active = 1 AND '.$alias.'.`visibility` IN ("both", "catalog")
3352 '.(count($products_ids) ? 'AND p.id_product IN ('.implode(',', $products_ids).')' : '').
3353 ' AND (fv.custom IS NULL OR fv.custom = 0)
3354 GROUP BY p.id_product');
3355
3356
3357 $shop_list = Shop::getShops(false, null, true);
3358
3359 $to_insert = false;
3360 while ($product = $db->nextRow($result))
3361 {
3362 $a = $c = $f = array();
3363 if (!empty($product['attributes']))
3364 $a = array_flip(explode(',', $product['attributes']));
3365 if (!empty($product['categories']))
3366 $c = array_flip(explode(',', $product['categories']));
3367 if (!empty($product['features']))
3368 $f = array_flip(explode(',', $product['features']));
3369
3370 $filter_data['shop_list'] = $shop_list;
3371
3372 foreach ($c as $id_category => $category)
3373 {
3374 if (!in_array($id_category, $filter_data['categories']))
3375 $filter_data['categories'][] = $id_category;
3376
3377 if (!isset($n_categories[(int)$id_category]))
3378 $n_categories[(int)$id_category] = 1;
3379 if (!isset($done_categories[(int)$id_category]['cat']))
3380 {
3381 $filter_data['layered_selection_subcategories'] = array('filter_type' => 0, 'filter_show_limit' => 0);
3382 $done_categories[(int)$id_category]['cat'] = true;
3383 $to_insert = true;
3384 }
3385 if (is_array($attribute_groups_by_id) && count($attribute_groups_by_id) > 0)
3386 foreach ($a as $k_attribute => $attribute)
3387 if (!isset($done_categories[(int)$id_category]['a'.(int)$attribute_groups_by_id[(int)$k_attribute]]))
3388 {
3389 $filter_data['layered_selection_ag_'.(int)$attribute_groups_by_id[(int)$k_attribute]] = array('filter_type' => 0, 'filter_show_limit' => 0);
3390 $done_categories[(int)$id_category]['a'.(int)$attribute_groups_by_id[(int)$k_attribute]] = true;
3391 $to_insert = true;
3392 }
3393 if (is_array($attribute_groups_by_id) && count($attribute_groups_by_id) > 0)
3394 foreach ($f as $k_feature => $feature)
3395 if (!isset($done_categories[(int)$id_category]['f'.(int)$features_by_id[(int)$k_feature]]))
3396 {
3397 $filter_data['layered_selection_feat_'.(int)$features_by_id[(int)$k_feature]] = array('filter_type' => 0, 'filter_show_limit' => 0);
3398 $done_categories[(int)$id_category]['f'.(int)$features_by_id[(int)$k_feature]] = true;
3399 $to_insert = true;
3400 }
3401 if (!isset($done_categories[(int)$id_category]['q']))
3402 {
3403 $filter_data['layered_selection_stock'] = array('filter_type' => 0, 'filter_show_limit' => 0);
3404 $done_categories[(int)$id_category]['q'] = true;
3405 $to_insert = true;
3406 }
3407 if (!isset($done_categories[(int)$id_category]['m']))
3408 {
3409 $filter_data['layered_selection_manufacturer'] = array('filter_type' => 0, 'filter_show_limit' => 0);
3410 $done_categories[(int)$id_category]['m'] = true;
3411 $to_insert = true;
3412 }
3413 if (!isset($done_categories[(int)$id_category]['c']))
3414 {
3415 $filter_data['layered_selection_condition'] = array('filter_type' => 0, 'filter_show_limit' => 0);
3416 $done_categories[(int)$id_category]['c'] = true;
3417 $to_insert = true;
3418 }
3419 if (!isset($done_categories[(int)$id_category]['w']))
3420 {
3421 $filter_data['layered_selection_weight_slider'] = array('filter_type' => 0, 'filter_show_limit' => 0);
3422 $done_categories[(int)$id_category]['w'] = true;
3423 $to_insert = true;
3424 }
3425 if (!isset($done_categories[(int)$id_category]['p']))
3426 {
3427 $filter_data['layered_selection_price_slider'] = array('filter_type' => 0, 'filter_show_limit' => 0);
3428 $done_categories[(int)$id_category]['p'] = true;
3429 $to_insert = true;
3430 }
3431 }
3432 }
3433 if ($to_insert)
3434 {
3435 Db::getInstance()->execute('INSERT INTO '._DB_PREFIX_.'layered_filter(name, filters, n_categories, date_add)
3436 VALUES (\''.sprintf($this->l('My template %s'), date('Y-m-d')).'\', \''.pSQL(serialize($filter_data)).'\', '.count($filter_data['categories']).', NOW())');
3437
3438 $last_id = Db::getInstance()->Insert_ID();
3439 Db::getInstance()->execute('DELETE FROM '._DB_PREFIX_.'layered_filter_shop WHERE `id_layered_filter` = '.$last_id);
3440 foreach ($shop_list as $id_shop)
3441 Db::getInstance()->execute('INSERT INTO '._DB_PREFIX_.'layered_filter_shop (`id_layered_filter`, `id_shop`)
3442 VALUES('.$last_id.', '.(int)$id_shop.')');
3443
3444 $this->buildLayeredCategories();
3445 }
3446 }
3447
3448 public function buildLayeredCategories()
3449 {
3450 // Get all filter template
3451 $res = Db::getInstance()->executeS('SELECT * FROM '._DB_PREFIX_.'layered_filter ORDER BY date_add DESC');
3452 $categories = array();
3453 // Remove all from layered_category
3454 Db::getInstance()->execute('TRUNCATE '._DB_PREFIX_.'layered_category');
3455
3456 if (!count($res)) // No filters templates defined, nothing else to do
3457 return true;
3458
3459 $sql_to_insert = 'INSERT INTO '._DB_PREFIX_.'layered_category (id_category, id_shop, id_value, type, position, filter_show_limit, filter_type) VALUES ';
3460 $values = false;
3461
3462 foreach ($res as $filter_template)
3463 {
3464 $data = Tools::unSerialize($filter_template['filters']);
3465 foreach ($data['shop_list'] as $id_shop)
3466 {
3467 if (!isset($categories[$id_shop]))
3468 $categories[$id_shop] = array();
3469
3470 foreach ($data['categories'] as $id_category)
3471 {
3472 $n = 0;
3473 if (!in_array($id_category, $categories[$id_shop])) // Last definition, erase preivious categories defined
3474 {
3475 $categories[$id_shop][] = $id_category;
3476
3477 foreach ($data as $key => $value)
3478 if (substr($key, 0, 17) == 'layered_selection')
3479 {
3480 $values = true;
3481 $type = $value['filter_type'];
3482 $limit = $value['filter_show_limit'];
3483 $n++;
3484
3485 if ($key == 'layered_selection_stock')
3486 $sql_to_insert .= '('.(int)$id_category.', '.(int)$id_shop.', NULL,\'quantity\','.(int)$n.', '.(int)$limit.', '.(int)$type.'),';
3487 else if ($key == 'layered_selection_subcategories')
3488 $sql_to_insert .= '('.(int)$id_category.', '.(int)$id_shop.', NULL,\'category\','.(int)$n.', '.(int)$limit.', '.(int)$type.'),';
3489 else if ($key == 'layered_selection_condition')
3490 $sql_to_insert .= '('.(int)$id_category.', '.(int)$id_shop.', NULL,\'condition\','.(int)$n.', '.(int)$limit.', '.(int)$type.'),';
3491 else if ($key == 'layered_selection_weight_slider')
3492 $sql_to_insert .= '('.(int)$id_category.', '.(int)$id_shop.', NULL,\'weight\','.(int)$n.', '.(int)$limit.', '.(int)$type.'),';
3493 else if ($key == 'layered_selection_price_slider')
3494 $sql_to_insert .= '('.(int)$id_category.', '.(int)$id_shop.', NULL,\'price\','.(int)$n.', '.(int)$limit.', '.(int)$type.'),';
3495 else if ($key == 'layered_selection_manufacturer')
3496 $sql_to_insert .= '('.(int)$id_category.', '.(int)$id_shop.', NULL,\'manufacturer\','.(int)$n.', '.(int)$limit.', '.(int)$type.'),';
3497 else if (substr($key, 0, 21) == 'layered_selection_ag_')
3498 $sql_to_insert .= '('.(int)$id_category.', '.(int)$id_shop.', '.(int)str_replace('layered_selection_ag_', '', $key).',
3499 \'id_attribute_group\','.(int)$n.', '.(int)$limit.', '.(int)$type.'),';
3500 else if (substr($key, 0, 23) == 'layered_selection_feat_')
3501 $sql_to_insert .= '('.(int)$id_category.', '.(int)$id_shop.', '.(int)str_replace('layered_selection_feat_', '', $key).',
3502 \'id_feature\','.(int)$n.', '.(int)$limit.', '.(int)$type.'),';
3503 }
3504 }
3505 }
3506 }
3507 }
3508 if ($values)
3509 Db::getInstance()->execute(rtrim($sql_to_insert, ','));
3510 }
3511
3512 protected function getAnchor()
3513 {
3514 static $anchor = null;
3515 if ($anchor === null)
3516 if (!$anchor = Configuration::get('PS_ATTRIBUTE_ANCHOR_SEPARATOR'))
3517 $anchor = '-';
3518 return $anchor;
3519 }
3520
3521 protected function showPriceFilter()
3522 {
3523 return Group::getCurrent()->show_prices;
3524 }
3525
3526 protected function filterVar($value)
3527 {
3528 if (version_compare(_PS_VERSION_, '1.6.0.7', '>=') === true)
3529 return Tools::purifyHTML($value);
3530 else
3531 return filter_var($value, FILTER_SANITIZE_STRING);
3532 }
3533 private function switchProductImages($selected_filters, &$products) {
3534 foreach ($products as &$product) {
3535 foreach($selected_filters['id_attribute_group'] as $key => $value) {
3536 $attributeImageId = $this->getProductImage($product['id_product'], $key);
3537 if((int)($attributeImageId) > 0) {
3538
3539 $product['id_image'] = $product['id_product'].'-'.$attributeImageId;
3540 }
3541 }
3542 }
3543 }
3544 private function getProductImage($id_product, $id_attribute) {
3545
3546 $query = 'SELECT `id_image` FROM `p_product_attribute_combination`
3547 INNER JOIN `p_product_attribute` ON `p_product_attribute`.`id_product_attribute` = `p_product_attribute_combination`.`id_product_attribute`
3548 INNER JOIN `p_product_attribute_image` ON `p_product_attribute_image`.`id_product_attribute` = `p_product_attribute`.`id_product_attribute`
3549 WHERE `id_attribute` = \''.$id_attribute.'\' AND `id_product` = \''.$id_product.'\'';
3550
3551 return DB::getInstance()->getValue($query);
3552 }
3553}