diff --git a/blocklayered-15.css b/blocklayered-15.css new file mode 100644 index 0000000..615e2b3 --- /dev/null +++ b/blocklayered-15.css @@ -0,0 +1,168 @@ +#enabled_filters { + margin: 0 0 10px 0; + background: #eee; + padding: 5px; +} +#enabled_filters .layered_subtitle { + font-weight: bold; + font-size: 12px; +} +#enabled_filters ul li {padding:2px 0} +#enabled_filters li a { + float: right; + display: inline-block; + height: 12px; + width: 12px; + text-indent: -5000px; + /*background: url(../../../img/icon/delete.gif) no-repeat 0 0 transparent;*/ + background: url(./delete.gif) no-repeat 0 0 transparent; + cursor: pointer; +} +#layered_block_left .layered_close { + float: right; + padding-right: 5px; +} +#layered_block_left span.layered_close a, +#layered_block_left span.layered_close a { + display:block; + height:13px; + width:13px; + text-decoration: none; + text-indent:-5000px; + background:url(img/icon/less.gif) no-repeat 2px 2px; +} +#layered_block_left span.closed a { + background:url(img/icon/more.gif) no-repeat 2px 2px; +} + +#layered_block_left .layered_subtitle { + display: inline-block; + font-weight: bold; + font-size: 12px; +} +#layered_block_left ul { + clear: both; + padding: 5px 0; +} +#layered_block_left ul li {padding:2px 0} +#layered_block_left ul li label {cursor: pointer;} +#layered_block_left div.block_content { +} +#layered_block_left ul { + padding-left: 0; +} +#layered_block_left ul li { + list-style-type: none; +} +#layered_block_left div > form > div > div { + margin-top: 5px; +} +#layered_block_left #enabled_filters , #layered_url_filter_block{ + font-size: 10px; + padding-bottom: 10px; + margin-bottom: 5px; + margin-top: 0px; + border-bottom: 1px dotted #CCC; +} +#layered_block_left #enabled_filters ul li { + margin-left: 0; + padding-left: 0; +} + + +/* Style moved form the template to the css */ + +#layered_block_left #enabled_filters ul li:hover { + background: inherit; + color: inherit; +} +#layered_block_left #enabled_filters a { + text-decoration: none; + color: red; +} +#layered_block_left ul li:hover { +} +#layered_block_left ul li input.checkbox { + vertical-align: middle; + margin-left: 2px; +} +#layered_block_left ul li .disabled { + color: #666; +} +#layered_block_left .color-option { + border: 1px solid #666666; + height: 16px; + margin-left: 0; + padding: 0; + width: 16px; +} +#layered_block_left .color-option.disable { + opacity: 0.5; + filter:alpha(opacity=50); + -moz-opacity:0.5; +} +#layered_block_left .nomargin a { + display: inline; +} +#layered_form .select { + width: 165px; +} +#layered_form .color-option { + margin-left: 0; + width: 16px; + height: 16px; + padding:0; + border: 1px solid #666; +} +#layered_form .color-option.on { + border: 1px solid red; +} +#layered_form input[type=radio] { + margin-left: 2px; + vertical-align: middle; +} +.layered_price_range { + margin-left: 7px; +} +.layered_price_range_unit { + margin-right: 7px; +} +.layered_slider_container { + margin: 6px 0 6px 7px; width: 91%; +} +.layered_slider { + margin-top:5px; +} +.layered_input_range_min { + width:30px; +} +.layered_input_range_max { + width:40px; +} +.layered_list_selected { + font-weight: bold; +} +.layered_list { + display: block; + cursor: pointer; +} +.layered_list:hover { + font-weight: bold; +} +.layered_ajax_loader p { + margin: 20px 0; + text-align: center; +} +.hide-action { + text-align: center; + text-align: right; + display: block; + cursor: pointer; +} +.hide-action:hover { + font-weight: bold; +} +#layered_form div div ul { + max-height: 200px; + overflow-x: auto; +} diff --git a/blocklayered-ajax.php b/blocklayered-ajax.php new file mode 100644 index 0000000..3574146 --- /dev/null +++ b/blocklayered-ajax.php @@ -0,0 +1,33 @@ + +* @copyright 2007-2014 PrestaShop SA +* @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) +* International Registred Trademark & Property of PrestaShop SA +*/ + +include(dirname(__FILE__).'/../../config/config.inc.php'); +include(dirname(__FILE__).'/../../init.php'); +include(dirname(__FILE__).'/blocklayered.php'); + +Context::getContext()->controller->php_self = 'category'; +$blockLayered = new BlockLayered(); +echo $blockLayered->ajaxCall(); \ No newline at end of file diff --git a/blocklayered-attribute-indexer.php b/blocklayered-attribute-indexer.php new file mode 100644 index 0000000..42d064e --- /dev/null +++ b/blocklayered-attribute-indexer.php @@ -0,0 +1,10 @@ +indexAttribute(); \ No newline at end of file diff --git a/blocklayered-footer.js b/blocklayered-footer.js new file mode 100644 index 0000000..900a90d --- /dev/null +++ b/blocklayered-footer.js @@ -0,0 +1,31 @@ +/* +* 2007-2014 PrestaShop +* +* NOTICE OF LICENSE +* +* This source file is subject to the Academic Free License (AFL 3.0) +* that is bundled with this package in the file LICENSE.txt. +* It is also available through the world-wide-web at this URL: +* http://opensource.org/licenses/afl-3.0.php +* If you did not receive a copy of the license and are unable to +* obtain it through the world-wide-web, please send an email +* to license@prestashop.com so we can send you a copy immediately. +* +* DISCLAIMER +* +* Do not edit or add to this file if you wish to upgrade PrestaShop to newer +* versions in the future. If you wish to customize PrestaShop for your +* needs please refer to http://www.prestashop.com for more information. +* +* @author PrestaShop SA +* @copyright 2007-2014 PrestaShop SA +* @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) +* International Registred Trademark & Property of PrestaShop SA +*/ +$(document).ready(function() +{ + $('#selectProductSort').unbind('change').bind('change', function() + { + reloadContent(); + }) +}); \ No newline at end of file diff --git a/blocklayered-no-products.tpl b/blocklayered-no-products.tpl new file mode 100644 index 0000000..c95d1c6 --- /dev/null +++ b/blocklayered-no-products.tpl @@ -0,0 +1,27 @@ +{* +* 2007-2014 PrestaShop +* +* NOTICE OF LICENSE +* +* This source file is subject to the Academic Free License (AFL 3.0) +* that is bundled with this package in the file LICENSE.txt. +* It is also available through the world-wide-web at this URL: +* http://opensource.org/licenses/afl-3.0.php +* If you did not receive a copy of the license and are unable to +* obtain it through the world-wide-web, please send an email +* to license@prestashop.com so we can send you a copy immediately. +* +* DISCLAIMER +* +* Do not edit or add to this file if you wish to upgrade PrestaShop to newer +* versions in the future. If you wish to customize PrestaShop for your +* needs please refer to http://www.prestashop.com for more information. +* +* @author PrestaShop SA +* @copyright 2007-2014 PrestaShop SA +* @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) +* International Registred Trademark & Property of PrestaShop SA +*} +
+

{l s='There are no products.' mod='blocklayered'}

+
diff --git a/blocklayered-price-indexer.php b/blocklayered-price-indexer.php new file mode 100644 index 0000000..2d5bc03 --- /dev/null +++ b/blocklayered-price-indexer.php @@ -0,0 +1,32 @@ +indexUrl($cursor, (int)Tools::getValue('truncate')); \ No newline at end of file diff --git a/blocklayered.css b/blocklayered.css new file mode 100644 index 0000000..25b271a --- /dev/null +++ b/blocklayered.css @@ -0,0 +1,159 @@ +#layered_block_left div.block_content { + padding: 6px; +} + +#layered_block_left ul { + padding-left: 0; + max-height: 120px; + overflow-y: auto; + overflow-x: hidden; + padding-left: 1px; + padding-right: 1px; +} + +#layered_form > div > div { + border-bottom: 1px dotted #CCCCCC; + padding-bottom: 5px; + margin-bottom: 5px; +} + +#layered_block_left .hide-action { + display: block; + text-align: right; + padding-right: 5px; +} +#layered_block_left .hide-action:hover { + text-decoration: underline; +} + +#layered_block_left ul li { + margin-left: 15px; + padding-left: 2px; + list-style-type: none; +} + +#layered_block_left #enabled_filters , #layered_url_filter_block { + font-size: 10px; + padding-bottom: 5px; +} + +#layered_block_left #enabled_filters ul li { + margin-left: 0; + padding-left: 0; +} + + +/* Style moved form the template to the css */ +#layered_block_left #enabled_filters ul li:hover { + background: inherit; + color: inherit; +} + +#layered_block_left #enabled_filters a { + text-decoration: none; + color: red; +} + +#layered_block_left ul li:hover { + background: #71BF47; + color: #FFF; +} + +#layered_block_left ul li label { + cursor: pointer; +} + +#layered_block_left ul li.nomargin { + margin-left: 0; +} + +#layered_block_left span.layered_subtitle { + float: left; + font-weight: bold; + font-size: 12px; + display: block; + margin-bottom: 4px; +} + +#layered_block_left span.layered_close a { text-decoration: none; } + +#layered_block_left span.layered_close { + float: right; + font-weight: bold; + font-size: 12px; + display: block; + margin-bottom: 4px; +} + +#layered_block_left ul li input.checkbox { + vertical-align: middle; + margin-left: 2px; +} + +#layered_block_left ul li .disabled { + color: #666; +} +#product_list .warning { + margin-top: 13px; +} + +#layered_form .select { + width: 165px; +} +#layered_form .color-option { + margin-left: 0; + width: 16px; + height: 16px; + padding:0; + border: 1px solid #666; +} +#layered_block_left .color-option.disable { + opacity: 0.5; + filter:alpha(opacity=50); + -moz-opacity:0.5; +} +#layered_form .color-option.on { + border: 1px solid red; +} +#layered_form input[type=radio] { + margin-left: 2px; + vertical-align: middle; +} +.layered_price_range { + margin-left: 7px; +} +.layered_price_range_unit { + margin-right: 7px; +} +.layered_slider_container { + margin: 6px 0 6px 6px; width: 93%; +} +.layered_slider { + margin-top:5px; +} +.layered_input_range_min { + width:30px; +} +.layered_input_range_max { + width:40px; +} +.layered_list_selected { + font-weight: bold; +} +.layered_ajax_loader p { + margin: 20px 0; + text-align: center; +} +.hide-action { + text-align: center; + text-align: right; + display: block; + cursor: pointer; +} +.hide-action:hover { + font-weight: bold; +} +#layered_form div div ul { + max-height: 200px; + overflow-x: auto; +} diff --git a/blocklayered.js b/blocklayered.js new file mode 100644 index 0000000..ed62a21 --- /dev/null +++ b/blocklayered.js @@ -0,0 +1,638 @@ +/* +* 2007-2014 PrestaShop +* +* NOTICE OF LICENSE +* +* This source file is subject to the Academic Free License (AFL 3.0) +* that is bundled with this package in the file LICENSE.txt. +* It is also available through the world-wide-web at this URL: +* http://opensource.org/licenses/afl-3.0.php +* If you did not receive a copy of the license and are unable to +* obtain it through the world-wide-web, please send an email +* to license@prestashop.com so we can send you a copy immediately. +* +* DISCLAIMER +* +* Do not edit or add to this file if you wish to upgrade PrestaShop to newer +* versions in the future. If you wish to customize PrestaShop for your +* needs please refer to http://www.prestashop.com for more information. +* +* @author PrestaShop SA +* @copyright 2007-2014 PrestaShop SA +* @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) +* International Registred Trademark & Property of PrestaShop SA +*/ + +var ajaxQueries = new Array(); +var ajaxLoaderOn = 0; +var sliderList = new Array(); +var slidersInit = false; + +$(document).ready(function() +{ + cancelFilter(); + openCloseFilter(); + + // Click on color + $('#layered_form input[type=button], #layered_form label.layered_color').live('click', function() + { + if (!$('input[name='+$(this).attr('name')+'][type=hidden]').length) + $('').attr('type', 'hidden').attr('name', $(this).attr('name')).val($(this).attr('rel')).appendTo('#layered_form'); + else + $('input[name='+$(this).attr('name')+'][type=hidden]').remove(); + reloadContent(); + }); + + // Click on checkbox + $('#layered_form input[type=checkbox], #layered_form input[type=radio], #layered_form select').live('change', function() + { + reloadContent(); + }); + + // Changing content of an input text + $('#layered_form input.layered_input_range').live('keyup', function() + { + if ($(this).attr('timeout_id')) + window.clearTimeout($(this).attr('timeout_id')); + + // IE Hack, setTimeout do not acept the third parameter + var reference = this; + + $(this).attr('timeout_id', window.setTimeout(function(it) { + if (!$(it).attr('id')) + it = reference; + + var filter = $(it).attr('id').replace(/^layered_(.+)_range_.*$/, '$1'); + + var value_min = parseInt($('#layered_'+filter+'_range_min').val()); + if (isNaN(value_min)) + value_min = 0; + $('#layered_'+filter+'_range_min').val(value_min); + + var value_max = parseInt($('#layered_'+filter+'_range_max').val()); + if (isNaN(value_max)) + value_max = 0; + $('#layered_'+filter+'_range_max').val(value_max); + + if (value_max < value_min) { + $('#layered_'+filter+'_range_max').val($(it).val()); + $('#layered_'+filter+'_range_min').val($(it).val()); + } + reloadContent(); + }, 500, this)); + }); + + $('#layered_block_left .radio').live('click', function() { + var name = $(this).attr('name'); + $.each($(this).parent().parent().find('input[type=button]'), function (it, item) { + if ($(item).hasClass('on') && $(item).attr('name') != name) { + $(item).click(); + } + }); + return true; + }); + + // Click on label + $('#layered_block_left label a').live({ + click: function() { + var disable = $(this).parent().parent().find('input').attr('disabled'); + if (disable == '' + || typeof(disable) == 'undefined' + || disable == false) + { + $(this).parent().parent().find('input').click(); + reloadContent(); + } + return false; + } + }); + + layered_hidden_list = {}; + $('.hide-action').live('click', function() { + if (typeof(layered_hidden_list[$(this).parent().find('ul').attr('id')]) == 'undefined' || layered_hidden_list[$(this).parent().find('ul').attr('id')] == false) + { + layered_hidden_list[$(this).parent().find('ul').attr('id')] = true; + } + else + { + layered_hidden_list[$(this).parent().find('ul').attr('id')] = false; + } + hideFilterValueAction(this); + }); + $('.hide-action').each(function() { + hideFilterValueAction(this); + }); + + // To be sure there is no other events attached to the selectProductSort, change the ID + var id = 1; + while ($('#selectPrductSort').length) { // Because ids are duplicated + // Unbind event change on #selectPrductSort + $('#selectPrductSort').unbind('change'); + $('#selectPrductSort').attr('onchange', ''); + $('#selectPrductSort').addClass('selectProductSort'); + $('#selectPrductSort').attr('id', 'selectPrductSort'+id); + $('label[for=selectPrductSort]').attr('for', 'selectPrductSort'+id); + id++; + } + + while ($('#selectProductSort').length) { // Because ids are duplicated + // Unbind event change on #selectProductSort + $('#selectProductSort').unbind('change'); + $('#selectProductSort').attr('onchange', ''); + $('#selectProductSort').addClass('selectProductSort'); + $('#selectProductSort').attr('id', 'selectProductSort'+id); + $('label[for=selectProductSort]').attr('for', 'selectProductSort'+id); + id++; + } + + // Since 1.5, event is add to .selectProductSort and not to #selectProductSort + setTimeout(function() { + $('.selectProductSort').unbind('change'); + }, 100); + + $('.selectProductSort').live('change', function(event) { + $('.selectProductSort').val($(this).val()); + reloadContent(); + }); + + // To be sure there is no other events attached to the nb_item, change the ID + var id = 1; + while ($('#nb_item').length) { // Because ids are duplicated + // Unbind event change on #nb_item + $('#nb_item').unbind('change'); + $('#nb_item').attr('onchange', ''); + $('#nb_item').addClass('nb_item'); + $('#nb_item').attr('id', 'nb_item'+id); + $('label[for=nb_item]').attr('for', 'nb_item'+id); + id++; + } + $('.nb_item').live('change', function(event) { + $('.nb_item').val($(this).val()); + reloadContent(); + }); + + paginationButton(); + initLayered(); +}); + +function hideFilterValueAction(it) +{ + if (typeof(layered_hidden_list[$(it).parent().find('ul').attr('id')]) == 'undefined' || layered_hidden_list[$(it).parent().find('ul').attr('id')] == false) + { + $(it).parent().find('.hiddable').hide(); + $(it).parent().find('.hide-action.less').hide(); + $(it).parent().find('.hide-action.more').show(); + } + else + { + $(it).parent().find('.hiddable').show(); + $(it).parent().find('.hide-action.less').show(); + $(it).parent().find('.hide-action.more').hide(); + } +} + +function addSlider(type, data, unit, format) +{ + sliderList.push({ + type: type, + data: data, + unit: unit, + format: format + }); +} + +function initSliders() +{ + $(sliderList).each(function(i, slider){ + $('#layered_'+slider['type']+'_slider').slider(slider['data']); + + var from = ''; + var to = ''; + switch (slider['format']) + { + case 1: + case 2: + case 3: + case 4: + from = blocklayeredFormatCurrency($('#layered_'+slider['type']+'_slider').slider('values', 0), slider['format'], slider['unit']); + to = blocklayeredFormatCurrency($('#layered_'+slider['type']+'_slider').slider('values', 1), slider['format'], slider['unit']); + break; + case 5: + from = $('#layered_'+slider['type']+'_slider').slider('values', 0)+slider['unit'] + to = $('#layered_'+slider['type']+'_slider').slider('values', 1)+slider['unit']; + break; + } + $('#layered_'+slider['type']+'_range').html(from+' - '+to); + }); +} + +function initLayered() +{ + initSliders(); + initLocationChange(); + updateProductUrl(); + if (window.location.href.split('#').length == 2 && window.location.href.split('#')[1] != '') + { + var params = window.location.href.split('#')[1]; + reloadContent('&selected_filters='+params); + } +} + +function paginationButton() { + $('#pagination a').not(':hidden').each(function () { + if ($(this).attr('href').search(/[&|\?]p=/) == -1) + var page = 1; + else + var page = $(this).attr('href').replace(/^.*[&|\?]p=(\d+).*$/, '$1'); + + var location = window.location.href.replace(/#.*$/, ''); + $(this).attr('href', location+current_friendly_url.replace(/\/page-(\d+)/, '')+'/page-'+page); + }); + $('#pagination li').not('.current, .disabled').each(function () { + var nbPage = 0; + if ($(this).attr('id') == 'pagination_next') + nbPage = parseInt($('#pagination li.current').children().html())+ 1; + else if ($(this).attr('id') == 'pagination_previous') + nbPage = parseInt($('#pagination li.current').children().html())- 1; + + $(this).children().click(function () { + if (nbPage == 0) + p = parseInt($(this).html()) + parseInt(nbPage); + else + p = nbPage; + p = '&p='+ p; + reloadContent(p); + nbPage = 0; + return false; + }); + }); +} + +function cancelFilter() +{ + $('#enabled_filters a').live('click', function(e) + { + if ($(this).attr('rel').search(/_slider$/) > 0) + { + if ($('#'+$(this).attr('rel')).length) + { + $('#'+$(this).attr('rel')).slider('values' , 0, $('#'+$(this).attr('rel')).slider('option' , 'min' )); + $('#'+$(this).attr('rel')).slider('values' , 1, $('#'+$(this).attr('rel')).slider('option' , 'max' )); + $('#'+$(this).attr('rel')).slider('option', 'slide')(0,{values:[$('#'+$(this).attr('rel')).slider( 'option' , 'min' ), $('#'+$(this).attr('rel')).slider( 'option' , 'max' )]}); + } + else if($('#'+$(this).attr('rel').replace(/_slider$/, '_range_min')).length) + { + $('#'+$(this).attr('rel').replace(/_slider$/, '_range_min')).val($('#'+$(this).attr('rel').replace(/_slider$/, '_range_min')).attr('limitValue')); + $('#'+$(this).attr('rel').replace(/_slider$/, '_range_max')).val($('#'+$(this).attr('rel').replace(/_slider$/, '_range_max')).attr('limitValue')); + } + } + else + { + if ($('option#'+$(this).attr('rel')).length) + { + $('#'+$(this).attr('rel')).parent().val(''); + } + else + { + $('#'+$(this).attr('rel')).attr('checked', false); + $('.'+$(this).attr('rel')).attr('checked', false); + $('#layered_form input[type=hidden][name='+$(this).attr('rel')+']').remove(); + } + } + reloadContent(); + e.preventDefault(); + }); +} + +function openCloseFilter() +{ + $('#layered_form span.layered_close a').live('click', function(e) + { + if ($(this).html() == '<') + { + $('#'+$(this).attr('rel')).show(); + $(this).html('v'); + $(this).parent().removeClass('closed'); + } + else + { + $('#'+$(this).attr('rel')).hide(); + $(this).html('<'); + $(this).parent().addClass('closed'); + } + + e.preventDefault(); + }); +} + +function stopAjaxQuery() { + if (typeof(ajaxQueries) == 'undefined') + ajaxQueries = new Array(); + for(i = 0; i < ajaxQueries.length; i++) { + if (typeof ajaxQueries[i] != 'undefined') + ajaxQueries[i].abort(); + } + ajaxQueries = new Array(); +} + +function reloadContent(params_plus) +{ + stopAjaxQuery(); + + if (!ajaxLoaderOn) + { + $('#product_list').prepend($('#layered_ajax_loader').html()); + $('#product_list').css('opacity', '0.7'); + ajaxLoaderOn = 1; + } + + data = $('#layered_form').serialize(); + $('.layered_slider').each( function () { + var sliderStart = $(this).slider('values', 0); + var sliderStop = $(this).slider('values', 1); + if (typeof(sliderStart) == 'number' && typeof(sliderStop) == 'number') + data += '&'+$(this).attr('id')+'='+sliderStart+'_'+sliderStop; + }); + + $(['price', 'weight']).each(function(it, sliderType) + { + if ($('#layered_'+sliderType+'_range_min').length) + { + data += '&layered_'+sliderType+'_slider='+$('#layered_'+sliderType+'_range_min').val()+'_'+$('#layered_'+sliderType+'_range_max').val(); + } + }); + + $('#layered_form .select option').each( function () { + if($(this).attr('id') && $(this).parent().val() == $(this).val()) + { + data += '&'+$(this).attr('id') + '=' + $(this).val(); + } + }); + + if ($('.selectProductSort').length && $('.selectProductSort').val()) + { + if ($('.selectProductSort').val().search(/orderby=/) > 0) + { + // Old ordering working + var splitData = [ + $('.selectProductSort').val().match(/orderby=(\w*)/)[1], + $('.selectProductSort').val().match(/orderway=(\w*)/)[1] + ]; + } + else + { + // New working for default theme 1.4 and theme 1.5 + var splitData = $('.selectProductSort').val().split(':'); + } + data += '&orderby='+splitData[0]+'&orderway='+splitData[1]; + } + if ($('.nb_item').length) + { + data += '&n='+$('.nb_item').val(); + } + + var slideUp = true; + if (params_plus == undefined) + { + params_plus = ''; + slideUp = false; + } + + // Get nb items per page + var n = ''; + $('#pagination #nb_item').children().each(function(it, option) { + if (option.selected) + n = '&n='+option.value; + }); + + ajaxQuery = $.ajax( + { + type: 'GET', + url: baseDir + 'modules/blocklayered/blocklayered-ajax.php', + data: data+params_plus+n, + dataType: 'json', + cache: false, // @todo see a way to use cache and to add a timestamps parameter to refresh cache each 10 minutes for example + success: function(result) + { + $('#layered_block_left').replaceWith(utf8_decode(result.filtersBlock)); + + $('.category-product-count').html(result.categoryCount); + + if (result.productList) + $('#product_list').replaceWith(utf8_decode(result.productList)); + else + $('#product_list').html(''); + + $('#product_list').css('opacity', '1'); + if ($.browser.msie) // Fix bug with IE8 and aliasing + $('#product_list').css('filter', ''); + + if (result.pagination.search(/[^\s]/) >= 0) { + if ($(result.pagination).find('ul.pagination').length) + { + $('div#pagination').show(); + $('ul.pagination').each(function () { + $(this).replaceWith($(result.pagination).find('ul.pagination')); + }); + } + else if (!$('ul.pagination').length) + { + $('div#pagination').show(); + $('div#pagination').each(function () { + $(this).html($(result.pagination)); + }); + } + else + { + $('ul.pagination').html(''); + $('div#pagination').hide(); + } + } + else + { + $('ul.pagination').html(''); + $('div#pagination').hide(); + } + + paginationButton(); + ajaxLoaderOn = 0; + + // On submiting nb items form, relaod with the good nb of items + $('#pagination form').submit(function() { + val = $('#pagination #nb_item').val(); + $('#pagination #nb_item').children().each(function(it, option) { + if (option.value == val) + $(option).attr('selected', true); + else + $(option).removeAttr('selected'); + }); + // Reload products and pagination + reloadContent(); + return false; + }); + if (typeof(ajaxCart) != "undefined") + ajaxCart.overrideButtonsInThePage(); + + if (typeof(reloadProductComparison) == 'function') + reloadProductComparison(); + initSliders(); + + // Currente page url + if (typeof(current_friendly_url) == 'undefined') + current_friendly_url = '#'; + + // Get all sliders value + $(['price', 'weight']).each(function(it, sliderType) + { + if ($('#layered_'+sliderType+'_slider').length) + { + // Check if slider is enable & if slider is used + if(typeof($('#layered_'+sliderType+'_slider').slider('values', 0)) != 'object') + { + if ($('#layered_'+sliderType+'_slider').slider('values', 0) != $('#layered_'+sliderType+'_slider').slider('option' , 'min') + || $('#layered_'+sliderType+'_slider').slider('values', 1) != $('#layered_'+sliderType+'_slider').slider('option' , 'max')) + current_friendly_url += '/'+sliderType+'-'+$('#layered_'+sliderType+'_slider').slider('values', 0)+'-'+$('#layered_'+sliderType+'_slider').slider('values', 1) + } + } + else if ($('#layered_'+sliderType+'_range_min').length) + { + current_friendly_url += '/'+sliderType+'-'+$('#layered_'+sliderType+'_range_min').val()+'-'+$('#layered_'+sliderType+'_range_max').val(); + } + }); + if (current_friendly_url == '#') + current_friendly_url = '#/'; + window.location = current_friendly_url; + lockLocationChecking = true; + + if(slideUp) + $.scrollTo('#product_list', 400); + updateProductUrl(); + + $('.hide-action').each(function() { + hideFilterValueAction(this); + }); + } + }); + ajaxQueries.push(ajaxQuery); +} + +function initLocationChange(func, time) +{ + if(!time) time = 500; + var current_friendly_url = getUrlParams(); + setInterval(function() + { + if(getUrlParams() != current_friendly_url && !lockLocationChecking) + { + // Don't reload page if current_friendly_url and real url match + if (current_friendly_url.replace(/^#(\/)?/, '') == getUrlParams().replace(/^#(\/)?/, '')) + return; + + lockLocationChecking = true; + reloadContent('&selected_filters='+getUrlParams().replace(/^#/, '')); + } + else { + lockLocationChecking = false; + current_friendly_url = getUrlParams(); + } + }, time); +} + +function getUrlParams() +{ + var params = current_friendly_url; + if(window.location.href.split('#').length == 2 && window.location.href.split('#')[1] != '') + params = '#'+window.location.href.split('#')[1]; + return params; +} + +function updateProductUrl() +{ + // Adding the filters to URL product + if (typeof(param_product_url) != 'undefined' && param_product_url != '' && param_product_url !='#') { + $.each($('ul#product_list li.ajax_block_product .product_img_link,'+ + 'ul#product_list li.ajax_block_product h3 a,'+ + 'ul#product_list li.ajax_block_product .product_desc a,'+ + 'ul#product_list li.ajax_block_product .lnk_view'), function() { + $(this).attr('href', $(this).attr('href') + param_product_url); + }); + } +} + + +/** + * Copy of the php function utf8_decode() + */ +function utf8_decode (utfstr) { + var res = ''; + for (var i = 0; i < utfstr.length;) { + var c = utfstr.charCodeAt(i); + + if (c < 128) + { + res += String.fromCharCode(c); + i++; + } + else if((c > 191) && (c < 224)) + { + var c1 = utfstr.charCodeAt(i+1); + res += String.fromCharCode(((c & 31) << 6) | (c1 & 63)); + i += 2; + } + else + { + var c1 = utfstr.charCodeAt(i+1); + var c2 = utfstr.charCodeAt(i+2); + res += String.fromCharCode(((c & 15) << 12) | ((c1 & 63) << 6) | (c2 & 63)); + i += 3; + } + } + return res; +} + + +/** + * Return a formatted price + * Copy from tools.js + */ +function blocklayeredFormatCurrency(price, currencyFormat, currencySign, currencyBlank) +{ + // if you modified this function, don't forget to modify the PHP function displayPrice (in the Tools.php class) + blank = ''; + price = parseFloat(price.toFixed(6)); + price = ps_round(price, priceDisplayPrecision); + if (currencyBlank > 0) + blank = ' '; + if (currencyFormat == 1) + return currencySign + blank + blocklayeredFormatNumber(price, priceDisplayPrecision, ',', '.'); + if (currencyFormat == 2) + return (blocklayeredFormatNumber(price, priceDisplayPrecision, ' ', ',') + blank + currencySign); + if (currencyFormat == 3) + return (currencySign + blank + blocklayeredFormatNumber(price, priceDisplayPrecision, '.', ',')); + if (currencyFormat == 4) + return (blocklayeredFormatNumber(price, priceDisplayPrecision, ',', '.') + blank + currencySign); + return price; +} + + +/** + * Return a formatted number + * Copy from tools.js + */ +function blocklayeredFormatNumber(value, numberOfDecimal, thousenSeparator, virgule) +{ + value = value.toFixed(numberOfDecimal); + var val_string = value+''; + var tmp = val_string.split('.'); + var abs_val_string = (tmp.length == 2) ? tmp[0] : val_string; + var deci_string = ('0.' + (tmp.length == 2 ? tmp[1] : 0)).substr(2); + var nb = abs_val_string.length; + + for (var i = 1 ; i < 4; i++) + if (value >= Math.pow(10, (3 * i))) + abs_val_string = abs_val_string.substring(0, nb - (3 * i)) + thousenSeparator + abs_val_string.substring(nb - (3 * i)); + + if (parseInt(numberOfDecimal) == 0) + return abs_val_string; + return abs_val_string + virgule + (deci_string > 0 ? deci_string : '00'); +} diff --git a/blocklayered.php b/blocklayered.php new file mode 100644 index 0000000..a842f4e --- /dev/null +++ b/blocklayered.php @@ -0,0 +1,3396 @@ + +* @copyright 2007-2014 PrestaShop SA +* @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) +* International Registred Trademark & Property of PrestaShop SA +*/ + +if (!defined('_PS_VERSION_')) + exit; + +class BlockLayered extends Module +{ + private $products; + private $nbr_products; + + private $page = 1; + + public function __construct() + { + $this->name = 'blocklayered'; + $this->tab = 'front_office_features'; + $this->version = '1.10.5'; + $this->author = 'PrestaShop'; + $this->need_instance = 0; + $this->bootstrap = true; + + parent::__construct(); + + $this->ps_versions_compliancy = array('min' => '1.5', 'max' => _PS_VERSION_); + $this->displayName = $this->l('Layered navigation block'); + $this->description = $this->l('Displays a block with layered navigation filters.'); + + if ((int)Tools::getValue('p')) + $this->page = (int)Tools::getValue('p'); + } + + public function install() + { + if (parent::install() && $this->registerHook('header') + && $this->registerHook('categoryAddition') && $this->registerHook('categoryUpdate') && $this->registerHook('attributeGroupForm') + && $this->registerHook('afterSaveAttributeGroup') && $this->registerHook('afterDeleteAttributeGroup') && $this->registerHook('featureForm') + && $this->registerHook('afterDeleteFeature') && $this->registerHook('afterSaveFeature') && $this->registerHook('categoryDeletion') + && $this->registerHook('afterSaveProduct') && $this->registerHook('productListAssign') && $this->registerHook('postProcessAttributeGroup') + && $this->registerHook('postProcessFeature') && $this->registerHook('featureValueForm') && $this->registerHook('postProcessFeatureValue') + && $this->registerHook('afterDeleteFeatureValue') && $this->registerHook('afterSaveFeatureValue') && $this->registerHook('attributeForm') + && $this->registerHook('postProcessAttribute') && $this->registerHook('afterDeleteAttribute') && $this->registerHook('afterSaveAttribute')) + { + if (version_compare(_PS_VERSION_, '1.6.0', '>=') === true) + { + // Hook the module either on the left or right column + $theme = new Theme(Context::getContext()->shop->id_theme); + if ((!$theme->default_left_column || !$this->registerHook('leftColumn')) + && (!$theme->default_right_column || !$this->registerHook('rightColumn'))) + { + // If there are no colums implemented by the template, throw an error and uninstall the module + $this->_errors[] = $this->l('This module need to be hooked in a column and your theme does not implement one'); + parent::uninstall(); + return false; + } + } + else + $this->registerHook('leftColumn'); + + Configuration::updateValue('PS_LAYERED_HIDE_0_VALUES', 1); + Configuration::updateValue('PS_LAYERED_SHOW_QTIES', 1); + Configuration::updateValue('PS_LAYERED_FULL_TREE', 1); + Configuration::updateValue('PS_LAYERED_FILTER_PRICE_USETAX', 1); + Configuration::updateValue('PS_LAYERED_FILTER_CATEGORY_DEPTH', 1); + Configuration::updateValue('PS_LAYERED_FILTER_INDEX_QTY', 0); + Configuration::updateValue('PS_LAYERED_FILTER_INDEX_CDT', 0); + Configuration::updateValue('PS_LAYERED_FILTER_INDEX_MNF', 0); + Configuration::updateValue('PS_LAYERED_FILTER_INDEX_CAT', 0); + Configuration::updateValue('PS_ATTRIBUTE_ANCHOR_SEPARATOR', '-'); + + $this->rebuildLayeredStructure(); + $this->buildLayeredCategories(); + + $products_count = Db::getInstance()->getValue('SELECT COUNT(*) FROM `'._DB_PREFIX_.'product`'); + + if ($products_count < 20000) // Lock template filter creation if too many products + $this->rebuildLayeredCache(); + + self::installPriceIndexTable(); + $this->installFriendlyUrlTable(); + $this->installIndexableAttributeTable(); + $this->installProductAttributeTable(); + + if ($products_count < 5000) // Lock indexation if too many products + { + self::fullPricesIndexProcess(); + $this->indexUrl(); + $this->indexAttribute(); + } + + return true; + } + else + { + // Installation failed (or hook registration) => uninstall the module + $this->uninstall(); + return false; + } + } + + public function uninstall() + { + /* Delete all configurations */ + Configuration::deleteByName('PS_LAYERED_HIDE_0_VALUES'); + Configuration::deleteByName('PS_LAYERED_SHOW_QTIES'); + Configuration::deleteByName('PS_LAYERED_FULL_TREE'); + Configuration::deleteByName('PS_LAYERED_INDEXED'); + Configuration::deleteByName('PS_LAYERED_FILTER_PRICE_USETAX'); + Configuration::deleteByName('PS_LAYERED_FILTER_CATEGORY_DEPTH'); + Configuration::deleteByName('PS_LAYERED_FILTER_INDEX_QTY'); + Configuration::deleteByName('PS_LAYERED_FILTER_INDEX_CDT'); + Configuration::deleteByName('PS_LAYERED_FILTER_INDEX_MNF'); + Configuration::deleteByName('PS_LAYERED_FILTER_INDEX_CAT'); + + Db::getInstance()->execute('DROP TABLE IF EXISTS '._DB_PREFIX_.'layered_price_index'); + Db::getInstance()->execute('DROP TABLE IF EXISTS '._DB_PREFIX_.'layered_friendly_url'); + Db::getInstance()->execute('DROP TABLE IF EXISTS '._DB_PREFIX_.'layered_indexable_attribute_group'); + Db::getInstance()->execute('DROP TABLE IF EXISTS '._DB_PREFIX_.'layered_indexable_feature'); + Db::getInstance()->execute('DROP TABLE IF EXISTS '._DB_PREFIX_.'layered_indexable_attribute_group_lang_value'); + Db::getInstance()->execute('DROP TABLE IF EXISTS '._DB_PREFIX_.'layered_indexable_feature_lang_value'); + Db::getInstance()->execute('DROP TABLE IF EXISTS '._DB_PREFIX_.'layered_category'); + Db::getInstance()->execute('DROP TABLE IF EXISTS '._DB_PREFIX_.'layered_filter'); + Db::getInstance()->execute('DROP TABLE IF EXISTS '._DB_PREFIX_.'layered_filter_shop'); + Db::getInstance()->execute('DROP TABLE IF EXISTS '._DB_PREFIX_.'layered_product_attribute'); + return parent::uninstall(); + } + + private static function installPriceIndexTable() + { + Db::getInstance()->execute('DROP TABLE IF EXISTS `'._DB_PREFIX_.'layered_price_index`'); + + Db::getInstance()->execute(' + CREATE TABLE `'._DB_PREFIX_.'layered_price_index` ( + `id_product` INT NOT NULL, + `id_currency` INT NOT NULL, + `id_shop` INT NOT NULL, + `price_min` INT NOT NULL, + `price_max` INT NOT NULL, + PRIMARY KEY (`id_product`, `id_currency`, `id_shop`), + INDEX `id_currency` (`id_currency`), + INDEX `price_min` (`price_min`), INDEX `price_max` (`price_max`) + ) ENGINE='._MYSQL_ENGINE_.' DEFAULT CHARSET=utf8;'); + } + + private function installFriendlyUrlTable() + { + Db::getInstance()->execute('DROP TABLE IF EXISTS `'._DB_PREFIX_.'layered_friendly_url`'); + Db::getInstance()->execute(' + CREATE TABLE `'._DB_PREFIX_.'layered_friendly_url` ( + `id_layered_friendly_url` INT NOT NULL AUTO_INCREMENT, + `url_key` varchar(32) NOT NULL, + `data` varchar(200) NOT NULL, + `id_lang` INT NOT NULL, + PRIMARY KEY (`id_layered_friendly_url`), + INDEX `id_lang` (`id_lang`) + ) ENGINE='._MYSQL_ENGINE_.' DEFAULT CHARSET=utf8;'); + + Db::getInstance()->execute('CREATE INDEX `url_key` ON `'._DB_PREFIX_.'layered_friendly_url`(url_key(5))'); + } + + private function installIndexableAttributeTable() + { + // Attributes Groups + Db::getInstance()->execute('DROP TABLE IF EXISTS `'._DB_PREFIX_.'layered_indexable_attribute_group`'); + Db::getInstance()->execute(' + CREATE TABLE `'._DB_PREFIX_.'layered_indexable_attribute_group` ( + `id_attribute_group` INT NOT NULL, + `indexable` BOOL NOT NULL DEFAULT 0, + PRIMARY KEY (`id_attribute_group`) + ) ENGINE='._MYSQL_ENGINE_.' DEFAULT CHARSET=utf8;'); + Db::getInstance()->execute(' + INSERT INTO `'._DB_PREFIX_.'layered_indexable_attribute_group` + SELECT id_attribute_group, 1 FROM `'._DB_PREFIX_.'attribute_group`'); + + Db::getInstance()->execute('DROP TABLE IF EXISTS `'._DB_PREFIX_.'layered_indexable_attribute_group_lang_value`'); + Db::getInstance()->execute(' + CREATE TABLE `'._DB_PREFIX_.'layered_indexable_attribute_group_lang_value` ( + `id_attribute_group` INT NOT NULL, + `id_lang` INT NOT NULL, + `url_name` VARCHAR(20), + `meta_title` VARCHAR(20), + PRIMARY KEY (`id_attribute_group`, `id_lang`) + ) ENGINE='._MYSQL_ENGINE_.' DEFAULT CHARSET=utf8;'); + + // Attributes + Db::getInstance()->execute('DROP TABLE IF EXISTS `'._DB_PREFIX_.'layered_indexable_attribute_lang_value`'); + Db::getInstance()->execute(' + CREATE TABLE `'._DB_PREFIX_.'layered_indexable_attribute_lang_value` ( + `id_attribute` INT NOT NULL, + `id_lang` INT NOT NULL, + `url_name` VARCHAR(20), + `meta_title` VARCHAR(20), + PRIMARY KEY (`id_attribute`, `id_lang`) + ) ENGINE='._MYSQL_ENGINE_.' DEFAULT CHARSET=utf8;'); + + + // Features + Db::getInstance()->execute('DROP TABLE IF EXISTS `'._DB_PREFIX_.'layered_indexable_feature`'); + Db::getInstance()->execute(' + CREATE TABLE `'._DB_PREFIX_.'layered_indexable_feature` ( + `id_feature` INT NOT NULL, + `indexable` BOOL NOT NULL DEFAULT 0, + PRIMARY KEY (`id_feature`) + ) ENGINE='._MYSQL_ENGINE_.' DEFAULT CHARSET=utf8;'); + + Db::getInstance()->execute(' + INSERT INTO `'._DB_PREFIX_.'layered_indexable_feature` + SELECT id_feature, 1 FROM `'._DB_PREFIX_.'feature`'); + + Db::getInstance()->execute('DROP TABLE IF EXISTS `'._DB_PREFIX_.'layered_indexable_feature_lang_value`'); + Db::getInstance()->execute(' + CREATE TABLE `'._DB_PREFIX_.'layered_indexable_feature_lang_value` ( + `id_feature` INT NOT NULL, + `id_lang` INT NOT NULL, + `url_name` VARCHAR(20) NOT NULL, + `meta_title` VARCHAR(20), + PRIMARY KEY (`id_feature`, `id_lang`) + ) ENGINE='._MYSQL_ENGINE_.' DEFAULT CHARSET=utf8;'); + + // Features values + Db::getInstance()->execute('DROP TABLE IF EXISTS `'._DB_PREFIX_.'layered_indexable_feature_value_lang_value`'); + Db::getInstance()->execute(' + CREATE TABLE `'._DB_PREFIX_.'layered_indexable_feature_value_lang_value` ( + `id_feature_value` INT NOT NULL, + `id_lang` INT NOT NULL, + `url_name` VARCHAR(20), + `meta_title` VARCHAR(20), + PRIMARY KEY (`id_feature_value`, `id_lang`) + ) ENGINE='._MYSQL_ENGINE_.' DEFAULT CHARSET=utf8;'); + } + + /** + * + * create table product attribute + */ + public function installProductAttributeTable() + { + Db::getInstance()->execute('DROP TABLE IF EXISTS `'._DB_PREFIX_.'layered_product_attribute`'); + Db::getInstance()->execute(' + CREATE TABLE `'._DB_PREFIX_.'layered_product_attribute` ( + `id_attribute` int(10) unsigned NOT NULL, + `id_product` int(10) unsigned NOT NULL, + `id_attribute_group` int(10) unsigned NOT NULL DEFAULT "0", + `id_shop` int(10) unsigned NOT NULL DEFAULT "1", + KEY `id_attribute` (`id_attribute`) + ) ENGINE='._MYSQL_ENGINE_.' DEFAULT CHARSET=utf8;'); + } + + //ATTRIBUTES GROUP + public function hookAfterSaveAttributeGroup($params) + { + if (!$params['id_attribute_group'] || Tools::getValue('layered_indexable') === false) + return; + + Db::getInstance()->execute( + 'DELETE FROM '._DB_PREFIX_.'layered_indexable_attribute_group + WHERE `id_attribute_group` = '.(int)$params['id_attribute_group'] + ); + Db::getInstance()->execute( + 'DELETE FROM '._DB_PREFIX_.'layered_indexable_attribute_group_lang_value + WHERE `id_attribute_group` = '.(int)$params['id_attribute_group'] + ); + + Db::getInstance()->execute( + 'INSERT INTO '._DB_PREFIX_.'layered_indexable_attribute_group + VALUES ('.(int)$params['id_attribute_group'].', '.(int)Tools::getValue('layered_indexable').')' + ); + + + foreach (Language::getLanguages(false) as $language) + { + $seo_url = Tools::getValue('url_name_'.(int)$language['id_lang']); + + if(empty($seo_url)) + $seo_url = Tools::getValue('name_'.(int)$language['id_lang']); + + Db::getInstance()->execute( + 'INSERT INTO '._DB_PREFIX_.'layered_indexable_attribute_group_lang_value + VALUES ( + '.(int)$params['id_attribute_group'].', '.(int)$language['id_lang'].', + \''.pSQL(Tools::link_rewrite($seo_url)).'\', + \''.pSQL(Tools::getValue('meta_title_'.(int)$language['id_lang']), true).'\' + )' + ); + } + } + + public function hookAfterDeleteAttributeGroup($params) + { + if (!$params['id_attribute_group']) + return; + + Db::getInstance()->execute( + 'DELETE FROM '._DB_PREFIX_.'layered_indexable_attribute_group + WHERE `id_attribute_group` = '.(int)$params['id_attribute_group'] + ); + Db::getInstance()->execute( + 'DELETE FROM '._DB_PREFIX_.'layered_indexable_attribute_group_lang_value + WHERE `id_attribute_group` = '.(int)$params['id_attribute_group'] + ); + } + + public function hookPostProcessAttributeGroup($params) + { + $errors = array(); + + foreach (Language::getLanguages(false) as $language) + { + $id_lang = $language['id_lang']; + + if (Tools::getValue('url_name_'.$id_lang)) + if (Tools::link_rewrite(Tools::getValue('url_name_'.$id_lang)) != strtolower(Tools::getValue('url_name_'.$id_lang))) + $params['errors'][] = Tools::displayError(sprintf($this->l('"%s" is not a valid url'), + Tools::getValue('url_name_'.$id_lang))); + } + } + + public function hookAttributeGroupForm($params) + { + $values = array(); + $is_indexable = Db::getInstance(_PS_USE_SQL_SLAVE_)->getValue( + 'SELECT `indexable` + FROM '._DB_PREFIX_.'layered_indexable_attribute_group + WHERE `id_attribute_group` = '.(int)$params['id_attribute_group'] + ); + + if ($is_indexable === false) + $is_indexable = true; + + if ($result = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS( + 'SELECT `url_name`, `meta_title`, `id_lang` FROM '._DB_PREFIX_.'layered_indexable_attribute_group_lang_value + WHERE `id_attribute_group` = '.(int)$params['id_attribute_group'] + )) + foreach ($result as $data) + $values[$data['id_lang']] = array('url_name' => $data['url_name'], 'meta_title' => $data['meta_title']); + + $this->context->smarty->assign(array( + 'languages' => Language::getLanguages(false), + 'default_form_language' => (int)$this->context->controller->default_form_language, + 'values' => $values, + 'is_indexable' =>(bool)$is_indexable + )); + + if (version_compare(_PS_VERSION_, '1.6.0', '>=') === true) + return $this->display(__FILE__, 'attribute_group_form_1.6.tpl'); + else + return $this->display(__FILE__, 'attribute_group_form.tpl'); + } + + //ATTRIBUTES + public function hookAfterSaveAttribute($params) + { + if (!$params['id_attribute']) + return; + + Db::getInstance()->execute( + 'DELETE FROM '._DB_PREFIX_.'layered_indexable_attribute_lang_value + WHERE `id_attribute` = '.(int)$params['id_attribute'] + ); + + foreach (Language::getLanguages(false) as $language) + { + $seo_url = Tools::getValue('url_name_'.(int)$language['id_lang']); + + if(empty($seo_url)) + $seo_url = Tools::getValue('name_'.(int)$language['id_lang']); + + Db::getInstance()->execute( + 'INSERT INTO '._DB_PREFIX_.'layered_indexable_attribute_lang_value + VALUES ( + '.(int)$params['id_attribute'].', '.(int)$language['id_lang'].', + \''.pSQL(Tools::link_rewrite($seo_url)).'\', + \''.pSQL(Tools::getValue('meta_title_'.(int)$language['id_lang']), true).'\' + )' + ); + } + } + + public function hookAfterDeleteAttribute($params) + { + if (!$params['id_attribute']) + return; + + Db::getInstance()->execute( + 'DELETE FROM '._DB_PREFIX_.'layered_indexable_attribute_lang_value + WHERE `id_attribute` = '.(int)$params['id_attribute'] + ); + } + + public function hookPostProcessAttribute($params) + { + $errors = array(); + + foreach (Language::getLanguages(false) as $language) + { + $id_lang = $language['id_lang']; + + if (Tools::getValue('url_name_'.$id_lang)) + if (Tools::link_rewrite(Tools::getValue('url_name_'.$id_lang)) != strtolower(Tools::getValue('url_name_'.$id_lang))) + $params['errors'][] = Tools::displayError(sprintf($this->l('"%s" is not a valid url'), + Tools::getValue('url_name_'.$id_lang))); + } + } + + public function hookAttributeForm($params) + { + $values = array(); + + if ($result = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS( + 'SELECT `url_name`, `meta_title`, `id_lang` + FROM '._DB_PREFIX_.'layered_indexable_attribute_lang_value + WHERE `id_attribute` = '.(int)$params['id_attribute'] + )) + foreach ($result as $data) + $values[$data['id_lang']] = array('url_name' => $data['url_name'], 'meta_title' => $data['meta_title']); + + $this->context->smarty->assign(array( + 'languages' => Language::getLanguages(false), + 'default_form_language' => (int)$this->context->controller->default_form_language, + 'values' => $values + )); + + if (version_compare(_PS_VERSION_, '1.6.0', '>=') === true) + return $this->display(__FILE__, 'attribute_form_1.6.tpl'); + else + return $this->display(__FILE__, 'attribute_form.tpl'); + } + + //FEATURES + public function hookAfterSaveFeature($params) + { + if (!$params['id_feature'] || Tools::getValue('layered_indexable') === false) + return; + + Db::getInstance()->execute( + 'DELETE FROM '._DB_PREFIX_.'layered_indexable_feature + WHERE `id_feature` = '.(int)$params['id_feature'] + ); + Db::getInstance()->execute( + 'DELETE FROM '._DB_PREFIX_.'layered_indexable_feature_lang_value + WHERE `id_feature` = '.(int)$params['id_feature'] + ); + + Db::getInstance()->execute( + 'INSERT INTO '._DB_PREFIX_.'layered_indexable_feature + VALUES ('.(int)$params['id_feature'].', '.(int)Tools::getValue('layered_indexable').')' + ); + + foreach (Language::getLanguages(false) as $language) + { + $seo_url = Tools::getValue('url_name_'.(int)$language['id_lang']); + + if(empty($seo_url)) + $seo_url = Tools::getValue('name_'.(int)$language['id_lang']); + + Db::getInstance()->execute( + 'INSERT INTO '._DB_PREFIX_.'layered_indexable_feature_lang_value + VALUES ( + '.(int)$params['id_feature'].', '.(int)$language['id_lang'].', + \''.pSQL(Tools::link_rewrite($seo_url)).'\', + \''.pSQL(Tools::getValue('meta_title_'.(int)$language['id_lang']), true).'\' + )' + ); + } + } + + public function hookAfterDeleteFeature($params) + { + if (!$params['id_feature']) + return; + + Db::getInstance()->execute( + 'DELETE FROM '._DB_PREFIX_.'layered_indexable_feature + WHERE `id_feature` = '.(int)$params['id_feature'] + ); + } + + public function hookPostProcessFeature($params) + { + $errors = array(); + + foreach (Language::getLanguages(false) as $language) + { + $id_lang = $language['id_lang']; + + if (Tools::getValue('url_name_'.$id_lang)) + if (Tools::link_rewrite(Tools::getValue('url_name_'.$id_lang)) != strtolower(Tools::getValue('url_name_'.$id_lang))) + $params['errors'][] = Tools::displayError(sprintf($this->l('"%s" is not a valid url'), + Tools::getValue('url_name_'.$id_lang))); + } + } + + public function hookFeatureForm($params) + { + $values = array(); + $is_indexable = Db::getInstance(_PS_USE_SQL_SLAVE_)->getValue( + 'SELECT `indexable` + FROM '._DB_PREFIX_.'layered_indexable_feature + WHERE `id_feature` = '.(int)$params['id_feature'] + ); + + if ($is_indexable === false) + $is_indexable = true; + + if ($result = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS( + 'SELECT `url_name`, `meta_title`, `id_lang` FROM '._DB_PREFIX_.'layered_indexable_feature_lang_value + WHERE `id_feature` = '.(int)$params['id_feature'] + )) + foreach ($result as $data) + $values[$data['id_lang']] = array('url_name' => $data['url_name'], 'meta_title' => $data['meta_title']); + + $this->context->smarty->assign(array( + 'languages' => Language::getLanguages(false), + 'default_form_language' => (int)$this->context->controller->default_form_language, + 'values' => $values, + 'is_indexable' =>(bool)$is_indexable + )); + + if (version_compare(_PS_VERSION_, '1.6.0', '>=') === true) + return $this->display(__FILE__, 'feature_form_1.6.tpl'); + else + return $this->display(__FILE__, 'feature_form.tpl'); + } + + //FEATURES VALUE + public function hookAfterSaveFeatureValue($params) + { + if (!$params['id_feature_value']) + return; + + //Removing all indexed language data for this attribute value id + Db::getInstance()->execute( + 'DELETE FROM '._DB_PREFIX_.'layered_indexable_feature_value_lang_value + WHERE `id_feature_value` = '.(int)$params['id_feature_value'] + ); + + foreach (Language::getLanguages(false) as $language) + { + $seo_url = Tools::getValue('url_name_'.(int)$language['id_lang']); + + if(empty($seo_url)) + $seo_url = Tools::getValue('name_'.(int)$language['id_lang']); + + Db::getInstance()->execute( + 'INSERT INTO '._DB_PREFIX_.'layered_indexable_feature_value_lang_value + VALUES ( + '.(int)$params['id_feature_value'].', '.(int)$language['id_lang'].', + \''.pSQL(Tools::link_rewrite($seo_url)).'\', + \''.pSQL(Tools::getValue('meta_title_'.(int)$language['id_lang']), true).'\' + )' + ); + } + } + + public function hookAfterDeleteFeatureValue($params) + { + if (!$params['id_feature_value']) + return; + + Db::getInstance()->execute( + 'DELETE FROM '._DB_PREFIX_.'layered_indexable_feature_value_lang_value + WHERE `id_feature_value` = '.(int)$params['id_feature_value'] + ); + } + + public function hookPostProcessFeatureValue($params) + { + $errors = array(); + + foreach (Language::getLanguages(false) as $language) + { + $id_lang = $language['id_lang']; + + if (Tools::getValue('url_name_'.$id_lang)) + if (Tools::link_rewrite(Tools::getValue('url_name_'.$id_lang)) != strtolower(Tools::getValue('url_name_'.$id_lang))) + $params['errors'][] = Tools::displayError(sprintf($this->l('"%s" is not a valid url'), + Tools::getValue('url_name_'.$id_lang))); + } + } + + public function hookFeatureValueForm($params) + { + $values = array(); + + if ($result = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS( + 'SELECT `url_name`, `meta_title`, `id_lang` + FROM '._DB_PREFIX_.'layered_indexable_feature_value_lang_value + WHERE `id_feature_value` = '.(int)$params['id_feature_value'] + )) + foreach ($result as $data) + $values[$data['id_lang']] = array('url_name' => $data['url_name'], 'meta_title' => $data['meta_title']); + + $this->context->smarty->assign(array( + 'languages' => Language::getLanguages(false), + 'default_form_language' => (int)$this->context->controller->default_form_language, + 'values' => $values + )); + + if (version_compare(_PS_VERSION_, '1.6.0', '>=') === true) + return $this->display(__FILE__, 'feature_value_form_1.6.tpl'); + else + return $this->display(__FILE__, 'feature_value_form.tpl'); + } + + public function hookProductListAssign($params) + { + global $smarty; + if (!Configuration::getGlobalValue('PS_LAYERED_INDEXED')) + return; + + $categories_count = Db::getInstance()->getValue(' + SELECT COUNT(*) + FROM '._DB_PREFIX_.'layered_category + WHERE id_category = '.(int)Tools::getValue('id_category', Tools::getValue('id_category_layered', Configuration::get('PS_HOME_CATEGORY'))).' + AND id_shop = '.(int) Context::getContext()->shop->id + ); + + if ($categories_count == 0) + return; + + // Inform the hook was executed + $params['hookExecuted'] = true; + // List of product to overrride categoryController + $params['catProducts'] = array(); + $selected_filters = $this->getSelectedFilters(); + $filter_block = $this->getFilterBlock($selected_filters); + $title = ''; + + if (is_array($filter_block['title_values'])) + foreach ($filter_block['title_values'] as $key => $val) + $title .= ' > '.$key.' '.implode('/', $val); + + $smarty->assign('categoryNameComplement', $title); + $this->getProducts($selected_filters, $params['catProducts'], $params['nbProducts'], $p, $n, $pages_nb, $start, $stop, $range); + // Need a nofollow on the pagination links? + $smarty->assign('no_follow', $filter_block['no_follow']); + } + + public function hookAfterSaveProduct($params) + { + if (!$params['id_product']) + return; + + self::indexProductPrices((int)$params['id_product']); + $this->indexAttribute((int)$params['id_product']); + } + + public function hookLeftColumn($params) + { + return $this->generateFiltersBlock($this->getSelectedFilters()); + } + + public function hookRightColumn($params) + { + return $this->hookLeftColumn($params); + } + + public function hookHeader($params) + { + global $smarty, $cookie; + + // No filters => module disable + if ($filter_block = $this->getFilterBlock($this->getSelectedFilters())) + if ($filter_block['nbr_filterBlocks'] == 0) + return false; + + if (Tools::getValue('id_category', Tools::getValue('id_category_layered', Configuration::get('PS_HOME_CATEGORY'))) == Configuration::get('PS_HOME_CATEGORY')) + return; + + $id_lang = (int)$cookie->id_lang; + $category = new Category((int)Tools::getValue('id_category')); + + // Generate meta title and meta description + $category_title = (empty($category->meta_title[$id_lang]) ? $category->name[$id_lang] : $category->meta_title[$id_lang]); + $category_metas = Meta::getMetaTags($id_lang, 'category'); + $title = ''; + $keywords = ''; + + if (is_array($filter_block['title_values'])) + foreach ($filter_block['title_values'] as $key => $val) + { + $title .= ' > '.$key.' '.implode('/', $val); + $keywords .= $key.' '.implode('/', $val).', '; + } + + $title = $category_title.$title; + + if (!empty($title)) + $smarty->assign('meta_title', $title.' - '.Configuration::get('PS_SHOP_NAME')); + else + $smarty->assign('meta_title', $category_metas['meta_title']); + + $smarty->assign('meta_description', $category_metas['meta_description']); + + $keywords = substr(strtolower($keywords), 0, 1000); + if (!empty($keywords)) + $smarty->assign('meta_keywords', rtrim($category_title.', '.$keywords.', '.$category_metas['meta_keywords'], ', ')); + + + $this->context->controller->addJS(($this->_path).'blocklayered.js'); + $this->context->controller->addJS(_PS_JS_DIR_.'jquery/jquery-ui-1.8.10.custom.min.js'); + $this->context->controller->addJQueryUI('ui.slider'); + $this->context->controller->addCSS(_PS_CSS_DIR_.'jquery-ui-1.8.10.custom.css'); + + if (version_compare(_PS_VERSION_, '1.6.0', '>=') === true) + $this->context->controller->addCSS(($this->_path).'blocklayered.css', 'all'); + else + $this->context->controller->addCSS(($this->_path).'blocklayered-15.css', 'all'); + $this->context->controller->addJQueryPlugin('scrollTo'); + + $filters = $this->getSelectedFilters(); + + // Get non indexable attributes + $attribute_group_list = Db::getInstance()->executeS('SELECT id_attribute_group FROM '._DB_PREFIX_.'layered_indexable_attribute_group WHERE indexable = 0'); + // Get non indexable features + $feature_list = Db::getInstance()->executeS('SELECT id_feature FROM '._DB_PREFIX_.'layered_indexable_feature WHERE indexable = 0'); + + $attributes = array(); + $features = array(); + + $blacklist = array('weight', 'price'); + if (!Configuration::get('PS_LAYERED_FILTER_INDEX_CDT')) + $blacklist[] = 'condition'; + if (!Configuration::get('PS_LAYERED_FILTER_INDEX_QTY')) + $blacklist[] = 'quantity'; + if (!Configuration::get('PS_LAYERED_FILTER_INDEX_MNF')) + $blacklist[] = 'manufacturer'; + if (!Configuration::get('PS_LAYERED_FILTER_INDEX_CAT')) + $blacklist[] = 'category'; + + foreach ($filters as $type => $val) + { + switch ($type) + { + case 'id_attribute_group': + foreach ($val as $attr) + { + $attr_id = preg_replace('/_\d+$/', '', $attr); + if (in_array($attr_id, $attributes) || in_array(array('id_attribute_group' => $attr_id), $attribute_group_list)) + { + $smarty->assign('nobots', true); + $smarty->assign('nofollow', true); + return; + } + $attributes[] = $attr_id; + } + break; + case 'id_feature': + foreach ($val as $feat) + { + $feat_id = preg_replace('/_\d+$/', '', $feat); + if (in_array($feat_id, $features) || in_array(array('id_feature' => $feat_id), $feature_list)) + { + $smarty->assign('nobots', true); + $smarty->assign('nofollow', true); + return; + } + $features[] = $feat_id; + } + break; + default: + if (in_array($type, $blacklist)) + { + if (count($val)) + { + $smarty->assign('nobots', true); + $smarty->assign('nofollow', true); + return; + } + } + elseif (count($val) > 1) + { + $smarty->assign('nobots', true); + $smarty->assign('nofollow', true); + return; + } + break; + } + } + } + + public function hookFooter($params) + { + // No filters => module disable + if ($filter_block = $this->getFilterBlock($this->getSelectedFilters())) + if ($filter_block['nbr_filterBlocks'] == 0) + return false; + + if (Dispatcher::getInstance()->getController() == 'category') + $this->context->controller->addJS($this->_path.'blocklayered-footer.js'); + } + + public function hookCategoryAddition($params) + { + $this->rebuildLayeredCache(array(), array((int)$params['category']->id)); + } + + public function hookCategoryUpdate($params) + { + /* The category status might (active, inactive) have changed, we have to update the layered cache table structure */ + if (isset($params['category']) && !$params['category']->active) + $this->hookCategoryDeletion($params); + } + + public function hookCategoryDeletion($params) + { + $layered_filter_list = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS( + 'SELECT * FROM '._DB_PREFIX_.'layered_filter' + ); + + foreach ($layered_filter_list as $layered_filter) + { + $data = Tools::unSerialize($layered_filter['filters']); + + if (in_array((int)$params['category']->id, $data['categories'])) + { + unset($data['categories'][array_search((int)$params['category']->id, $data['categories'])]); + Db::getInstance()->execute( + 'UPDATE `'._DB_PREFIX_.'layered_filter` + SET `filters` = \''.pSQL(serialize($data)).'\' + WHERE `id_layered_filter` = '.(int)$layered_filter['id_layered_filter'] + ); + } + } + + $this->buildLayeredCategories(); + } + + /* + * Generate data product attribute + */ + public function indexAttribute($id_product = null) + { + if (is_null($id_product)) + Db::getInstance()->execute('TRUNCATE '._DB_PREFIX_.'layered_product_attribute'); + else + Db::getInstance()->execute(' + DELETE FROM '._DB_PREFIX_.'layered_product_attribute + WHERE id_product = '.(int)$id_product + ); + + Db::getInstance()->execute(' + INSERT INTO `'._DB_PREFIX_.'layered_product_attribute` (`id_attribute`, `id_product`, `id_attribute_group`, `id_shop`) + SELECT pac.id_attribute, pa.id_product, ag.id_attribute_group, product_attribute_shop.`id_shop` + FROM '._DB_PREFIX_.'product_attribute pa'. + Shop::addSqlAssociation('product_attribute', 'pa').' + INNER JOIN '._DB_PREFIX_.'product_attribute_combination pac ON pac.id_product_attribute = pa.id_product_attribute + INNER JOIN '._DB_PREFIX_.'attribute a ON (a.id_attribute = pac.id_attribute) + INNER JOIN '._DB_PREFIX_.'attribute_group ag ON ag.id_attribute_group = a.id_attribute_group + '.(is_null($id_product) ? '' : 'AND pa.id_product = '.(int)$id_product).' + GROUP BY a.id_attribute, pa.id_product , product_attribute_shop.`id_shop`' + ); + + return 1; + } + + /* + * Url indexation + */ + public function indexUrl($ajax = false, $truncate = true) + { + if ($truncate) + Db::getInstance()->execute('TRUNCATE '._DB_PREFIX_.'layered_friendly_url'); + + $attribute_values_by_lang = array(); + $filters = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS(' + SELECT lc.*, id_lang, name, link_rewrite, cl.id_category + FROM '._DB_PREFIX_.'layered_category lc + INNER JOIN '._DB_PREFIX_.'category_lang cl ON (cl.id_category = lc.id_category AND lc.id_category <> 1 ) + GROUP BY type, id_value, id_lang' + ); + + if (!$filters) + return; + + foreach ($filters as $filter) + switch ($filter['type']) + { + case 'id_attribute_group': + $attributes = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS(' + SELECT agl.public_name name, a.id_attribute_group id_name, al.name value, a.id_attribute id_value, al.id_lang, + liagl.url_name name_url_name, lial.url_name value_url_name + FROM '._DB_PREFIX_.'attribute_group ag + INNER JOIN '._DB_PREFIX_.'attribute_group_lang agl ON (agl.id_attribute_group = ag.id_attribute_group) + INNER JOIN '._DB_PREFIX_.'attribute a ON (a.id_attribute_group = ag.id_attribute_group) + INNER JOIN '._DB_PREFIX_.'attribute_lang al ON (al.id_attribute = a.id_attribute) + LEFT JOIN '._DB_PREFIX_.'layered_indexable_attribute_group liag ON (liag.id_attribute_group = a.id_attribute_group) + LEFT JOIN '._DB_PREFIX_.'layered_indexable_attribute_group_lang_value liagl + ON (liagl.id_attribute_group = ag.id_attribute_group AND liagl.id_lang = '.(int)$filter['id_lang'].') + LEFT JOIN '._DB_PREFIX_.'layered_indexable_attribute_lang_value lial + ON (lial.id_attribute = a.id_attribute AND lial.id_lang = '.(int)$filter['id_lang'].') + WHERE a.id_attribute_group = '.(int)$filter['id_value'].' AND agl.id_lang = al.id_lang AND agl.id_lang = '.(int)$filter['id_lang'] + ); + + foreach ($attributes as $attribute) + { + if (!isset($attribute_values_by_lang[$attribute['id_lang']])) + $attribute_values_by_lang[$attribute['id_lang']] = array(); + if (!isset($attribute_values_by_lang[$attribute['id_lang']]['c'.$attribute['id_name']])) + $attribute_values_by_lang[$attribute['id_lang']]['c'.$attribute['id_name']] = array(); + $attribute_values_by_lang[$attribute['id_lang']]['c'.$attribute['id_name']][] = array( + 'name' => (!empty($attribute['name_url_name']) ? $attribute['name_url_name'] : $attribute['name']), + 'id_name' => 'c'.$attribute['id_name'], + 'value' => (!empty($attribute['value_url_name']) ? $attribute['value_url_name'] : $attribute['value']), + 'id_value' => $attribute['id_name'].'_'.$attribute['id_value'], + 'id_id_value' => $attribute['id_value'], + 'category_name' => $filter['link_rewrite'], + 'type' => $filter['type']); + } + break; + + case 'id_feature': + $features = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS(' + SELECT fl.name name, fl.id_feature id_name, fvl.id_feature_value id_value, fvl.value value, fl.id_lang, fl.id_lang, + lifl.url_name name_url_name, lifvl.url_name value_url_name + FROM '._DB_PREFIX_.'feature_lang fl + LEFT JOIN '._DB_PREFIX_.'layered_indexable_feature lif ON (lif.id_feature = fl.id_feature) + INNER JOIN '._DB_PREFIX_.'feature_value fv ON (fv.id_feature = fl.id_feature) + INNER JOIN '._DB_PREFIX_.'feature_value_lang fvl ON (fvl.id_feature_value = fv.id_feature_value) + LEFT JOIN '._DB_PREFIX_.'layered_indexable_feature_lang_value lifl + ON (lifl.id_feature = fl.id_feature AND lifl.id_lang = '.(int)$filter['id_lang'].') + LEFT JOIN '._DB_PREFIX_.'layered_indexable_feature_value_lang_value lifvl + ON (lifvl.id_feature_value = fvl.id_feature_value AND lifvl.id_lang = '.(int)$filter['id_lang'].') + WHERE fl.id_feature = '.(int)$filter['id_value'].' AND fvl.id_lang = fl.id_lang AND fvl.id_lang = '.(int)$filter['id_lang'] + ); + + foreach ($features as $feature) + { + if (!isset($attribute_values_by_lang[$feature['id_lang']])) + $attribute_values_by_lang[$feature['id_lang']] = array(); + if (!isset($attribute_values_by_lang[$feature['id_lang']]['f'.$feature['id_name']])) + $attribute_values_by_lang[$feature['id_lang']]['f'.$feature['id_name']] = array(); + $attribute_values_by_lang[$feature['id_lang']]['f'.$feature['id_name']][] = array( + 'name' => (!empty($feature['name_url_name']) ? $feature['name_url_name'] : $feature['name']), + 'id_name' => 'f'.$feature['id_name'], + 'value' => (!empty($feature['value_url_name']) ? $feature['value_url_name'] : $feature['value']), + 'id_value' => $feature['id_name'].'_'.$feature['id_value'], + 'id_id_value' => $feature['id_value'], + 'category_name' => $filter['link_rewrite'], + 'type' => $filter['type']); + } + break; + + case 'category': + $categories = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS(' + SELECT cl.name, cl.id_lang, c.id_category + FROM '._DB_PREFIX_.'category c + INNER JOIN '._DB_PREFIX_.'category_lang cl ON (c.id_category = cl.id_category) + WHERE cl.id_lang = '.(int)$filter['id_lang'] + ); + + foreach ($categories as $category) + { + if (!isset($attribute_values_by_lang[$category['id_lang']])) + $attribute_values_by_lang[$category['id_lang']] = array(); + if (!isset($attribute_values_by_lang[$category['id_lang']]['category'])) + $attribute_values_by_lang[$category['id_lang']]['category'] = array(); + $attribute_values_by_lang[$category['id_lang']]['category'][] = array('name' => $this->translateWord('Categories', $category['id_lang']), + 'id_name' => null, 'value' => $category['name'], 'id_value' => $category['id_category'], + 'category_name' => $filter['link_rewrite'], 'type' => $filter['type']); + } + break; + + case 'manufacturer': + $manufacturers = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS(' + SELECT m.name as name,l.id_lang as id_lang, id_manufacturer + FROM '._DB_PREFIX_.'manufacturer m , '._DB_PREFIX_.'lang l + WHERE l.id_lang = '.(int)$filter['id_lang'] + ); + + foreach ($manufacturers as $manufacturer) + { + if (!isset($attribute_values_by_lang[$manufacturer['id_lang']])) + $attribute_values_by_lang[$manufacturer['id_lang']] = array(); + if (!isset($attribute_values_by_lang[$manufacturer['id_lang']]['manufacturer'])) + $attribute_values_by_lang[$manufacturer['id_lang']]['manufacturer'] = array(); + $attribute_values_by_lang[$manufacturer['id_lang']]['manufacturer'][] = array('name' => $this->translateWord('Manufacturer', $manufacturer['id_lang']), + 'id_name' => null, 'value' => $manufacturer['name'], 'id_value' => $manufacturer['id_manufacturer'], + 'category_name' => $filter['link_rewrite'], 'type' => $filter['type']); + } + break; + + case 'quantity': + $avaibility_list = array( + $this->translateWord('Not available', (int)$filter['id_lang']), + $this->translateWord('In stock', (int)$filter['id_lang']) + ); + foreach ($avaibility_list as $key => $quantity) + $attribute_values_by_lang[$filter['id_lang']]['quantity'][] = array('name' => $this->translateWord('Availability', (int)$filter['id_lang']), + 'id_name' => null, 'value' => $quantity, 'id_value' => $key, 'id_id_value' => 0, + 'category_name' => $filter['link_rewrite'], 'type' => $filter['type']); + break; + + case 'condition': + $condition_list = array( + 'new' => $this->translateWord('New', (int)$filter['id_lang']), + 'used' => $this->translateWord('Used', (int)$filter['id_lang']), + 'refurbished' => $this->translateWord('Refurbished', (int)$filter['id_lang']) + ); + foreach ($condition_list as $key => $condition) + $attribute_values_by_lang[$filter['id_lang']]['condition'][] = array('name' => $this->translateWord('Condition', (int)$filter['id_lang']), + 'id_name' => null, 'value' => $condition, 'id_value' => $key, + 'category_name' => $filter['link_rewrite'], 'type' => $filter['type']); + break; + } + + // Foreach langs + foreach ($attribute_values_by_lang as $id_lang => $attribute_values) + { + // Foreach attributes generate a couple "/_". For example: color_blue + foreach ($attribute_values as $attribute) + foreach ($attribute as $param) + { + $selected_filters = array(); + $link = '/'.str_replace($this->getAnchor(), '_', Tools::link_rewrite($param['name'])).$this->getAnchor().str_replace($this->getAnchor(), '_', Tools::link_rewrite($param['value'])); + $selected_filters[$param['type']] = array(); + + if (!isset($param['id_id_value'])) + $param['id_id_value'] = $param['id_value']; + + $selected_filters[$param['type']][$param['id_id_value']] = $param['id_value']; + $url_key = md5($link); + $id_layered_friendly_url = Db::getInstance()->getValue(' + SELECT id_layered_friendly_url + FROM `'._DB_PREFIX_.'layered_friendly_url` WHERE `id_lang` = '.$id_lang.' AND `url_key` = \''.$url_key.'\'' + ); + + if ($id_layered_friendly_url == false) + { + Db::getInstance()->AutoExecute(_DB_PREFIX_.'layered_friendly_url', array('url_key' => $url_key, 'data' => serialize($selected_filters), 'id_lang' => $id_lang), 'INSERT'); + $id_layered_friendly_url = Db::getInstance()->Insert_ID(); + } + } + } + + if ($ajax) + return '{"result": 1}'; + else + return 1; + } + + /* + * $cursor $cursor in order to restart indexing from the last state + */ + public static function fullPricesIndexProcess($cursor = 0, $ajax = false, $smart = false) + { + if ($cursor == 0 && !$smart) + self::installPriceIndexTable(); + + return self::indexPrices($cursor, true, $ajax, $smart); + } + + /* + * $cursor $cursor in order to restart indexing from the last state + */ + public static function pricesIndexProcess($cursor = 0, $ajax = false) + { + return self::indexPrices($cursor, false, $ajax); + } + + private static function indexPrices($cursor = null, $full = false, $ajax = false, $smart = false) + { + if ($full) + $nb_products = (int)Db::getInstance()->getValue(' + SELECT count(DISTINCT p.`id_product`) + FROM '._DB_PREFIX_.'product p + INNER JOIN `'._DB_PREFIX_.'product_shop` ps + ON (ps.`id_product` = p.`id_product` AND ps.`active` = 1 AND ps.`visibility` IN ("both", "catalog"))'); + else + $nb_products = (int)Db::getInstance()->getValue(' + SELECT COUNT(DISTINCT p.`id_product`) FROM `'._DB_PREFIX_.'product` p + INNER JOIN `'._DB_PREFIX_.'product_shop` ps + ON (ps.`id_product` = p.`id_product` AND ps.`active` = 1 AND ps.`visibility` IN ("both", "catalog")) + LEFT JOIN `'._DB_PREFIX_.'layered_price_index` psi ON (psi.id_product = p.id_product) + WHERE psi.id_product IS NULL'); + + $max_executiontime = @ini_get('max_execution_time'); + if ($max_executiontime > 5 || $max_executiontime <= 0) + $max_executiontime = 5; + + $start_time = microtime(true); + + if (function_exists('memory_get_peak_usage')) + do + { + $cursor = (int)self::indexPricesUnbreakable((int)$cursor, $full, $smart); + $time_elapsed = microtime(true) - $start_time; + } + while ($cursor < $nb_products && Tools::getMemoryLimit() > memory_get_peak_usage() && $time_elapsed < $max_executiontime); + else + do + { + $cursor = (int)self::indexPricesUnbreakable((int)$cursor, $full, $smart); + $time_elapsed = microtime(true) - $start_time; + } + while ($cursor < $nb_products && $time_elapsed < $max_executiontime); + + if (($nb_products > 0 && !$full || $cursor < $nb_products && $full) && !$ajax) + { + $token = substr(Tools::encrypt('blocklayered/index'), 0, 10); + if (Tools::usingSecureMode()) + $domain = Tools::getShopDomainSsl(true); + else + $domain = Tools::getShopDomain(true); + + if (!Tools::file_get_contents($domain.__PS_BASE_URI__.'modules/blocklayered/blocklayered-price-indexer.php?token='.$token.'&cursor='.(int)$cursor.'&full='.(int)$full)) + self::indexPrices((int)$cursor, (int)$full); + return $cursor; + } + if ($ajax && $nb_products > 0 && $cursor < $nb_products && $full) + return '{"cursor": '.$cursor.', "count": '.($nb_products - $cursor).'}'; + else if ($ajax && $nb_products > 0 && !$full) + return '{"cursor": '.$cursor.', "count": '.($nb_products).'}'; + else + { + Configuration::updateGlobalValue('PS_LAYERED_INDEXED', 1); + + if ($ajax) + return '{"result": "ok"}'; + else + return -1; + } + } + + /* + * $cursor $cursor in order to restart indexing from the last state + */ + private static function indexPricesUnbreakable($cursor, $full = false, $smart = false) + { + static $length = 100; // Nb of products to index + + if (is_null($cursor)) + $cursor = 0; + + if ($full) + $query = ' + SELECT p.`id_product` + FROM `'._DB_PREFIX_.'product` p + INNER JOIN `'._DB_PREFIX_.'product_shop` ps + ON (ps.`id_product` = p.`id_product` AND ps.`active` = 1 AND ps.`visibility` IN ("both", "catalog")) + GROUP BY p.`id_product` + ORDER BY p.`id_product` LIMIT '.(int)$cursor.','.(int)$length; + else + $query = ' + SELECT p.`id_product` + FROM `'._DB_PREFIX_.'product` p + INNER JOIN `'._DB_PREFIX_.'product_shop` ps + ON (ps.`id_product` = p.`id_product` AND ps.`active` = 1 AND ps.`visibility` IN ("both", "catalog")) + LEFT JOIN `'._DB_PREFIX_.'layered_price_index` psi ON (psi.id_product = p.id_product) + WHERE psi.id_product IS NULL + GROUP BY p.`id_product` + ORDER BY p.`id_product` LIMIT 0,'.(int)$length; + + foreach (Db::getInstance()->executeS($query) as $product) + self::indexProductPrices((int)$product['id_product'], ($smart && $full)); + + return (int)($cursor + $length); + } + + public static function indexProductPrices($id_product, $smart = true) + { + static $groups = null; + + if (is_null($groups)) + { + $groups = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS('SELECT id_group FROM `'._DB_PREFIX_.'group_reduction`'); + if (!$groups) + $groups = array(); + } + + $shop_list = Shop::getShops(false, null, true); + + foreach ($shop_list as $id_shop) + { + static $currency_list = null; + + if (is_null($currency_list)) + $currency_list = Currency::getCurrencies(false, 1, new Shop($id_shop)); + + $min_price = array(); + $max_price = array(); + + if ($smart) + Db::getInstance()->execute('DELETE FROM `'._DB_PREFIX_.'layered_price_index` WHERE `id_product` = '.(int)$id_product.' AND `id_shop` = '.(int)$id_shop); + + if (Configuration::get('PS_LAYERED_FILTER_PRICE_USETAX')) + $max_tax_rate = Db::getInstance()->getValue(' + SELECT max(t.rate) max_rate + FROM `'._DB_PREFIX_.'product_shop` p + 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.') + LEFT JOIN `'._DB_PREFIX_.'tax_rule` tr ON (tr.id_tax_rules_group = trg.id_tax_rules_group) + LEFT JOIN `'._DB_PREFIX_.'tax` t ON (t.id_tax = tr.id_tax AND t.active = 1) + WHERE id_product = '.(int)$id_product.' + GROUP BY id_product'); + else + $max_tax_rate = 0; + + $product_min_prices = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS(' + SELECT id_shop, id_currency, id_country, id_group, from_quantity + FROM `'._DB_PREFIX_.'specific_price` + WHERE id_product = '.(int)$id_product); + + // Get min price + foreach ($currency_list as $currency) + { + $price = Product::priceCalculation($id_shop, (int)$id_product, null, null, null, null, + $currency['id_currency'], null, null, false, 6, false, true, true, + $specific_price_output, true); + + if (!isset($max_price[$currency['id_currency']])) + $max_price[$currency['id_currency']] = 0; + if (!isset($min_price[$currency['id_currency']])) + $min_price[$currency['id_currency']] = null; + if ($price > $max_price[$currency['id_currency']]) + $max_price[$currency['id_currency']] = $price; + if ($price == 0) + continue; + if (is_null($min_price[$currency['id_currency']]) || $price < $min_price[$currency['id_currency']]) + $min_price[$currency['id_currency']] = $price; + } + + foreach ($product_min_prices as $specific_price) + foreach ($currency_list as $currency) + { + if ($specific_price['id_currency'] && $specific_price['id_currency'] != $currency['id_currency']) + continue; + $price = Product::priceCalculation((($specific_price['id_shop'] == 0) ? null : (int)$specific_price['id_shop']), (int)$id_product, + null, (($specific_price['id_country'] == 0) ? null : $specific_price['id_country']), null, null, + $currency['id_currency'], (($specific_price['id_group'] == 0) ? null : $specific_price['id_group']), + $specific_price['from_quantity'], false, 6, false, true, true, $specific_price_output, true); + + if (!isset($max_price[$currency['id_currency']])) + $max_price[$currency['id_currency']] = 0; + if (!isset($min_price[$currency['id_currency']])) + $min_price[$currency['id_currency']] = null; + if ($price > $max_price[$currency['id_currency']]) + $max_price[$currency['id_currency']] = $price; + if ($price == 0) + continue; + if (is_null($min_price[$currency['id_currency']]) || $price < $min_price[$currency['id_currency']]) + $min_price[$currency['id_currency']] = $price; + } + + foreach ($groups as $group) + foreach ($currency_list as $currency) + { + $price = Product::priceCalculation(null, (int)$id_product, null, null, null, null, (int)$currency['id_currency'], (int)$group['id_group'], + null, false, 6, false, true, true, $specific_price_output, true); + + if (!isset($max_price[$currency['id_currency']])) + $max_price[$currency['id_currency']] = 0; + if (!isset($min_price[$currency['id_currency']])) + $min_price[$currency['id_currency']] = null; + if ($price > $max_price[$currency['id_currency']]) + $max_price[$currency['id_currency']] = $price; + if ($price == 0) + continue; + if (is_null($min_price[$currency['id_currency']]) || $price < $min_price[$currency['id_currency']]) + $min_price[$currency['id_currency']] = $price; + } + + $values = array(); + foreach ($currency_list as $currency) + $values[] = '('.(int)$id_product.', + '.(int)$currency['id_currency'].', + '.$id_shop.', + '.(int)$min_price[$currency['id_currency']].', + '.(int)Tools::ps_round($max_price[$currency['id_currency']] * (100 + $max_tax_rate) / 100, 0).')'; + + Db::getInstance()->execute(' + INSERT INTO `'._DB_PREFIX_.'layered_price_index` (id_product, id_currency, id_shop, price_min, price_max) + VALUES '.implode(',', $values).' + ON DUPLICATE KEY UPDATE id_product = id_product # avoid duplicate keys'); + } + } + + public function translateWord($string, $id_lang ) + { + static $_MODULES = array(); + global $_MODULE; + + $file = _PS_MODULE_DIR_.$this->name.'/translations/'.Language::getIsoById($id_lang).'.php'; + + if (!array_key_exists($id_lang, $_MODULES)) + { + if (file_exists($file1 = _PS_MODULE_DIR_.$this->name.'/translations/'.Language::getIsoById($id_lang).'.php')) + { + include($file1); + $_MODULES[$id_lang] = $_MODULE; + } + elseif (file_exists($file2 = _PS_MODULE_DIR_.$this->name.'/'.Language::getIsoById($id_lang).'.php')) + { + include($file2); + $_MODULES[$id_lang] = $_MODULE; + } + else + return $string; + } + + $string = str_replace('\'', '\\\'', $string); + + // set array key to lowercase for 1.3 compatibility + $_MODULES[$id_lang] = array_change_key_case($_MODULES[$id_lang]); + $current_key = '<{'.strtolower( $this->name).'}'.strtolower(_THEME_NAME_).'>'.strtolower($this->name).'_'.md5($string); + $default_key = '<{'.strtolower( $this->name).'}prestashop>'.strtolower($this->name).'_'.md5($string); + + if (isset($_MODULES[$id_lang][$current_key])) + $ret = stripslashes($_MODULES[$id_lang][$current_key]); + else if (isset($_MODULES[$id_lang][Tools::strtolower($current_key)])) + $ret = stripslashes($_MODULES[$id_lang][Tools::strtolower($current_key)]); + else if (isset($_MODULES[$id_lang][$default_key])) + $ret = stripslashes($_MODULES[$id_lang][$default_key]); + else if (isset($_MODULES[$id_lang][Tools::strtolower($default_key)])) + $ret = stripslashes($_MODULES[$id_lang][Tools::strtolower($default_key)]); + else + $ret = stripslashes($string); + + return str_replace('"', '"', $ret); + } + + public function getContent() + { + global $cookie; + $message = ''; + + if (Tools::isSubmit('SubmitFilter')) + { + if (!Tools::getValue('layered_tpl_name')) + $message = $this->displayError($this->l('Filter template name required (cannot be empty)')); + elseif (!Tools::getValue('categoryBox')) + $message = $this->displayError($this->l('You must select at least a category')); + else + { + if (Tools::getValue('id_layered_filter')) + { + Db::getInstance()->execute(' + DELETE FROM '._DB_PREFIX_.'layered_filter + WHERE id_layered_filter = '.(int)Tools::getValue('id_layered_filter') + ); + $this->buildLayeredCategories(); + } + + if (Tools::getValue('scope') == 1) + { + Db::getInstance()->execute('TRUNCATE TABLE '._DB_PREFIX_.'layered_filter'); + $categories = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS(' + SELECT id_category + FROM '._DB_PREFIX_.'category' + ); + + foreach ($categories as $category) + $_POST['categoryBox'][] = (int)$category['id_category']; + } + + $id_layered_filter = (int)Tools::getValue('id_layered_filter'); + + if (!$id_layered_filter) + $id_layered_filter = (int)Db::getInstance()->Insert_ID(); + + $shop_list = array(); + + if (isset($_POST['checkBoxShopAsso_layered_filter'])) + { + foreach ($_POST['checkBoxShopAsso_layered_filter'] as $id_shop => $row) + { + $assos[] = array('id_object' => (int)$id_layered_filter, 'id_shop' => (int)$id_shop); + $shop_list[] = (int)$id_shop; + } + } + else + $shop_list = array(Context::getContext()->shop->id); + + Db::getInstance()->execute(' + DELETE FROM '._DB_PREFIX_.'layered_filter_shop + WHERE `id_layered_filter` = '.(int)$id_layered_filter + ); + + if (count($_POST['categoryBox'])) + { + /* Clean categoryBox before use */ + if (isset($_POST['categoryBox']) && is_array($_POST['categoryBox'])) + foreach ($_POST['categoryBox'] as &$category_box_tmp) + $category_box_tmp = (int)$category_box_tmp; + + $filter_values = array(); + + foreach ($_POST['categoryBox'] as $idc) + $filter_values['categories'][] = (int)$idc; + + $filter_values['shop_list'] = $shop_list; + $values = false; + + foreach ($_POST['categoryBox'] as $id_category_layered) + { + foreach ($_POST as $key => $value) + if (substr($key, 0, 17) == 'layered_selection' && $value == 'on') + { + $values = true; + $type = 0; + $limit = 0; + + if (Tools::getValue($key.'_filter_type')) + $type = Tools::getValue($key.'_filter_type'); + if (Tools::getValue($key.'_filter_show_limit')) + $limit = Tools::getValue($key.'_filter_show_limit'); + + $filter_values[$key] = array( + 'filter_type' => (int)$type, + 'filter_show_limit' => (int)$limit + ); + } + } + + $values_to_insert = array( + 'name' => pSQL(Tools::getValue('layered_tpl_name')), + 'filters' => pSQL(serialize($filter_values)), + 'n_categories' => (int)count($filter_values['categories']), + 'date_add' => date('Y-m-d H:i:s')); + + if (isset($_POST['id_layered_filter']) && $_POST['id_layered_filter']) + $values_to_insert['id_layered_filter'] = (int)Tools::getValue('id_layered_filter'); + + Db::getInstance()->autoExecute(_DB_PREFIX_.'layered_filter', $values_to_insert, 'INSERT'); + $id_layered_filter = (int)Db::getInstance()->Insert_ID(); + + if (isset($assos)) + foreach ($assos as $asso) + Db::getInstance()->execute(' + INSERT INTO '._DB_PREFIX_.'layered_filter_shop (`id_layered_filter`, `id_shop`) + VALUES('.$id_layered_filter.', '.(int)$asso['id_shop'].')' + ); + + $this->buildLayeredCategories(); + $message = $this->displayConfirmation($this->l('Your filter').' "'.Tools::safeOutput(Tools::getValue('layered_tpl_name')).'" '. + ((isset($_POST['id_layered_filter']) && $_POST['id_layered_filter']) ? $this->l('was updated successfully.') : $this->l('was added successfully.'))); + } + } + } + else if (Tools::isSubmit('submitLayeredSettings')) + { + Configuration::updateValue('PS_LAYERED_HIDE_0_VALUES', (int)Tools::getValue('ps_layered_hide_0_values')); + Configuration::updateValue('PS_LAYERED_SHOW_QTIES', (int)Tools::getValue('ps_layered_show_qties')); + Configuration::updateValue('PS_LAYERED_FULL_TREE', (int)Tools::getValue('ps_layered_full_tree')); + Configuration::updateValue('PS_LAYERED_FILTER_PRICE_USETAX', (int)Tools::getValue('ps_layered_filter_price_usetax')); + Configuration::updateValue('PS_LAYERED_FILTER_CATEGORY_DEPTH', (int)Tools::getValue('ps_layered_filter_category_depth')); + Configuration::updateValue('PS_LAYERED_FILTER_INDEX_QTY', (int)Tools::getValue('ps_layered_filter_index_availability')); + Configuration::updateValue('PS_LAYERED_FILTER_INDEX_CDT', (int)Tools::getValue('ps_layered_filter_index_condition')); + Configuration::updateValue('PS_LAYERED_FILTER_INDEX_MNF', (int)Tools::getValue('ps_layered_filter_index_manufacturer')); + Configuration::updateValue('PS_LAYERED_FILTER_INDEX_CAT', (int)Tools::getValue('ps_layered_filter_index_category')); + + if (version_compare(_PS_VERSION_, '1.6.0', '>=') === true) + $message = '
'.$this->l('Settings saved successfully').'
'; + else + $message = '
'.$this->l('Settings saved successfully').'
'; + } + else if (Tools::getValue('deleteFilterTemplate')) + { + $layered_values = Db::getInstance(_PS_USE_SQL_SLAVE_)->getValue(' + SELECT filters + FROM '._DB_PREFIX_.'layered_filter + WHERE id_layered_filter = '.(int)Tools::getValue('id_layered_filter') + ); + + if ($layered_values) + { + Db::getInstance()->execute(' + DELETE FROM '._DB_PREFIX_.'layered_filter + WHERE id_layered_filter = '.(int)Tools::getValue('id_layered_filter').' LIMIT 1' + ); + $this->buildLayeredCategories(); + $message = $this->displayConfirmation($this->l('Filter template deleted, categories updated (reverted to default Filter template).')); + } + else + $message = $this->displayError($this->l('Filter template not found')); + } + + $category_box = array(); + $attribute_groups = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS(' + SELECT ag.id_attribute_group, ag.is_color_group, agl.name, COUNT(DISTINCT(a.id_attribute)) n + FROM '._DB_PREFIX_.'attribute_group ag + LEFT JOIN '._DB_PREFIX_.'attribute_group_lang agl ON (agl.id_attribute_group = ag.id_attribute_group) + LEFT JOIN '._DB_PREFIX_.'attribute a ON (a.id_attribute_group = ag.id_attribute_group) + WHERE agl.id_lang = '.(int)$cookie->id_lang.' + GROUP BY ag.id_attribute_group' + ); + + $features = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS(' + SELECT fl.id_feature, fl.name, COUNT(DISTINCT(fv.id_feature_value)) n + FROM '._DB_PREFIX_.'feature_lang fl + LEFT JOIN '._DB_PREFIX_.'feature_value fv ON (fv.id_feature = fl.id_feature) + WHERE (fv.custom IS NULL OR fv.custom = 0) AND fl.id_lang = '.(int)$cookie->id_lang.' + GROUP BY fl.id_feature' + ); + + if (Shop::isFeatureActive() && count(Shop::getShops(true, null, true)) > 1) + { + $helper = new HelperForm(); + $helper->id = Tools::getValue('id_layered_filter', null); + $helper->table = 'layered_filter'; + $helper->identifier = 'id_layered_filter'; + $this->context->smarty->assign('asso_shops', $helper->renderAssoShop()); + } + + if (version_compare(_PS_VERSION_, '1.6.0', '>=') === true) + { + $tree_categories_helper = new HelperTreeCategories('categories-treeview'); + $tree_categories_helper->setRootCategory((Shop::getContext() == Shop::CONTEXT_SHOP ? Category::getRootCategory()->id_category : 0)) + ->setUseCheckBox(true); + } + else + { + if (Shop::getContext() == Shop::CONTEXT_SHOP) + { + $root_category = Category::getRootCategory(); + $root_category = array('id_category' => $root_category->id_category, 'name' => $root_category->name); + } + else + $root_category = array('id_category' => '0', 'name' => $this->l('Root')); + + $tree_categories_helper = new Helper(); + } + + $module_url = Tools::getProtocol(Tools::usingSecureMode()).$_SERVER['HTTP_HOST'].$this->getPathUri(); + + if (method_exists($this->context->controller, 'addJquery')) + { + $this->context->controller->addJS($this->_path.'js/blocklayered_admin.js'); + + if (version_compare(_PS_VERSION_, '1.6.0.3', '>=') === true) + $this->context->controller->addjqueryPlugin('sortable'); + elseif (version_compare(_PS_VERSION_, '1.6.0', '>=') === true) + $this->context->controller->addJS(_PS_JS_DIR_.'jquery/plugins/jquery.sortable.js'); + else + $this->context->controller->addJS($this->_path.'js/jquery.sortable.js'); + } + + if (version_compare(_PS_VERSION_, '1.6.0', '>=') === true) + $this->context->controller->addCSS($this->_path.'css/blocklayered_admin_1.6.css'); + else + $this->context->controller->addCSS($this->_path.'css/blocklayered_admin.css'); + + if (Tools::getValue('add_new_filters_template')) + { + $this->context->smarty->assign(array( + 'current_url' => $this->context->link->getAdminLink('AdminModules').'&configure=blocklayered&tab_module=front_office_features&module_name=blocklayered', + 'uri' => $this->getPathUri(), + 'id_layered_filter' => 0, + 'template_name' => sprintf($this->l('My template - %s'), date('Y-m-d')), + 'attribute_groups' => $attribute_groups, + 'features' => $features, + 'total_filters' => 6+count($attribute_groups)+count($features) + )); + + if (version_compare(_PS_VERSION_, '1.6.0', '>=') === true) + $this->context->smarty->assign('categories_tree', $tree_categories_helper->render()); + else + $this->context->smarty->assign('categories_tree', $tree_categories_helper->renderCategoryTree( + $root_category, array(), 'categoryBox')); + + if (version_compare(_PS_VERSION_, '1.6.0', '>=') === true) + return $this->display(__FILE__, 'views/templates/admin/add_1.6.tpl'); + else + return $this->display(__FILE__, 'views/templates/admin/add.tpl'); + } + else if (Tools::getValue('edit_filters_template')) + { + $template = Db::getInstance()->getRow(' + SELECT * + FROM `'._DB_PREFIX_.'layered_filter` + WHERE id_layered_filter = '.(int)Tools::getValue('id_layered_filter') + ); + + $filters = unserialize($template['filters']); + + if (version_compare(_PS_VERSION_, '1.6.0', '>=') === true) + { + $tree_categories_helper->setSelectedCategories($filters['categories']); + $this->context->smarty->assign('categories_tree', $tree_categories_helper->render()); + } + else + $this->context->smarty->assign('categories_tree',$tree_categories_helper->renderCategoryTree( + $root_category, $filters['categories'], 'categoryBox')); + + $select_shops = $filters['shop_list']; + unset($filters['categories']); + unset($filters['shop_list']); + + $this->context->smarty->assign(array( + 'current_url' => $this->context->link->getAdminLink('AdminModules').'&configure=blocklayered&tab_module=front_office_features&module_name=blocklayered', + 'uri' => $this->getPathUri(), + 'id_layered_filter' => (int)Tools::getValue('id_layered_filter'), + 'template_name' => $template['name'], + 'attribute_groups' => $attribute_groups, + 'features' => $features, + 'filters' => Tools::jsonEncode($filters), + 'total_filters' => 6+count($attribute_groups)+count($features) + )); + + if (version_compare(_PS_VERSION_, '1.6.0', '>=') === true) + return $this->display(__FILE__, 'views/templates/admin/add_1.6.tpl'); + else + return $this->display(__FILE__, 'views/templates/admin/add.tpl'); + } + else + { + $this->context->smarty->assign(array( + 'message' => $message, + 'uri' => $this->getPathUri(), + 'PS_LAYERED_INDEXED' => Configuration::getGlobalValue('PS_LAYERED_INDEXED'), + 'current_url' => Tools::safeOutput(preg_replace('/&deleteFilterTemplate=[0-9]*&id_layered_filter=[0-9]*/', '', $_SERVER['REQUEST_URI'])), + 'id_lang' => Context::getContext()->cookie->id_lang, + 'token' => substr(Tools::encrypt('blocklayered/index'), 0, 10), + 'base_folder' => urlencode(_PS_ADMIN_DIR_), + 'price_indexer_url' => $module_url.'blocklayered-price-indexer.php'.'?token='.substr(Tools::encrypt('blocklayered/index'), 0, 10), + 'full_price_indexer_url' => $module_url.'blocklayered-price-indexer.php'.'?token='.substr(Tools::encrypt('blocklayered/index'), 0, 10).'&full=1', + 'attribute_indexer_url' => $module_url.'blocklayered-attribute-indexer.php'.'?token='.substr(Tools::encrypt('blocklayered/index'), 0, 10), + 'url_indexer_url' => $module_url.'blocklayered-url-indexer.php'.'?token='.substr(Tools::encrypt('blocklayered/index'), 0, 10).'&truncate=1', + 'filters_templates' => Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS('SELECT * FROM '._DB_PREFIX_.'layered_filter ORDER BY date_add DESC'), + 'hide_values' => Configuration::get('PS_LAYERED_HIDE_0_VALUES'), + 'show_quantities' => Configuration::get('PS_LAYERED_SHOW_QTIES'), + 'full_tree' => Configuration::get('PS_LAYERED_FULL_TREE'), + 'category_depth' => Configuration::get('PS_LAYERED_FILTER_CATEGORY_DEPTH'), + 'price_use_tax' => Configuration::get('PS_LAYERED_FILTER_PRICE_USETAX'), + 'index_cdt' => Configuration::get('PS_LAYERED_FILTER_INDEX_CDT'), + 'index_qty' => Configuration::get('PS_LAYERED_FILTER_INDEX_QTY'), + 'index_mnf' => Configuration::get('PS_LAYERED_FILTER_INDEX_MNF'), + 'index_cat' => Configuration::get('PS_LAYERED_FILTER_INDEX_CAT'), + 'limit_warning' => $this->displayLimitPostWarning(21+count($attribute_groups)*3+count($features)*3) + )); + + if (version_compare(_PS_VERSION_, '1.6.0', '>=') === true) + return $this->display(__FILE__, 'views/templates/admin/view_1.6.tpl'); + else + return $this->display(__FILE__, 'views/templates/admin/view.tpl'); + } + } + + public function displayLimitPostWarning($count) + { + $return = array(); + if ((ini_get('suhosin.post.max_vars') && ini_get('suhosin.post.max_vars') < $count) || (ini_get('suhosin.request.max_vars') && ini_get('suhosin.request.max_vars') < $count)) + { + $return['error_type'] = 'suhosin'; + $return['post.max_vars'] = ini_get('suhosin.post.max_vars'); + $return['request.max_vars'] = ini_get('suhosin.request.max_vars'); + $return['needed_limit'] = $count + 100; + } + elseif (ini_get('max_input_vars') && ini_get('max_input_vars') < $count) + { + $return['error_type'] = 'conf'; + $return['max_input_vars'] = ini_get('max_input_vars'); + $return['needed_limit'] = $count + 100; + } + return $return; + } + + private function getSelectedFilters() + { + $home_category = Configuration::get('PS_HOME_CATEGORY'); + $id_parent = (int)Tools::getValue('id_category', Tools::getValue('id_category_layered', $home_category)); + if ($id_parent == $home_category) + return; + + // Force attributes selection (by url '.../2-mycategory/color-blue' or by get parameter 'selected_filters') + if (strpos($_SERVER['SCRIPT_FILENAME'], 'blocklayered-ajax.php') === false || Tools::getValue('selected_filters') !== false) + { + if (Tools::getValue('selected_filters')) + $url = Tools::getValue('selected_filters'); + else + $url = preg_replace('/\/(?:\w*)\/(?:[0-9]+[-\w]*)([^\?]*)\??.*/', '$1', Tools::safeOutput($_SERVER['REQUEST_URI'], true)); + + $url_attributes = explode('/', ltrim($url, '/')); + $selected_filters = array('category' => array()); + if (!empty($url_attributes)) + { + foreach ($url_attributes as $url_attribute) + { + $url_parameters = explode($this->getAnchor(), $url_attribute); + $attribute_name = array_shift($url_parameters); + if ($attribute_name == 'page') + $this->page = (int)$url_parameters[0]; + else if (in_array($attribute_name, array('price', 'weight'))) + $selected_filters[$attribute_name] = array($url_parameters[0], $url_parameters[1]); + else + { + foreach ($url_parameters as $url_parameter) + { + $data = Db::getInstance()->getValue('SELECT data FROM `'._DB_PREFIX_.'layered_friendly_url` WHERE `url_key` = \''.md5('/'.$attribute_name.$this->getAnchor().$url_parameter).'\''); + if ($data) + foreach (Tools::unSerialize($data) as $key_params => $params) + { + if (!isset($selected_filters[$key_params])) + $selected_filters[$key_params] = array(); + foreach ($params as $key_param => $param) + { + if (!isset($selected_filters[$key_params][$key_param])) + $selected_filters[$key_params][$key_param] = array(); + $selected_filters[$key_params][$key_param] = $param; + } + } + } + } + } + return $selected_filters; + } + } + + /* Analyze all the filters selected by the user and store them into a tab */ + $selected_filters = array('category' => array(), 'manufacturer' => array(), 'quantity' => array(), 'condition' => array()); + foreach ($_GET as $key => $value) + if (substr($key, 0, 8) == 'layered_') + { + preg_match('/^(.*)_([0-9]+|new|used|refurbished|slider)$/', substr($key, 8, strlen($key) - 8), $res); + if (isset($res[1])) + { + $tmp_tab = explode('_', $value); + $value = $tmp_tab[0]; + $id_key = false; + if (isset($tmp_tab[1])) + $id_key = $tmp_tab[1]; + if ($res[1] == 'condition' && in_array($value, array('new', 'used', 'refurbished'))) + $selected_filters['condition'][] = $value; + else if ($res[1] == 'quantity' && (!$value || $value == 1)) + $selected_filters['quantity'][] = $value; + else if (in_array($res[1], array('category', 'manufacturer'))) + { + if (!isset($selected_filters[$res[1].($id_key ? '_'.$id_key : '')])) + $selected_filters[$res[1].($id_key ? '_'.$id_key : '')] = array(); + $selected_filters[$res[1].($id_key ? '_'.$id_key : '')][] = (int)$value; + } + else if (in_array($res[1], array('id_attribute_group', 'id_feature'))) + { + if (!isset($selected_filters[$res[1]])) + $selected_filters[$res[1]] = array(); + $selected_filters[$res[1]][(int)$value] = $id_key.'_'.(int)$value; + } + else if ($res[1] == 'weight') + $selected_filters[$res[1]] = $tmp_tab; + else if ($res[1] == 'price') + $selected_filters[$res[1]] = $tmp_tab; + } + } + return $selected_filters; + } + + public function getProductByFilters($selected_filters = array()) + { + global $cookie; + + if (!empty($this->products)) + return $this->products; + + $home_category = Configuration::get('PS_HOME_CATEGORY'); + /* If the current category isn't defined or if it's homepage, we have nothing to display */ + $id_parent = (int)Tools::getValue('id_category', Tools::getValue('id_category_layered', $home_category)); + if ($id_parent == $home_category) + return false; + + $alias_where = 'p'; + if (version_compare(_PS_VERSION_,'1.5','>')) + $alias_where = 'product_shop'; + + $query_filters_where = ' AND '.$alias_where.'.`active` = 1 AND '.$alias_where.'.`visibility` IN ("both", "catalog")'; + $query_filters_from = ''; + + $parent = new Category((int)$id_parent); + if (!count($selected_filters['category'])) + { + if (Configuration::get('PS_LAYERED_FULL_TREE')) + $query_filters_from .= ' INNER JOIN '._DB_PREFIX_.'category_product cp + ON p.id_product = cp.id_product + INNER JOIN '._DB_PREFIX_.'category c ON (c.id_category = cp.id_category AND + c.nleft >= '.(int)$parent->nleft.' AND c.nright <= '.(int)$parent->nright.' + AND c.active = 1) + RIGHT JOIN '._DB_PREFIX_.'layered_category lc ON (lc.id_category = '.(int)$id_parent.' AND + lc.id_shop = '.(int) Context::getContext()->shop->id.')'; + else + $query_filters_from .= ' INNER JOIN '._DB_PREFIX_.'category_product cp + ON p.id_product = cp.id_product + INNER JOIN '._DB_PREFIX_.'category c ON (c.id_category = cp.id_category + AND c.id_category = '.(int)$id_parent.' + AND c.active = 1)'; + } + + foreach ($selected_filters as $key => $filter_values) + { + if (!count($filter_values)) + continue; + + preg_match('/^(.*[^_0-9])/', $key, $res); + $key = $res[1]; + + switch ($key) + { + case 'id_feature': + $sub_queries = array(); + foreach ($filter_values as $filter_value) + { + $filter_value_array = explode('_', $filter_value); + if (!isset($sub_queries[$filter_value_array[0]])) + $sub_queries[$filter_value_array[0]] = array(); + $sub_queries[$filter_value_array[0]][] = 'fp.`id_feature_value` = '.(int)$filter_value_array[1]; + } + foreach ($sub_queries as $sub_query) + { + $query_filters_where .= ' AND p.id_product IN (SELECT `id_product` FROM `'._DB_PREFIX_.'feature_product` fp WHERE '; + $query_filters_where .= implode(' OR ', $sub_query).') '; + } + break; + + case 'id_attribute_group': + $sub_queries = array(); + + + foreach ($filter_values as $filter_value) + { + $filter_value_array = explode('_', $filter_value); + if (!isset($sub_queries[$filter_value_array[0]])) + $sub_queries[$filter_value_array[0]] = array(); + $sub_queries[$filter_value_array[0]][] = 'pac.`id_attribute` = '.(int)$filter_value_array[1]; + } + foreach ($sub_queries as $sub_query) + { + $query_filters_where .= ' AND p.id_product IN (SELECT pa.`id_product` + FROM `'._DB_PREFIX_.'product_attribute_combination` pac + LEFT JOIN `'._DB_PREFIX_.'product_attribute` pa + ON (pa.`id_product_attribute` = pac.`id_product_attribute`)'. + Shop::addSqlAssociation('product_attribute', 'pa').' + WHERE '.implode(' OR ', $sub_query).') '; + } + break; + + case 'category': + $query_filters_where .= ' AND p.id_product IN (SELECT id_product FROM '._DB_PREFIX_.'category_product cp WHERE '; + foreach ($selected_filters['category'] as $id_category) + $query_filters_where .= 'cp.`id_category` = '.(int)$id_category.' OR '; + $query_filters_where = rtrim($query_filters_where, 'OR ').')'; + break; + + case 'quantity': + if (count($selected_filters['quantity']) == 2) + break; + + $query_filters_where .= ' AND sa.quantity '.(!$selected_filters['quantity'][0] ? '<=' : '>').' 0 '; + $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.') '; + break; + + case 'manufacturer': + $query_filters_where .= ' AND p.id_manufacturer IN ('.implode($selected_filters['manufacturer'], ',').')'; + break; + + case 'condition': + if (count($selected_filters['condition']) == 3) + break; + $query_filters_where .= ' AND '.$alias_where.'.condition IN ('; + foreach ($selected_filters['condition'] as $cond) + $query_filters_where .= '\''.$cond.'\','; + $query_filters_where = rtrim($query_filters_where, ',').')'; + break; + + case 'weight': + if ($selected_filters['weight'][0] != 0 || $selected_filters['weight'][1] != 0) + $query_filters_where .= ' AND p.`weight` BETWEEN '.(float)($selected_filters['weight'][0] - 0.001).' AND '.(float)($selected_filters['weight'][1] + 0.001); + break; + + case 'price': + if (isset($selected_filters['price'])) + { + if ($selected_filters['price'][0] !== '' || $selected_filters['price'][1] !== '') + { + $price_filter = array(); + $price_filter['min'] = (float)($selected_filters['price'][0]); + $price_filter['max'] = (float)($selected_filters['price'][1]); + } + } + else + $price_filter = false; + break; + } + } + + $id_currency = (int)Context::getContext()->currency->id; + + $price_filter_query_in = ''; // All products with price range between price filters limits + $price_filter_query_out = ''; // All products with a price filters limit on it price range + if (isset($price_filter) && $price_filter) + { + $price_filter_query_in = 'INNER JOIN `'._DB_PREFIX_.'layered_price_index` psi + ON + ( + psi.price_min >= '.(int)$price_filter['min'].' + AND psi.price_max <= '.(int)$price_filter['max'].' + AND psi.`id_product` = p.`id_product` + AND psi.`id_currency` = '.$id_currency.' + )'; + + $price_filter_query_out = 'INNER JOIN `'._DB_PREFIX_.'layered_price_index` psi + ON + ((psi.price_min < '.(int)$price_filter['min'].' AND psi.price_max > '.(int)$price_filter['min'].') + OR + (psi.price_max > '.(int)$price_filter['max'].' AND psi.price_min < '.(int)$price_filter['max'].')) + AND psi.`id_product` = p.`id_product` + AND psi.`id_currency` = '.$id_currency; + } + + $query_filters_from .= Shop::addSqlAssociation('product', 'p'); + + $all_products_out = self::query(' + SELECT p.`id_product` id_product + FROM `'._DB_PREFIX_.'product` p + '.$price_filter_query_out.' + '.$query_filters_from.' + WHERE 1 '.$query_filters_where.' GROUP BY id_product'); + + $all_products_in = self::query(' + SELECT p.`id_product` id_product + FROM `'._DB_PREFIX_.'product` p + '.$price_filter_query_in.' + '.$query_filters_from.' + WHERE 1 '.$query_filters_where.' GROUP BY id_product'); + + $product_id_list = array(); + + while ($product = DB::getInstance()->nextRow($all_products_in)) + $product_id_list[] = (int)$product['id_product']; + + while ($product = DB::getInstance()->nextRow($all_products_out)) + if (isset($price_filter) && $price_filter) + { + $price = (int)Product::getPriceStatic($product['id_product'], Configuration::get('PS_LAYERED_FILTER_PRICE_USETAX')); // Cast to int because we don't care about cents + if ($price < $price_filter['min'] || $price > $price_filter['max']) + continue; + $product_id_list[] = (int)$product['id_product']; + } + $this->nbr_products = count($product_id_list); + + if ($this->nbr_products == 0) + $this->products = array(); + else + { + $n = (int)Tools::getValue('n', Configuration::get('PS_PRODUCTS_PER_PAGE')); + $nb_day_new_product = (Validate::isUnsignedInt(Configuration::get('PS_NB_DAYS_NEW_PRODUCT')) ? Configuration::get('PS_NB_DAYS_NEW_PRODUCT') : 20); + + $this->products = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS(' + SELECT + p.*, + '.($alias_where == 'p' ? '' : 'product_shop.*,' ).' + '.$alias_where.'.id_category_default, + pl.*, + MAX(image_shop.`id_image`) id_image, + il.legend, + m.name manufacturer_name, + MAX(product_attribute_shop.id_product_attribute) id_product_attribute, + DATEDIFF('.$alias_where.'.`date_add`, DATE_SUB(NOW(), INTERVAL '.(int)$nb_day_new_product.' DAY)) > 0 AS new, + stock.out_of_stock, IFNULL(stock.quantity, 0) as quantity + FROM `'._DB_PREFIX_.'category_product` cp + LEFT JOIN '._DB_PREFIX_.'category c ON (c.id_category = cp.id_category) + LEFT JOIN `'._DB_PREFIX_.'product` p ON p.`id_product` = cp.`id_product` + '.Shop::addSqlAssociation('product', 'p').' + '.Product::sqlStock('p', null, false, Context::getContext()->shop).' + LEFT JOIN '._DB_PREFIX_.'product_lang pl ON (pl.id_product = p.id_product'.Shop::addSqlRestrictionOnLang('pl').' AND pl.id_lang = '.(int)$cookie->id_lang.') + LEFT JOIN `'._DB_PREFIX_.'image` i ON (i.`id_product` = p.`id_product`)'. + Shop::addSqlAssociation('image', 'i', false, 'image_shop.cover=1').' + LEFT JOIN `'._DB_PREFIX_.'image_lang` il ON (image_shop.`id_image` = il.`id_image` AND il.`id_lang` = '.(int)$cookie->id_lang.') + LEFT JOIN '._DB_PREFIX_.'manufacturer m ON (m.id_manufacturer = p.id_manufacturer) + LEFT JOIN '._DB_PREFIX_.'product_attribute pa ON (p.id_product = pa.id_product)'. + Shop::addSqlAssociation('product_attribute', 'pa', false, 'product_attribute_shop.`default_on` = 1').' + WHERE '.$alias_where.'.`active` = 1 AND '.$alias_where.'.`visibility` IN ("both", "catalog") + AND '.(Configuration::get('PS_LAYERED_FULL_TREE') ? 'c.nleft >= '.(int)$parent->nleft.' AND c.nright <= '.(int)$parent->nright : 'c.id_category = '.(int)$id_parent).' + AND c.active = 1 + AND p.id_product IN ('.implode(',', $product_id_list).') + GROUP BY product_shop.id_product + ORDER BY '.Tools::getProductsOrder('by', Tools::getValue('orderby'), true).' '.Tools::getProductsOrder('way', Tools::getValue('orderway')). + ' LIMIT '.(((int)$this->page - 1) * $n.','.$n)); + } + + if (Tools::getProductsOrder('by', Tools::getValue('orderby'), true) == 'p.price') + Tools::orderbyPrice($this->products, Tools::getProductsOrder('way', Tools::getValue('orderway'))); + + return $this->products; + } + + private static function query($sql_query) + { + return Db::getInstance(_PS_USE_SQL_SLAVE_)->query($sql_query); + } + + public function getFilterBlock($selected_filters = array()) + { + global $cookie; + static $cache = null; + + $id_lang = Context::getContext()->language->id; + $currency = Context::getContext()->currency; + $id_shop = (int) Context::getContext()->shop->id; + $alias = 'product_shop'; + + if (is_array($cache)) + return $cache; + + $home_category = Configuration::get('PS_HOME_CATEGORY'); + $id_parent = (int)Tools::getValue('id_category', Tools::getValue('id_category_layered', $home_category)); + if ($id_parent == $home_category) + return; + + $parent = new Category((int)$id_parent, $id_lang); + + /* Get the filters for the current category */ + $filters = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS(' + SELECT * FROM '._DB_PREFIX_.'layered_category + WHERE id_category = '.(int)$id_parent.' + AND id_shop = '.$id_shop.' + GROUP BY `type`, id_value ORDER BY position ASC' + ); + // Remove all empty selected filters + foreach ($selected_filters as $key => $value) + switch ($key) + { + case 'price': + case 'weight': + if ($value[0] === '' && $value[1] === '') + unset($selected_filters[$key]); + break; + default: + if ($value == '') + unset($selected_filters[$key]); + break; + } + + $filter_blocks = array(); + foreach ($filters as $filter) + { + $sql_query = array('select' => '', 'from' => '', 'join' => '', 'where' => '', 'group' => '', 'second_query' => ''); + switch ($filter['type']) + { + // conditions + quantities + weight + price + case 'price': + case 'weight': + case 'condition': + case 'quantity': + + $sql_query['select'] = 'SELECT p.`id_product`, product_shop.`condition`, p.`id_manufacturer`, sa.`quantity`, p.`weight` '; + + $sql_query['from'] = ' + FROM '._DB_PREFIX_.'product p '; + $sql_query['join'] = ' + INNER JOIN '._DB_PREFIX_.'category_product cp ON (cp.id_product = p.id_product) + INNER JOIN '._DB_PREFIX_.'category c ON (c.id_category = cp.id_category AND + '.(Configuration::get('PS_LAYERED_FULL_TREE') ? 'c.nleft >= '.(int)$parent->nleft.' + AND c.nright <= '.(int)$parent->nright : 'c.id_category = '.(int)$id_parent).' + AND c.active = 1) '; + + $sql_query['join'] .= 'LEFT JOIN `'._DB_PREFIX_.'stock_available` sa + ON (sa.id_product = p.id_product AND sa.id_shop = '.(int)$this->context->shop->id.') '; + $sql_query['where'] = 'WHERE product_shop.`active` = 1 AND product_shop.`visibility` IN ("both", "catalog") '; + + $sql_query['group'] = ' GROUP BY p.id_product '; + break; + + case 'manufacturer': + $sql_query['select'] = 'SELECT m.name, COUNT(DISTINCT p.id_product) nbr, m.id_manufacturer '; + $sql_query['from'] = ' + FROM `'._DB_PREFIX_.'category_product` cp + INNER JOIN `'._DB_PREFIX_.'category` c ON (c.id_category = cp.id_category) + INNER JOIN '._DB_PREFIX_.'product p ON (p.id_product = cp.id_product) + INNER JOIN '._DB_PREFIX_.'manufacturer m ON (m.id_manufacturer = p.id_manufacturer) '; + $sql_query['where'] = 'WHERE + '.(Configuration::get('PS_LAYERED_FULL_TREE') ? 'c.nleft >= '.(int)$parent->nleft.' + AND c.nright <= '.(int)$parent->nright : 'c.id_category = '.(int)$id_parent).' + AND c.active = 1 + AND '.$alias.'.active = 1 AND '.$alias.'.`visibility` IN ("both", "catalog")'; + $sql_query['group'] = ' GROUP BY p.id_manufacturer ORDER BY m.name'; + + if (!Configuration::get('PS_LAYERED_HIDE_0_VALUES')) + { + $sql_query['second_query'] = ' + SELECT m.name, 0 nbr, m.id_manufacturer + + FROM `'._DB_PREFIX_.'category_product` cp'. + Shop::addSqlAssociation('product', 'cp').' + INNER JOIN `'._DB_PREFIX_.'category` c ON (c.id_category = cp.id_category) + INNER JOIN '._DB_PREFIX_.'product p ON (p.id_product = cp.id_product) + INNER JOIN '._DB_PREFIX_.'manufacturer m ON (m.id_manufacturer = p.id_manufacturer) + + WHERE '.(Configuration::get('PS_LAYERED_FULL_TREE') ? 'c.nleft >= '.(int)$parent->nleft.' + AND c.nright <= '.(int)$parent->nright : 'c.id_category = '.(int)$id_parent).' + AND c.active = 1 + AND '.$alias.'.active = 1 AND '.$alias.'.`visibility` IN ("both", "catalog") + GROUP BY p.id_manufacturer ORDER BY m.name'; + } + + break; + case 'id_attribute_group':// attribute group + $sql_query['select'] = ' + SELECT COUNT(DISTINCT p.id_product) nbr, lpa.id_attribute_group, + a.color, al.name attribute_name, agl.public_name attribute_group_name , lpa.id_attribute, ag.is_color_group, + liagl.url_name name_url_name, liagl.meta_title name_meta_title, lial.url_name value_url_name, lial.meta_title value_meta_title'; + $sql_query['from'] = ' + FROM '._DB_PREFIX_.'layered_product_attribute lpa + INNER JOIN '._DB_PREFIX_.'attribute a + ON a.id_attribute = lpa.id_attribute + INNER JOIN '._DB_PREFIX_.'attribute_lang al + ON al.id_attribute = a.id_attribute + AND al.id_lang = '.(int)$id_lang.' + INNER JOIN '._DB_PREFIX_.'product as p + ON p.id_product = lpa.id_product + INNER JOIN '._DB_PREFIX_.'attribute_group ag + ON ag.id_attribute_group = lpa.id_attribute_group + INNER JOIN '._DB_PREFIX_.'attribute_group_lang agl + ON agl.id_attribute_group = lpa.id_attribute_group + AND agl.id_lang = '.(int)$id_lang.' + LEFT JOIN '._DB_PREFIX_.'layered_indexable_attribute_group_lang_value liagl + ON (liagl.id_attribute_group = lpa.id_attribute_group AND liagl.id_lang = '.(int)$id_lang.') + LEFT JOIN '._DB_PREFIX_.'layered_indexable_attribute_lang_value lial + ON (lial.id_attribute = lpa.id_attribute AND lial.id_lang = '.(int)$id_lang.') '; + + $sql_query['where'] = 'WHERE a.id_attribute_group = '.(int)$filter['id_value']; + $sql_query['where'] .= ' AND lpa.`id_shop` = '.(int)Context::getContext()->shop->id; + $sql_query['where'] .= ' AND '.$alias.'.active = 1 AND '.$alias.'.`visibility` IN ("both", "catalog") + AND p.id_product IN ( + SELECT id_product + FROM '._DB_PREFIX_.'category_product cp + INNER JOIN '._DB_PREFIX_.'category c ON (c.id_category = cp.id_category AND + '.(Configuration::get('PS_LAYERED_FULL_TREE') ? 'c.nleft >= '.(int)$parent->nleft.' + AND c.nright <= '.(int)$parent->nright : 'c.id_category = '.(int)$id_parent).' + AND c.active = 1) + ) '; + $sql_query['group'] = ' + GROUP BY lpa.id_attribute + ORDER BY ag.`position` ASC, a.`position` ASC'; + + if (!Configuration::get('PS_LAYERED_HIDE_0_VALUES')) + { + $sql_query['second_query'] = ' + SELECT 0 nbr, lpa.id_attribute_group, + a.color, al.name attribute_name, agl.public_name attribute_group_name , lpa.id_attribute, ag.is_color_group, + liagl.url_name name_url_name, liagl.meta_title name_meta_title, lial.url_name value_url_name, lial.meta_title value_meta_title + FROM '._DB_PREFIX_.'layered_product_attribute lpa'. + Shop::addSqlAssociation('product', 'lpa').' + INNER JOIN '._DB_PREFIX_.'attribute a + ON a.id_attribute = lpa.id_attribute + INNER JOIN '._DB_PREFIX_.'attribute_lang al + ON al.id_attribute = a.id_attribute AND al.id_lang = '.(int)$id_lang.' + INNER JOIN '._DB_PREFIX_.'product as p + ON p.id_product = lpa.id_product + INNER JOIN '._DB_PREFIX_.'attribute_group ag + ON ag.id_attribute_group = lpa.id_attribute_group + INNER JOIN '._DB_PREFIX_.'attribute_group_lang agl + ON agl.id_attribute_group = lpa.id_attribute_group + AND agl.id_lang = '.(int)$id_lang.' + LEFT JOIN '._DB_PREFIX_.'layered_indexable_attribute_group_lang_value liagl + ON (liagl.id_attribute_group = lpa.id_attribute_group AND liagl.id_lang = '.(int)$id_lang.') + LEFT JOIN '._DB_PREFIX_.'layered_indexable_attribute_lang_value lial + ON (lial.id_attribute = lpa.id_attribute AND lial.id_lang = '.(int)$id_lang.') + WHERE '.$alias.'.active = 1 AND '.$alias.'.`visibility` IN ("both", "catalog") + AND a.id_attribute_group = '.(int)$filter['id_value'].' + AND lpa.`id_shop` = '.(int)Context::getContext()->shop->id.' + GROUP BY lpa.id_attribute + ORDER BY id_attribute_group, id_attribute'; + } + break; + + case 'id_feature': + $sql_query['select'] = 'SELECT fl.name feature_name, fp.id_feature, fv.id_feature_value, fvl.value, + COUNT(DISTINCT p.id_product) nbr, + lifl.url_name name_url_name, lifl.meta_title name_meta_title, lifvl.url_name value_url_name, lifvl.meta_title value_meta_title '; + $sql_query['from'] = ' + FROM '._DB_PREFIX_.'feature_product fp + INNER JOIN '._DB_PREFIX_.'product p ON (p.id_product = fp.id_product) + LEFT JOIN '._DB_PREFIX_.'feature_lang fl ON (fl.id_feature = fp.id_feature AND fl.id_lang = '.$id_lang.') + 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)) + LEFT JOIN '._DB_PREFIX_.'feature_value_lang fvl ON (fvl.id_feature_value = fp.id_feature_value AND fvl.id_lang = '.$id_lang.') + LEFT JOIN '._DB_PREFIX_.'layered_indexable_feature_lang_value lifl + ON (lifl.id_feature = fp.id_feature AND lifl.id_lang = '.$id_lang.') + LEFT JOIN '._DB_PREFIX_.'layered_indexable_feature_value_lang_value lifvl + ON (lifvl.id_feature_value = fp.id_feature_value AND lifvl.id_lang = '.$id_lang.') '; + $sql_query['where'] = 'WHERE '.$alias.'.`active` = 1 AND '.$alias.'.`visibility` IN ("both", "catalog") + AND fp.id_feature = '.(int)$filter['id_value'].' + AND p.id_product IN ( + SELECT id_product + FROM '._DB_PREFIX_.'category_product cp + INNER JOIN '._DB_PREFIX_.'category c ON (c.id_category = cp.id_category AND + '.(Configuration::get('PS_LAYERED_FULL_TREE') ? 'c.nleft >= '.(int)$parent->nleft.' + AND c.nright <= '.(int)$parent->nright : 'c.id_category = '.(int)$id_parent).' + AND c.active = 1)) '; + $sql_query['group'] = 'GROUP BY fv.id_feature_value '; + + if (!Configuration::get('PS_LAYERED_HIDE_0_VALUES')) + { + $sql_query['second_query'] = ' + SELECT fl.name feature_name, fp.id_feature, fv.id_feature_value, fvl.value, + 0 nbr, + lifl.url_name name_url_name, lifl.meta_title name_meta_title, lifvl.url_name value_url_name, lifvl.meta_title value_meta_title + + FROM '._DB_PREFIX_.'feature_product fp'. + Shop::addSqlAssociation('product', 'fp').' + INNER JOIN '._DB_PREFIX_.'product p ON (p.id_product = fp.id_product) + LEFT JOIN '._DB_PREFIX_.'feature_lang fl ON (fl.id_feature = fp.id_feature AND fl.id_lang = '.(int)$id_lang.') + 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)) + LEFT JOIN '._DB_PREFIX_.'feature_value_lang fvl ON (fvl.id_feature_value = fp.id_feature_value AND fvl.id_lang = '.(int)$id_lang.') + LEFT JOIN '._DB_PREFIX_.'layered_indexable_feature_lang_value lifl + ON (lifl.id_feature = fp.id_feature AND lifl.id_lang = '.(int)$id_lang.') + LEFT JOIN '._DB_PREFIX_.'layered_indexable_feature_value_lang_value lifvl + ON (lifvl.id_feature_value = fp.id_feature_value AND lifvl.id_lang = '.(int)$id_lang.') + WHERE '.$alias.'.`active` = 1 AND '.$alias.'.`visibility` IN ("both", "catalog") + AND fp.id_feature = '.(int)$filter['id_value'].' + GROUP BY fv.id_feature_value'; + } + + break; + + case 'category': + if (Group::isFeatureActive()) + $this->user_groups = ($this->context->customer->isLogged() ? $this->context->customer->getGroups() : array(Configuration::get('PS_UNIDENTIFIED_GROUP'))); + + $depth = Configuration::get('PS_LAYERED_FILTER_CATEGORY_DEPTH'); + if ($depth === false) + $depth = 1; + + $sql_query['select'] = ' + SELECT c.id_category, c.id_parent, cl.name, (SELECT count(DISTINCT p.id_product) # '; + $sql_query['from'] = ' + FROM '._DB_PREFIX_.'category_product cp + LEFT JOIN '._DB_PREFIX_.'product p ON (p.id_product = cp.id_product) '; + $sql_query['where'] = ' + WHERE cp.id_category = c.id_category + AND '.$alias.'.active = 1 AND '.$alias.'.`visibility` IN ("both", "catalog")'; + $sql_query['group'] = ') count_products + FROM '._DB_PREFIX_.'category c + LEFT JOIN '._DB_PREFIX_.'category_lang cl ON (cl.id_category = c.id_category AND cl.`id_shop` = '.(int)Context::getContext()->shop->id.' and cl.id_lang = '.$id_lang.') '; + + if (Group::isFeatureActive()) + $sql_query['group'] .= 'RIGHT JOIN '._DB_PREFIX_.'category_group cg ON (cg.id_category = c.id_category AND cg.`id_group` IN ('.implode(', ', $this->user_groups).')) '; + + $sql_query['group'] .= 'WHERE c.nleft > '.(int)$parent->nleft.' + AND c.nright < '.(int)$parent->nright.' + '.($depth ? 'AND c.level_depth <= '.($parent->level_depth+(int)$depth) : '').' + AND c.active = 1 + GROUP BY c.id_category ORDER BY c.nleft, c.position'; + } + foreach ($filters as $filter_tmp) + { + $method_name = 'get'.ucfirst($filter_tmp['type']).'FilterSubQuery'; + if (method_exists('BlockLayered', $method_name) && + (!in_array($filter['type'], array('price', 'weight')) && $filter['type'] != $filter_tmp['type'] || $filter['type'] == $filter_tmp['type'])) + { + if ($filter['type'] == $filter_tmp['type'] && $filter['id_value'] == $filter_tmp['id_value']) + $sub_query_filter = self::$method_name(array(), true); + else + { + if (!is_null($filter_tmp['id_value'])) + $selected_filters_cleaned = $this->cleanFilterByIdValue(@$selected_filters[$filter_tmp['type']], $filter_tmp['id_value']); + else + $selected_filters_cleaned = @$selected_filters[$filter_tmp['type']]; + $sub_query_filter = self::$method_name($selected_filters_cleaned, $filter['type'] == $filter_tmp['type']); + } + foreach ($sub_query_filter as $key => $value) + $sql_query[$key] .= $value; + } + } + + $products = false; + if (!empty($sql_query['from'])) + { + $sql_query['from'] .= Shop::addSqlAssociation('product', 'p'); + $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']); + } + + foreach ($filters as $filter_tmp) + { + $method_name = 'filterProductsBy'.ucfirst($filter_tmp['type']); + if (method_exists('BlockLayered', $method_name) && + (!in_array($filter['type'], array('price', 'weight')) && $filter['type'] != $filter_tmp['type'] || $filter['type'] == $filter_tmp['type'])) + if ($filter['type'] == $filter_tmp['type']) + $products = self::$method_name(array(), $products); + else + $products = self::$method_name(@$selected_filters[$filter_tmp['type']], $products); + } + + if (!empty($sql_query['second_query'])) + { + $res = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS($sql_query['second_query']); + if ($res) + $products = array_merge($products, $res); + } + + switch ($filter['type']) + { + case 'price': + $price_array = array( + 'type_lite' => 'price', + 'type' => 'price', + 'id_key' => 0, + 'name' => $this->l('Price'), + 'slider' => true, + 'max' => '0', + 'min' => null, + 'values' => array ('1' => 0), + 'unit' => $currency->sign, + 'format' => $currency->format, + 'filter_show_limit' => $filter['filter_show_limit'], + 'filter_type' => $filter['filter_type'] + ); + if (isset($products) && $products) + foreach ($products as $product) + { + if (is_null($price_array['min'])) + { + $price_array['min'] = $product['price_min']; + $price_array['values'][0] = $product['price_min']; + } + else if ($price_array['min'] > $product['price_min']) + { + $price_array['min'] = $product['price_min']; + $price_array['values'][0] = $product['price_min']; + } + + if ($price_array['max'] < $product['price_max']) + { + $price_array['max'] = $product['price_max']; + $price_array['values'][1] = $product['price_max']; + } + } + + if ($price_array['max'] != $price_array['min'] && $price_array['min'] != null) + { + if ($filter['filter_type'] == 2) + { + $price_array['list_of_values'] = array(); + $nbr_of_value = $filter['filter_show_limit']; + if ($nbr_of_value < 2) + $nbr_of_value = 4; + $delta = ($price_array['max'] - $price_array['min']) / $nbr_of_value; + $current_step = $price_array['min']; + for ($i = 0; $i < $nbr_of_value; $i++) + $price_array['list_of_values'][] = array( + (int)($price_array['min'] + $i * $delta), + (int)($price_array['min'] + ($i + 1) * $delta) + ); + } + if (isset($selected_filters['price']) && isset($selected_filters['price'][0]) + && isset($selected_filters['price'][1])) + { + $price_array['values'][0] = $selected_filters['price'][0]; + $price_array['values'][1] = $selected_filters['price'][1]; + } + $filter_blocks[] = $price_array; + } + break; + + case 'weight': + $weight_array = array( + 'type_lite' => 'weight', + 'type' => 'weight', + 'id_key' => 0, + 'name' => $this->l('Weight'), + 'slider' => true, + 'max' => '0', + 'min' => null, + 'values' => array ('1' => 0), + 'unit' => Configuration::get('PS_WEIGHT_UNIT'), + 'format' => 5, // Ex: xxxxx kg + 'filter_show_limit' => $filter['filter_show_limit'], + 'filter_type' => $filter['filter_type'] + ); + if (isset($products) && $products) + foreach ($products as $product) + { + if (is_null($weight_array['min'])) + { + $weight_array['min'] = $product['weight']; + $weight_array['values'][0] = $product['weight']; + } + else if ($weight_array['min'] > $product['weight']) + { + $weight_array['min'] = $product['weight']; + $weight_array['values'][0] = $product['weight']; + } + + if ($weight_array['max'] < $product['weight']) + { + $weight_array['max'] = $product['weight']; + $weight_array['values'][1] = $product['weight']; + } + } + if ($weight_array['max'] != $weight_array['min'] && $weight_array['min'] != null) + { + if (isset($selected_filters['weight']) && isset($selected_filters['weight'][0]) + && isset($selected_filters['weight'][1])) + { + $weight_array['values'][0] = $selected_filters['weight'][0]; + $weight_array['values'][1] = $selected_filters['weight'][1]; + } + $filter_blocks[] = $weight_array; + } + break; + + case 'condition': + $condition_array = array( + 'new' => array('name' => $this->l('New'),'nbr' => 0), + 'used' => array('name' => $this->l('Used'), 'nbr' => 0), + 'refurbished' => array('name' => $this->l('Refurbished'), + 'nbr' => 0) + ); + if (isset($products) && $products) + foreach ($products as $product) + if (isset($selected_filters['condition']) && in_array($product['condition'], $selected_filters['condition'])) + $condition_array[$product['condition']]['checked'] = true; + foreach ($condition_array as $key => $condition) + if (isset($selected_filters['condition']) && in_array($key, $selected_filters['condition'])) + $condition_array[$key]['checked'] = true; + if (isset($products) && $products) + foreach ($products as $product) + if (isset($condition_array[$product['condition']])) + $condition_array[$product['condition']]['nbr']++; + $filter_blocks[] = array( + 'type_lite' => 'condition', + 'type' => 'condition', + 'id_key' => 0, + 'name' => $this->l('Condition'), + 'values' => $condition_array, + 'filter_show_limit' => $filter['filter_show_limit'], + 'filter_type' => $filter['filter_type'] + ); + break; + + case 'quantity': + $quantity_array = array ( + 0 => array('name' => $this->l('Not available'), 'nbr' => 0), + 1 => array('name' => $this->l('In stock'), 'nbr' => 0) + ); + foreach ($quantity_array as $key => $quantity) + if (isset($selected_filters['quantity']) && in_array($key, $selected_filters['quantity'])) + $quantity_array[$key]['checked'] = true; + if (isset($products) && $products) + foreach ($products as $product) + { + //If oosp move all not available quantity to available quantity + if ((int)$product['quantity'] > 0 || Product::isAvailableWhenOutOfStock(StockAvailable::outOfStock($product['id_product']))) + $quantity_array[1]['nbr']++; + else + $quantity_array[0]['nbr']++; + } + + $filter_blocks[] = array( + 'type_lite' => 'quantity', + 'type' => 'quantity', + 'id_key' => 0, + 'name' => $this->l('Availability'), + 'values' => $quantity_array, + 'filter_show_limit' => $filter['filter_show_limit'], + 'filter_type' => $filter['filter_type'] + ); + + break; + + case 'manufacturer': + if (isset($products) && $products) + { + $manufaturers_array = array(); + foreach ($products as $manufacturer) + { + if (!isset($manufaturers_array[$manufacturer['id_manufacturer']])) + $manufaturers_array[$manufacturer['id_manufacturer']] = array('name' => $manufacturer['name'], 'nbr' => $manufacturer['nbr']); + if (isset($selected_filters['manufacturer']) && in_array((int)$manufacturer['id_manufacturer'], $selected_filters['manufacturer'])) + $manufaturers_array[$manufacturer['id_manufacturer']]['checked'] = true; + } + $filter_blocks[] = array( + 'type_lite' => 'manufacturer', + 'type' => 'manufacturer', + 'id_key' => 0, + 'name' => $this->l('Manufacturer'), + 'values' => $manufaturers_array, + 'filter_show_limit' => $filter['filter_show_limit'], + 'filter_type' => $filter['filter_type'] + ); + } + break; + + case 'id_attribute_group': + $attributes_array = array(); + if (isset($products) && $products) + { + foreach ($products as $attributes) + { + if (!isset($attributes_array[$attributes['id_attribute_group']])) + $attributes_array[$attributes['id_attribute_group']] = array ( + 'type_lite' => 'id_attribute_group', + 'type' => 'id_attribute_group', + 'id_key' => (int)$attributes['id_attribute_group'], + 'name' => $attributes['attribute_group_name'], + 'is_color_group' => (bool)$attributes['is_color_group'], + 'values' => array(), + 'url_name' => $attributes['name_url_name'], + 'meta_title' => $attributes['name_meta_title'], + 'filter_show_limit' => $filter['filter_show_limit'], + 'filter_type' => $filter['filter_type'] + ); + + if (!isset($attributes_array[$attributes['id_attribute_group']]['values'][$attributes['id_attribute']])) + $attributes_array[$attributes['id_attribute_group']]['values'][$attributes['id_attribute']] = array( + 'color' => $attributes['color'], + 'name' => $attributes['attribute_name'], + 'nbr' => (int)$attributes['nbr'], + 'url_name' => $attributes['value_url_name'], + 'meta_title' => $attributes['value_meta_title'] + ); + + if (isset($selected_filters['id_attribute_group'][$attributes['id_attribute']])) + $attributes_array[$attributes['id_attribute_group']]['values'][$attributes['id_attribute']]['checked'] = true; + } + + $filter_blocks = array_merge($filter_blocks, $attributes_array); + } + break; + case 'id_feature': + $feature_array = array(); + if (isset($products) && $products) + { + foreach ($products as $feature) + { + if (!isset($feature_array[$feature['id_feature']])) + $feature_array[$feature['id_feature']] = array( + 'type_lite' => 'id_feature', + 'type' => 'id_feature', + 'id_key' => (int)$feature['id_feature'], + 'values' => array(), + 'name' => $feature['feature_name'], + 'url_name' => $feature['name_url_name'], + 'meta_title' => $feature['name_meta_title'], + 'filter_show_limit' => $filter['filter_show_limit'], + 'filter_type' => $filter['filter_type'] + ); + + if (!isset($feature_array[$feature['id_feature']]['values'][$feature['id_feature_value']])) + $feature_array[$feature['id_feature']]['values'][$feature['id_feature_value']] = array( + 'nbr' => (int)$feature['nbr'], + 'name' => $feature['value'], + 'url_name' => $feature['value_url_name'], + 'meta_title' => $feature['value_meta_title'] + ); + + if (isset($selected_filters['id_feature'][$feature['id_feature_value']])) + $feature_array[$feature['id_feature']]['values'][$feature['id_feature_value']]['checked'] = true; + } + + //Natural sort + foreach ($feature_array as $key => $value) + { + $temp = array(); + foreach ($feature_array[$key]['values'] as $keyint => $valueint) + $temp[$keyint] = $valueint['name']; + + natcasesort($temp); + $temp2 = array(); + + foreach ($temp as $keytemp => $valuetemp) + $temp2[$keytemp] = $feature_array[$key]['values'][$keytemp]; + + $feature_array[$key]['values'] = $temp2; + } + + $filter_blocks = array_merge($filter_blocks, $feature_array); + } + break; + + case 'category': + $tmp_array = array(); + if (isset($products) && $products) + { + $categories_with_products_count = 0; + foreach ($products as $category) + { + $tmp_array[$category['id_category']] = array( + 'name' => $category['name'], + 'nbr' => (int)$category['count_products'] + ); + + if ((int)$category['count_products']) + $categories_with_products_count++; + + if (isset($selected_filters['category']) && in_array($category['id_category'], $selected_filters['category'])) + $tmp_array[$category['id_category']]['checked'] = true; + } + if ($categories_with_products_count || !Configuration::get('PS_LAYERED_HIDE_0_VALUES')) + $filter_blocks[] = array ( + 'type_lite' => 'category', + 'type' => 'category', + 'id_key' => 0, 'name' => $this->l('Categories'), + 'values' => $tmp_array, + 'filter_show_limit' => $filter['filter_show_limit'], + 'filter_type' => $filter['filter_type'] + ); + } + break; + } + } + + // All non indexable attribute and feature + $non_indexable = array(); + + // Get all non indexable attribute groups + foreach (Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS(' + SELECT public_name + FROM `'._DB_PREFIX_.'attribute_group_lang` agl + LEFT JOIN `'._DB_PREFIX_.'layered_indexable_attribute_group` liag + ON liag.id_attribute_group = agl.id_attribute_group + WHERE indexable IS NULL OR indexable = 0 + AND id_lang = '.$id_lang) as $attribute) + $non_indexable[] = Tools::link_rewrite($attribute['public_name']); + + // Get all non indexable features + foreach (Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS(' + SELECT name + FROM `'._DB_PREFIX_.'feature_lang` fl + LEFT JOIN `'._DB_PREFIX_.'layered_indexable_feature` lif + ON lif.id_feature = fl.id_feature + WHERE indexable IS NULL OR indexable = 0 + AND id_lang = '.$id_lang) as $attribute) + $non_indexable[] = Tools::link_rewrite($attribute['name']); + + //generate SEO link + $param_selected = ''; + $param_product_url = ''; + $option_checked_array = array(); + $param_group_selected_array = array(); + $title_values = array(); + $meta_values = array(); + + //get filters checked by group + + foreach ($filter_blocks as $type_filter) + { + $filter_name = (!empty($type_filter['url_name']) ? $type_filter['url_name'] : $type_filter['name']); + $filter_meta = (!empty($type_filter['meta_title']) ? $type_filter['meta_title'] : $type_filter['name']); + $attr_key = $type_filter['type'].'_'.$type_filter['id_key']; + + $param_group_selected = ''; + + if (in_array(strtolower($type_filter['type']), array('price', 'weight')) + && (float)$type_filter['values'][0] > (float)$type_filter['min'] + && (float)$type_filter['values'][1] > (float)$type_filter['max']) + { + $param_group_selected .= $this->getAnchor().str_replace($this->getAnchor(), '_', $type_filter['values'][0]) + .$this->getAnchor().str_replace($this->getAnchor(), '_', $type_filter['values'][1]); + $param_group_selected_array[Tools::link_rewrite($filter_name)][] = Tools::link_rewrite($filter_name); + + if (!isset($title_values[$filter_meta])) + $title_values[$filter_meta] = array(); + $title_values[$filter_meta][] = $filter_meta; + if (!isset($meta_values[$attr_key])) + $meta_values[$attr_key] = array('title' => $filter_meta, 'values' => array()); + $meta_values[$attr_key]['values'][] = $filter_meta; + } + else + { + foreach ($type_filter['values'] as $key => $value) + { + if (is_array($value) && array_key_exists('checked', $value )) + { + $value_name = !empty($value['url_name']) ? $value['url_name'] : $value['name']; + $value_meta = !empty($value['meta_title']) ? $value['meta_title'] : $value['name']; + $param_group_selected .= $this->getAnchor().str_replace($this->getAnchor(), '_', Tools::link_rewrite($value_name)); + $param_group_selected_array[Tools::link_rewrite($filter_name)][] = Tools::link_rewrite($value_name); + + if (!isset($title_values[$filter_meta])) + $title_values[$filter_meta] = array(); + $title_values[$filter_meta][] = $value_name; + if (!isset($meta_values[$attr_key])) + $meta_values[$attr_key] = array('title' => $filter_meta, 'values' => array()); + $meta_values[$attr_key]['values'][] = $value_meta; + } + else + $param_group_selected_array[Tools::link_rewrite($filter_name)][] = array(); + } + } + + if (!empty($param_group_selected)) + { + $param_selected .= '/'.str_replace($this->getAnchor(), '_', Tools::link_rewrite($filter_name)).$param_group_selected; + $option_checked_array[Tools::link_rewrite($filter_name)] = $param_group_selected; + } + // select only attribute and group attribute to display an unique product combination link + if (!empty($param_group_selected) && $type_filter['type'] == 'id_attribute_group') + $param_product_url .= '/'.str_replace($this->getAnchor(), '_', Tools::link_rewrite($filter_name)).$param_group_selected; + + } + + if ($this->page > 1) + $param_selected .= '/page-'.$this->page; + + $blacklist = array('weight', 'price'); + + if (!Configuration::get('PS_LAYERED_FILTER_INDEX_CDT')) + $blacklist[] = 'condition'; + if (!Configuration::get('PS_LAYERED_FILTER_INDEX_QTY')) + $blacklist[] = 'quantity'; + if (!Configuration::get('PS_LAYERED_FILTER_INDEX_MNF')) + $blacklist[] = 'manufacturer'; + if (!Configuration::get('PS_LAYERED_FILTER_INDEX_CAT')) + $blacklist[] = 'category'; + + $global_nofollow = false; + + foreach ($filter_blocks as &$type_filter) + { + $filter_name = (!empty($type_filter['url_name']) ? $type_filter['url_name'] : $type_filter['name']); + + if (count($type_filter) > 0 && !isset($type_filter['slider'])) + { + foreach ($type_filter['values'] as $key => $values) + { + $nofollow = false; + if (!empty($values['checked']) && in_array($type_filter['type'], $blacklist)) + $global_nofollow = true; + + $option_checked_clone_array = $option_checked_array; + + // If not filters checked, add parameter + $value_name = !empty($values['url_name']) ? $values['url_name'] : $values['name']; + + if (!in_array(Tools::link_rewrite($value_name), $param_group_selected_array[Tools::link_rewrite($filter_name)])) + { + // Update parameter filter checked before + if (array_key_exists(Tools::link_rewrite($filter_name), $option_checked_array)) + { + $option_checked_clone_array[Tools::link_rewrite($filter_name)] = $option_checked_clone_array[Tools::link_rewrite($filter_name)].$this->getAnchor().str_replace($this->getAnchor(), '_', Tools::link_rewrite($value_name)); + + if (in_array($type_filter['type'], $blacklist)) + $nofollow = true; + } + else + $option_checked_clone_array[Tools::link_rewrite($filter_name)] = $this->getAnchor().str_replace($this->getAnchor(), '_', Tools::link_rewrite($value_name)); + } + else + { + // Remove selected parameters + $option_checked_clone_array[Tools::link_rewrite($filter_name)] = str_replace($this->getAnchor().str_replace($this->getAnchor(), '_', Tools::link_rewrite($value_name)), '', $option_checked_clone_array[Tools::link_rewrite($filter_name)]); + if (empty($option_checked_clone_array[Tools::link_rewrite($filter_name)])) + unset($option_checked_clone_array[Tools::link_rewrite($filter_name)]); + } + $parameters = ''; + ksort($option_checked_clone_array); // Order parameters + foreach ($option_checked_clone_array as $key_group => $value_group) + $parameters .= '/'.str_replace($this->getAnchor(), '_', $key_group).$value_group; + + // Add nofollow if any blacklisted filters ins in parameters + foreach ($filter_blocks as $filter) + { + $name = Tools::link_rewrite((!empty($filter['url_name']) ? $filter['url_name'] : $filter['name'])); + if (in_array($filter['type'], $blacklist) && strpos($parameters, $name.'-') !== false) + $nofollow = true; + } + + // Check if there is an non indexable attribute or feature in the url + foreach ($non_indexable as $value) + if (strpos($parameters, '/'.$value) !== false) + $nofollow = true; + + $type_filter['values'][$key]['link'] = Context::getContext()->link->getCategoryLink($parent, null, null).'#'.ltrim($parameters, '/'); + $type_filter['values'][$key]['rel'] = ($nofollow) ? 'nofollow' : ''; + } + } + } + + $n_filters = 0; + if (isset($selected_filters['price'])) + if ($price_array['min'] == $selected_filters['price'][0] && $price_array['max'] == $selected_filters['price'][1]) + unset($selected_filters['price']); + if (isset($selected_filters['weight'])) + if ($weight_array['min'] == $selected_filters['weight'][0] && $weight_array['max'] == $selected_filters['weight'][1]) + unset($selected_filters['weight']); + + foreach ($selected_filters as $filters) + $n_filters += count($filters); + + $cache = array( + 'layered_show_qties' => (int)Configuration::get('PS_LAYERED_SHOW_QTIES'), + 'id_category_layered' => (int)$id_parent, + 'selected_filters' => $selected_filters, + 'n_filters' => (int)$n_filters, + 'nbr_filterBlocks' => count($filter_blocks), + 'filters' => $filter_blocks, + 'title_values' => $title_values, + 'meta_values' => $meta_values, + 'current_friendly_url' => $param_selected, + 'param_product_url' => $param_product_url, + 'no_follow' => (!empty($param_selected) || $global_nofollow) + ); + + return $cache; + } + + public function cleanFilterByIdValue($attributes, $id_value) + { + $selected_filters = array(); + if (is_array($attributes)) + foreach ($attributes as $attribute) + { + $attribute_data = explode('_', $attribute); + if ($attribute_data[0] == $id_value) + $selected_filters[] = $attribute_data[1]; + } + return $selected_filters; + } + + public function generateFiltersBlock($selected_filters) + { + global $smarty; + if ($filter_block = $this->getFilterBlock($selected_filters)) + { + if ($filter_block['nbr_filterBlocks'] == 0) + return false; + + $translate = array(); + $translate['price'] = $this->l('price'); + $translate['weight'] = $this->l('weight'); + + $smarty->assign($filter_block); + $smarty->assign(array( + 'hide_0_values' => Configuration::get('PS_LAYERED_HIDE_0_VALUES'), + 'blocklayeredSliderName' => $translate + )); + return $this->display(__FILE__, 'blocklayered.tpl'); + } + else + return false; + } + + private static function getPriceFilterSubQuery($filter_value) + { + $id_currency = (int)Context::getContext()->currency->id; + + if (isset($filter_value) && $filter_value) + { + $price_filter_query = ' + INNER JOIN `'._DB_PREFIX_.'layered_price_index` psi ON (psi.id_product = p.id_product AND psi.id_currency = '.(int)$id_currency.' + AND psi.price_min <= '.(int)$filter_value[1].' AND psi.price_max >= '.(int)$filter_value[0].' AND psi.id_shop='.(int)Context::getContext()->shop->id.') '; + } + else + { + $price_filter_query = ' + INNER JOIN `'._DB_PREFIX_.'layered_price_index` psi + ON (psi.id_product = p.id_product AND psi.id_currency = '.(int)$id_currency.' AND psi.id_shop='.(int)Context::getContext()->shop->id.') '; + } + + return array('join' => $price_filter_query, 'select' => ', psi.price_min, psi.price_max'); + } + + private static function filterProductsByPrice($filter_value, $product_collection) + { + if (empty($filter_value)) + return $product_collection; + foreach ($product_collection as $key => $product) + { + if (isset($filter_value) && $filter_value && isset($product['price_min']) && isset($product['id_product']) + && ((int)$filter_value[0] > $product['price_min'] || (int)$filter_value[1] < $product['price_max'])) + { + $price = Product::getPriceStatic($product['id_product'], Configuration::get('PS_LAYERED_FILTER_PRICE_USETAX')); + if ($price < $filter_value[0] || $price > $filter_value[1]) + continue; + unset($product_collection[$key]); + } + } + return $product_collection; + } + + private static function getWeightFilterSubQuery($filter_value, $ignore_join) + { + if (isset($filter_value) && $filter_value) + if ($filter_value[0] != 0 || $filter_value[1] != 0) + return array('where' => ' AND p.`weight` BETWEEN '.(float)($filter_value[0] - 0.001).' AND '.(float)($filter_value[1] + 0.001).' '); + + return array(); + } + + private static function getId_featureFilterSubQuery($filter_value, $ignore_join) + { + if (empty($filter_value)) + return array(); + $query_filters = ' AND p.id_product IN (SELECT id_product FROM '._DB_PREFIX_.'feature_product fp WHERE '; + foreach ($filter_value as $filter_val) + $query_filters .= 'fp.`id_feature_value` = '.(int)$filter_val.' OR '; + $query_filters = rtrim($query_filters, 'OR ').') '; + + return array('where' => $query_filters); + } + private static function getId_attribute_groupFilterSubQuery($filter_value, $ignore_join) + { + if (empty($filter_value)) + return array(); + $query_filters = ' + AND p.id_product IN (SELECT pa.`id_product` + FROM `'._DB_PREFIX_.'product_attribute_combination` pac + LEFT JOIN `'._DB_PREFIX_.'product_attribute` pa ON (pa.`id_product_attribute` = pac.`id_product_attribute`) + WHERE '; + + foreach ($filter_value as $filter_val) + $query_filters .= 'pac.`id_attribute` = '.(int)$filter_val.' OR '; + $query_filters = rtrim($query_filters, 'OR ').') '; + + return array('where' => $query_filters); + } + + private static function getCategoryFilterSubQuery($filter_value, $ignore_join) + { + if (empty($filter_value)) + return array(); + $query_filters_join = ''; + $query_filters_where = ' AND p.id_product IN (SELECT id_product FROM '._DB_PREFIX_.'category_product cp WHERE '; + foreach ($filter_value as $id_category) + $query_filters_where .= 'cp.`id_category` = '.(int)$id_category.' OR '; + $query_filters_where = rtrim($query_filters_where, 'OR ').') '; + + return array('where' => $query_filters_where, 'join' => $query_filters_join); + } + + private static function getQuantityFilterSubQuery($filter_value, $ignore_join) + { + if (count($filter_value) == 2 || empty($filter_value)) + return array(); + + $query_filters_join = ''; + + $query_filters = ' AND sav.quantity '.(!$filter_value[0] ? '<=' : '>').' 0 '; + $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.') '; + + return array('where' => $query_filters, 'join' => $query_filters_join); + } + + private static function getManufacturerFilterSubQuery($filter_value, $ignore_join) + { + if (empty($filter_value)) + $query_filters = ''; + else + { + array_walk($filter_value, create_function('&$id_manufacturer', '$id_manufacturer = (int)$id_manufacturer;')); + $query_filters = ' AND p.id_manufacturer IN ('.implode($filter_value, ',').')'; + } + if ($ignore_join) + return array('where' => $query_filters, 'select' => ', m.name'); + else + return array('where' => $query_filters, 'select' => ', m.name', 'join' => 'LEFT JOIN `'._DB_PREFIX_.'manufacturer` m ON (m.id_manufacturer = p.id_manufacturer) '); + } + + private static function getConditionFilterSubQuery($filter_value, $ignore_join) + { + if (count($filter_value) == 3 || empty($filter_value)) + return array(); + + $query_filters = ' AND product_shop.condition IN ('; + + foreach ($filter_value as $cond) + $query_filters .= '\''.$cond.'\','; + $query_filters = rtrim($query_filters, ',').') '; + + return array('where' => $query_filters); + } + + public function ajaxCall() + { + global $smarty, $cookie; + + $selected_filters = $this->getSelectedFilters(); + $filter_block = $this->getFilterBlock($this->getSelectedFilters()); + $this->getProducts($selected_filters, $products, $nb_products, $p, $n, $pages_nb, $start, $stop, $range); + + // Add pagination variable + $nArray = (int)Configuration::get('PS_PRODUCTS_PER_PAGE') != 10 ? array((int)Configuration::get('PS_PRODUCTS_PER_PAGE'), 10, 20, 50) : array(10, 20, 50); + // Clean duplicate values + $nArray = array_unique($nArray); + asort($nArray); + + if (version_compare(_PS_VERSION_, '1.6.0', '>=') === true) + $this->context->controller->addColorsToProductList($products); + + $category = new Category(Tools::getValue('id_category_layered', Configuration::get('PS_HOME_CATEGORY')), (int)$cookie->id_lang); + + // Generate meta title and meta description + $category_title = (empty($category->meta_title) ? $category->name : $category->meta_title); + $category_metas = Meta::getMetaTags((int)$cookie->id_lang, 'category'); + $title = ''; + $keywords = ''; + + if (is_array($filter_block['title_values'])) + foreach ($filter_block['title_values'] as $key => $val) + { + $title .= ' > '.$key.' '.implode('/', $val); + $keywords .= $key.' '.implode('/', $val).', '; + } + + $title = $category_title.$title; + + if (!empty($title)) + $meta_title = $title; + else + $meta_title = $category_metas['meta_title']; + + $meta_description = $category_metas['meta_description']; + + $keywords = substr(strtolower($keywords), 0, 1000); + if (!empty($keywords)) + $meta_keywords = rtrim($category_title.', '.$keywords.', '.$category_metas['meta_keywords'], ', '); + + $smarty->assign( + array( + 'homeSize' => Image::getSize(ImageType::getFormatedName('home')), + 'nb_products' => $nb_products, + 'category' => new Category(Tools::getValue('id_category_layered', Configuration::get('PS_HOME_CATEGORY')), (int)$cookie->id_lang), + 'pages_nb' => (int)$pages_nb, + 'p' => (int)$p, + 'n' => (int)$n, + 'range' => (int)$range, + 'start' => (int)$start, + 'stop' => (int)$stop, + '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), + 'comparator_max_item' => (int)(Configuration::get('PS_COMPARATOR_MAX_ITEM')), + 'products' => $products, + 'products_per_page' => (int)Configuration::get('PS_PRODUCTS_PER_PAGE'), + 'static_token' => Tools::getToken(false), + 'page_name' => 'category', + 'nArray' => $nArray, + ) + ); + + // Prevent bug with old template where category.tpl contain the title of the category and category-count.tpl do not exists + if (file_exists(_PS_THEME_DIR_.'category-count.tpl')) + $category_count = $smarty->fetch(_PS_THEME_DIR_.'category-count.tpl'); + else + $category_count = ''; + + if ($nb_products == 0) + $product_list = $this->display(__FILE__, 'blocklayered-no-products.tpl'); + else + $product_list = $smarty->fetch(_PS_THEME_DIR_.'product-list.tpl'); + + /* We are sending an array in jSon to the .js controller, it will update both the filters and the products zones */ + return Tools::jsonEncode(array( + 'filtersBlock' => utf8_encode($this->generateFiltersBlock($selected_filters)), + 'productList' => utf8_encode($product_list), + 'pagination' => $smarty->fetch(_PS_THEME_DIR_.'pagination.tpl'), + 'categoryCount' => $category_count, + 'meta_title' => $meta_title.' - '.Configuration::get('PS_SHOP_NAME'), + 'heading' => $meta_title, + 'meta_keywords' => isset($meta_keywords) ? $meta_keywords : null, + 'meta_description' => $meta_description, + 'current_friendly_url' => '#'.$filter_block['current_friendly_url'], + 'filters' => $filter_block['filters'])); + } + + public function getProducts($selected_filters, &$products, &$nb_products, &$p, &$n, &$pages_nb, &$start, &$stop, &$range) + { + global $cookie; + + $products = $this->getProductByFilters($selected_filters); + $products = Product::getProductsProperties((int)$cookie->id_lang, $products); + $nb_products = $this->nbr_products; + $range = 2; /* how many pages around page selected */ + + $n = (int)Tools::getValue('n', Configuration::get('PS_PRODUCTS_PER_PAGE')); + + if ($n <= 0) + $n = 1; + + $p = $this->page; + + if ($p < 0) + $p = 0; + + if ($p > ($nb_products / $n)) + $p = ceil($nb_products / $n); + $pages_nb = ceil($nb_products / (int)($n)); + + $start = (int)($p - $range); + if ($start < 1) + $start = 1; + + $stop = (int)($p + $range); + if ($stop > $pages_nb) + $stop = (int)($pages_nb); + + foreach ($products as &$product) + { + if ($product['id_product_attribute'] && isset($product['product_attribute_minimal_quantity'])) + $product['minimal_quantity'] = $product['product_attribute_minimal_quantity']; + } + } + + public function rebuildLayeredStructure() + { + @set_time_limit(0); + + /* Set memory limit to 128M only if current is lower */ + $memory_limit = @ini_get('memory_limit'); + 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))) + @ini_set('memory_limit', '128M'); + + /* Delete and re-create the layered categories table */ + Db::getInstance()->execute('DROP TABLE IF EXISTS '._DB_PREFIX_.'layered_category'); + Db::getInstance()->execute(' + CREATE TABLE IF NOT EXISTS `'._DB_PREFIX_.'layered_category` ( + `id_layered_category` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, + `id_shop` INT(11) UNSIGNED NOT NULL, + `id_category` INT(10) UNSIGNED NOT NULL, + `id_value` INT(10) UNSIGNED NULL DEFAULT \'0\', + `type` ENUM(\'category\',\'id_feature\',\'id_attribute_group\',\'quantity\',\'condition\',\'manufacturer\',\'weight\',\'price\') NOT NULL, + `position` INT(10) UNSIGNED NOT NULL, + `filter_type` int(10) UNSIGNED NOT NULL DEFAULT 0, + `filter_show_limit` int(10) UNSIGNED NOT NULL DEFAULT 0, + PRIMARY KEY (`id_layered_category`), + KEY `id_category` (`id_category`,`type`) + ) ENGINE='._MYSQL_ENGINE_.' DEFAULT CHARSET=latin1 AUTO_INCREMENT=1;'); /* MyISAM + latin1 = Smaller/faster */ + + Db::getInstance()->execute(' + CREATE TABLE IF NOT EXISTS `'._DB_PREFIX_.'layered_filter` ( + `id_layered_filter` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, + `name` VARCHAR(64) NOT NULL, + `filters` TEXT NULL, + `n_categories` INT(10) UNSIGNED NOT NULL, + `date_add` DATETIME NOT NULL + ) ENGINE='._MYSQL_ENGINE_.' DEFAULT CHARSET=utf8;'); + + Db::getInstance()->execute(' + CREATE TABLE IF NOT EXISTS `'._DB_PREFIX_.'layered_filter_shop` ( + `id_layered_filter` INT(10) UNSIGNED NOT NULL, + `id_shop` INT(11) UNSIGNED NOT NULL, + PRIMARY KEY (`id_layered_filter`, `id_shop`), + KEY `id_shop` (`id_shop`) + ) ENGINE='._MYSQL_ENGINE_.' DEFAULT CHARSET=utf8;'); + } + + public function rebuildLayeredCache($products_ids = array(), $categories_ids = array()) + { + @set_time_limit(0); + + $filter_data = array('categories' => array()); + + /* Set memory limit to 128M only if current is lower */ + $memory_limit = @ini_get('memory_limit'); + 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))) + @ini_set('memory_limit', '128M'); + + $db = Db::getInstance(_PS_USE_SQL_SLAVE_); + $n_categories = array(); + $done_categories = array(); + $alias = 'p'; + $join_product_attribute = $join_product = ''; + + $alias = 'product_shop'; + $join_product = Shop::addSqlAssociation('product', 'p'); + $join_product_attribute = Shop::addSqlAssociation('product_attribute', 'pa'); + + + $attribute_groups = self::query(' + SELECT a.id_attribute, a.id_attribute_group + FROM '._DB_PREFIX_.'attribute a + LEFT JOIN '._DB_PREFIX_.'product_attribute_combination pac ON (pac.id_attribute = a.id_attribute) + LEFT JOIN '._DB_PREFIX_.'product_attribute pa ON (pa.id_product_attribute = pac.id_product_attribute) + LEFT JOIN '._DB_PREFIX_.'product p ON (p.id_product = pa.id_product) + '.$join_product.$join_product_attribute.' + LEFT JOIN '._DB_PREFIX_.'category_product cp ON (cp.id_product = p.id_product) + LEFT JOIN '._DB_PREFIX_.'category c ON (c.id_category = cp.id_category) + WHERE c.active = 1'. + (count($categories_ids) ? ' AND cp.id_category IN ('.implode(',', $categories_ids).')' : '').' + AND '.$alias.'.active = 1 AND '.$alias.'.`visibility` IN ("both", "catalog") + '.(count($products_ids) ? 'AND p.id_product IN ('.implode(',', $products_ids).')' : '')); + + $attribute_groups_by_id = array(); + while ($row = $db->nextRow($attribute_groups)) + $attribute_groups_by_id[(int)$row['id_attribute']] = (int)$row['id_attribute_group']; + + $features = self::query(' + SELECT fv.id_feature_value, fv.id_feature + FROM '._DB_PREFIX_.'feature_value fv + LEFT JOIN '._DB_PREFIX_.'feature_product fp ON (fp.id_feature_value = fv.id_feature_value) + LEFT JOIN '._DB_PREFIX_.'product p ON (p.id_product = fp.id_product) + '.$join_product.' + LEFT JOIN '._DB_PREFIX_.'category_product cp ON (cp.id_product = p.id_product) + LEFT JOIN '._DB_PREFIX_.'category c ON (c.id_category = cp.id_category) + WHERE (fv.custom IS NULL OR fv.custom = 0) AND c.active = 1'.(count($categories_ids) ? ' AND cp.id_category IN ('.implode(',', $categories_ids).')' : '').' + AND '.$alias.'.active = 1 AND '.$alias.'.`visibility` IN ("both", "catalog") '.(count($products_ids) ? 'AND p.id_product IN ('.implode(',', $products_ids).')' : '')); + + $features_by_id = array(); + while ($row = $db->nextRow($features)) + $features_by_id[(int)$row['id_feature_value']] = (int)$row['id_feature']; + + $result = self::query(' + SELECT p.id_product, + GROUP_CONCAT(DISTINCT fv.id_feature_value) features, + GROUP_CONCAT(DISTINCT cp.id_category) categories, + GROUP_CONCAT(DISTINCT pac.id_attribute) attributes + FROM '._DB_PREFIX_.'product p + LEFT JOIN '._DB_PREFIX_.'category_product cp ON (cp.id_product = p.id_product) + LEFT JOIN '._DB_PREFIX_.'category c ON (c.id_category = cp.id_category) + LEFT JOIN '._DB_PREFIX_.'feature_product fp ON (fp.id_product = p.id_product) + LEFT JOIN '._DB_PREFIX_.'feature_value fv ON (fv.id_feature_value = fp.id_feature_value) + LEFT JOIN '._DB_PREFIX_.'product_attribute pa ON (pa.id_product = p.id_product) + '.$join_product.$join_product_attribute.' + LEFT JOIN '._DB_PREFIX_.'product_attribute_combination pac ON (pac.id_product_attribute = pa.id_product_attribute) + WHERE c.active = 1'.(count($categories_ids) ? ' AND cp.id_category IN ('.implode(',', $categories_ids).')' : '').' + AND '.$alias.'.active = 1 AND '.$alias.'.`visibility` IN ("both", "catalog") + '.(count($products_ids) ? 'AND p.id_product IN ('.implode(',', $products_ids).')' : ''). + ' AND (fv.custom IS NULL OR fv.custom = 0) + GROUP BY p.id_product'); + + + $shop_list = Shop::getShops(false, null, true); + + $to_insert = false; + while ($product = $db->nextRow($result)) + { + $a = $c = $f = array(); + if (!empty($product['attributes'])) + $a = array_flip(explode(',', $product['attributes'])); + if (!empty($product['categories'])) + $c = array_flip(explode(',', $product['categories'])); + if (!empty($product['features'])) + $f = array_flip(explode(',', $product['features'])); + + $filter_data['shop_list'] = $shop_list; + + foreach ($shop_list as $id_shop) + { + foreach ($c as $id_category => $category) + { + if (!in_array($id_category, $filter_data['categories'])) + $filter_data['categories'][] = $id_category; + + if (!isset($n_categories[(int)$id_category])) + $n_categories[(int)$id_category] = 1; + if (!isset($done_categories[(int)$id_category]['cat'])) + { + $filter_data['layered_selection_subcategories'] = array('filter_type' => 0, 'filter_show_limit' => 0); + $done_categories[(int)$id_category]['cat'] = true; + $to_insert = true; + } + if (is_array($attribute_groups_by_id) && count($attribute_groups_by_id) > 0) + foreach ($a as $k_attribute => $attribute) + if (!isset($done_categories[(int)$id_category]['a'.(int)$attribute_groups_by_id[(int)$k_attribute]])) + { + $filter_data['layered_selection_ag_'.(int)$attribute_groups_by_id[(int)$k_attribute]] = array('filter_type' => 0, 'filter_show_limit' => 0); + $done_categories[(int)$id_category]['a'.(int)$attribute_groups_by_id[(int)$k_attribute]] = true; + $to_insert = true; + } + if (is_array($attribute_groups_by_id) && count($attribute_groups_by_id) > 0) + foreach ($f as $k_feature => $feature) + if (!isset($done_categories[(int)$id_category]['f'.(int)$features_by_id[(int)$k_feature]])) + { + $filter_data['layered_selection_feat_'.(int)$features_by_id[(int)$k_feature]] = array('filter_type' => 0, 'filter_show_limit' => 0); + $done_categories[(int)$id_category]['f'.(int)$features_by_id[(int)$k_feature]] = true; + $to_insert = true; + } + if (!isset($done_categories[(int)$id_category]['q'])) + { + $filter_data['layered_selection_stock'] = array('filter_type' => 0, 'filter_show_limit' => 0); + $done_categories[(int)$id_category]['q'] = true; + $to_insert = true; + } + if (!isset($done_categories[(int)$id_category]['m'])) + { + $filter_data['layered_selection_manufacturer'] = array('filter_type' => 0, 'filter_show_limit' => 0); + $done_categories[(int)$id_category]['m'] = true; + $to_insert = true; + } + if (!isset($done_categories[(int)$id_category]['c'])) + { + $filter_data['layered_selection_condition'] = array('filter_type' => 0, 'filter_show_limit' => 0); + $done_categories[(int)$id_category]['c'] = true; + $to_insert = true; + } + if (!isset($done_categories[(int)$id_category]['w'])) + { + $filter_data['layered_selection_weight_slider'] = array('filter_type' => 0, 'filter_show_limit' => 0); + $done_categories[(int)$id_category]['w'] = true; + $to_insert = true; + } + if (!isset($done_categories[(int)$id_category]['p'])) + { + $filter_data['layered_selection_price_slider'] = array('filter_type' => 0, 'filter_show_limit' => 0); + $done_categories[(int)$id_category]['p'] = true; + $to_insert = true; + } + } + } + } + if ($to_insert) + { + Db::getInstance()->execute('INSERT INTO '._DB_PREFIX_.'layered_filter(name, filters, n_categories, date_add) + VALUES (\''.sprintf($this->l('My template %s'), date('Y-m-d')).'\', \''.pSQL(serialize($filter_data)).'\', '.count($filter_data['categories']).', NOW())'); + + $last_id = Db::getInstance()->Insert_ID(); + Db::getInstance()->execute('DELETE FROM '._DB_PREFIX_.'layered_filter_shop WHERE `id_layered_filter` = '.$last_id); + foreach ($shop_list as $id_shop) + Db::getInstance()->execute('INSERT INTO '._DB_PREFIX_.'layered_filter_shop (`id_layered_filter`, `id_shop`) + VALUES('.$last_id.', '.(int)$id_shop.')'); + + $this->buildLayeredCategories(); + } + } + + public function buildLayeredCategories() + { + // Get all filter template + $res = Db::getInstance()->executeS('SELECT * FROM '._DB_PREFIX_.'layered_filter ORDER BY date_add DESC'); + $categories = array(); + // Remove all from layered_category + Db::getInstance()->execute('TRUNCATE '._DB_PREFIX_.'layered_category'); + + if (!count($res)) // No filters templates defined, nothing else to do + return true; + + $sql_to_insert = 'INSERT INTO '._DB_PREFIX_.'layered_category (id_category, id_shop, id_value, type, position, filter_show_limit, filter_type) VALUES '; + $values = false; + + foreach ($res as $filter_template) + { + $data = Tools::unSerialize($filter_template['filters']); + foreach ($data['shop_list'] as $id_shop) + { + if (!isset($categories[$id_shop])) + $categories[$id_shop] = array(); + + foreach ($data['categories'] as $id_category) + { + $n = 0; + if (!in_array($id_category, $categories[$id_shop])) // Last definition, erase preivious categories defined + { + $categories[$id_shop][] = $id_category; + + foreach ($data as $key => $value) + if (substr($key, 0, 17) == 'layered_selection') + { + $values = true; + $type = $value['filter_type']; + $limit = $value['filter_show_limit']; + $n++; + + if ($key == 'layered_selection_stock') + $sql_to_insert .= '('.(int)$id_category.', '.(int)$id_shop.', NULL,\'quantity\','.(int)$n.', '.(int)$limit.', '.(int)$type.'),'; + else if ($key == 'layered_selection_subcategories') + $sql_to_insert .= '('.(int)$id_category.', '.(int)$id_shop.', NULL,\'category\','.(int)$n.', '.(int)$limit.', '.(int)$type.'),'; + else if ($key == 'layered_selection_condition') + $sql_to_insert .= '('.(int)$id_category.', '.(int)$id_shop.', NULL,\'condition\','.(int)$n.', '.(int)$limit.', '.(int)$type.'),'; + else if ($key == 'layered_selection_weight_slider') + $sql_to_insert .= '('.(int)$id_category.', '.(int)$id_shop.', NULL,\'weight\','.(int)$n.', '.(int)$limit.', '.(int)$type.'),'; + else if ($key == 'layered_selection_price_slider') + $sql_to_insert .= '('.(int)$id_category.', '.(int)$id_shop.', NULL,\'price\','.(int)$n.', '.(int)$limit.', '.(int)$type.'),'; + else if ($key == 'layered_selection_manufacturer') + $sql_to_insert .= '('.(int)$id_category.', '.(int)$id_shop.', NULL,\'manufacturer\','.(int)$n.', '.(int)$limit.', '.(int)$type.'),'; + else if (substr($key, 0, 21) == 'layered_selection_ag_') + $sql_to_insert .= '('.(int)$id_category.', '.(int)$id_shop.', '.(int)str_replace('layered_selection_ag_', '', $key).', + \'id_attribute_group\','.(int)$n.', '.(int)$limit.', '.(int)$type.'),'; + else if (substr($key, 0, 23) == 'layered_selection_feat_') + $sql_to_insert .= '('.(int)$id_category.', '.(int)$id_shop.', '.(int)str_replace('layered_selection_feat_', '', $key).', + \'id_feature\','.(int)$n.', '.(int)$limit.', '.(int)$type.'),'; + } + } + } + } + } + if ($values) + Db::getInstance()->execute(rtrim($sql_to_insert, ',')); + } + + protected function getAnchor() + { + static $anchor = null; + if ($anchor === null) + if (!$anchor = Configuration::get('PS_ATTRIBUTE_ANCHOR_SEPARATOR')) + $anchor = '-'; + return $anchor; + } +} diff --git a/blocklayered.tpl b/blocklayered.tpl new file mode 100644 index 0000000..a7a771b --- /dev/null +++ b/blocklayered.tpl @@ -0,0 +1,254 @@ +{* +* 2007-2014 PrestaShop +* +* NOTICE OF LICENSE +* +* This source file is subject to the Academic Free License (AFL 3.0) +* that is bundled with this package in the file LICENSE.txt. +* It is also available through the world-wide-web at this URL: +* http://opensource.org/licenses/afl-3.0.php +* If you did not receive a copy of the license and are unable to +* obtain it through the world-wide-web, please send an email +* to license@prestashop.com so we can send you a copy immediately. +* +* DISCLAIMER +* +* Do not edit or add to this file if you wish to upgrade PrestaShop to newer +* versions in the future. If you wish to customize PrestaShop for your +* needs please refer to http://www.prestashop.com for more information. +* +* @author PrestaShop SA +* @copyright 2007-2014 PrestaShop SA +* @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) +* International Registred Trademark & Property of PrestaShop SA +*} + + +{if $nbr_filterBlocks != 0} + +
+

{l s='Catalog' mod='blocklayered'}

+
+
+
+ {if isset($selected_filters) && $n_filters > 0} +
+ {l s='Enabled filters:' mod='blocklayered'} +
    + {foreach from=$selected_filters key=filter_type item=filter_values} + {foreach from=$filter_values key=filter_key item=filter_value name=f_values} + {foreach from=$filters item=filter} + {if $filter.type == $filter_type && isset($filter.values)} + {if isset($filter.slider)} + {if $smarty.foreach.f_values.first} +
  • + x + {if $filter.format == 1} + {l s='%1$s: %2$s - %3$s'|sprintf:$filter.name:{displayPrice price=$filter.values[0]}:{displayPrice price=$filter.values[1]}|escape:html:'UTF-8' mod='blocklayered'} + {else} + {l s='%1$s: %2$s %4$s - %3$s %4$s'|sprintf:$filter.name:$filter.values[0]:$filter.values[1]:$filter.unit|escape:html:'UTF-8' mod='blocklayered'} + {/if} +
  • + {/if} + {else} + {foreach from=$filter.values key=id_value item=value} + {if $id_value == $filter_key && !is_numeric($filter_value) && ($filter.type eq 'id_attribute_group' || $filter.type eq 'id_feature') || $id_value == $filter_value && $filter.type neq 'id_attribute_group' && $filter.type neq 'id_feature'} +
  • + x + {$filter.name|escape:html:'UTF-8'}{l s=':' mod='blocklayered'} {$value.name|escape:html:'UTF-8'} +
  • + {/if} + {/foreach} + {/if} + {/if} + {/foreach} + {/foreach} + {/foreach} +
+
+ {/if} + {foreach from=$filters item=filter} + {if isset($filter.values)} + {if isset($filter.slider)} + + + {foreach from=$filters item=filter} + {if $filter.type_lite == 'id_attribute_group' && isset($filter.is_color_group) && $filter.is_color_group && $filter.filter_type != 2} + {foreach from=$filter.values key=id_value item=value} + {if isset($value.checked)} + + {/if} + {/foreach} + {/if} + {/foreach} + +
+ +
+{else} +
+
+
+ +
+
+
+


{l s='Loading...' mod='blocklayered'}

+
+
+{/if} + diff --git a/config.xml b/config.xml new file mode 100644 index 0000000..79655a3 --- /dev/null +++ b/config.xml @@ -0,0 +1,12 @@ + + + blocklayered + + + + + + 1 + 0 + + \ No newline at end of file diff --git a/css/blocklayered_admin.css b/css/blocklayered_admin.css new file mode 100644 index 0000000..67b33d9 --- /dev/null +++ b/css/blocklayered_admin.css @@ -0,0 +1,53 @@ +.table-filter-templates, .table-configurations { + width: 100%; +} + +.table-configurations td { + padding: 10px; +} + +.table-configurations .label { + text-align: right; + font-weight: bold; + width: 30%; + vertical-align: top; +} + +.help-block { + font-weight: normal; + font-style: italic; + font-size: 11px; +} + +.text-right { + text-align: right; +} + +.text-left { + text-align: left; +} + +.filter_panel header { + margin-bottom: 10px; + font-weight: bold; +} + +.filter_list { + background-color: #fff; + padding-top: 1px; + padding-bottom: 1px; +} + +.filter_list_item { + background-color: #E9E9E9; + margin: 5px; +} + +.filter_list_item td { + white-space: nowrap; +} + +.filter_list_item .filter-title { + font-weight: bold; + width: 100%; +} \ No newline at end of file diff --git a/css/blocklayered_admin_1.6.css b/css/blocklayered_admin_1.6.css new file mode 100644 index 0000000..e949b83 --- /dev/null +++ b/css/blocklayered_admin_1.6.css @@ -0,0 +1,29 @@ +.bootstrap .filter_list .filter_list_item { + display: table; + width: 100%; + padding: 5px 0; + margin-bottom: 4px; + background-color: white; + -webkit-box-shadow: rgba(0, 0, 0, 0.3) 0 0 3px, rgba(0, 0, 0, 0.1) 0 -2px 0 inset; + box-shadow: rgba(0, 0, 0, 0.3) 0 0 3px, rgba(0, 0, 0, 0.1) 0 -2px 0 inset; + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + -ms-border-radius: 3px; + -o-border-radius: 3px; + border-radius: 3px; +} + +.bootstrap .filter_panel { + min-height: 20px; + padding: 7px 7px 0px 7px; + margin-bottom: 20px; + background-color: #ebebeb; + border: 1px solid #d9d9d9; + border-radius: 3px; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); +} + +.bootstrap .filter_panel header { + margin-bottom: 7px; +} \ No newline at end of file diff --git a/delete.gif b/delete.gif new file mode 100644 index 0000000..6d186d1 Binary files /dev/null and b/delete.gif differ diff --git a/img/cogs.gif b/img/cogs.gif new file mode 100644 index 0000000..659aace Binary files /dev/null and b/img/cogs.gif differ diff --git a/img/cross.png b/img/cross.png new file mode 100644 index 0000000..560081d Binary files /dev/null and b/img/cross.png differ diff --git a/img/edit.gif b/img/edit.gif new file mode 100644 index 0000000..7934bbc Binary files /dev/null and b/img/edit.gif differ diff --git a/img/icon/index.php b/img/icon/index.php new file mode 100644 index 0000000..a41987d --- /dev/null +++ b/img/icon/index.php @@ -0,0 +1,35 @@ + +* @copyright 2007-2014 PrestaShop SA +* @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) +* International Registered Trademark & Property of PrestaShop SA +*/ + +header("Expires: Mon, 26 Jul 1997 05:00:00 GMT"); +header("Last-Modified: ".gmdate("D, d M Y H:i:s")." GMT"); + +header("Cache-Control: no-store, no-cache, must-revalidate"); +header("Cache-Control: post-check=0, pre-check=0", false); +header("Pragma: no-cache"); + +header("Location: ../"); +exit; \ No newline at end of file diff --git a/img/icon/less.gif b/img/icon/less.gif new file mode 100644 index 0000000..52928a2 Binary files /dev/null and b/img/icon/less.gif differ diff --git a/img/icon/more.gif b/img/icon/more.gif new file mode 100644 index 0000000..072e49d Binary files /dev/null and b/img/icon/more.gif differ diff --git a/img/index.php b/img/index.php new file mode 100644 index 0000000..a41987d --- /dev/null +++ b/img/index.php @@ -0,0 +1,35 @@ + +* @copyright 2007-2014 PrestaShop SA +* @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) +* International Registered Trademark & Property of PrestaShop SA +*/ + +header("Expires: Mon, 26 Jul 1997 05:00:00 GMT"); +header("Last-Modified: ".gmdate("D, d M Y H:i:s")." GMT"); + +header("Cache-Control: no-store, no-cache, must-revalidate"); +header("Cache-Control: post-check=0, pre-check=0", false); +header("Pragma: no-cache"); + +header("Location: ../"); +exit; \ No newline at end of file diff --git a/index.php b/index.php new file mode 100644 index 0000000..a41987d --- /dev/null +++ b/index.php @@ -0,0 +1,35 @@ + +* @copyright 2007-2014 PrestaShop SA +* @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) +* International Registered Trademark & Property of PrestaShop SA +*/ + +header("Expires: Mon, 26 Jul 1997 05:00:00 GMT"); +header("Last-Modified: ".gmdate("D, d M Y H:i:s")." GMT"); + +header("Cache-Control: no-store, no-cache, must-revalidate"); +header("Cache-Control: post-check=0, pre-check=0", false); +header("Pragma: no-cache"); + +header("Location: ../"); +exit; \ No newline at end of file diff --git a/js/blocklayered_admin.js b/js/blocklayered_admin.js new file mode 100644 index 0000000..0631cdc --- /dev/null +++ b/js/blocklayered_admin.js @@ -0,0 +1,219 @@ +function checkForm() +{ + var is_category_selected = false; + var is_filter_selected = false; + + $('#categories-treeview input[type=checkbox]').each( + function() + { + if ($(this).prop('checked')) + { + is_category_selected = true; + return false; + } + } + ); + + $('.filter_list_item input[type=checkbox]').each( + function() + { + if ($(this).prop('checked')) + { + is_filter_selected = true; + return false; + } + } + ); + + if (!is_category_selected) + { + alert(translations['no_selected_categories']); + $('#categories-treeview input[type=checkbox]').first().focus(); + return false; + } + + if (!is_filter_selected) + { + alert(translations['no_selected_filters']); + $('#filter_list_item input[type=checkbox]').first().focus(); + return false; + } + + + return true; +} + +$(document).ready( + function() + { + $('.ajaxcall').click( + function() + { + if (this.legend == undefined) + this.legend = $(this).html(); + + if (this.running == undefined) + this.running = false; + + if (this.running == true) + return false; + + $('.ajax-message').hide(); + this.running = true; + + if (typeof(this.restartAllowed) == 'undefined' || this.restartAllowed) + { + $(this).html(this.legend+translations['in_progress']); + $('#indexing-warning').show(); + } + + this.restartAllowed = false; + var type = $(this).attr('rel'); + + $.ajax( + { + url: this.href+'&ajax=1', + context: this, + dataType: 'json', + cache: 'false', + success: function(res) + { + this.running = false; + this.restartAllowed = true; + $('#indexing-warning').hide(); + $(this).html(this.legend); + + if (type == 'price') + $('#ajax-message-ok span').html(translations['url_indexation_finished']); + else + $('#ajax-message-ok span').html(translations['attribute_indexation_finished']); + + $('#ajax-message-ok').show(); + return; + }, + error: function(res) + { + this.restartAllowed = true; + $('#indexing-warning').hide(); + + if (type == 'price') + $('#ajax-message-ko span').html(translations['url_indexation_failed']); + else + $('#ajax-message-ko span').html(translations['attribute_indexation_failed']); + + $('#ajax-message-ko').show(); + $(this).html(this.legend); + this.running = false; + } + } + ); + return false; + }); + + $('.ajaxcall-recurcive').each( + function(it, elm) + { + $(elm).click( + function() + { + if (this.cursor == undefined) + this.cursor = 0; + + if (this.legend == undefined) + this.legend = $(this).html(); + + if (this.running == undefined) + this.running = false; + + if (this.running == true) + return false; + + $('.ajax-message').hide(); + + this.running = true; + + if (typeof(this.restartAllowed) == 'undefined' || this.restartAllowed) + { + $(this).html(this.legend+translations['in_progress']); + $('#indexing-warning').show(); + } + + this.restartAllowed = false; + + $.ajax( + { + url: this.href+'&ajax=1&cursor='+this.cursor, + context: this, + dataType: 'json', + cache: 'false', + success: function(res) + { + this.running = false; + if (res.result) + { + this.cursor = 0; + $('#indexing-warning').hide(); + $(this).html(this.legend); + $('#ajax-message-ok span').html(translations['price_indexation_finished']); + $('#ajax-message-ok').show(); + return; + } + this.cursor = parseInt(res.cursor); + $(this).html(this.legend+translations['price_indexation_in_progress'].replace('%s', res.count)); + $(this).click(); + }, + error: function(res) + { + this.restartAllowed = true; + $('#indexing-warning').hide(); + $('#ajax-message-ko span').html(translations['price_indexation_failed']); + $('#ajax-message-ko').show(); + $(this).html(this.legend); + + this.cursor = 0; + this.running = false; + } + }); + return false; + } + ); + } + ); + + if (typeof PS_LAYERED_INDEXED !== 'undefined' && PS_LAYERED_INDEXED) + { + $('#url-indexe').click(); + $('#full-index').click(); + } + + $('.sortable').sortable( + { + forcePlaceholderSize: true + }); + + $('.filter_list_item input[type=checkbox]').click( + function() + { + var current_selected_filters_count = parseInt($('#selected_filters').html()); + + if ($(this).prop('checked')) + $('#selected_filters').html(current_selected_filters_count+1); + else + $('#selected_filters').html(current_selected_filters_count-1); + } + ); + + if (typeof filters !== 'undefined') + { + filters = JSON.parse(filters); + + for (filter in filters) + { + $('#'+filter).attr("checked","checked"); + $('#selected_filters').html(parseInt($('#selected_filters').html())+1); + $('select[name="'+filter+'_filter_type"]').val(filters[filter].filter_type); + $('select[name="'+filter+'_filter_show_limit"]').val(filters[filter].filter_show_limit); + } + } + } +); \ No newline at end of file diff --git a/js/index.php b/js/index.php new file mode 100644 index 0000000..1ff0753 --- /dev/null +++ b/js/index.php @@ -0,0 +1,35 @@ + +* @copyright 2007-2014 PrestaShop SA +* @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) +* International Registered Trademark & Property of PrestaShop SA +*/ + +header("Expires: Mon, 26 Jul 1997 05:00:00 GMT"); +header("Last-Modified: ".gmdate("D, d M Y H:i:s")." GMT"); + +header("Cache-Control: no-store, no-cache, must-revalidate"); +header("Cache-Control: post-check=0, pre-check=0", false); +header("Pragma: no-cache"); + +header("Location: ../../"); +exit; \ No newline at end of file diff --git a/js/jquery.sortable.js b/js/jquery.sortable.js new file mode 100644 index 0000000..a0023ef --- /dev/null +++ b/js/jquery.sortable.js @@ -0,0 +1,85 @@ +/* + * HTML5 Sortable jQuery Plugin + * http://farhadi.ir/projects/html5sortable + * + * Copyright 2012, Ali Farhadi + * Released under the MIT license. + */ +(function($) { +var dragging, placeholders = $(); +$.fn.sortable = function(options) { + var method = String(options); + options = $.extend({ + connectWith: false + }, options); + return this.each(function() { + if (/^enable|disable|destroy$/.test(method)) { + var items = $(this).children($(this).data('items')).attr('draggable', method == 'enable'); + if (method == 'destroy') { + items.add(this).removeData('connectWith items') + .off('dragstart.h5s dragend.h5s selectstart.h5s dragover.h5s dragenter.h5s drop.h5s'); + } + return; + } + var isHandle, index, items = $(this).children(options.items); + var placeholder = $('<' + (/^ul|ol$/i.test(this.tagName) ? 'li' : 'div') + ' class="sortable-placeholder">'); + items.find(options.handle).mousedown(function() { + isHandle = true; + }).mouseup(function() { + isHandle = false; + }); + $(this).data('items', options.items) + placeholders = placeholders.add(placeholder); + if (options.connectWith) { + $(options.connectWith).add(this).data('connectWith', options.connectWith); + } + items.attr('draggable', 'true').on('dragstart.h5s', function(e) { + if (options.handle && !isHandle) { + return false; + } + isHandle = false; + var dt = e.originalEvent.dataTransfer; + dt.effectAllowed = 'move'; + dt.setData('Text', 'dummy'); + index = (dragging = $(this)).addClass('sortable-dragging').index(); + }).on('dragend.h5s', function() { + if (!dragging) { + return; + } + dragging.removeClass('sortable-dragging').show(); + placeholders.detach(); + if (index != dragging.index()) { + dragging.parent().trigger('sortupdate', {item: dragging, start_index: index, end_index: dragging.index()}); + } + dragging = null; + }).not('a[href], img').on('selectstart.h5s', function() { + this.dragDrop && this.dragDrop(); + return false; + }).end().add([this, placeholder]).on('dragover.h5s dragenter.h5s drop.h5s', function(e) { + if (!items.is(dragging) && options.connectWith !== $(dragging).parent().data('connectWith')) { + return true; + } + if (e.type == 'drop') { + e.stopPropagation(); + placeholders.filter(':visible').after(dragging); + dragging.trigger('dragend.h5s'); + return false; + } + e.preventDefault(); + e.originalEvent.dataTransfer.dropEffect = 'move'; + if (items.is(this)) { + if (options.forcePlaceholderSize) { + placeholder.height(dragging.outerHeight()); + } + dragging.hide(); + $(this)[placeholder.index() < $(this).index() ? 'after' : 'before'](placeholder); + placeholders.not(placeholder).detach(); + } else if (!placeholders.is(this) && !$(this).children(options.items).length) { + placeholders.detach(); + $(this).append(placeholder); + } + return false; + }); + }); +}; +})(jQuery); diff --git a/logo.gif b/logo.gif new file mode 100644 index 0000000..40df30a Binary files /dev/null and b/logo.gif differ diff --git a/logo.png b/logo.png new file mode 100644 index 0000000..18a4bf5 Binary files /dev/null and b/logo.png differ diff --git a/upgrade/index.php b/upgrade/index.php new file mode 100644 index 0000000..1ff0753 --- /dev/null +++ b/upgrade/index.php @@ -0,0 +1,35 @@ + +* @copyright 2007-2014 PrestaShop SA +* @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) +* International Registered Trademark & Property of PrestaShop SA +*/ + +header("Expires: Mon, 26 Jul 1997 05:00:00 GMT"); +header("Last-Modified: ".gmdate("D, d M Y H:i:s")." GMT"); + +header("Cache-Control: no-store, no-cache, must-revalidate"); +header("Cache-Control: post-check=0, pre-check=0", false); +header("Pragma: no-cache"); + +header("Location: ../../"); +exit; \ No newline at end of file diff --git a/upgrade/install-1.9.php b/upgrade/install-1.9.php new file mode 100644 index 0000000..e227430 --- /dev/null +++ b/upgrade/install-1.9.php @@ -0,0 +1,9 @@ +registerHook('displayBackOfficeHeader'); +} diff --git a/views/index.php b/views/index.php new file mode 100644 index 0000000..1ff0753 --- /dev/null +++ b/views/index.php @@ -0,0 +1,35 @@ + +* @copyright 2007-2014 PrestaShop SA +* @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) +* International Registered Trademark & Property of PrestaShop SA +*/ + +header("Expires: Mon, 26 Jul 1997 05:00:00 GMT"); +header("Last-Modified: ".gmdate("D, d M Y H:i:s")." GMT"); + +header("Cache-Control: no-store, no-cache, must-revalidate"); +header("Cache-Control: post-check=0, pre-check=0", false); +header("Pragma: no-cache"); + +header("Location: ../../"); +exit; \ No newline at end of file diff --git a/views/templates/admin/add.tpl b/views/templates/admin/add.tpl new file mode 100644 index 0000000..54f5b70 --- /dev/null +++ b/views/templates/admin/add.tpl @@ -0,0 +1,308 @@ +{if isset($message)}{$message}{/if} +
+ {l s='Configuration' mod='blocklayered'} {l s='New filters template' mod='blocklayered'} +
+ + + + + + + + + + + + {if isset($asso_shops)} + + + + + {/if} + + + + + + + + + +
{l s='Template name:' mod='blocklayered'} + +

{l s='Only as a reminder' mod='blocklayered'}

+
{l s='Categories used for this template:' mod='blocklayered'}{$categories_tree}
+ {l s='Choose shop association:' mod='blocklayered'} + {$asso_shops}
+ {l s='Filters:' mod='blocklayered'} +

{l s='You can drag and drop filters to adjust position' mod='blocklayered'}

+
+
+
+ {l s='Total filters: %s'|sprintf:$total_filters mod='blocklayered'} - {l s='Selected filters: %s'|sprintf:0 mod='blocklayered'} +
+
+
    +
  • + + + + + + + + + + + +
    {l s='Sub-categories filter' mod='blocklayered'}{l s='Filter result limit:' mod='blocklayered'} + + {l s='Filter style:' mod='blocklayered'} + +
    +
  • +
  • + + + + + + + + + + + +
    {l s='Product stock filter' mod='blocklayered'}{l s='Filter result limit:' mod='blocklayered'} + + {l s='Filter style:' mod='blocklayered'} + +
    +
  • +
  • + + + + + + + + + + + +
    {l s='Product condition filter' mod='blocklayered'}{l s='Filter result limit:' mod='blocklayered'} + + {l s='Filter style:' mod='blocklayered'} + +
    +
  • +
  • + + + + + + + + + + + +
    {l s='Product manufacturer filter' mod='blocklayered'}{l s='Filter result limit:' mod='blocklayered'} + + {l s='Filter style:' mod='blocklayered'} + +
    +
  • +
  • + + + + + + + + + + + +
    {l s='Product weight filter (slider)' mod='blocklayered'}{l s='Filter result limit:' mod='blocklayered'} + + {l s='Filter style:' mod='blocklayered'} + +
    +
  • +
  • + + + + + + + + + + + +
    {l s='Product price filter (slider)' mod='blocklayered'}{l s='Filter result limit:' mod='blocklayered'} + + {l s='Filter style:' mod='blocklayered'} + +
    +
  • + {if $attribute_groups|count > 0} + {foreach $attribute_groups as $attribute_group} +
  • + + + + + + + + + + + +
    + {if $attribute_group['n'] > 1} + {l s='Attribute group: %1$s (%2$d attributes)'|sprintf:$attribute_group['name']:$attribute_group['n'] mod='blocklayered'} + {else} + {l s='Attribute group: %1$s (%2$d attribute)'|sprintf:$attribute_group['name']:$attribute_group['n'] mod='blocklayered'} + {/if} + {if $attribute_group['is_color_group']} + + {/if} + {l s='Filter result limit:' mod='blocklayered'} + + {l s='Filter style:' mod='blocklayered'} + +
    +
  • + {/foreach} + {/if} + + {if $features|count > 0} + {foreach $features as $feature} +
  • + + + + + + + + + + + +
    + {if $feature['n'] > 1}{l s='Feature: %1$s (%2$d values)'|sprintf:$feature['name']:$feature['n'] mod='blocklayered'}{else}{l s='Feature: %1$s (%2$d value)'|sprintf:$feature['name']:$feature['n'] mod='blocklayered'}{/if} + {l s='Filter result limit:' mod='blocklayered'} + + {l s='Filter style:' mod='blocklayered'} + +
    +
  • + {/foreach} + {/if} +
+
+
+
  + + + Cancel + +
+
+
+ + \ No newline at end of file diff --git a/views/templates/admin/add_1.6.tpl b/views/templates/admin/add_1.6.tpl new file mode 100644 index 0000000..7319f81 --- /dev/null +++ b/views/templates/admin/add_1.6.tpl @@ -0,0 +1,372 @@ +{if isset($message)}{$message}{/if} +
+

{l s='New filters template' mod='blocklayered'}

+
+ +
+ +
+ +

{l s='Only as a reminder' mod='blocklayered'}

+
+
+
+ +
+ {$categories_tree} +
+
+ {if isset($asso_shops)} +
+ +
{$asso_shops}
+
+ {/if} +
+ +
+
+
+ {l s='Total filters: %s'|sprintf:$total_filters mod='blocklayered'} +
+
+
    +
  • +
    + +
    +
    +

    {l s='Sub-categories filter' mod='blocklayered'}

    +
    +
    + +
    + +
    +
    +
    + +
    + +
    +
    +
  • +
  • +
    + +
    +
    + {l s='Product stock filter' mod='blocklayered'} +
    +
    + +
    + +
    +
    +
    + +
    + +
    +
    +
  • +
  • +
    + +
    +
    + {l s='Product condition filter' mod='blocklayered'} +
    +
    + +
    + +
    +
    +
    + +
    + +
    +
    +
  • +
  • +
    + +
    +
    + {l s='Product manufacturer filter' mod='blocklayered'} +
    +
    + +
    + +
    +
    +
    + +
    + +
    +
    +
  • +
  • +
    + +
    +
    + {l s='Product weight filter (slider)' mod='blocklayered'} +
    +
    + +
    + +
    +
    +
    + +
    + +
    +
    +
  • +
  • +
    + +
    +
    + {l s='Product price filter (slider)' mod='blocklayered'} +
    +
    + +
    + +
    +
    +
    + +
    + +
    +
    +
  • + {if $attribute_groups|count > 0} + {foreach $attribute_groups as $attribute_group} +
  • +
    + +
    +
    + + {if $attribute_group['n'] > 1} + {l s='Attribute group: %1$s (%2$d attributes)'|sprintf:$attribute_group['name']:$attribute_group['n'] mod='blocklayered'} + {else} + {l s='Attribute group: %1$s (%2$d attribute)'|sprintf:$attribute_group['name']:$attribute_group['n'] mod='blocklayered'} + {/if} + {if $attribute_group['is_color_group']} + + {/if} + +
    +
    + +
    + +
    +
    +
    + +
    + +
    +
    +
  • + {/foreach} + {/if} + + {if $features|count > 0} + {foreach $features as $feature} +
  • +
    + +
    +
    + + {if $feature['n'] > 1}{l s='Feature: %1$s (%2$d values)'|sprintf:$feature['name']:$feature['n'] mod='blocklayered'}{else}{l s='Feature: %1$s (%2$d value)'|sprintf:$feature['name']:$feature['n'] mod='blocklayered'}{/if} + +
    +
    + +
    + +
    +
    +
    + +
    + +
    +
    +
  • + {/foreach} + {/if} +
+
+
+
+
+ +
+
+ + \ No newline at end of file diff --git a/views/templates/admin/index.php b/views/templates/admin/index.php new file mode 100644 index 0000000..2e98e55 --- /dev/null +++ b/views/templates/admin/index.php @@ -0,0 +1,35 @@ + +* @copyright 2007-2014 PrestaShop SA +* @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) +* International Registered Trademark & Property of PrestaShop SA +*/ + +header("Expires: Mon, 26 Jul 1997 05:00:00 GMT"); +header("Last-Modified: ".gmdate("D, d M Y H:i:s")." GMT"); + +header("Cache-Control: no-store, no-cache, must-revalidate"); +header("Cache-Control: post-check=0, pre-check=0", false); +header("Pragma: no-cache"); + +header("Location: ../../../../"); +exit; \ No newline at end of file diff --git a/views/templates/admin/view.tpl b/views/templates/admin/view.tpl new file mode 100644 index 0000000..b48473c --- /dev/null +++ b/views/templates/admin/view.tpl @@ -0,0 +1,264 @@ +{if isset($message)}{$message}{/if} + + +{if !empty($limit_warning)} +
+ {if $limit_warning['error_type'] == 'suhosin'} + {l s='Warning! Your hosting provider is using the Suhosin patch for PHP, which limits the maximum number of fields allowed in a form:'} + + {$limit_warning['post.max_vars']} {l s='for suhosin.post.max_vars.'}
+ {$limit_warning['request.max_vars']} {l s='for suhosin.request.max_vars.'}
+ {l s='Please ask your hosting provider to increase the Suhosin limit to'} + {else} + {l s='Warning! Your PHP configuration limits the maximum number of fields allowed in a form:'}
+ {$limit_warning['max_input_vars']} {l s='for max_input_vars.'}
+ {l s='Please ask your hosting provider to increase this limit to'} + {/if} + {l s='%s at least, or you will have to edit the translation files manually.' sprintf=$limit_warning['needed_limit']} +
+{/if} +
+ {l s='Indexes and caches' mod='blocklayered'} {l s='Indexes and caches' mod='blocklayered'} + + +
 
+
+
+ {l s='You can set a cron job that will rebuild price index using the following URL:' mod='blocklayered'} +
+ {$price_indexer_url} +
+
+ {l s='You can set a cron job that will rebuild attribute index using the following URL:' mod='blocklayered'} +
+ {$attribute_indexer_url} +
+
+ {l s='You can set a cron job that will rebuild URL index using the following URL:' mod='blocklayered'} +
+ {$url_indexer_url} +
+
+
+
{l s='A nightly rebuild is recommended.' mod='blocklayered'}
+
+
+
 
+
+ {l s='Filters templates' mod='blocklayered'} {l s='Filters templates (%d)' sprintf=[$filters_templates|count] mod='blocklayered'} + {if $filters_templates|count > 0} +
+ + + + + + + + + + + + {foreach $filters_templates as $template} + + + + + + + + {/foreach} + +
{l s='ID' mod='blocklayered'}{l s='Name' mod='blocklayered'}{l s='Categories' mod='blocklayered'}{l s='Created on' mod='blocklayered'}{l s='Actions' mod='blocklayered'}
{(int)$template['id_layered_filter']}{$template['name']}{(int)$template['n_categories']}{Tools::displayDate($template['date_add'],null , true)} + {if empty($limit_warning)} + {l s='Edit' mod='blocklayered'}{l s='Edit' mod='blocklayered'} + {/if} + {l s='Delete' mod='blocklayered'}{l s='Delete' mod='blocklayered'} +
+
 
+
+ {else} +
{l s='No filter template found.' mod='blocklayered'}
+ {/if} + {if empty($limit_warning)} + + {/if} +
+
 
+
+ {l s='Configuration' mod='blocklayered'} {l s='Configuration' mod='blocklayered'} +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ {l s='Hide filter values with no product is matching' mod='blocklayered'} + + + + + +
+ {l s='Show the number of matching products' mod='blocklayered'} + + + + + +
+ {l s='Show products from subcategories' mod='blocklayered'} + + + + + +
+ {l s='Category filter depth (0 for no limits, 1 by default)' mod='blocklayered'} + + +
+ {l s='Use tax to filter price' mod='blocklayered'} + + + + + +
+ {l s='Allow indexing robots (google, yahoo, bing, ...) to use condition filter' mod='blocklayered'} + + + + + +
+ {l s='Allow indexing robots (google, yahoo, bing, ...) to use availability filter' mod='blocklayered'} + + + + + +
+ {l s='Allow indexing robots (google, yahoo, bing, ...) to use manufacturer filter' mod='blocklayered'} + + + + + +
+ {l s='Allow indexing robots (google, yahoo, bing, ...) to use category filter' mod='blocklayered'} + + + + + +
  + +
+
+
+ + \ No newline at end of file diff --git a/views/templates/admin/view_1.6.tpl b/views/templates/admin/view_1.6.tpl new file mode 100644 index 0000000..e365e5f --- /dev/null +++ b/views/templates/admin/view_1.6.tpl @@ -0,0 +1,277 @@ +{if isset($message)}{$message}{/if} + + +{if !empty($limit_warning)} +
+ {if $limit_warning['error_type'] == 'suhosin'} + {l s='Warning! Your hosting provider is using the Suhosin patch for PHP, which limits the maximum number of fields allowed in a form:'} + + {$limit_warning['post.max_vars']} {l s='for suhosin.post.max_vars.'}
+ {$limit_warning['request.max_vars']} {l s='for suhosin.request.max_vars.'}
+ {l s='Please ask your hosting provider to increase the Suhosin limit to'} + {else} + {l s='Warning! Your PHP configuration limits the maximum number of fields allowed in a form:'}
+ {$limit_warning['max_input_vars']} {l s='for max_input_vars.'}
+ {l s='Please ask your hosting provider to increase this limit to'} + {/if} + {l s='%s at least, or you will have to edit the translation files manually.' sprintf=$limit_warning['needed_limit']} +
+{/if} +
+

{l s='Indexes and caches' mod='blocklayered'}

+ + +
+
+ {l s='You can set a cron job that will rebuild price index using the following URL:' mod='blocklayered'} +
+ {$price_indexer_url} +
+
+ {l s='You can set a cron job that will rebuild attribute index using the following URL:' mod='blocklayered'} +
+ {$attribute_indexer_url} +
+
+ {l s='You can set a cron job that will rebuild URL index using the following URL:' mod='blocklayered'} +
+ {$url_indexer_url} +
+
+
+
{l s='A nightly rebuild is recommended.' mod='blocklayered'}
+
+
+
+

{l s='Filters templates' mod='blocklayered'}{$filters_templates|count}

+ {if $filters_templates|count > 0} +
+ + + + + + + + + + + + {foreach $filters_templates as $template} + + + + + + + + {/foreach} + +
{l s='ID' mod='blocklayered'}{l s='Name' mod='blocklayered'}{l s='Categories' mod='blocklayered'}{l s='Created on' mod='blocklayered'}{l s='Actions' mod='blocklayered'}
{(int)$template['id_layered_filter']}{$template['name']}{(int)$template['n_categories']}{Tools::displayDate($template['date_add'],null , true)} + {if empty($limit_warning)} + + {/if} +
+
 
+
+ {else} +
{l s='No filter template found.' mod='blocklayered'}
+ {/if} + {if empty($limit_warning)} + + {/if} +
+
+

{l s='Configuration' mod='blocklayered'}

+
+
+ +
+ + + + + + + +
+
+
+ +
+ + + + + + + +
+
+
+ +
+ + + + + + + +
+
+
+ +
+ +
+
+
+ +
+ + + + + + + +
+
+
+ +
+ + + + + + + +
+
+
+ +
+ + + + + + + +
+
+
+ +
+ + + + + + + +
+
+
+ +
+ + + + + + + +
+
+ +
+
+ + \ No newline at end of file diff --git a/views/templates/front/index.php b/views/templates/front/index.php new file mode 100644 index 0000000..2e98e55 --- /dev/null +++ b/views/templates/front/index.php @@ -0,0 +1,35 @@ + +* @copyright 2007-2014 PrestaShop SA +* @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) +* International Registered Trademark & Property of PrestaShop SA +*/ + +header("Expires: Mon, 26 Jul 1997 05:00:00 GMT"); +header("Last-Modified: ".gmdate("D, d M Y H:i:s")." GMT"); + +header("Cache-Control: no-store, no-cache, must-revalidate"); +header("Cache-Control: post-check=0, pre-check=0", false); +header("Pragma: no-cache"); + +header("Location: ../../../../"); +exit; \ No newline at end of file diff --git a/views/templates/hook/attribute_form.tpl b/views/templates/hook/attribute_form.tpl new file mode 100644 index 0000000..eacceee --- /dev/null +++ b/views/templates/hook/attribute_form.tpl @@ -0,0 +1,53 @@ +{* +* 2007-2014 PrestaShop +* +* NOTICE OF LICENSE +* +* This source file is subject to the Academic Free License (AFL 3.0) +* that is bundled with this package in the file LICENSE.txt. +* It is also available through the world-wide-web at this URL: +* http://opensource.org/licenses/afl-3.0.php +* If you did not receive a copy of the license and are unable to +* obtain it through the world-wide-web, please send an email +* to license@prestashop.com so we can send you a copy immediately. +* +* DISCLAIMER +* +* Do not edit or add to this file if you wish to upgrade PrestaShop to newer +* versions in the future. If you wish to customize PrestaShop for your +* needs please refer to http://www.prestashop.com for more information. +* +* @author PrestaShop SA +* @copyright 2007-2014 PrestaShop SA +* @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) +* International Registred Trademark & Property of PrestaShop SA +*} + +
+
+ {foreach $languages as $language} +
+ +

{l s='Specific URL format in block layered generation' mod='blocklayered'}

+ {l s='Invalid characters: <>;=#{}_' mod='blocklayered'}  +
+ {/foreach} +
+
+
+ +
+
+ {foreach $languages as $language} +
+ +

{l s='Specific format for meta title.' mod='blocklayered'}

+
+ {/foreach} +
+
+
\ No newline at end of file diff --git a/views/templates/hook/attribute_form_1.6.tpl b/views/templates/hook/attribute_form_1.6.tpl new file mode 100644 index 0000000..6588103 --- /dev/null +++ b/views/templates/hook/attribute_form_1.6.tpl @@ -0,0 +1,82 @@ +{* +* 2007-2014 PrestaShop +* +* NOTICE OF LICENSE +* +* This source file is subject to the Academic Free License (AFL 3.0) +* that is bundled with this package in the file LICENSE.txt. +* It is also available through the world-wide-web at this URL: +* http://opensource.org/licenses/afl-3.0.php +* If you did not receive a copy of the license and are unable to +* obtain it through the world-wide-web, please send an email +* to license@prestashop.com so we can send you a copy immediately. +* +* DISCLAIMER +* +* Do not edit or add to this file if you wish to upgrade PrestaShop to newer +* versions in the future. If you wish to customize PrestaShop for your +* needs please refer to http://www.prestashop.com for more information. +* +* @author PrestaShop SA +* @copyright 2007-2014 PrestaShop SA +* @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) +* International Registred Trademark & Property of PrestaShop SA +*} +
+ +
+
+ {foreach $languages as $language} +
+
+ +
+
+ + +
+
+ {/foreach} +
+

{l s='Specific URL format in block layered generation.' mod='blocklayered'}

+
+
+
+
+
+ +
+
+ {foreach $languages as $language} +
+
+ +
+
+ + +
+
+ {/foreach} +
+

{l s='Specific format for meta title.' mod='blocklayered'}

+
+
+
+
\ No newline at end of file diff --git a/views/templates/hook/attribute_group_form.tpl b/views/templates/hook/attribute_group_form.tpl new file mode 100644 index 0000000..0098e0c --- /dev/null +++ b/views/templates/hook/attribute_group_form.tpl @@ -0,0 +1,64 @@ +{* +* 2007-2014 PrestaShop +* +* NOTICE OF LICENSE +* +* This source file is subject to the Academic Free License (AFL 3.0) +* that is bundled with this package in the file LICENSE.txt. +* It is also available through the world-wide-web at this URL: +* http://opensource.org/licenses/afl-3.0.php +* If you did not receive a copy of the license and are unable to +* obtain it through the world-wide-web, please send an email +* to license@prestashop.com so we can send you a copy immediately. +* +* DISCLAIMER +* +* Do not edit or add to this file if you wish to upgrade PrestaShop to newer +* versions in the future. If you wish to customize PrestaShop for your +* needs please refer to http://www.prestashop.com for more information. +* +* @author PrestaShop SA +* @copyright 2007-2014 PrestaShop SA +* @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) +* International Registred Trademark & Property of PrestaShop SA +*} + +
+
+ {foreach $languages as $language} +
+ +

{l s='Specific URL format in block layered generation.' mod='blocklayered'}

+ {l s='Invalid characters: <>;=#{}_' mod='blocklayered'}  +
+ {/foreach} +
+
+
+ +
+
+ {foreach $languages as $language} +
+ +

{l s='Specific format for meta title.' mod='blocklayered'}

+
+ {/foreach} +
+
+
+ +
+ + + + + + +

{l s='Use this attribute in URL generated by the layered navigation module.' mod='blocklayered'}

+
+
\ No newline at end of file diff --git a/views/templates/hook/attribute_group_form_1.6.tpl b/views/templates/hook/attribute_group_form_1.6.tpl new file mode 100644 index 0000000..a2065c1 --- /dev/null +++ b/views/templates/hook/attribute_group_form_1.6.tpl @@ -0,0 +1,101 @@ +{* +* 2007-2014 PrestaShop +* +* NOTICE OF LICENSE +* +* This source file is subject to the Academic Free License (AFL 3.0) +* that is bundled with this package in the file LICENSE.txt. +* It is also available through the world-wide-web at this URL: +* http://opensource.org/licenses/afl-3.0.php +* If you did not receive a copy of the license and are unable to +* obtain it through the world-wide-web, please send an email +* to license@prestashop.com so we can send you a copy immediately. +* +* DISCLAIMER +* +* Do not edit or add to this file if you wish to upgrade PrestaShop to newer +* versions in the future. If you wish to customize PrestaShop for your +* needs please refer to http://www.prestashop.com for more information. +* +* @author PrestaShop SA +* @copyright 2007-2014 PrestaShop SA +* @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) +* International Registred Trademark & Property of PrestaShop SA +*} +
+ +
+
+ {foreach $languages as $language} +
+
+ +
+
+ + +
+
+ {/foreach} +
+

{l s='Specific URL format in block layered generation.' mod='blocklayered'}

+
+
+
+
+
+ +
+
+ {foreach $languages as $language} +
+
+ +
+
+ + +
+
+ {/foreach} +
+

{l s='Specific format for meta title' mod='blocklayered'}

+
+
+
+
+
+ +
+ + + + + + + +
+
+

{l s='Use this attribute in URL generated by the layered navigation module' mod='blocklayered'}

+
+
\ No newline at end of file diff --git a/views/templates/hook/feature_form.tpl b/views/templates/hook/feature_form.tpl new file mode 100644 index 0000000..0098e0c --- /dev/null +++ b/views/templates/hook/feature_form.tpl @@ -0,0 +1,64 @@ +{* +* 2007-2014 PrestaShop +* +* NOTICE OF LICENSE +* +* This source file is subject to the Academic Free License (AFL 3.0) +* that is bundled with this package in the file LICENSE.txt. +* It is also available through the world-wide-web at this URL: +* http://opensource.org/licenses/afl-3.0.php +* If you did not receive a copy of the license and are unable to +* obtain it through the world-wide-web, please send an email +* to license@prestashop.com so we can send you a copy immediately. +* +* DISCLAIMER +* +* Do not edit or add to this file if you wish to upgrade PrestaShop to newer +* versions in the future. If you wish to customize PrestaShop for your +* needs please refer to http://www.prestashop.com for more information. +* +* @author PrestaShop SA +* @copyright 2007-2014 PrestaShop SA +* @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) +* International Registred Trademark & Property of PrestaShop SA +*} + +
+
+ {foreach $languages as $language} +
+ +

{l s='Specific URL format in block layered generation.' mod='blocklayered'}

+ {l s='Invalid characters: <>;=#{}_' mod='blocklayered'}  +
+ {/foreach} +
+
+
+ +
+
+ {foreach $languages as $language} +
+ +

{l s='Specific format for meta title.' mod='blocklayered'}

+
+ {/foreach} +
+
+
+ +
+ + + + + + +

{l s='Use this attribute in URL generated by the layered navigation module.' mod='blocklayered'}

+
+
\ No newline at end of file diff --git a/views/templates/hook/feature_form_1.6.tpl b/views/templates/hook/feature_form_1.6.tpl new file mode 100644 index 0000000..f412de2 --- /dev/null +++ b/views/templates/hook/feature_form_1.6.tpl @@ -0,0 +1,101 @@ +{* +* 2007-2014 PrestaShop +* +* NOTICE OF LICENSE +* +* This source file is subject to the Academic Free License (AFL 3.0) +* that is bundled with this package in the file LICENSE.txt. +* It is also available through the world-wide-web at this URL: +* http://opensource.org/licenses/afl-3.0.php +* If you did not receive a copy of the license and are unable to +* obtain it through the world-wide-web, please send an email +* to license@prestashop.com so we can send you a copy immediately. +* +* DISCLAIMER +* +* Do not edit or add to this file if you wish to upgrade PrestaShop to newer +* versions in the future. If you wish to customize PrestaShop for your +* needs please refer to http://www.prestashop.com for more information. +* +* @author PrestaShop SA +* @copyright 2007-2014 PrestaShop SA +* @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) +* International Registred Trademark & Property of PrestaShop SA +*} +
+ +
+
+ {foreach $languages as $language} +
+
+ +
+
+ + +
+
+ {/foreach} +
+

{l s='Specific URL format in block layered generation.' mod='blocklayered'}

+
+
+
+
+
+ +
+
+ {foreach $languages as $language} +
+
+ +
+
+ + +
+
+ {/foreach} +
+

{l s='Specific format for meta title.' mod='blocklayered'}

+
+
+
+
+
+ +
+ + + + + + + +
+
+

{l s='Use this attribute in URL generated by the layered navigation module.' mod='blocklayered'}

+
+
\ No newline at end of file diff --git a/views/templates/hook/feature_value_form.tpl b/views/templates/hook/feature_value_form.tpl new file mode 100644 index 0000000..2fb4097 --- /dev/null +++ b/views/templates/hook/feature_value_form.tpl @@ -0,0 +1,53 @@ +{* +* 2007-2014 PrestaShop +* +* NOTICE OF LICENSE +* +* This source file is subject to the Academic Free License (AFL 3.0) +* that is bundled with this package in the file LICENSE.txt. +* It is also available through the world-wide-web at this URL: +* http://opensource.org/licenses/afl-3.0.php +* If you did not receive a copy of the license and are unable to +* obtain it through the world-wide-web, please send an email +* to license@prestashop.com so we can send you a copy immediately. +* +* DISCLAIMER +* +* Do not edit or add to this file if you wish to upgrade PrestaShop to newer +* versions in the future. If you wish to customize PrestaShop for your +* needs please refer to http://www.prestashop.com for more information. +* +* @author PrestaShop SA +* @copyright 2007-2014 PrestaShop SA +* @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) +* International Registred Trademark & Property of PrestaShop SA +*} + +
+
+ {foreach $languages as $language} +
+ +

{l s='Specific URL format in block layered generation.' mod='blocklayered'}

+ {l s='Invalid characters: <>;=#{}_' mod='blocklayered'}  +
+ {/foreach} +
+
+
+ +
+
+ {foreach $languages as $language} +
+ +

{l s='Specific format for meta title.' mod='blocklayered'}

+
+ {/foreach} +
+
+
\ No newline at end of file diff --git a/views/templates/hook/feature_value_form_1.6.tpl b/views/templates/hook/feature_value_form_1.6.tpl new file mode 100644 index 0000000..6588103 --- /dev/null +++ b/views/templates/hook/feature_value_form_1.6.tpl @@ -0,0 +1,82 @@ +{* +* 2007-2014 PrestaShop +* +* NOTICE OF LICENSE +* +* This source file is subject to the Academic Free License (AFL 3.0) +* that is bundled with this package in the file LICENSE.txt. +* It is also available through the world-wide-web at this URL: +* http://opensource.org/licenses/afl-3.0.php +* If you did not receive a copy of the license and are unable to +* obtain it through the world-wide-web, please send an email +* to license@prestashop.com so we can send you a copy immediately. +* +* DISCLAIMER +* +* Do not edit or add to this file if you wish to upgrade PrestaShop to newer +* versions in the future. If you wish to customize PrestaShop for your +* needs please refer to http://www.prestashop.com for more information. +* +* @author PrestaShop SA +* @copyright 2007-2014 PrestaShop SA +* @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) +* International Registred Trademark & Property of PrestaShop SA +*} +
+ +
+
+ {foreach $languages as $language} +
+
+ +
+
+ + +
+
+ {/foreach} +
+

{l s='Specific URL format in block layered generation.' mod='blocklayered'}

+
+
+
+
+
+ +
+
+ {foreach $languages as $language} +
+
+ +
+
+ + +
+
+ {/foreach} +
+

{l s='Specific format for meta title.' mod='blocklayered'}

+
+
+
+
\ No newline at end of file diff --git a/views/templates/hook/index.php b/views/templates/hook/index.php new file mode 100644 index 0000000..2e98e55 --- /dev/null +++ b/views/templates/hook/index.php @@ -0,0 +1,35 @@ + +* @copyright 2007-2014 PrestaShop SA +* @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) +* International Registered Trademark & Property of PrestaShop SA +*/ + +header("Expires: Mon, 26 Jul 1997 05:00:00 GMT"); +header("Last-Modified: ".gmdate("D, d M Y H:i:s")." GMT"); + +header("Cache-Control: no-store, no-cache, must-revalidate"); +header("Cache-Control: post-check=0, pre-check=0", false); +header("Pragma: no-cache"); + +header("Location: ../../../../"); +exit; \ No newline at end of file diff --git a/views/templates/index.php b/views/templates/index.php new file mode 100644 index 0000000..09bab55 --- /dev/null +++ b/views/templates/index.php @@ -0,0 +1,35 @@ + +* @copyright 2007-2014 PrestaShop SA +* @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) +* International Registered Trademark & Property of PrestaShop SA +*/ + +header("Expires: Mon, 26 Jul 1997 05:00:00 GMT"); +header("Last-Modified: ".gmdate("D, d M Y H:i:s")." GMT"); + +header("Cache-Control: no-store, no-cache, must-revalidate"); +header("Cache-Control: post-check=0, pre-check=0", false); +header("Pragma: no-cache"); + +header("Location: ../../../"); +exit; \ No newline at end of file