PageRenderTime 45ms CodeModel.GetById 33ms RepoModel.GetById 0ms app.codeStats 0ms

/modules/blocklayered/blocklayered.php

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