PageRenderTime 294ms CodeModel.GetById 24ms RepoModel.GetById 2ms app.codeStats 1ms

/modules/blocklayered/blocklayered.php

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