PageRenderTime 49ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 1ms

/modules/blocklayered/blocklayered.php

https://bitbucket.org/enurkov/prestashop
PHP | 4228 lines | 3761 code | 362 blank | 105 comment | 599 complexity | a81cd61a87fe8d3a823ae29d79a93c58 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

  1. <?php
  2. /*
  3. * 2007-2012 PrestaShop
  4. *
  5. * NOTICE OF LICENSE
  6. *
  7. * This source file is subject to the Academic Free License (AFL 3.0)
  8. * that is bundled with this package in the file LICENSE.txt.
  9. * It is also available through the world-wide-web at this URL:
  10. * http://opensource.org/licenses/afl-3.0.php
  11. * If you did not receive a copy of the license and are unable to
  12. * obtain it through the world-wide-web, please send an email
  13. * to license@prestashop.com so we can send you a copy immediately.
  14. *
  15. * DISCLAIMER
  16. *
  17. * Do not edit or add to this file if you wish to upgrade PrestaShop to newer
  18. * versions in the future. If you wish to customize PrestaShop for your
  19. * needs please refer to http://www.prestashop.com for more information.
  20. *
  21. * @author PrestaShop SA <contact@prestashop.com>
  22. * @copyright 2007-2012 PrestaShop SA
  23. * @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0)
  24. * International Registred Trademark & Property of PrestaShop SA
  25. */
  26. if (!defined('_PS_VERSION_'))
  27. exit;
  28. class BlockLayered extends Module
  29. {
  30. private $products;
  31. private $nbr_products;
  32. private $page = 1;
  33. public function __construct()
  34. {
  35. $this->name = 'blocklayered';
  36. $this->tab = 'front_office_features';
  37. $this->version = '1.8.9';
  38. $this->author = 'PrestaShop';
  39. $this->need_instance = 0;
  40. parent::__construct();
  41. $this->displayName = $this->l('Layered navigation block');
  42. $this->description = $this->l('Displays a block with layered navigation filters.');
  43. if ((int)Tools::getValue('p'))
  44. $this->page = (int)Tools::getValue('p');
  45. }
  46. public function install()
  47. {
  48. if (parent::install() && $this->registerHook('leftColumn') && $this->registerHook('header') && $this->registerHook('footer')
  49. && $this->registerHook('categoryAddition') && $this->registerHook('categoryUpdate') && $this->registerHook('attributeGroupForm')
  50. && $this->registerHook('afterSaveAttributeGroup') && $this->registerHook('afterDeleteAttributeGroup') && $this->registerHook('featureForm')
  51. && $this->registerHook('afterDeleteFeature') && $this->registerHook('afterSaveFeature') && $this->registerHook('categoryDeletion')
  52. && $this->registerHook('afterSaveProduct') && $this->registerHook('productListAssign') && $this->registerHook('postProcessAttributeGroup')
  53. && $this->registerHook('postProcessFeature') && $this->registerHook('featureValueForm') && $this->registerHook('postProcessFeatureValue')
  54. && $this->registerHook('afterDeleteFeatureValue') && $this->registerHook('afterSaveFeatureValue') && $this->registerHook('attributeForm')
  55. && $this->registerHook('postProcessAttribute') && $this->registerHook('afterDeleteAttribute') && $this->registerHook('afterSaveAttribute'))
  56. {
  57. Configuration::updateValue('PS_LAYERED_HIDE_0_VALUES', 1);
  58. Configuration::updateValue('PS_LAYERED_SHOW_QTIES', 1);
  59. Configuration::updateValue('PS_LAYERED_FULL_TREE', 1);
  60. Configuration::updateValue('PS_LAYERED_FILTER_PRICE_USETAX', 1);
  61. Configuration::updateValue('PS_LAYERED_FILTER_CATEGORY_DEPTH', 1);
  62. Configuration::updateValue('PS_LAYERED_FILTER_INDEX_QTY', 0);
  63. Configuration::updateValue('PS_LAYERED_FILTER_INDEX_CDT', 0);
  64. Configuration::updateValue('PS_LAYERED_FILTER_INDEX_MNF', 0);
  65. Configuration::updateValue('PS_LAYERED_FILTER_INDEX_CAT', 0);
  66. $this->rebuildLayeredStructure();
  67. $products_count = Db::getInstance()->getValue('SELECT COUNT(*) FROM `'._DB_PREFIX_.'product`');
  68. if ($products_count < 20000) // Lock template filter creation if too many products
  69. $this->rebuildLayeredCache();
  70. self::installPriceIndexTable();
  71. $this->installFriendlyUrlTable();
  72. $this->installIndexableAttributeTable();
  73. $this->installProductAttributeTable();
  74. if ($products_count < 5000) // Lock indexation if too many products
  75. {
  76. $this->indexUrl();
  77. $this->indexAttribute();
  78. self::fullPricesIndexProcess();
  79. }
  80. return true;
  81. }
  82. else
  83. {
  84. // Installation failed (or hook registration) => uninstall the module
  85. $this->uninstall();
  86. return false;
  87. }
  88. }
  89. public function uninstall()
  90. {
  91. /* Delete all configurations */
  92. Configuration::deleteByName('PS_LAYERED_HIDE_0_VALUES');
  93. Configuration::deleteByName('PS_LAYERED_SHOW_QTIES');
  94. Configuration::deleteByName('PS_LAYERED_FULL_TREE');
  95. Configuration::deleteByName('PS_LAYERED_INDEXED');
  96. Configuration::deleteByName('PS_LAYERED_FILTER_PRICE_USETAX');
  97. Configuration::deleteByName('PS_LAYERED_FILTER_CATEGORY_DEPTH');
  98. Configuration::deleteByName('PS_LAYERED_FILTER_INDEX_QTY');
  99. Configuration::deleteByName('PS_LAYERED_FILTER_INDEX_CDT');
  100. Configuration::deleteByName('PS_LAYERED_FILTER_INDEX_MNF');
  101. Configuration::deleteByName('PS_LAYERED_FILTER_INDEX_CAT');
  102. Db::getInstance()->execute('DROP TABLE IF EXISTS '._DB_PREFIX_.'layered_price_index');
  103. Db::getInstance()->execute('DROP TABLE IF EXISTS '._DB_PREFIX_.'layered_friendly_url');
  104. Db::getInstance()->execute('DROP TABLE IF EXISTS '._DB_PREFIX_.'layered_indexable_attribute_group');
  105. Db::getInstance()->execute('DROP TABLE IF EXISTS '._DB_PREFIX_.'layered_indexable_feature');
  106. Db::getInstance()->execute('DROP TABLE IF EXISTS '._DB_PREFIX_.'layered_indexable_attribute_group_lang_value');
  107. Db::getInstance()->execute('DROP TABLE IF EXISTS '._DB_PREFIX_.'layered_indexable_feature_lang_value');
  108. Db::getInstance()->execute('DROP TABLE IF EXISTS '._DB_PREFIX_.'layered_category');
  109. Db::getInstance()->execute('DROP TABLE IF EXISTS '._DB_PREFIX_.'layered_filter');
  110. Db::getInstance()->Execute('DROP TABLE IF EXISTS '._DB_PREFIX_.'layered_filter_shop');
  111. Db::getInstance()->execute('DROP TABLE IF EXISTS '._DB_PREFIX_.'layered_product_attribute');
  112. return parent::uninstall();
  113. }
  114. private static function installPriceIndexTable()
  115. {
  116. Db::getInstance()->execute('DROP TABLE IF EXISTS `'._DB_PREFIX_.'layered_price_index`');
  117. Db::getInstance()->execute('
  118. CREATE TABLE `'._DB_PREFIX_.'layered_price_index` (
  119. `id_product` INT NOT NULL,
  120. `id_currency` INT NOT NULL,
  121. `id_shop` INT NOT NULL,
  122. `price_min` INT NOT NULL,
  123. `price_max` INT NOT NULL,
  124. PRIMARY KEY (`id_product`, `id_currency`, `id_shop`),
  125. INDEX `id_currency` (`id_currency`),
  126. INDEX `price_min` (`price_min`), INDEX `price_max` (`price_max`)
  127. ) ENGINE='._MYSQL_ENGINE_.' DEFAULT CHARSET=utf8;');
  128. }
  129. private function installFriendlyUrlTable()
  130. {
  131. Db::getInstance()->execute('DROP TABLE IF EXISTS `'._DB_PREFIX_.'layered_friendly_url`');
  132. Db::getInstance()->execute('
  133. CREATE TABLE `'._DB_PREFIX_.'layered_friendly_url` (
  134. `id_layered_friendly_url` INT NOT NULL AUTO_INCREMENT,
  135. `url_key` varchar(32) NOT NULL,
  136. `data` varchar(200) NOT NULL,
  137. `id_lang` INT NOT NULL,
  138. PRIMARY KEY (`id_layered_friendly_url`),
  139. INDEX `id_lang` (`id_lang`)
  140. ) ENGINE='._MYSQL_ENGINE_.' DEFAULT CHARSET=utf8;');
  141. Db::getInstance()->execute('CREATE INDEX `url_key` ON `'._DB_PREFIX_.'layered_friendly_url`(url_key(5))');
  142. }
  143. private function installIndexableAttributeTable()
  144. {
  145. // Attributes Groups
  146. Db::getInstance()->execute('DROP TABLE IF EXISTS `'._DB_PREFIX_.'layered_indexable_attribute_group`');
  147. Db::getInstance()->execute('
  148. CREATE TABLE `'._DB_PREFIX_.'layered_indexable_attribute_group` (
  149. `id_attribute_group` INT NOT NULL,
  150. `indexable` BOOL NOT NULL DEFAULT 0,
  151. PRIMARY KEY (`id_attribute_group`)
  152. ) ENGINE='._MYSQL_ENGINE_.' DEFAULT CHARSET=utf8;');
  153. Db::getInstance()->execute('
  154. INSERT INTO `'._DB_PREFIX_.'layered_indexable_attribute_group`
  155. SELECT id_attribute_group, 1 FROM `'._DB_PREFIX_.'attribute_group`');
  156. Db::getInstance()->execute('DROP TABLE IF EXISTS `'._DB_PREFIX_.'layered_indexable_attribute_group_lang_value`');
  157. Db::getInstance()->execute('
  158. CREATE TABLE `'._DB_PREFIX_.'layered_indexable_attribute_group_lang_value` (
  159. `id_attribute_group` INT NOT NULL,
  160. `id_lang` INT NOT NULL,
  161. `url_name` VARCHAR(20),
  162. `meta_title` VARCHAR(20),
  163. PRIMARY KEY (`id_attribute_group`, `id_lang`)
  164. ) ENGINE='._MYSQL_ENGINE_.' DEFAULT CHARSET=utf8;');
  165. // Attributes
  166. Db::getInstance()->execute('DROP TABLE IF EXISTS `'._DB_PREFIX_.'layered_indexable_attribute_lang_value`');
  167. Db::getInstance()->execute('
  168. CREATE TABLE `'._DB_PREFIX_.'layered_indexable_attribute_lang_value` (
  169. `id_attribute` INT NOT NULL,
  170. `id_lang` INT NOT NULL,
  171. `url_name` VARCHAR(20),
  172. `meta_title` VARCHAR(20),
  173. PRIMARY KEY (`id_attribute`, `id_lang`)
  174. ) ENGINE='._MYSQL_ENGINE_.' DEFAULT CHARSET=utf8;');
  175. // Features
  176. Db::getInstance()->execute('DROP TABLE IF EXISTS `'._DB_PREFIX_.'layered_indexable_feature`');
  177. Db::getInstance()->execute('
  178. CREATE TABLE `'._DB_PREFIX_.'layered_indexable_feature` (
  179. `id_feature` INT NOT NULL,
  180. `indexable` BOOL NOT NULL DEFAULT 0,
  181. PRIMARY KEY (`id_feature`)
  182. ) ENGINE='._MYSQL_ENGINE_.' DEFAULT CHARSET=utf8;');
  183. Db::getInstance()->execute('
  184. INSERT INTO `'._DB_PREFIX_.'layered_indexable_feature`
  185. SELECT id_feature, 1 FROM `'._DB_PREFIX_.'feature`');
  186. Db::getInstance()->execute('DROP TABLE IF EXISTS `'._DB_PREFIX_.'layered_indexable_feature_lang_value`');
  187. Db::getInstance()->execute('
  188. CREATE TABLE `'._DB_PREFIX_.'layered_indexable_feature_lang_value` (
  189. `id_feature` INT NOT NULL,
  190. `id_lang` INT NOT NULL,
  191. `url_name` VARCHAR(20) NOT NULL,
  192. `meta_title` VARCHAR(20),
  193. PRIMARY KEY (`id_feature`, `id_lang`)
  194. ) ENGINE='._MYSQL_ENGINE_.' DEFAULT CHARSET=utf8;');
  195. // Features values
  196. Db::getInstance()->execute('DROP TABLE IF EXISTS `'._DB_PREFIX_.'layered_indexable_feature_value_lang_value`');
  197. Db::getInstance()->execute('
  198. CREATE TABLE `'._DB_PREFIX_.'layered_indexable_feature_value_lang_value` (
  199. `id_feature_value` INT NOT NULL,
  200. `id_lang` INT NOT NULL,
  201. `url_name` VARCHAR(20),
  202. `meta_title` VARCHAR(20),
  203. PRIMARY KEY (`id_feature_value`, `id_lang`)
  204. ) ENGINE='._MYSQL_ENGINE_.' DEFAULT CHARSET=utf8;');
  205. }
  206. /**
  207. *
  208. * create table product attribute
  209. */
  210. public function installProductAttributeTable()
  211. {
  212. Db::getInstance()->execute('DROP TABLE IF EXISTS `'._DB_PREFIX_.'layered_product_attribute`');
  213. Db::getInstance()->execute('
  214. CREATE TABLE `'._DB_PREFIX_.'layered_product_attribute` (
  215. `id_attribute` int(10) unsigned NOT NULL,
  216. `id_product` int(10) unsigned NOT NULL,
  217. `id_attribute_group` int(10) unsigned NOT NULL DEFAULT "0",
  218. `id_shop` int(10) unsigned NOT NULL DEFAULT "1",
  219. KEY `id_attribute` (`id_attribute`)
  220. ) ENGINE='._MYSQL_ENGINE_.' DEFAULT CHARSET=utf8;');
  221. }
  222. /**
  223. *
  224. * Generate data product attribute
  225. */
  226. public function indexAttribute($id_product = null)
  227. {
  228. if (is_null($id_product))
  229. Db::getInstance()->execute('TRUNCATE '._DB_PREFIX_.'layered_product_attribute');
  230. else
  231. Db::getInstance()->execute('DELETE FROM '._DB_PREFIX_.'layered_product_attribute WHERE id_product = '.(int)$id_product);
  232. Db::getInstance()->execute('INSERT INTO `'._DB_PREFIX_.'layered_product_attribute` (`id_attribute`, `id_product`, `id_attribute_group`, `id_shop`)
  233. SELECT pac.id_attribute, pa.id_product, ag.id_attribute_group, '.(version_compare(_PS_VERSION_,'1.5','>') ? 'product_attribute_shop.`id_shop`' : '1').'
  234. FROM '._DB_PREFIX_.'product_attribute pa
  235. '.(version_compare(_PS_VERSION_,'1.5','>') ? Shop::addSqlAssociation('product_attribute', 'pa') : '').'
  236. INNER JOIN '._DB_PREFIX_.'product_attribute_combination pac ON pac.id_product_attribute = pa.id_product_attribute
  237. INNER JOIN '._DB_PREFIX_.'attribute a ON (a.id_attribute = pac.id_attribute)
  238. INNER JOIN '._DB_PREFIX_.'attribute_group ag ON ag.id_attribute_group = a.id_attribute_group
  239. '.(is_null($id_product) ? '' : 'AND pa.id_product = '.(int)$id_product).'
  240. GROUP BY a.id_attribute, pa.id_product '.(version_compare(_PS_VERSION_,'1.5','>') ? ', product_attribute_shop.`id_shop`' : ''));
  241. return 1;
  242. }
  243. /*
  244. * Url indexation
  245. */
  246. public function indexUrl($ajax = false, $truncate = true)
  247. {
  248. if ($truncate)
  249. Db::getInstance()->execute('TRUNCATE '._DB_PREFIX_.'layered_friendly_url');
  250. $attribute_values_by_lang = array();
  251. $filters = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS('SELECT lc.*, id_lang, name, link_rewrite, cl.id_category
  252. FROM '._DB_PREFIX_.'layered_category lc
  253. INNER JOIN '._DB_PREFIX_.'category_lang cl ON (cl.id_category = lc.id_category AND lc.id_category <> 1 )
  254. GROUP BY type, id_value, id_lang');
  255. if (!$filters)
  256. return;
  257. foreach ($filters as $filter)
  258. switch ($filter['type'])
  259. {
  260. case 'id_attribute_group':
  261. $attributes = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS('
  262. SELECT agl.public_name name, a.id_attribute_group id_name, al.name value, a.id_attribute id_value, al.id_lang,
  263. liagl.url_name name_url_name, lial.url_name value_url_name
  264. FROM '._DB_PREFIX_.'attribute_group ag
  265. INNER JOIN '._DB_PREFIX_.'attribute_group_lang agl ON (agl.id_attribute_group = ag.id_attribute_group)
  266. INNER JOIN '._DB_PREFIX_.'attribute a ON (a.id_attribute_group = ag.id_attribute_group)
  267. INNER JOIN '._DB_PREFIX_.'attribute_lang al ON (al.id_attribute = a.id_attribute)
  268. LEFT JOIN '._DB_PREFIX_.'layered_indexable_attribute_group liag ON (liag.id_attribute_group = a.id_attribute_group)
  269. LEFT JOIN '._DB_PREFIX_.'layered_indexable_attribute_group_lang_value liagl
  270. ON (liagl.id_attribute_group = ag.id_attribute_group AND liagl.id_lang = '.(int)$filter['id_lang'].')
  271. LEFT JOIN '._DB_PREFIX_.'layered_indexable_attribute_lang_value lial
  272. ON (lial.id_attribute = a.id_attribute AND lial.id_lang = '.(int)$filter['id_lang'].')
  273. WHERE a.id_attribute_group = '.(int)$filter['id_value'].' AND agl.id_lang = al.id_lang AND agl.id_lang = '.(int)$filter['id_lang']);
  274. foreach ($attributes as $attribute)
  275. {
  276. if (!isset($attribute_values_by_lang[$attribute['id_lang']]))
  277. $attribute_values_by_lang[$attribute['id_lang']] = array();
  278. if (!isset($attribute_values_by_lang[$attribute['id_lang']]['c'.$attribute['id_name']]))
  279. $attribute_values_by_lang[$attribute['id_lang']]['c'.$attribute['id_name']] = array();
  280. $attribute_values_by_lang[$attribute['id_lang']]['c'.$attribute['id_name']][] = array(
  281. 'name' => (!empty($attribute['name_url_name']) ? $attribute['name_url_name'] : $attribute['name']),
  282. 'id_name' => 'c'.$attribute['id_name'],
  283. 'value' => (!empty($attribute['value_url_name']) ? $attribute['value_url_name'] : $attribute['value']),
  284. 'id_value' => $attribute['id_name'].'_'.$attribute['id_value'],
  285. 'id_id_value' => $attribute['id_value'],
  286. 'category_name' => $filter['link_rewrite'],
  287. 'type' => $filter['type']);
  288. }
  289. break;
  290. case 'id_feature':
  291. $features = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS('
  292. SELECT fl.name name, fl.id_feature id_name, fvl.id_feature_value id_value, fvl.value value, fl.id_lang, fl.id_lang,
  293. lifl.url_name name_url_name, lifvl.url_name value_url_name
  294. FROM '._DB_PREFIX_.'feature_lang fl
  295. LEFT JOIN '._DB_PREFIX_.'layered_indexable_feature lif ON (lif.id_feature = fl.id_feature)
  296. INNER JOIN '._DB_PREFIX_.'feature_value fv ON (fv.id_feature = fl.id_feature)
  297. INNER JOIN '._DB_PREFIX_.'feature_value_lang fvl ON (fvl.id_feature_value = fv.id_feature_value)
  298. LEFT JOIN '._DB_PREFIX_.'layered_indexable_feature_lang_value lifl
  299. ON (lifl.id_feature = fl.id_feature AND lifl.id_lang = '.(int)$filter['id_lang'].')
  300. LEFT JOIN '._DB_PREFIX_.'layered_indexable_feature_value_lang_value lifvl
  301. ON (lifvl.id_feature_value = fvl.id_feature_value AND lifvl.id_lang = '.(int)$filter['id_lang'].')
  302. WHERE fl.id_feature = '.(int)$filter['id_value'].' AND fvl.id_lang = fl.id_lang AND fvl.id_lang = '.(int)$filter['id_lang']);
  303. foreach ($features as $feature)
  304. {
  305. if (!isset($attribute_values_by_lang[$feature['id_lang']]))
  306. $attribute_values_by_lang[$feature['id_lang']] = array();
  307. if (!isset($attribute_values_by_lang[$feature['id_lang']]['f'.$feature['id_name']]))
  308. $attribute_values_by_lang[$feature['id_lang']]['f'.$feature['id_name']] = array();
  309. $attribute_values_by_lang[$feature['id_lang']]['f'.$feature['id_name']][] = array(
  310. 'name' => (!empty($feature['name_url_name']) ? $feature['name_url_name'] : $feature['name']),
  311. 'id_name' => 'f'.$feature['id_name'],
  312. 'value' => (!empty($feature['value_url_name']) ? $feature['value_url_name'] : $feature['value']),
  313. 'id_value' => $feature['id_name'].'_'.$feature['id_value'],
  314. 'id_id_value' => $feature['id_value'],
  315. 'category_name' => $filter['link_rewrite'],
  316. 'type' => $filter['type']);
  317. }
  318. break;
  319. case 'category':
  320. $categories = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS('
  321. SELECT cl.name, cl.id_lang, c.id_category
  322. FROM '._DB_PREFIX_.'category c
  323. INNER JOIN '._DB_PREFIX_.'category_lang cl ON (c.id_category = cl.id_category)
  324. WHERE cl.id_lang = '.(int)$filter['id_lang']);
  325. foreach ($categories as $category)
  326. {
  327. if (!isset($attribute_values_by_lang[$category['id_lang']]))
  328. $attribute_values_by_lang[$category['id_lang']] = array();
  329. if (!isset($attribute_values_by_lang[$category['id_lang']]['category']))
  330. $attribute_values_by_lang[$category['id_lang']]['category'] = array();
  331. $attribute_values_by_lang[$category['id_lang']]['category'][] = array('name' => $this->translateWord('Categories', $category['id_lang']),
  332. 'id_name' => null, 'value' => $category['name'], 'id_value' => $category['id_category'],
  333. 'category_name' => $filter['link_rewrite'], 'type' => $filter['type']);
  334. }
  335. break;
  336. case 'manufacturer':
  337. $manufacturers = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS('
  338. SELECT m.name as name,l.id_lang as id_lang, id_manufacturer
  339. FROM '._DB_PREFIX_.'manufacturer m , '._DB_PREFIX_.'lang l
  340. WHERE l.id_lang = '.(int)$filter['id_lang'].' ');
  341. foreach ($manufacturers as $manufacturer)
  342. {
  343. if (!isset($attribute_values_by_lang[$manufacturer['id_lang']]))
  344. $attribute_values_by_lang[$manufacturer['id_lang']] = array();
  345. if (!isset($attribute_values_by_lang[$manufacturer['id_lang']]['manufacturer']))
  346. $attribute_values_by_lang[$manufacturer['id_lang']]['manufacturer'] = array();
  347. $attribute_values_by_lang[$manufacturer['id_lang']]['manufacturer'][] = array('name' => $this->translateWord('Manufacturer', $manufacturer['id_lang']),
  348. 'id_name' => null, 'value' => $manufacturer['name'], 'id_value' => $manufacturer['id_manufacturer'],
  349. 'category_name' => $filter['link_rewrite'], 'type' => $filter['type']);
  350. }
  351. break;
  352. case 'quantity':
  353. $avaibility_list = array(
  354. $this->translateWord('Not available', (int)$filter['id_lang']),
  355. $this->translateWord('In stock', (int)$filter['id_lang'])
  356. );
  357. foreach ($avaibility_list as $key => $quantity)
  358. $attribute_values_by_lang[$filter['id_lang']]['quantity'][] = array('name' => $this->translateWord('Availability', (int)$filter['id_lang']),
  359. 'id_name' => null, 'value' => $quantity, 'id_value' => $key, 'id_id_value' => 0,
  360. 'category_name' => $filter['link_rewrite'], 'type' => $filter['type']);
  361. break;
  362. case 'condition':
  363. $condition_list = array(
  364. 'new' => $this->translateWord('New', (int)$filter['id_lang']),
  365. 'used' => $this->translateWord('Used', (int)$filter['id_lang']),
  366. 'refurbished' => $this->translateWord('Refurbished', (int)$filter['id_lang'])
  367. );
  368. foreach ($condition_list as $key => $condition)
  369. $attribute_values_by_lang[$filter['id_lang']]['condition'][] = array('name' => $this->translateWord('Condition', (int)$filter['id_lang']),
  370. 'id_name' => null, 'value' => $condition, 'id_value' => $key,
  371. 'category_name' => $filter['link_rewrite'], 'type' => $filter['type']);
  372. break;
  373. }
  374. // Foreach langs
  375. foreach ($attribute_values_by_lang as $id_lang => $attribute_values)
  376. {
  377. // Foreach attributes generate a couple "/<attribute_name>_<atttribute_value>". For example: color_blue
  378. foreach ($attribute_values as $attribute)
  379. foreach ($attribute as $param)
  380. {
  381. $selected_filters = array();
  382. $link = '/'.str_replace('-', '_', Tools::link_rewrite($param['name'])).'-'.str_replace('-', '_', Tools::link_rewrite($param['value']));
  383. $selected_filters[$param['type']] = array();
  384. if (!isset($param['id_id_value']))
  385. $param['id_id_value'] = $param['id_value'];
  386. $selected_filters[$param['type']][$param['id_id_value']] = $param['id_value'];
  387. $url_key = md5($link);
  388. $id_layered_friendly_url = Db::getInstance()->getValue('SELECT id_layered_friendly_url
  389. FROM `'._DB_PREFIX_.'layered_friendly_url` WHERE `id_lang` = '.$id_lang.' AND `url_key` = \''.$url_key.'\'');
  390. if ($id_layered_friendly_url == false)
  391. {
  392. Db::getInstance()->AutoExecute(_DB_PREFIX_.'layered_friendly_url', array('url_key' => $url_key, 'data' => serialize($selected_filters), 'id_lang' => $id_lang), 'INSERT');
  393. $id_layered_friendly_url = Db::getInstance()->Insert_ID();
  394. }
  395. }
  396. }
  397. if ($ajax)
  398. return '{"result": 1}';
  399. else
  400. return 1;
  401. }
  402. public function translateWord($string, $id_lang )
  403. {
  404. static $_MODULES = array();
  405. global $_MODULE;
  406. $file = _PS_MODULE_DIR_.$this->name.'/'.Language::getIsoById($id_lang).'.php';
  407. if (!array_key_exists($id_lang, $_MODULES))
  408. {
  409. if (!file_exists($file))
  410. return $string;
  411. include($file);
  412. $_MODULES[$id_lang] = $_MODULE;
  413. }
  414. $string = str_replace('\'', '\\\'', $string);
  415. // set array key to lowercase for 1.3 compatibility
  416. $_MODULES[$id_lang] = array_change_key_case($_MODULES[$id_lang]);
  417. $current_key = '<{'.strtolower( $this->name).'}'.strtolower(_THEME_NAME_).'>'.strtolower($this->name).'_'.md5($string);
  418. $default_key = '<{'.strtolower( $this->name).'}prestashop>'.strtolower($this->name).'_'.md5($string);
  419. if (isset($_MODULES[$id_lang][$current_key]))
  420. $ret = stripslashes($_MODULES[$id_lang][$current_key]);
  421. else if (isset($_MODULES[$id_lang][Tools::strtolower($current_key)]))
  422. $ret = stripslashes($_MODULES[$id_lang][Tools::strtolower($current_key)]);
  423. else if (isset($_MODULES[$id_lang][$default_key]))
  424. $ret = stripslashes($_MODULES[$id_lang][$default_key]);
  425. else if (isset($_MODULES[$id_lang][Tools::strtolower($default_key)]))
  426. $ret = stripslashes($_MODULES[$id_lang][Tools::strtolower($default_key)]);
  427. else
  428. $ret = stripslashes($string);
  429. return str_replace('"', '&quot;', $ret);
  430. }
  431. public function hookProductListAssign($params)
  432. {
  433. global $smarty;
  434. if (version_compare(_PS_VERSION_,'1.5','<') && !Configuration::get('PS_LAYERED_INDEXED')
  435. || version_compare(_PS_VERSION_,'1.5','>') && !Configuration::getGlobalValue('PS_LAYERED_INDEXED'))
  436. return;
  437. // Inform the hook was executed
  438. $params['hookExecuted'] = true;
  439. // List of product to overrride categoryController
  440. $params['catProducts'] = array();
  441. $selected_filters = $this->getSelectedFilters();
  442. $filter_block = $this->getFilterBlock($selected_filters);
  443. $title = '';
  444. if (is_array($filter_block['title_values']))
  445. foreach ($filter_block['title_values'] as $key => $val)
  446. $title .= ' – '.$key.' '.implode('/', $val);
  447. $smarty->assign('categoryNameComplement', $title);
  448. $this->getProducts($selected_filters, $params['catProducts'], $params['nbProducts'], $p, $n, $pages_nb, $start, $stop, $range);
  449. // Need a nofollow on the pagination links?
  450. $smarty->assign('no_follow', $filter_block['no_follow']);
  451. }
  452. public function hookAfterSaveProduct($params)
  453. {
  454. if (!$params['id_product'])
  455. return;
  456. self::indexProductPrices((int)$params['id_product']);
  457. $this->indexAttribute((int)$params['id_product']);
  458. }
  459. public function hookAfterSaveFeature($params)
  460. {
  461. if (!$params['id_feature'] || Tools::getValue('layered_indexable') === false)
  462. return;
  463. Db::getInstance()->execute('DELETE FROM '._DB_PREFIX_.'layered_indexable_feature WHERE id_feature = '.(int)$params['id_feature']);
  464. Db::getInstance()->execute('INSERT INTO '._DB_PREFIX_.'layered_indexable_feature VALUES ('.(int)$params['id_feature'].', '.(int)Tools::getValue('layered_indexable').')');
  465. Db::getInstance()->execute('DELETE FROM '._DB_PREFIX_.'layered_indexable_feature_lang_value WHERE id_feature = '.(int)$params['id_feature']); // don't care about the id_lang
  466. foreach (Language::getLanguages(false) as $language)
  467. {
  468. // Data are validated by method "hookPostProcessFeature"
  469. $id_lang = (int)$language['id_lang'];
  470. Db::getInstance()->execute('INSERT INTO '._DB_PREFIX_.'layered_indexable_feature_lang_value
  471. VALUES ('.(int)$params['id_feature'].', '.$id_lang.', \''.pSQL(Tools::link_rewrite(Tools::getValue('url_name_'.$id_lang))).'\',
  472. \''.pSQL(Tools::safeOutput(Tools::getValue('meta_title_'.$id_lang), true)).'\')');
  473. }
  474. }
  475. public function hookAfterSaveFeatureValue($params)
  476. {
  477. if (!$params['id_feature_value'])
  478. return;
  479. Db::getInstance()->execute('DELETE FROM '._DB_PREFIX_.'layered_indexable_feature_value_lang_value WHERE id_feature_value = '.(int)$params['id_feature_value']); // don't care about the id_lang
  480. foreach (Language::getLanguages(false) as $language)
  481. {
  482. // Data are validated by method "hookPostProcessFeatureValue"
  483. $id_lang = (int)$language['id_lang'];
  484. Db::getInstance()->execute('INSERT INTO '._DB_PREFIX_.'layered_indexable_feature_value_lang_value
  485. VALUES ('.(int)$params['id_feature_value'].', '.$id_lang.', \''.pSQL(Tools::link_rewrite(Tools::getValue('url_name_'.$id_lang))).'\',
  486. \''.pSQL(Tools::safeOutput(Tools::getValue('meta_title_'.$id_lang), true)).'\')');
  487. }
  488. }
  489. public function hookAfterDeleteFeatureValue($params)
  490. {
  491. if (!$params['id_feature_value'])
  492. return;
  493. Db::getInstance()->execute('DELETE FROM '._DB_PREFIX_.'layered_indexable_feature_value_lang_value WHERE id_feature_value = '.(int)$params['id_feature_value']);
  494. }
  495. public function hookPostProcessFeatureValue($params)
  496. {
  497. $this->hookPostProcessAttributeGroup($params);
  498. }
  499. public function hookFeatureValueForm($params)
  500. {
  501. $languages = Language::getLanguages(false);
  502. $default_form_language = (int)(Configuration::get('PS_LANG_DEFAULT'));
  503. $lang_value = array();
  504. if (version_compare(_PS_VERSION_,'1.5','>'))
  505. $return = '
  506. <script type="text/javascript">
  507. flag_fields = \'\';
  508. </script>';
  509. else
  510. $return = '';
  511. $result = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS(
  512. 'SELECT url_name, meta_title, id_lang FROM '._DB_PREFIX_.'layered_indexable_feature_value_lang_value
  513. WHERE id_feature_value = '.(int)$params['id_feature_value']);
  514. if ($result)
  515. foreach ($result as $data)
  516. $lang_value[$data['id_lang']] = array('url_name' => $data['url_name'], 'meta_title' => $data['meta_title']);
  517. $return .= '<div class="clear"></div>
  518. <label>'.$this->l('URL:').'</label>
  519. <div class="margin-form">
  520. <script type="text/javascript">
  521. flag_fields += \'¤url_name¤meta_title\';
  522. </script>
  523. <div class="translatable">';
  524. foreach ($languages as $language)
  525. $return .= '
  526. <div class="lang_'.$language['id_lang'].'" id="url_name_'.$language['id_lang'].'" style="display: '.($language['id_lang'] == $default_form_language ? 'block' : 'none').'; float: left;">
  527. <input size="33" type="text" name="url_name_'.$language['id_lang'].'" value="'.Tools::safeOutput(@$lang_value[$language['id_lang']]['url_name'], true).'" />
  528. <span class="hint" name="help_box">'.$this->l('Invalid characters:').' <>;=#{}_<span class="hint-pointer">&nbsp;</span></span>
  529. <p style="clear: both">'.$this->l('Specific URL format in block layered generation').'</p>
  530. </div>';
  531. if (version_compare(_PS_VERSION_,'1.5','<'))
  532. $return .= $this->displayFlags($languages, $default_form_language, 'flag_fields', 'url_name', true, true);
  533. $return .= '
  534. </div>
  535. <div class="clear"></div>
  536. </div>
  537. <label>'.$this->l('Meta title:').' </label>
  538. <div class="margin-form">
  539. <div class="translatable">';
  540. foreach ($languages as $language)
  541. $return .= '
  542. <div class="lang_'.$language['id_lang'].'" id="meta_title_'.$language['id_lang'].'" style="display: '.($language['id_lang'] == $default_form_language ? 'block' : 'none').'; float: left;">
  543. <input size="33" type="text" name="meta_title_'.$language['id_lang'].'" value="'.Tools::safeOutput(@$lang_value[$language['id_lang']]['meta_title'], true).'" />
  544. <p style="clear: both">'.$this->l('Specific format for meta title').'</p>
  545. </div>';
  546. if (version_compare(_PS_VERSION_,'1.5','<'))
  547. $return .= $this->displayFlags($languages, $default_form_language, 'flag_fields', 'meta_title', true, true);
  548. $return .= '
  549. </div>
  550. <div class="clear"></div>
  551. </div>';
  552. return $return;
  553. }
  554. public function hookAfterSaveAttribute($params)
  555. {
  556. if (!$params['id_attribute'])
  557. return;
  558. Db::getInstance()->execute('DELETE FROM '._DB_PREFIX_.'layered_indexable_attribute_lang_value WHERE id_attribute = '.(int)$params['id_attribute']); // don't care about the id_lang
  559. foreach (Language::getLanguages(false) as $language)
  560. {
  561. // Data are validated by method "hookPostProcessAttribute"
  562. $id_lang = (int)$language['id_lang'];
  563. Db::getInstance()->execute('INSERT INTO '._DB_PREFIX_.'layered_indexable_attribute_lang_value
  564. VALUES ('.(int)$params['id_attribute'].', '.$id_lang.', \''.pSQL(Tools::link_rewrite(Tools::getValue('url_name_'.$id_lang))).'\',
  565. \''.pSQL(Tools::safeOutput(Tools::getValue('meta_title_'.$id_lang), true)).'\')');
  566. }
  567. }
  568. public function hookAfterDeleteAttribute($params)
  569. {
  570. if (!$params['id_attribute'])
  571. return;
  572. Db::getInstance()->execute('DELETE FROM '._DB_PREFIX_.'layered_indexable_attribute_lang_value WHERE id_attribute = '.(int)$params['id_attribute']);
  573. }
  574. public function hookPostProcessAttribute($params)
  575. {
  576. $this->hookPostProcessAttributeGroup($params);
  577. }
  578. public function hookAttributeForm($params)
  579. {
  580. $languages = Language::getLanguages(false);
  581. $default_form_language = (int)(Configuration::get('PS_LANG_DEFAULT'));
  582. $lang_value = array();
  583. if (version_compare(_PS_VERSION_,'1.5','>'))
  584. $return = '
  585. <script type="text/javascript">
  586. flag_fields = \'\';
  587. </script>';
  588. else
  589. $return = '';
  590. $result = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS(
  591. 'SELECT url_name, meta_title, id_lang FROM '._DB_PREFIX_.'layered_indexable_attribute_lang_value
  592. WHERE id_attribute = '.(int)$params['id_attribute']);
  593. if ($result)
  594. foreach ($result as $data)
  595. $lang_value[$data['id_lang']] = array('url_name' => $data['url_name'], 'meta_title' => $data['meta_title']);
  596. $return .= '<div class="clear"></div>
  597. <label>'.$this->l('URL:').'</label>
  598. <div class="margin-form">
  599. <script type="text/javascript">
  600. flag_fields += \'¤url_name¤meta_title\';
  601. </script>
  602. <div class="translatable">';
  603. foreach ($languages as $language)
  604. $return .= '
  605. <div class="lang_'.$language['id_lang'].'" id="url_name_'.$language['id_lang'].'" style="display: '.($language['id_lang'] == $default_form_language ? 'block' : 'none').'; float: left;">
  606. <input size="33" type="text" name="url_name_'.$language['id_lang'].'" value="'.Tools::safeOutput(@$lang_value[$language['id_lang']]['url_name'], true).'" />
  607. <span class="hint" name="help_box">'.$this->l('Invalid characters:').' <>;=#{}_<span class="hint-pointer">&nbsp;</span></span>
  608. <p style="clear: both">'.$this->l('Specific URL format in block layered generation').'</p>
  609. </div>';
  610. if (version_compare(_PS_VERSION_,'1.5','<'))
  611. $return .= $this->displayFlags($languages, $default_form_language, 'flag_fields', 'url_name', true, true);
  612. $return .= '
  613. </div>
  614. <div class="clear"></div>
  615. </div>
  616. <label>'.$this->l('Meta title:').' </label>
  617. <div class="margin-form">
  618. <div class="translatable">';
  619. foreach ($languages as $language)
  620. $return .= '
  621. <div class="lang_'.$language['id_lang'].'" id="meta_title_'.$language['id_lang'].'" style="display: '.($language['id_lang'] == $default_form_language ? 'block' : 'none').'; float: left;">
  622. <input size="33" type="text" name="meta_title_'.$language['id_lang'].'" value="'.Tools::safeOutput(@$lang_value[$language['id_lang']]['meta_title'], true).'" />
  623. <p style="clear: both">'.$this->l('Specific format for meta title').'</p>
  624. </div>';
  625. if (version_compare(_PS_VERSION_,'1.5','<'))
  626. $return .= $this->displayFlags($languages, $default_form_language, 'flag_fields', 'meta_title', true, true);
  627. $return .= '
  628. </div>
  629. <div class="clear"></div>
  630. </div>';
  631. return $return;
  632. }
  633. public function hookPostProcessFeature($params)
  634. {
  635. $this->hookPostProcessAttributeGroup($params);
  636. }
  637. public function hookAfterDeleteFeature($params)
  638. {
  639. if (!$params['id_feature'])
  640. return;
  641. Db::getInstance()->execute('DELETE FROM '._DB_PREFIX_.'layered_indexable_feature WHERE id_feature = '.(int)$params['id_feature']);
  642. }
  643. public function hookAfterSaveAttributeGroup($params)
  644. {
  645. if (!$params['id_attribute_group'] || Tools::getValue('layered_indexable') === false)
  646. return;
  647. Db::getInstance()->execute('DELETE FROM '._DB_PREFIX_.'layered_indexable_attribute_group WHERE id_attribute_group = '.(int)$params['id_attribute_group']);
  648. Db::getInstance()->execute('INSERT INTO '._DB_PREFIX_.'layered_indexable_attribute_group VALUES ('.(int)$params['id_attribute_group'].', '.(int)Tools::getValue('layered_indexable').')');
  649. Db::getInstance()->execute('DELETE FROM '._DB_PREFIX_.'layered_indexable_attribute_group_lang_value WHERE id_attribute_group = '.(int)$params['id_attribute_group']); // don't care about the id_lang
  650. foreach (Language::getLanguages(false) as $language)
  651. {
  652. // Data are validated by method "hookPostProcessAttributeGroup"
  653. $id_lang = (int)$language['id_lang'];
  654. Db::getInstance()->execute('INSERT INTO '._DB_PREFIX_.'layered_indexable_attribute_group_lang_value
  655. VALUES ('.(int)$params['id_attribute_group'].', '.$id_lang.', \''.pSQL(Tools::link_rewrite(Tools::getValue('url_name_'.$id_lang))).'\',
  656. \''.pSQL(Tools::safeOutput(Tools::getValue('meta_title_'.$id_lang), true)).'\')');
  657. }
  658. }
  659. public function hookPostProcessAttributeGroup($params)
  660. {
  661. // Limit to one call
  662. static $once = false;
  663. if ($once)
  664. return;
  665. $once = true;
  666. $errors = array();
  667. foreach (Language::getLanguages(false) as $language)
  668. {
  669. $id_lang = $language['id_lang'];
  670. if (Tools::getValue('url_name_'.$id_lang))
  671. if (Tools::link_rewrite(Tools::getValue('url_name_'.$id_lang)) != strtolower( Tools::getValue('url_name_'.$id_lang)))
  672. {
  673. // Here use the reference "errors" to stop saving process
  674. $params['errors'][] = Tools::displayError(sprintf($this->l('"%s" is not a valid url'), Tools::getValue('url_name_'.$id_lang)));
  675. }
  676. }
  677. }
  678. public function hookAfterDeleteAttributeGroup($params)
  679. {
  680. if (!$params['id_attribute_group'])
  681. return;
  682. Db::getInstance()->execute('DELETE FROM '._DB_PREFIX_.'layered_indexable_attribute_group WHERE id_attribute_group = '.(int)$params['id_attribute_group']);
  683. Db::getInstance()->execute('DELETE FROM '._DB_PREFIX_.'layered_indexable_attribute_group_lang_value WHERE id_attribute_group = '.(int)$params['id_attribute_group']);
  684. }
  685. public function hookAttributeGroupForm($params)
  686. {
  687. $languages = Language::getLanguages(false);
  688. $default_form_language = (int)(Configuration::get('PS_LANG_DEFAULT'));
  689. $indexable = Db::getInstance(_PS_USE_SQL_SLAVE_)->getValue('SELECT indexable FROM '._DB_PREFIX_.'layered_indexable_attribute_group
  690. WHERE id_attribute_group = '.(int)$params['id_attribute_group']);
  691. $lang_value = array();
  692. $result = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS(
  693. 'SELECT url_name, meta_title, id_lang FROM '._DB_PREFIX_.'layered_indexable_attribute_group_lang_value
  694. WHERE id_attribute_group = '.(int)$params['id_attribute_group']);
  695. if ($result)
  696. foreach ($result as $data)
  697. $lang_value[$data['id_lang']] = array('url_name' => $data['url_name'], 'meta_title' => $data['meta_title']);
  698. if ($indexable === false)
  699. $on = true;
  700. else
  701. $on = (bool)$indexable;
  702. if (version_compare(_PS_VERSION_,'1.5','>'))
  703. $return = '
  704. <script type="text/javascript">
  705. flag_fields = \'\';
  706. </script>';
  707. else
  708. $return = '';
  709. $return .= '<div class="clear"></div>
  710. <label>'.$this->l('URL:').'</label>
  711. <div class="margin-form">
  712. <script type="text/javascript">
  713. flag_fields += \'¤url_name¤meta_title\';
  714. </script>
  715. <div class="translatable">';
  716. foreach ($languages as $language)
  717. $return .= '
  718. <div class="lang_'.$language['id_lang'].'" id="url_name_'.$language['id_lang'].'" style="display: '.($language['id_lang'] == $default_form_language ? 'block' : 'none').'; float: left;">
  719. <input size="33" type="text" name="url_name_'.$language['id_lang'].'" value="'.Tools::safeOutput(@$lang_value[$language['id_lang']]['url_name'], true).'" />
  720. <span class="hint" name="help_box">'.$this->l('Invalid characters:').' <>;=#{}_<span class="hint-pointer">&nbsp;</span></span>
  721. <p style="clear: both">'.$this->l('Specific URL format in block layered generation').'</p>
  722. </div>';
  723. if (version_compare(_PS_VERSION_,'1.5','<'))
  724. $return .= $this->displayFlags($languages, $default_form_language, 'flag_fields', 'url_name', true, true);
  725. $return .= '
  726. </div>
  727. <div class="clear"></div>
  728. </div>
  729. <label>'.$this->l('Meta title:').' </label>
  730. <div class="margin-form">
  731. <div class="translatable">';
  732. foreach ($languages as $language)
  733. $return .= '
  734. <div class="lang_'.$language['id_lang'].'" id="meta_title_'.$language['id_lang'].'" style="display: '.($language['id_lang'] == $default_form_language ? 'block' : 'none').'; float: left;">
  735. <input size="33" type="text" name="meta_title_'.$language['id_lang'].'" value="'.Tools::safeOutput(@$lang_value[$language['id_lang']]['meta_title'], true).'" />
  736. <p style="clear: both">'.$this->l('Specific format for meta title').'</p>
  737. </div>';
  738. if (version_compare(_PS_VERSION_,'1.5','<'))
  739. $return .= $this->displayFlags($languages, $default_form_language, 'flag_fields', 'meta_title', true, true);
  740. $return .= '
  741. </div>
  742. <div class="clear"></div>
  743. </div>
  744. <label>'.$this->l('Indexable:').' </label>
  745. <div class="margin-form">
  746. <input type="radio" '.(($on) ? 'checked="checked"' : '').' value="1" id="indexable_on" name="layered_indexable">
  747. <label for="indexable_on" class="t"><img title="Yes" alt="Enabled" src="../img/admin/enabled.gif"></label>
  748. <input type="radio" '.((!$on) ? 'checked="checked"' : '').' value="0" id="indexable_off" name="layered_indexable">
  749. <label for="indexable_off" class="t"><img title="No" alt="Disabled" src="../img/admin/disabled.gif"></label>
  750. <p>'.$this->l('Use this attribute in URL generated by the layered navigation module').'</p>
  751. </div>';
  752. return $return;
  753. }
  754. public function hookFeatureForm($params)
  755. {
  756. $languages = Language::getLanguages(false);
  757. $default_form_language = (int)(Configuration::get('PS_LANG_DEFAULT'));
  758. $indexable = Db::getInstance(_PS_USE_SQL_SLAVE_)->getValue('SELECT indexable FROM '._DB_PREFIX_.'layered_indexable_feature WHERE id_feature = '.(int)$params['id_feature']);
  759. $lang_value = array();
  760. $result = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS(
  761. 'SELECT url_name, meta_title, id_lang FROM '._DB_PREFIX_.'layered_indexable_feature_lang_value
  762. WHERE id_feature = '.(int)$params['id_feature']);
  763. if ($result)
  764. foreach ($result as $data)
  765. $lang_value[$data['id_lang']] = array('url_name' => $data['url_name'], 'meta_title' => $data['meta_title']);
  766. if ($indexable === false)
  767. $on = true;
  768. else
  769. $on = (bool)$indexable;
  770. if (version_compare(_PS_VERSION_,'1.5','>'))
  771. $return = '
  772. <script type="text/javascript">
  773. flag_fields = \'\';
  774. </script>';
  775. else
  776. $return = '';
  777. $return .= '<div class="clear"></div>
  778. <label>'.$this->l('URL:').'</label>
  779. <div class="margin-form">
  780. <script type="text/javascript">
  781. flag_fields += \'¤url_name¤meta_title\';
  782. </script>
  783. <div class="translatable">';
  784. foreach ($languages as $language)
  785. $return .= '
  786. <div class="lang_'.$language['id_lang'].'" id="url_name_'.$language['id_lang'].'" style="display: '.($language['id_lang'] == $default_form_language ? 'block' : 'none').'; float: left;">
  787. <input size="33" type="text" name="url_name_'.$language['id_lang'].'" value="'.Tools::safeOutput(@$lang_value[$language['id_lang']]['url_name'], true).'" />
  788. <span class="hint" name="help_box">'.$this->l('Invalid characters:').' <>;=#{}_<span class="hint-pointer">&nbsp;</span></span>
  789. <p style="clear: both">'.$this->l('Specific URL format in block layered generation').'</p>
  790. </div>';
  791. if (version_compare(_PS_VERSION_,'1.5','<'))
  792. $return .= $this->displayFlags($languages, $default_form_language, 'flag_fields', 'url_name', true, true);
  793. $return .= '
  794. </div>
  795. <div class="clear"></div>
  796. </div>
  797. <label>'.$this->l('Meta title:').' </label>
  798. <div class="margin-form">
  799. <div class="translatable">';
  800. foreach ($languages as $language)
  801. $return .= '
  802. <div class="lang_'.$language['id_lang'].'" id="meta_title_'.$language['id_lang'].'" style="display: '.($language['id_lang'] == $default_form_language ? 'block' : 'none').'; float: left;">
  803. <input size="33" type="text" name="meta_title_'.$language['id_lang'].'" value="'.Tools::safeOutput(@$lang_value[$language['id_lang']]['meta_title'], true).'" />
  804. <p style="clear: both">'.$this->l('Specific format for meta title').'</p>
  805. </div>';
  806. if (version_compare(_PS_VERSION_,'1.5','<'))
  807. $return .= $this->displayFlags($languages, $default_form_language, 'flag_fields', 'meta_title', true, true);
  808. $return .= '
  809. </div>
  810. <div class="clear"></div>
  811. </div>
  812. <label>'.$this->l('Indexable:').' </label>
  813. <div class="margin-form">
  814. <input type="radio" '.(($on) ? 'checked="checked"' : '').' value="1" id="indexable_on" name="layered_indexable">
  815. <label for="indexable_on" class="t"><img title="Yes" alt="Enabled" src="../img/admin/enabled.gif"></label>
  816. <input type="radio" '.((!$on) ? 'checked="checked"' : '').' value="0" id="indexable_off" name="layered_indexable">
  817. <label for="indexable_off" class="t"><img title="No" alt="Disabled" src="../img/admin/disabled.gif"></label>
  818. <p>'.$this->l('Use this attribute in URL generated by the layered navigation module').'</p>
  819. </div>';
  820. return $return;
  821. }
  822. /*
  823. * $cursor $cursor in order to restart indexing from the last state
  824. */
  825. public static function fullPricesIndexProcess($cursor = 0, $ajax = false, $smart = false)
  826. {
  827. if ($cursor == 0 && !$smart)
  828. self::installPriceIndexTable();
  829. return self::indexPrices($cursor, true, $ajax, $smart);
  830. }
  831. /*
  832. * $cursor $cursor in order to restart indexing from the last state
  833. */
  834. public static function pricesIndexProcess($cursor = 0, $ajax = false)
  835. {
  836. return self::indexPrices($cursor, false, $ajax);
  837. }
  838. private static function indexPrices($cursor = null, $full = false, $ajax = false, $smart = false)
  839. {
  840. if ($full)
  841. if (version_compare(_PS_VERSION_,'1.5','>'))
  842. $nb_products = (int)Db::getInstance()->getValue('
  843. SELECT count(DISTINCT p.`id_product`)
  844. FROM '._DB_PREFIX_.'product p
  845. INNER JOIN `'._DB_PREFIX_.'product_shop` ps
  846. ON (ps.`id_product` = p.`id_product` AND ps.`active` = 1)');
  847. else
  848. $nb_products = (int)Db::getInstance()->getValue('
  849. SELECT count(DISTINCT p.`id_product`)
  850. FROM '._DB_PREFIX_.'product p
  851. WHERE `active` = 1');
  852. else
  853. if (version_compare(_PS_VERSION_,'1.5','>'))
  854. $nb_products = (int)Db::getInstance()->getValue('
  855. SELECT COUNT(DISTINCT p.`id_product`) FROM `'._DB_PREFIX_.'product` p
  856. INNER JOIN `'._DB_PREFIX_.'product_shop` ps
  857. ON (ps.`id_product` = p.`id_product` AND ps.`active` = 1)
  858. LEFT JOIN `'._DB_PREFIX_.'layered_price_index` psi ON (psi.id_product = p.id_product)
  859. WHERE psi.id_product IS NULL');
  860. else
  861. $nb_products = (int)Db::getInstance()->getValue('
  862. SELECT COUNT(DISTINCT p.`id_product`) FROM `'._DB_PREFIX_.'product` p
  863. LEFT JOIN `'._DB_PREFIX_.'layered_price_index` psi ON (psi.id_product = p.id_product)
  864. WHERE `active` = 1 AND psi.id_product IS NULL');
  865. $max_executiontime = @ini_get('max_execution_time');
  866. if ($max_executiontime > 5 || $max_executiontime <= 0)
  867. $max_executiontime = 5;
  868. $start_time = microtime(true);
  869. do
  870. {
  871. $cursor = (int)self::indexPricesUnbreakable((int)$cursor, $full, $smart);
  872. $time_elapsed = microtime(true) - $start_time;
  873. }
  874. while ($cursor < $nb_products && (Tools::getMemoryLimit()) > memory_get_peak_usage() && $time_elapsed < $max_executiontime);
  875. if (($nb_products > 0 && !$full || $cursor < $nb_products && $full) && !$ajax)
  876. {
  877. $token = substr(Tools::encrypt('blocklayered/index'), 0, 10);
  878. if (Tools::usingSecureMode())
  879. $domain = Tools::getShopDomainSsl(true);
  880. else
  881. $domain = Tools::getShopDomain(true);
  882. if (!Tools::file_get_contents($domain.__PS_BASE_URI__.'modules/blocklayered/blocklayered-price-indexer.php?token='.$token.'&cursor='.(int)$cursor.'&full='.(int)$full))
  883. self::indexPrices((int)$cursor, (int)$full);
  884. return $cursor;
  885. }
  886. if ($ajax && $nb_products > 0 && $cursor < $nb_products && $full)
  887. return '{"cursor": '.$cursor.', "count": '.($nb_products - $cursor).'}';
  888. else if ($ajax && $nb_products > 0 && !$full)
  889. return '{"cursor": '.$cursor.', "count": '.($nb_products).'}';
  890. else
  891. {
  892. if (version_compare(_PS_VERSION_,'1.5','>'))
  893. Configuration::updateGlobalValue('PS_LAYERED_INDEXED', 1);
  894. else
  895. Configuration::updateValue('PS_LAYERED_INDEXED', 1);
  896. if ($ajax)
  897. return '{"result": "ok"}';
  898. else
  899. return -1;
  900. }
  901. }
  902. /*
  903. * $cursor $cursor in order to restart indexing from the last state
  904. */
  905. private static function indexPricesUnbreakable($cursor, $full = false, $smart = false)
  906. {
  907. static $length = 100; // Nb of products to index
  908. if (is_null($cursor))
  909. $cursor = 0;
  910. if ($full)
  911. if (version_compare(_PS_VERSION_,'1.5','>'))
  912. $query = '
  913. SELECT p.`id_product`
  914. FROM `'._DB_PREFIX_.'product` p
  915. INNER JOIN `'._DB_PREFIX_.'product_shop` ps
  916. ON (ps.`id_product` = p.`id_product` AND ps.`active` = 1)
  917. GROUP BY p.`id_product`
  918. ORDER BY p.`id_product` LIMIT '.(int)$cursor.','.(int)$length;
  919. else
  920. $query = '
  921. SELECT p.`id_product`
  922. FROM `'._DB_PREFIX_.'product` p
  923. WHERE `active` = 1
  924. GROUP BY p.`id_product`
  925. ORDER BY p.`id_product` LIMIT '.(int)$cursor.','.(int)$length;
  926. else
  927. if (version_compare(_PS_VERSION_,'1.5','>'))
  928. $query = '
  929. SELECT p.`id_product`
  930. FROM `'._DB_PREFIX_.'product` p
  931. INNER JOIN `'._DB_PREFIX_.'product_shop` ps
  932. ON (ps.`id_product` = p.`id_product` AND ps.`active` = 1)
  933. LEFT JOIN `'._DB_PREFIX_.'layered_price_index` psi ON (psi.id_product = p.id_product)
  934. WHERE psi.id_product IS NULL
  935. GROUP BY p.`id_product`
  936. ORDER BY p.`id_product` LIMIT 0,'.(int)$length;
  937. else
  938. $query = '
  939. SELECT p.`id_product`
  940. FROM `'._DB_PREFIX_.'product` p
  941. LEFT JOIN `'._DB_PREFIX_.'layered_price_index` psi ON (psi.id_product = p.id_product)
  942. WHERE `active` = 1 AND psi.id_product IS NULL
  943. GROUP BY p.`id_product`
  944. ORDER BY p.`id_product` LIMIT 0,'.(int)$length;
  945. foreach (Db::getInstance()->executeS($query) as $product)
  946. self::indexProductPrices((int)$product['id_product'], ($smart && $full));
  947. return (int)($cursor + $length);
  948. }
  949. public static function indexProductPrices($id_product, $smart = true)
  950. {
  951. static $groups = null;
  952. if (is_null($groups))
  953. {
  954. $groups = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS('SELECT id_group FROM `'._DB_PREFIX_.'group_reduction`');
  955. if (!$groups)
  956. $groups = array();
  957. }
  958. $shop_list = array();
  959. if (version_compare(_PS_VERSION_,'1.5','>'))
  960. $shop_list = Shop::getShops(false, null, true);
  961. else
  962. $shop_list[] = 0;
  963. foreach ($shop_list as $id_shop)
  964. {
  965. static $currency_list = null;
  966. if (is_null($currency_list))
  967. {
  968. if (version_compare(_PS_VERSION_,'1.5','>'))
  969. $currency_list = Currency::getCurrencies(false, 1, new Shop($id_shop));
  970. else
  971. $currency_list = Currency::getCurrencies(false, 1);
  972. }
  973. $min_price = array();
  974. $max_price = array();
  975. if ($smart)
  976. Db::getInstance()->execute('DELETE FROM `'._DB_PREFIX_.'layered_price_index` WHERE `id_product` = '.(int)$id_product.' AND `id_shop` = '.(int)$id_shop);
  977. if (Configuration::get('PS_LAYERED_FILTER_PRICE_USETAX'))
  978. {
  979. if (version_compare(_PS_VERSION_,'1.5','>'))
  980. $max_tax_rate = Db::getInstance()->getValue('
  981. SELECT max(t.rate) max_rate
  982. FROM `'._DB_PREFIX_.'product_shop` p
  983. LEFT JOIN `'._DB_PREFIX_.'tax_rules_group` trg ON (trg.id_tax_rules_group = p.id_tax_rules_group AND p.id_shop = '.(int)$shop_list.')
  984. LEFT JOIN `'._DB_PREFIX_.'tax_rule` tr ON (tr.id_tax_rules_group = trg.id_tax_rules_group)
  985. LEFT JOIN `'._DB_PREFIX_.'tax` t ON (t.id_tax = tr.id_tax AND t.active = 1)
  986. WHERE id_product = '.(int)$id_product.'
  987. GROUP BY id_product');
  988. else
  989. $max_tax_rate = Db::getInstance()->getValue('
  990. SELECT max(t.rate) max_rate
  991. FROM `'._DB_PREFIX_.'product` p
  992. LEFT JOIN `'._DB_PREFIX_.'tax_rules_group` trg ON (trg.id_tax_rules_group = p.id_tax_rules_group)
  993. LEFT JOIN `'._DB_PREFIX_.'tax_rule` tr ON (tr.id_tax_rules_group = trg.id_tax_rules_group)
  994. LEFT JOIN `'._DB_PREFIX_.'tax` t ON (t.id_tax = tr.id_tax AND t.active = 1)
  995. WHERE id_product = '.(int)$id_product.'
  996. GROUP BY id_product');
  997. }
  998. else
  999. $max_tax_rate = 0;
  1000. $product_min_prices = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS('
  1001. SELECT id_shop, id_currency, id_country, id_group, from_quantity
  1002. FROM `'._DB_PREFIX_.'specific_price`
  1003. WHERE id_product = '.(int)$id_product);
  1004. // Get min price
  1005. foreach ($currency_list as $currency)
  1006. {
  1007. $price = P

Large files files are truncated, but you can click here to view the full file