Advertisement
Guest User

Untitled

a guest
Aug 3rd, 2018
150
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 174.34 KB | None | 0 0
  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.  
  27. if (!defined('_PS_VERSION_'))
  28. exit;
  29.  
  30. class 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('"', '&quot;', $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. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement