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