move the similar products block into one tab in product details

- fix the number of products displayed in the 'best sales for family' case
    - replace the slider by an adapted block with visuals differents from the best sales slider.
    - adapt the show cart javascript in order to get the correct height of product blocks on which
      the user click
    - fix selection between accessories and best sales for family in hooks
    - add a hook in order to display the correct label in the new tab
    - restore olds methods for best sales product. Routines related to the best sales family products have their own methods.
    - fix cache generation
This commit is contained in:
Rodney Figaro 2016-09-22 15:14:39 +02:00
parent 5baa5eaf9a
commit c9ad2799d0
11 changed files with 369 additions and 1585 deletions

View File

@ -21,6 +21,15 @@ function showExpressCart(event) {
m.toggleClass('active');
var height = m.parents('.slider_sales_product_footer').height();
if (height < $('#product .expresscart_frame').height()) {
height = $('#product .expresscart_frame').height();
}
$('#product .expresscart_frame').height(height);
$('#product .expresscart_frame .loading').height(height);
if(expresscart_frame_visible.length > 0) {
m.removeClass('active');
hideFooterExpressCart();
@ -29,6 +38,8 @@ function showExpressCart(event) {
if(expresscart_frame.children("p.loading").length > 0) {
var id_product = m.parent().children(".ajax_add_to_cart_product_footer_button").attr("rel");
expresscart_frame.append("<iframe src=\"{/literal}{$base_dir_ssl}{literal}modules/expresscart/ajax.php?page=1&id_product=" + id_product + "\" style=\"display: none;\" border=\"0\" marginheight=\"0\" marginwidth=\"0\" frameborder=\"0\" onload=\"$(this).show(); $(this).parent().children('p.loading').remove();\"></iframe>");
$('#product .expresscart_frame iframe').height(height);
}
expresscart_frame.append('<a href="#" class="close_popup_footer"></a>');

View File

@ -1,96 +0,0 @@
/*
* jQuery FlexSlider v2.2.0
* http://www.woothemes.com/flexslider/
*
* Copyright 2012 WooThemes
* Free to use under the GPLv2 license.
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Contributing author: Tyler Smith (@mbmufffin)
*/
/* Browser Resets
*********************************/
.flex-container a:active,
.flexslider a:active,
.flex-container a:focus,
.flexslider a:focus {outline: none;}
.slides,
.flex-control-nav,
.flex-direction-nav {margin: 0; padding: 0; list-style: none;}
/* Icon Fonts
*********************************/
/* Font-face Icons */
@font-face {
font-family: 'flexslider-icon';
src:url('fonts/flexslider-icon.eot');
src:url('fonts/flexslider-icon.eot?#iefix') format('embedded-opentype'),
url('fonts/flexslider-icon.woff') format('woff'),
url('fonts/flexslider-icon.ttf') format('truetype'),
url('fonts/flexslider-icon.svg#flexslider-icon') format('svg');
font-weight: normal;
font-style: normal;
}
/* FlexSlider Necessary Styles
*********************************/
.flexslider {margin: 0; padding: 0;}
.flexslider .slides > li {display: none; -webkit-backface-visibility: hidden;} /* Hide the slides before the JS is loaded. Avoids image jumping */
.flexslider .slides img {width: 100%; display: block;}
.flex-pauseplay span {text-transform: capitalize;}
/* Clearfix for the .slides element */
.slides:after {content: "\0020"; display: block; clear: both; visibility: hidden; line-height: 0; height: 0;}
html[xmlns] .slides {display: block;}
* html .slides {height: 1%;}
/* No JavaScript Fallback */
/* If you are not using another script, such as Modernizr, make sure you
* include js that eliminates this class on page load */
.no-js .slides > li:first-child {display: block;}
/* FlexSlider Default Theme
*********************************/
.flexslider { margin: 0 0 60px; background: #fff; border: 4px solid #fff; position: relative; -webkit-border-radius: 4px; -moz-border-radius: 4px; -o-border-radius: 4px; border-radius: 4px; -webkit-box-shadow: 0 1px 4px rgba(0,0,0,.2); -moz-box-shadow: 0 1px 4px rgba(0,0,0,.2); -o-box-shadow: 0 1px 4px rgba(0,0,0,.2); box-shadow: 0 1px 4px rgba(0,0,0,.2); zoom: 1; }
.flex-viewport { max-height: 2000px; -webkit-transition: all 1s ease; -moz-transition: all 1s ease; -o-transition: all 1s ease; transition: all 1s ease; }
.loading .flex-viewport { max-height: 300px; }
.flexslider .slides { zoom: 1; }
.carousel li { margin-right: 5px; }
/* Direction Nav */
.flex-direction-nav {*height: 0;}
.flex-direction-nav a { display: block; width: 40px; height: 40px; margin: -20px 0 0; position: absolute; top: 50%; z-index: 10; overflow: hidden; opacity: 0; cursor: pointer; color: rgba(0,0,0,0.8); text-shadow: 1px 1px 0 rgba(255,255,255,0.3); -webkit-transition: all .3s ease; -moz-transition: all .3s ease; transition: all .3s ease; }
.flex-direction-nav .flex-prev { left: -50px; }
.flex-direction-nav .flex-next { right: -50px; text-align: right; }
.flexslider:hover .flex-prev { opacity: 0.7; left: 10px; }
.flexslider:hover .flex-next { opacity: 0.7; right: 10px; }
.flexslider:hover .flex-next:hover, .flexslider:hover .flex-prev:hover { opacity: 1; }
.flex-direction-nav .flex-disabled { opacity: 0!important; filter:alpha(opacity=0); cursor: default; }
.flex-direction-nav a:before { font-family: "flexslider-icon"; font-size: 40px; display: inline-block; content: '\f001'; }
.flex-direction-nav a.flex-next:before { content: '\f002'; }
/* Pause/Play */
.flex-pauseplay a { display: block; width: 20px; height: 20px; position: absolute; bottom: 5px; left: 10px; opacity: 0.8; z-index: 10; overflow: hidden; cursor: pointer; color: #000; }
.flex-pauseplay a:before { font-family: "flexslider-icon"; font-size: 20px; display: inline-block; content: '\f004'; }
.flex-pauseplay a:hover { opacity: 1; }
.flex-pauseplay a.flex-play:before { content: '\f003'; }
/* Control Nav */
.flex-control-nav {width: 100%; position: absolute; bottom: -40px; text-align: center;}
.flex-control-nav li {margin: 0 6px; display: inline-block; zoom: 1; *display: inline;}
.flex-control-paging li a {width: 11px; height: 11px; display: block; background: #666; background: rgba(0,0,0,0.5); cursor: pointer; text-indent: -9999px; -webkit-border-radius: 20px; -moz-border-radius: 20px; -o-border-radius: 20px; border-radius: 20px; -webkit-box-shadow: inset 0 0 3px rgba(0,0,0,0.3); -moz-box-shadow: inset 0 0 3px rgba(0,0,0,0.3); -o-box-shadow: inset 0 0 3px rgba(0,0,0,0.3); box-shadow: inset 0 0 3px rgba(0,0,0,0.3); }
.flex-control-paging li a:hover { background: #333; background: rgba(0,0,0,0.7); }
.flex-control-paging li a.flex-active { background: #000; background: rgba(0,0,0,0.9); cursor: default; }
.flex-control-thumbs {margin: 5px 0 0; position: static; overflow: hidden;}
.flex-control-thumbs li {width: 25%; float: left; margin: 0;}
.flex-control-thumbs img {width: 100%; display: block; opacity: .7; cursor: pointer;}
.flex-control-thumbs img:hover {opacity: 1;}
.flex-control-thumbs .flex-active {opacity: 1; cursor: default;}
@media screen and (max-width: 860px) {
.flex-direction-nav .flex-prev { opacity: 1; left: 10px;}
.flex-direction-nav .flex-next { opacity: 1; right: 10px;}
}

View File

@ -1,60 +1,41 @@
<div id="privatesales_similarproduct" class="block products_block">
{if $is_accessories}
<h1 class="">{l s='Autres produits associés' mod='privatesales_similarproducts'}</h1>
{else}
<h1 class="">{l s='Les meilleures ventes dans' mod='privatesales_similarproducts'} {$family_name}</h1>
{/if}
<div id="privatesales_similarproduct">
{if $ps_other_products|@count > 0}
<div class="slider_sales_product_footer">
<ul class="slides">
{foreach from=$ps_other_products item=product name=myLoop}
<li class='slide-item'>
<div class="hover-slide">
<a onclick="showExpressCart(event); return false;" class="add-to-cart ajax_add_to_cart_product_footer_button" rel="{$product.id_product}" href="" title="{l s='Add to cart' mod='privatesales_similarproducts'}"></a>
<a class="see-product" href="{$product.link}" title="{l s='See this product' mod='privatesales_similarproducts'}"></a>
</div>
<div class="product_added product_added_{$product.id_product}">
<p>{l s='Product added to your cart' mod='privatesales_similarproducts'}</p>
</div>
<div class="img_sellers">
<a href="{$product.link}" title="{$product.name|escape:'htmlall':'UTF-8'}"><img src="{$link->getImageLink($product.link_rewrite, $product.id_image, 'medium')}" alt="{$product.legend|escape:'htmlall':'UTF-8'}" /></a>
</div>
<div class="info_sellers">
<a href="{$product.link}" title="{$product.name|escape:'htmlall':'UTF-8'}" class="product_link_sellers">{$product.name}</a>
<p class="price">{$product.price}</p>
<p class="price_info">
<span class="old_price">{convertPrice price=$product.price_without_reduction}</span>
{if $product.specific_prices.reduction_type =='percentage'}
{assign var="reduc_value" value=$product.specific_prices.reduction * 100}
<span class="percentage">(-{$reduc_value|intval}%)</span>
{elseif $product.specific_prices.reduction_type == 'amount'}
<span class="amount">(-{$product.specific_prices.reduction|number_format:2:",":"."}€)</span>
{/if}
</p>
</div>
</li>
{/foreach}
</ul>
</div>
<div class='slider_sales_product_footer'>
<ul>
{foreach from=$ps_other_products item=product}
<li>
<div class="hover-slide">
<a onclick="showExpressCart(event); return false;" class="add-to-cart ajax_add_to_cart_product_footer_button" rel="{$product.id_product}" href="" title="{l s='Add to cart' mod='privatesales_similarproducts'}"></a>
<a class="see-product" href="{$product.link}" title="{l s='See this product' mod='privatesales_similarproducts'}"></a>
</div>
<div class="product_added product_added_{$product.id_product}">
<p>{l s='Product added to your cart' mod='privatesales_similarproducts'}</p>
</div>
<div class="similar_product">
<div class="product_sellers_label">
<span class="product_link_sellers">{$product.name}</span>
</div>
<div class="img_sellers">
<img src="{$link->getImageLink($product.link_rewrite, $product.id_image, 'medium')}" alt="{$product.legend|escape:'htmlall':'UTF-8'}" />
</div>
<div class="info_sellers">
<p class="price">{$product.price}</p>
<p class="price_info">
<span class="old_price">{convertPrice price=$product.price_without_reduction}</span>
{if $product.specific_prices.reduction_type =='percentage'}
{assign var="reduc_value" value=$product.specific_prices.reduction * 100}
<span class="percentage">(-{$reduc_value|intval}%)</span>
{elseif $product.specific_prices.reduction_type == 'amount'}
<span class="amount">(-{$product.specific_prices.reduction|number_format:2:",":"."}€)</span>
{/if}
</p>
</div>
</div>
</li>
{/foreach}
</ul>
</div>
{/if}
</div>
<link rel="stylesheet" href="/modules/privatesales_similarproducts/assets/css/flexslider.css">
<script type="text/javascript" src="/modules/privatesales_similarproducts/assets/js/jquery.flexslider.js"></script>
<script>
{literal}
$( document ).ready(function() {
$('.slider_sales_product_footer').flexslider({
animation: "slide",
directionNav: true,
itemWidth: (860/3),
useCSS : false,
pauseOnHover: true,
});
$('.slider_sales_product_footer .flex-direction-nav .flex-next').text('');
$('.slider_sales_product_footer .flex-direction-nav .flex-prev').text('');
});
{/literal}
</script>

View File

@ -1,36 +0,0 @@
<?php
/*
* 2007-2011 PrestaShop
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 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/osl-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 <contact@prestashop.com>
* @copyright 2007-2011 PrestaShop SA
* @version Release: $Revision: 7233 $
* @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 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;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 601 B

View File

@ -4,10 +4,18 @@ if (!defined('_PS_VERSION_'))
class Privatesales_similarproducts extends Module
{
private $_html = '';
private $_postErrors = array();
const HAS_ACCESSORIES_UNKNOWN = 0;
const HAS_ACCESSORIES_YES = 1;
const HAS_ACCESSORIES_NO = 2;
private $has_accessories_state = self::HAS_ACCESSORIES_UNKNOWN;
private $family = null;
private $label = '';
private $hooks = array(
'similarProduct' => array('Accessories or similar products', 'Called on displaying similar product in product details'),
'simlarProductTabLabel' => array('Tab labl accessories or similar products', 'Called on displaying tab in product details'
)
);
public function __construct()
@ -44,8 +52,17 @@ class Privatesales_similarproducts extends Module
}
}
if (!parent::install() ||
!$this->registerHook('similarProduct')) {
$install_success = parent::install();
if ($install_success) {
foreach($this->hooks as $k => $v) {
$install_success = $this->registerHook($k);
if (!$install_success) {
break;
}
}
}
if (!$install_success) {
$this->uninstall();
return false;
}
@ -70,11 +87,54 @@ class Privatesales_similarproducts extends Module
return false;
}
$result = $this->displayAccessories($params);
if ($result=='') {
$this->initFromHook($params);
if ($this->has_accessories_state == self::HAS_ACCESSORIES_YES) {
return $this->displayAccessories($params);
}
if ($this->has_accessories_state == self::HAS_ACCESSORIES_NO) {
return $this->displayBestVPSalesSameFamily($params);
}
return $result;
return '';
}
public function hookSimlarProductTabLabel($params)
{
if(_PS_MOBILE_) {
return false;
}
$this->initFromHook($params);
return $this->label;
}
private function initFromHook(array &$params)
{
if ($this->has_accessories_state != self::HAS_ACCESSORIES_UNKNOWN) {
return;
}
$query = '
SELECT COUNT(`id_product_2`)
FROM `'._DB_PREFIX_.'accessory`
JOIN `'._DB_PREFIX_.'product` p ON p.`id_product` = `id_product_2`
WHERE `id_product_1` = '.(int)$params['product']->id.'
AND p.`active` = 1
';
if (Db::getInstance()->getValue($query)>0) {
$this->has_accessories_state = self::HAS_ACCESSORIES_YES;
$this->label = $this->l('Autres produits associés');
}
else {
$this->has_accessories_state = self::HAS_ACCESSORIES_NO;
$this->family = ProductSale::getSaleFamily(
$params['sale']->id,
$params['cookie']->id_lang
);
$this->label = $this->l('Autres produits dans').' '.$this->family['name'];
}
}
@ -82,38 +142,36 @@ class Privatesales_similarproducts extends Module
{
global $smarty;
$cache_params = [
'prefix_cache' => '1_'.$params['product']->id
];
$cache_id = (int)$params['cookie']->id_lang.'_'.(int)$params['product']->id;
if (!$this->isTemplateCached($cache_params)) {
if (!$this->isTemplateCached($cache_id)) {
$other_products = array();
$accessories = $params['product']->getAccessories($params['cookie']->id_lang);
if(is_array($accessories)) {
$currency = new Currency((int)($params['cookie']->id_currency));
// get all accessories of this product
$other_products = array();
$accessories = $params['product']->getAccessories($params['cookie']->id_lang);
if(is_array($accessories)) {
foreach ($accessories AS $accessory)
{
$accessory['price'] = Tools::displayPrice(Product::getPriceStatic((int)($accessory['id_product'])), $currency);
$other_products[] = $accessory;
}
foreach ($accessories AS $accessory)
{
$accessory['price'] = Tools::displayPrice(Product::getPriceStatic((int)($accessory['id_product'])), $currency);
$other_products[] = $accessory;
}
}
if (empty($other_products)) {
return '';
}
if (empty($other_products)) {
return '';
}
$smarty->assign(
array(
'is_accessories' => TRUE,
'ps_other_products' => $other_products,
)
);
$smarty->assign(
array(
'label' => $this->label,
'ps_other_products' => $other_products,
)
);
}
return $this->flushTemplateCache($cache_params);
return $this->flushTemplateCache($cache_id);
}
@ -121,81 +179,59 @@ class Privatesales_similarproducts extends Module
{
global $smarty;
$cache_params = [
'prefix_cache' => '2_'.$params['sale']->id,
];
$cache_id = (int)$params['cookie']->id_lang.'_'.(int)$params['product']->id;
if (!$this->isTemplateCached($cache_params)) {
$currency = new Currency((int)($params['cookie']->id_currency));
if (!$this->isTemplateCached($cache_id)) {
$currency = new Currency((int)($params['cookie']->id_currency));
// get all best sales of the product family
$other_products = array();
// get all best sales of the product family
$other_products = array();
$family = ProductSale::getSaleFamily(
$params['sale']->id,
$params['cookie']->id_lang
);
$bestsellers = ProductSale::getFamilyBestSales(
$family['id_category_family'],
$params['cookie']->id_lang,
0, 3, NULL, NULL, 10
);
$family_name = '';
if(is_array($bestsellers)) {
foreach ($bestsellers AS $bestseller) {
$bestseller['price'] = Tools::displayPrice(Product::getPriceStatic((int)($bestseller['id_product'])), $currency);
$other_products[] = $bestseller;
$family_name = $bestseller['category'];
}
}
if (empty($other_products)) {
return '';
}
$smarty->assign(
array(
'is_accessories' => FALSE,
'ps_other_products' => $other_products,
'family_name' => $family['name']
)
$bestsellers = ProductSale::getFamilyBestSales(
$this->family['id_category_family'],
$params['cookie']->id_lang,
3, 10,
$params['product']->id
);
if(is_array($bestsellers)) {
foreach ($bestsellers AS $bestseller) {
$bestseller['price'] = Tools::displayPrice(Product::getPriceStatic((int)($bestseller['id_product'])), $currency);
$other_products[] = $bestseller;
}
}
if (empty($other_products)) {
return '';
}
$smarty->assign(
array(
'label' => $this->label,
'ps_other_products' => $other_products,
)
);
}
return $this->flushTemplateCache($cache_params);
return $this->flushTemplateCache($cache_id);
}
private function isTemplateCached(&$cache_params)
private function isTemplateCached(&$cache_id)
{
global $smarty;
global $cookie;
Tools::enableCache();
$smarty->cache_lifetime = 3600;
$cache_params['compile_id'] = $cache_params['prefix_cache'].'_'.(int)$cookie->id_lang;
$cache_params['cache_id'] = $cache_params['compile_id'].'_front_similarproducts';
return $this->isCached(
'front_similarproducts.tpl',
$cache_params['cache_id'],
$cache_params['compile_id']
);
return $this->isCached('front_similarproducts.tpl', $cache_id);
}
private function flushTemplateCache($cache_params)
private function flushTemplateCache($cache_id)
{
global $smarty;
$display = $this->display(
__FILE__,
'front_similarproducts.tpl' ,
$cache_params['cache_id'],
$cache_params['compile_id']
);
$display = $this->display(__FILE__, 'front_similarproducts.tpl', $cache_id);
Tools::restoreCacheSettings();
$smarty->cache_lifetime = -1;
return $display;

View File

@ -1,59 +1,9 @@
<?php
class ProductSale extends ProductSaleCore
{
public static function getSaleFamily($id_sale, $id_lang)
{
$query = '
SELECT DISTINCT a.`id_category_family`, c.`name`
FROM `'._DB_PREFIX_.'category_family_association` a
JOIN `'._DB_PREFIX_.'category_family_lang` c ON c.`id_category_family` = a.`id_category_family`
JOIN `'._DB_PREFIX_.'privatesale_category` b ON b.`id_category` = a.`id_category`
WHERE b.`id_sale` = '.pSql($id_sale).'
AND c.`id_lang` = '.pSql($id_lang);
return Db::getInstance()->getRow($query);
}
public static function getFamilyBestSales($id_category_family, $id_lang, $pageNumber = 0, $nbProducts = 3, $orderBy=NULL, $orderWay=NULL, $filterPrice=NULL)
{
return self::getBestSalesForCategories(
array(
'categories' => self::_getPrivateSalesCategoriesOfFamily($id_category_family),
'id_lang' => (int)$id_lang,
'page_number' => $pageNumber,
'nb_products' => $nbProducts,
'order_by' => $orderBy,
'order_way' => $orderWay,
'filter_price' => $filterPrice,
'include_category_children' => FALSE
)
);
}
public static function getBestSalesVp($id_lang, $pageNumber = 0, $nbProducts = 10, $orderBy=NULL, $orderWay=NULL, $filterPrice=NULL)
{
return self::getBestSalesForCategories(
array(
'categories' => self::_getIdCategoryPrivateSales(),
'id_lang' => $id_lang,
'page_number' => $pageNumber,
'nb_products' => $nbProducts,
'order_by' => $orderBy,
'order_way' => $orderWay,
'filter_price' => $filterPrice,
'include_category_children' => TRUE
)
);
}
private static function getBestSalesForCategories($params)
{
$id_category_vp = $params['categories'];
$id_lang = $params['id_lang'];
$pageNumber = $params['page_number'];
$nbProducts = $params['nb_products'];
$orderBy = $params['order_by'];
$orderWay = $params['order_way'];
$filterPrice = $params['filter_price'];
$id_category_vp = self::_getIdCategoryPrivateSales();
// $id_category_vp = array_unique($id_category_vp);
if ($pageNumber < 0) $pageNumber = 0;
@ -61,10 +11,6 @@ class ProductSale extends ProductSaleCore
if (empty($orderBy) || $orderBy == 'position') $orderBy = 'sales';
if (empty($orderWay)) $orderWay = 'DESC';
if (empty($id_category_vp)) {
return [];
}
$groups = FrontController::getCurrentCustomerGroups();
$sqlGroups = (count($groups) ? 'IN ('.implode(',', $groups).')' : '= 1');
@ -72,18 +18,16 @@ class ProductSale extends ProductSaleCore
foreach ($id_category_vp as $key => $sale) {
$categories = array($sale['id_category']);
if ($params['include_category_children']) {
$children = Category::getChildren($sale['id_category'] , 1);
if(count($children) > 0){
foreach ($children as $key => $child) {
$categories[] = $child['id_category'];
}
$children = Category::getChildren($sale['id_category'] , 1);
if(count($children) > 0){
foreach ($children as $key => $child) {
$categories[] = $child['id_category'];
}
}
$sale_product = Db::getInstance(_PS_USE_SQL_SLAVE_)->ExecuteS('
SELECT
DISTINCT(p.id_product), p.out_of_stock, p.id_category_default, p.ean13,
DISTINCT(p.id_product),
pl.`description`, pl.`description_short`, pl.`link_rewrite`, pl.`meta_description`, pl.`meta_keywords`, pl.`meta_title`, pl.`name`, m.`name` AS manufacturer_name, p.`id_manufacturer` as id_manufacturer,
i.`id_image`, il.`legend`,
ps.`quantity` AS sales, t.`rate`, pl.`meta_keywords`, pl.`meta_title`, pl.`meta_description`,
@ -110,13 +54,11 @@ class ProductSale extends ProductSaleCore
LIMIT 1
)
');
if (!$sale_product)
continue;
if (self::alreadyInArray($sale_product[0]['id_product'], $result)) {
continue;
}
$data = Product::getProductsProperties($id_lang, $sale_product, false);
if (isset($filterPrice) && is_int($filterPrice) && $data[0]['price']<$filterPrice) {
continue;
@ -130,16 +72,6 @@ class ProductSale extends ProductSaleCore
return array_slice($final_array,0,$nbProducts);
}
public static function alreadyInArray($id_product, $result)
{
foreach ($result as $row) {
if ($row['id_product'] == $id_product) {
return TRUE;
}
}
return FALSE;
}
public static function bubble_sort($array)
{
$i = count($array);
@ -177,21 +109,114 @@ class ProductSale extends ProductSaleCore
return $sales = Db::getInstance()->ExecuteS($query);
}
public static function getSaleFamily(
$id_sale,
$id_lang
)
{
$query = '
SELECT DISTINCT a.`id_category_family`, c.`name`
FROM `'._DB_PREFIX_.'category_family_association` a
JOIN `'._DB_PREFIX_.'category_family_lang` c ON c.`id_category_family` = a.`id_category_family`
JOIN `'._DB_PREFIX_.'privatesale_category` b ON b.`id_category` = a.`id_category`
WHERE b.`id_sale` = '.pSql($id_sale).'
AND c.`id_lang` = '.pSql($id_lang);
return Db::getInstance()->getRow($query);
}
public static function getFamilyBestSales(
$id_category_family,
$id_lang,
$nb_products_max = 3,
$filter_price=0,
$exclude_id_product
)
{
if ($nb_products_max < 1) {
$nb_products_max = 3;
}
$categories = self::_getPrivateSalesCategoriesOfFamily($id_category_family);
if (empty($categories)) {
return [];
}
$query = '
SELECT
DISTINCT(p.`id_product`), p.`out_of_stock`, p.`id_category_default`, p.`ean13`,
pl.`description`, pl.`description_short`, pl.`link_rewrite`, pl.`meta_description`, pl.`meta_keywords`, pl.`meta_title`, pl.`name`,
m.`name` AS manufacturer_name,
p.`id_manufacturer` AS id_manufacturer,
i.`id_image`, il.`legend`,
ps.`quantity` AS sales,
t.`rate`,
pl.`meta_keywords`, pl.`meta_title`, pl.`meta_description`,
DATEDIFF(p.`date_add`, DATE_SUB(NOW(), INTERVAL '.(Validate::isUnsignedInt(Configuration::get('PS_NB_DAYS_NEW_PRODUCT')) ? Configuration::get('PS_NB_DAYS_NEW_PRODUCT') : 20).' DAY)) > 0 AS new
FROM `'._DB_PREFIX_.'product` p
JOIN `'._DB_PREFIX_.'product_sale` ps ON (ps.`id_product` = p.`id_product`)
JOIN `'._DB_PREFIX_.'category_product` cp ON (cp.`id_product` = p.`id_product`)
JOIN `'._DB_PREFIX_.'category_family_association` cfa ON cfa.id_category = cp.id_category
JOIN `'._DB_PREFIX_.'product_lang` pl ON (p.`id_product` = pl.`id_product` AND pl.`id_lang` = '.(int)($id_lang).')
LEFT JOIN `'._DB_PREFIX_.'image` i ON (i.`id_product` = p.`id_product` AND i.`cover` = 1)
LEFT JOIN `'._DB_PREFIX_.'image_lang` il ON (i.`id_image` = il.`id_image` AND il.`id_lang` = '.(int)($id_lang).')
LEFT JOIN `'._DB_PREFIX_.'manufacturer` m ON (m.`id_manufacturer` = p.`id_manufacturer`)
LEFT JOIN `'._DB_PREFIX_.'tax_rule` tr ON (p.`id_tax_rules_group` = tr.`id_tax_rules_group`
AND tr.`id_country` = '.(int)Country::getDefaultCountryId().'
AND tr.`id_state` = 0)
LEFT JOIN `'._DB_PREFIX_.'tax` t ON (t.`id_tax` = tr.`id_tax`)
WHERE p.`active` = 1
AND p.`quantity` > 0
AND cp.`id_category` IN ('. pSql(implode(',', array_column($categories, 'id_category'))).')
AND cfa.`id_category_family` = '.(int)$id_category_family.'
AND p.`id_product` <> '.(int)$exclude_id_product.'
ORDER BY sales DESC
';
$best_sale_products = Db::getInstance(_PS_USE_SQL_SLAVE_)->ExecuteS($query);
if (empty($best_sale_products)) {
return [];
}
$filtered_best_sale_products = array();
foreach ($best_sale_products as $product) {
$properties = Product::getProductProperties($id_lang, $product, false);
if (isset($filter_price) && is_int($filter_price) && $properties['price']<$filter_price) {
continue;
}
if ($nb_products_max==0) {
break;
}
$filtered_best_sale_products[] = $properties;
$nb_products_max--;
}
return $filtered_best_sale_products;
}
/**
* display all categories (parents/children) having opened private sales the current version (lang)
* and for the given family
*/
private static function _getPrivateSalesCategoriesOfFamily($id_category_family)
{
global $site_version_front;
$query = '
SELECT pc.`id_category`
FROM `'._DB_PREFIX_.'privatesale_category` pc
JOIN `'._DB_PREFIX_.'category_family_association` cfa ON cfa.id_category = pc.id_category
JOIN `'._DB_PREFIX_.'privatesale` p ON p.`id_sale` = pc.`id_sale`
LEFT JOIN `'._DB_PREFIX_.'privatesale_site_version` v ON v.`id_sale` = p.`id_sale`
WHERE cfa.`id_category_family` = '.$id_category_family.'
AND p.`date_start` <= NOW()
AND p.`date_end` >= NOW()
AND v.`version` = "'.pSql($site_version_front).'"
';
SELECT `sale_cat`.`id_category`
FROM `'._DB_PREFIX_.'privatesale_category` `sale_cat`
JOIN `'._DB_PREFIX_.'privatesale` `sale` ON `sale`.`id_sale` = `sale_cat`.`id_sale`
JOIN `'._DB_PREFIX_.'privatesale_site_version` `vers` ON `vers`.`id_sale` = `sale`.`id_sale`
JOIN `'._DB_PREFIX_.'category_family_association` `cat_fam` ON `cat_fam`.`id_category` = `sale_cat`.`id_category`
WHERE `cat_fam`.`id_category_family` = '.$id_category_family.'
AND `sale`.`date_start` <= NOW()
AND `sale`.`date_end` >= NOW()
AND `vers`.`version` = "'.pSql($site_version_front).'"
';
return Db::getInstance()->ExecuteS($query);
}
}

View File

@ -45,6 +45,7 @@ class ProductController extends ProductControllerCore {
self::$smarty->assign(array(
'sale' => $sale,
'HOOK_SIMILAR_PRODUCT' => Module::hookExec('similarProduct', array('product' => $this->product, 'sale' => $sale)),
'HOOK_SIMILAR_PRODUCT_TABLABEL' => Module::hookExec('simlarProductTabLabel', array('product' => $this->product, 'sale' => $sale)),
'HOOK_PRIVATESALES_PRODUCT' => Module::hookExec('privatesales_product', array('sale' => $sale)),
'is_sale_home' => ($sale? $sale->id_category == $id_category: FALSE),
'is_thumb_vp' => (file_exists(_PS_ROOT_DIR_.'/img/c/'.$id_category_thumb.'_thumb_vp.jpg')),

View File

@ -6760,66 +6760,26 @@ table#carrierTable tbody td {
/* ticket 8048 - Product details - Other similar products / best sales for product family */
/* Slider product footer (blockbestsellers)*/
#product #privatesales_similarproduct {
position: relative;
position: relative;
}
#product #privatesales_similarproduct h1 {
#product #privatesales_similarproduct strong {
background-size: 100% 100%;
color: #4F4E9A;
margin-right: 0px;
}
#product #privatesales_similarproduct .slider_sales_product_footer {
padding: 20px 60px;
#product #privatesales_similarproduct ul {
list-style: none;
width:880px;
}
#product #privatesales_similarproduct ul li{
position: relative;
-moz-box-shadow: 0px 0px 5px 1px #cccccc;
-webkit-box-shadow: 0px 0px 5px 1px #CCC;
-ms-box-shadow:: 0px 0px 5px 1px #cccccc;
-o-box-shadow: 0px 0px 5px 1px #cccccc;
box-shadow: 0px 0px 5px 1px #CCC;
margin-bottom:20px;
}
#product #privatesales_similarproduct .flex-direction-nav {
position: inherit;
}
#product #privatesales_similarproduct .flex-control-nav{
display: none;
}
#product #privatesales_similarproduct .flex-direction-nav .flex-prev,
#product #privatesales_similarproduct .flex-direction-nav .flex-next {
top: -80px;
border-radius: 50%;
width: 50px;
height: 50px;
content: '';
background: #fff;
color: transparent;
opacity: 1;
-moz-box-shadow: 0px 0px 5px 1px #cccccc;
-webkit-box-shadow: 0px 0px 5px 1px #CCC;
-ms-box-shadow:: 0px 0px 5px 1px #cccccc;
-o-box-shadow: 0px 0px 5px 1px #cccccc;
box-shadow: 0px 0px 5px 1px #CCC;
}
#product #privatesales_similarproduct .flex-direction-nav .flex-prev{
background: url("../img/arrow_blue_left.png") no-repeat center center #fff;
}
#product #privatesales_similarproduct .flex-direction-nav .flex-next{
background: url("../img/arrow_blue_right.png") no-repeat center center #fff;
}
#product #privatesales_similarproduct .flex-direction-nav .flex-prev:before,
#product #privatesales_similarproduct .flex-direction-nav .flex-next:before{
display: none;
}
#product #privatesales_similarproduct .slider_sales_product_footer .slides {
padding: 10px 0;
}
#product #privatesales_similarproduct .slider_sales_product_footer .slides li{
margin-right: 10px;
margin-left: 10px;
background: #fff;
padding: 10px;
box-sizing: border-box;
position: relative;
max-height: 134px;
max-height: 300px;
background: #fff;
margin: 0 10px 0 10px;
display:inline-block;
-moz-box-shadow: 0px 0px 5px 1px #cccccc;
-webkit-box-shadow: 0px 0px 5px 1px #CCC;
@ -6827,8 +6787,8 @@ table#carrierTable tbody td {
-o-box-shadow: 0px 0px 5px 1px #cccccc;
box-shadow: 0px 0px 5px 1px #CCC;
}
#product #privatesales_similarproduct .slider_sales_product_footer .slides li .hover-slide,
#product #privatesales_similarproduct .slider_sales_product_footer .slides li .product_added{
#product #privatesales_similarproduct ul li .hover-slide,
#product #privatesales_similarproduct ul li .product_added{
display: none;
top: 0;
left: 0;
@ -6839,63 +6799,67 @@ table#carrierTable tbody td {
text-align: center;
position: absolute;
}
#product #privatesales_similarproduct .slider_sales_product_footer .slides li .product_added{
#product #privatesales_similarproduct ul li .product_added{
z-index: 999;
padding: 45px 20px;
}
#product #privatesales_similarproduct .slider_sales_product_footer .slides li .product_added p{
color: #fff;
#product #privatesales_similarproduct ul li .product_added p{
font-family: "georgia";
font-style: italic;
font-weight: bold;
}
#product #privatesales_similarproduct .slider_sales_product_footer .slides li:hover div.hover-slide{
#product #privatesales_similarproduct ul li:hover div.hover-slide{
display: block;
}
#product #privatesales_similarproduct .slider_sales_product_footer .slides li .hover-slide a{
#product #privatesales_similarproduct ul li .hover-slide a{
display: inline-block;
width: 40%;
height: 100%;
}
#product #privatesales_similarproduct .slider_sales_product_footer .slides li .hover-slide a.add-to-cart {
#product #privatesales_similarproduct ul li .hover-slide a.add-to-cart {
background: url("../img/panier2.png") no-repeat center center;
}
#product #privatesales_similarproduct .slider_sales_product_footer .slides li .hover-slide a.see-product {
#product #privatesales_similarproduct ul li .hover-slide a.see-product {
background: url("../img/loupe.png") no-repeat center center;
}
#product #privatesales_similarproduct .slider_sales_product_footer .slides li:after{
#product #privatesales_similarproduct .slider_sales_product_footer {
border: 1px solid transparent; /* trick to get the correct height in javascript */
}
#product #privatesales_similarproduct ul li:after{
content: '';
display: block;
clear: both;
}
#product #privatesales_similarproduct .slider_sales_product_footer .slides li .img_sellers,
#product #privatesales_similarproduct .slider_sales_product_footer .slides li .info_sellers {
#product #privatesales_similarproduct ul li .similar_product {
background-color: #fff;
width:246.667px;
padding:10px;
}
#product #privatesales_similarproduct ul li .img_sellers,
#product #privatesales_similarproduct ul li .info_sellers {
text-align: center;
}
#product #privatesales_similarproduct .slider_sales_product_footer .slides li .img_sellers{
width: 40%;
float: left;
}
#product #privatesales_similarproduct .slider_sales_product_footer .slides li .info_sellers {
width: 60%;
padding: 5px 10px;
#product #privatesales_similarproduct ul li .info_sellers {
background: #fff;
padding: 10px 10px;
box-sizing: border-box;
background-color: #E4E4EE;
float: right;
}
#product #privatesales_similarproduct .slider_sales_product_footer .slides li .info_sellers .product_link_sellers,
#product #privatesales_similarproduct .slider_sales_product_footer .slides li .info_sellers p.price,
#product #privatesales_similarproduct .slider_sales_product_footer .slides li .info_sellers .old_price,
#product #privatesales_similarproduct .slider_sales_product_footer .slides li .info_sellers .percentage,
#product #privatesales_similarproduct .slider_sales_product_footer .slides li .info_sellers .amount{
#product #privatesales_similarproduct ul li .product_link_sellers,
#product #privatesales_similarproduct ul li .info_sellers p.price,
#product #privatesales_similarproduct ul li .info_sellers .old_price,
#product #privatesales_similarproduct ul li .info_sellers .percentage,
#product #privatesales_similarproduct ul li .info_sellers .amount{
font-family: "georgia";
font-style: italic;
font-weight: bold;
}
#product #privatesales_similarproduct .slider_sales_product_footer .slides li .info_sellers .product_link_sellers{
#product #privatesales_similarproduct ul li .product_link_sellers{
display: block;
display: -webkit-box;
max-width: 110px;
max-width: 70%;
height: 44px;
margin: 0 auto;
font-size: 12px;
@ -6904,23 +6868,43 @@ table#carrierTable tbody td {
-webkit-box-orient: vertical;
overflow: hidden;
text-overflow: ellipsis;
color: #4F4E9A;
font-weight: 500;
color: #1e1633;
}
#product #privatesales_similarproduct .slider_sales_product_footer .slides li .info_sellers .product_link_sellers:hover{
#product #privatesales_similarproduct ul li .product_sellers_label {
border-bottom: 1px solid #e9e5e6;
width:100%;
margin-bottom:2px;
text-align: center;
background: #fff;
padding-top:10px;
}
#product #privatesales_similarproduct ul li .img_sellers {
border-top: 1px solid #e9e5e6;
width:100%;
}
#product #privatesales_similarproduct ul li .product_link_sellers:hover{
text-decoration: none;
}
#product #privatesales_similarproduct .slider_sales_product_footer .slides li .info_sellers p.price{
border:0;
color: #FB69AA;
font-size: 16px;
padding-top: 18px;
#product #privatesales_similarproduct ul li .info_sellers p {
margin:0px;
}
#product #privatesales_similarproduct .slider_sales_product_footer .slides li .info_sellers .old_price {
#product #privatesales_similarproduct ul li .info_sellers p.price {
border:0;
color: #70bc2c;
font-size: 16px;
}
#product #privatesales_similarproduct ul li .info_sellers .price_info {
padding:0px;
margin:0px;
}
#product #privatesales_similarproduct ul li .info_sellers .price_info .old_price {
color: #B0B0B0;
text-decoration: line-through;
padding:0px;
margin:0px;
}
#product #privatesales_similarproduct .slider_sales_product_footer .slides li .info_sellers .percentage,
#product #privatesales_similarproduct .slider_sales_product_footer .slides li .info_sellers .amount{
#product #privatesales_similarproduct ul li .info_sellers .price_info .percentage,
#product #privatesales_similarproduct ul li .info_sellers .price_info .amount{
color: #4F4E9A;
}

View File

@ -459,6 +459,9 @@ var oneQuantityAvailableSentence = '{l s='Warning: 1 item in stock!' js=1}';
{if !empty($product->description)}<li><a id="more_info_tab_data_sheet" href="#idTab2">{l s='Good to know'}</a></li>{/if}
{*if $product->buy_guide}<li><a id="more_info_tab_data_sheet" href="#idTab3">{l s='Buy guide'}</a></li>{/if*}
{if !empty($product->videos)}<li><a id="more_info_tab_data_sheet" href="#idTab4">{l s='Videos'}</a></li>{/if}
{if isset($HOOK_SIMILAR_PRODUCT)}
<li><a id="more_info_tab_data_sheet" href="#idTab6">{$HOOK_SIMILAR_PRODUCT_TABLABEL}</a></li>
{/if}
<li><a id="more_info_tab_data_sheet" href="#idTab5">{l s='Shipping'}</a></li>
{*{if $features}<li><a id="more_info_tab_data_sheet" href="#idTab2">{l s='Data sheet'}</a></li>{/if}
{if $attachments}<li><a id="more_info_tab_attachments" href="#idTab9">{l s='Download'}</a></li>{/if}
@ -531,6 +534,13 @@ var oneQuantityAvailableSentence = '{l s='Warning: 1 item in stock!' js=1}';
{$product->description_delivery}
{/if}
</div>
{if isset($HOOK_SIMILAR_PRODUCT)}
<div id="idTab6" class="rte">
{$HOOK_SIMILAR_PRODUCT}
</div>
{/if}
{if $features}
<!-- product's features -->
<ul id="idTab8" class="bullet">
@ -624,10 +634,6 @@ var oneQuantityAvailableSentence = '{l s='Warning: 1 item in stock!' js=1}';
</div>
{/if}
{if isset($HOOK_SIMILAR_PRODUCT)}
{$HOOK_SIMILAR_PRODUCT}
{/if}
{$HOOK_PRODUCT_FOOTER}
<!-- Customizable products -->