Need assistance with Prestashop label code - prestashop

I want to add a Best Seller label to products in Prestashop 1.6, on both product list and product pages, the same as the labels for New Products. Can anybody point me in the right direction to accomplishing this, if it is possible, i.e will it require a controller etc.
At this stage all I have tried is simply,
{if isset($best_sellers) && $best_sellers|#count > 0}
<span class="bestseller">
<label>{l s='best seller' mod='modulename'}</label>
</span>
{/if}
This did not work. I don't need any help with the css, I can handle that. Thanks
Hello again,
Using some of the information you gave me below I have come up with the following to add a best seller label to my custom module without using an override.
Following is the function:
public function getcustomBestSellersLabel($params)
{
$id_product = (int)$params['product']['id_product'];
return Db::getInstance()->executeS('SELECT `id_product`, `sale_nbr` AS customnbr FROM '._DB_PREFIX_.'product_sale ps WHERE `id_product` = '.(int)$id_product.' ORDER BY `customnbr` DESC LIMIT 5');
}
and in my hook
public function hookdisplayCustomBestSellersTag($params)
{
$id_product = (int)Tools::getValue('id_product');
if (!$this->isCached('custombestsellertag.tpl', $this->getCacheId($id_product))) {
$custombestsellers = CustomModule::getCustomBestSellersLabel($params);
$this->context->smarty->assign(array(
'custombestsellers' => Module::getInstanceByName('CustomModule')->getCustomBestSellersLabel($params),
'id_product' => $id_product,
));
}
return $this->display(__FILE__, 'views/templates/front/custombestsellerlabel.tpl', $this->getCacheId());
}
and in my tpl
{if (isset($custombestsellers) && $custombestsellers)}
<span class="custom-best-seller-label">
<label>{l s='best seller' mod='CustomModule'}</label>
</span>
{/if}
This actually works except for one problem, the LIMIT doesn't work. I have tested it in phpMyAdmin where it does work. I have tested it with LIMIT 5 and there is no change, instead all products in the product_sale database table are showing a label.
Can you give me some advice on how to fix this problem.
Thank you.

It's much more complicated than that, to achieve that you need:
create override of Product.php method called getProductProperties
check if product is in TOP X in terms of sales in PREFIX_product_sale table
if so, mark it as a bestseller by adding a new variable (alternative is to do this directly in SQL statement)
if you'll do everything properly and new variable will be available you would have a chance to use it like this:
{if $product.is_bestseller}bestseller label{/if}

Related

$Images not loaded properly on one product

I have an old version of prestashop (1.4) and facing issue with images for a new product. In my product.tpl, the $images Array is empty (count($images) = 0), so my thumbnails does not display.
// Images
var img_prod_dir = '{$img_prod_dir}';
var combinationImages = new Array();
{if isset($combinationImages)}
{foreach from=$combinationImages item='combination' key='combinationId' name='f_combinationImages'}
combinationImages[{$combinationId}] = new Array();
{foreach from=$combination item='image' name='f_combinationImage'}
combinationImages[{$combinationId}][{$smarty.foreach.f_combinationImage.index}] = {$image.id_image|intval};
{/foreach}
{/foreach}
{/if}
combinationImages[0] = new Array();
{if isset($images)}
//images : {$images} {count($images)}
{foreach from=$images item='image' name='f_defaultImages'}
//img - {$image.id_image}
combinationImages[0][{$smarty.foreach.f_defaultImages.index}] = {$image.id_image};
{/foreach}
{/if}
I checked in prestashop database and the structure seems OK, when I play SQL request found in product.php
return Db::getInstance()->ExecuteS('
SELECT i.`cover`, i.`id_image`, il.`legend`, i.`position`
FROM `'._DB_PREFIX_.'image` i
LEFT JOIN `'._DB_PREFIX_.'image_lang` il ON (i.`id_image` = il.`id_image` AND il.`id_lang` = '.(int)($id_lang).')
WHERE i.`id_product` = '.(int)($this->id).'
ORDER BY `position`');
the images are loaded properly for my product.
It seems that the link between the PHP and the TPL fails somewhere, but I don't know how to look deeper between those two elements. It only occurs on one product.
What I tested :
upload the image to another product --> it works
upload a working image on my failed product --> same behaviour
compare database content on ps_image, ps_product, and data seems to be the same
checked on the FTP if images are well generated --> OK
Do you have any idea where I can look further to check what fails with my product ?
Best regards
I finally found the issue, a previous developper made an override in the folder override/controllers/ProductController.php that causes, on some really specific case, a change of product ID and the images were not loaded.
git blame is fantastic :)

ASP .NET Core: View grid of multiple items to be edited

I'm trying to construct a view where the records from a table (machines) get uploaded and that each one can be edited in a view.
This is my Get method:
public async Task<IActionResult> Test()
{
return View(await _context.Machines.Include(t => t.MachineTypes).Include(p => p.Suppliers).AsNoTracking().ToListAsync());
}
Pretty simple for what I need. I'm getting a list of the table Machines and including the related tables: MachineTypes and Suppliers because this information should be able to be edited in the view.
Now, with the view I'm quite lost, sorry for this.
I started defining the model to be used:
#model IEnumerable<Application.Models.Machine>
Question[Answered] Is correct to use an IEnumerable?
Answer by uni: Yes
Then, as seen in some examples, I use a Html.BeginForm to indicate the controller and action:
#using (Html.BeginForm("Machines","TestPost"))
Sidenote: The Post method is not defined yet so I'm using any name for now
Then comes the iteration to read every element of the list:
#Html.ValidationSummary(true)
#for(int i = 0; i < Model.Count() ; i++)
Question[Answered]: In this part I'm not sure if 'Model.Machine.Count()' is correct.
Answer by uni: It's correct. No need to specify 'Machine'
Inside the iteration I don't know how to code to show the MachineName (MchName) to begin with. I'm doing this:
#for(int i = 0; i < Model.Count() ; i++)
{
<tr>
<td>
#Html.HiddenFor(modelItem => modelItem.Machine[i].Id)
#Html.DisplayNameFor(model => model.MchName)
</td>
</tr>
}
Question[Answered]: In this part:
#Html.HiddenFor(modelItem => modelItem.Machine[i].Id)
I don't know why I need to use the HiddenFor, I saw it on another example in the forum.
Answer by uni: This is one of many ways to send the values to the controller.
Question[Answered]: Also, this part
#Html.HiddenFor(modelItem => modelItem.Machine[i].Id)
Answer by uni: 'Machine' don't need to be included, same as before
Continuation of the View
For the iteration part, since 'Machine' don't need to be included...
[NEW]Question: How should I call the item wanted from the Machine table?
I've got this:
#for(int i = 0; i < Model.Count() ; i++)
{
<tr>
<td>
#Html.HiddenFor(modelItem => i.Id)
#Html.DisplayNameFor(modelItem => model.MchName)
</td>
</tr>
}
Both of this are wrong, but I was trying to figure out how to show the item inside the iteration. I wonder if, since I'm using an IEnumerable, it would be best if I use a #foreach instead of a for();
Thanks
Question: Is correct to use an IEnumerable?
Answer: Yes
Question: In this part I'm not sure if 'Model.Machine.Count()' is correct, and well, I'm getting the error: IEnumerable does not contain a definition for Machine. Is it enough if I put it this way?: #for(int i = 0; i < Model.Count() ; i++)
Answer: You already have a collection of Machines (IEnumerable<Application.Models.Machine>) which is stored in your model so Model.Count() would be the correct way to go as each Machine model doesn't have a child Machine object
Question: #Html.HiddenFor(modelItem => modelItem.Machine[i].Id) I don't know why I need to use the HiddenFor, I saw it on another example in the forum.
Answer: When you do an HTTP post you have many ways to send values to the controller action. Hidden fields are a way to do that. In this case you are sending the id of the machine as part of your form submission.
Question: #Html.HiddenFor(modelItem => modelItem.Machine[i].Id) is showing the same error: IEnumerable does not contain a definition for Machine. I tough I should mention the table.
Answer: The answer to this one is the same as the answer that I mentioned above regarding the Count() invocation in the for loop.
Hope this helps!

How to check if there is a strikethrough on some text in selenium webdriver?

There are many planbox, which are having same class and ids, inside them there are a number of <p> tags and decorated text.
<div class="planbox">
<p class="baseprice">
<span>
<strike> $70 </strike>
</span>
</p>
<p> New discount price is etc. </p>
</div>
<div class="planbox">
<p class="baseprice">
<span> $70 </span>
</p>
</div>
Now, My test case is - if the base price is strikethrough, only then <p> 'New discount price .. </p> will show, otherwise not.
How to check whether a text is strikethrough or not? And even if we get this how will I check that <p> New discount.. </p> should not show if the text is not striked.
As there is no class in <p> tag on which I can check whether it displayed or not.
One solution in my mind was - add one dummy class in <span> tag and using findChildren('span.dummyCLass') it will result all the webelements having dummyClass.
Now I will check whether web-elements have strike tag or not, and this is the place where I got stuck.
Initially, i was thinking of a Jquery solution, but is it possible to do without adding new class and jquery?
You don't need to add a class to any element to accomplish this task. In general, you don't want to edit the HTML. Another issue is... if you can find the element to add a class, then you don't need to add the class to find the element. :)
The way I approach tasks like these is to find the outermost element that contains all the elements that you are interested in. I refer to this as a "container". What you want to do in this case is to find the containers and loop through them looking for the strikethrough price and for the "New discount price..." text.
The containers are DIVs with the planbox class. The strikethrough price is indicated by the STRIKE tag. The "New discount price..." text is in a P tag. With this info we can write some code. This is Java because I don't know what language you want and I'm not familiar with the Galen framework.
// gets the collection of containers using a CSS selector
List<WebElement> containers = driver.findElements(By.cssSelector("div.planbox"));
// loops through the containers
for (WebElement container : containers)
{
// determine if the STRIKE tag exists
boolean strikeExists = !container.findElements(By.tagName("strike")).isEmpty();
// determine if the "New discount price is..." text exists in the 2nd P tag
boolean discountPriceExists = container.findElements(By.tagName("p")).get(1).getText().trim().contains("New discount price is");
// if both are true log a pass, otherwise log a fail
if (strikeExists && discountPriceExists)
{
// log a pass
}
else
{
// log a fail
}
}
I haven't used much of selenium. But you can port this jquery code to selenium,
//if there is a strike element
if($(".baseprice span strike").length > 0){
//next() will select the sibling of the p tag with baseprice class
$("p.baseprice).next() != undefined){
return true
}else{
return false
}
}
you can use Galen for this. There you can verify certain CSS properties.

Prestashop subcategories menu inside a subcategory

I am trying to show the subcategories menu of prestashop categories inside all subcategories. By default you only can see the subcategories menu inside a category but you cant see the "brother" subcategories of a subcategory.
I think I only need to make this code to work inside a subcategory because this code works well inside a category:
{foreach from=$subcategories item=subcategory}
<li > <a href="{$link->getCategoryLink($subcategory.id_category, $subcategory.link_rewrite)|escape:'htmlall':'UTF-8'}"
class="cat_name">{$subcategory.name|escape:'htmlall':'UTF-8'}</a>
</li> {/foreach}
Any ideas?
Thanks so much
as always i don't give you a full code, but i tell you how to do it.
in smarty you need to create a function that takes as param number of parent category,
and in this function you need to use Category::getChildren( $id_category );then in smarty you need only take a loop through the smarty function.
regards
and sorry for my English.
For to start i would have created a override file in /override/controllers/, named CategoryController.php
And add this:
<?php
class CategoryController extends CategoryControllerCore
{
public function displayContent()
{
// Get the global smarty object.
global $smarty;
// Get current category's parent.
$parent_category = new Category($this->category->id_parent, self::$cookie->id_lang);
// Get parent category's subcategories (which is current category's siblings, including it self).
$category_siblings = $parent_category->getSubCategories((int)self::$cookie->id_lang)
/* Assign your siblings array to smarty. */
$smarty->assign(
array(
"category_siblings" => $category_siblings
)
);
/* This we run the normal displayContent, but pass the siblings array to
category.tpl */
parent::displayContent();
}
}
?>
And in product-list.tpl file:
<ul>
{foreach from=$category_siblings item=elemento}
<li {if $category->id == $elemento.id_category}class="active"{/if}> {$elemento.name} </li>
{/foreach}
</ul>
via Get sibling categories in category.tpl for the current category in prestashop

jQuery: Select elements with Incrementing ID names?

and thanks in advance for your help!
Here's my situation: I have a set of divs whose IDs have an incrementing number applied to the end of the name using PHP. Each of these divs are added dynamically with PHP (They are a series of FAQ questions with a hidden div container with the answers, that slide down when the question is clicked.) [Live Example][1]
There is no limit to the number of questions that appear on the page, because this is being used for a Wordpress theme and my client wants to add new questions as they go along.
Here's an example of the structure for each FAQ question using the PHP:
<?php var $faqnum = 0; $faqnum++; ?>
<div id="faqwrap<?php $faqnum; ?>">
<h4>What data is shared?</h4>
<div id="faqbox<?php $faqnum; ?>" class="slidebox">
<p>Data sharing is defined by the type of service:</p>
<ul class="list">
<li>Third-party access to data (Enhanced Services only is strictly controlled and determined by the SDA)</li>
<li>All members must participate in points of contact and conjunction assessment but can choose whether to participate in other services</li>
<li>Participation in a service requires the member to provide associated data<br />
</ul>
</div>
</div>
Now this is what I have currently in jQuery, and it works, but only if I add a new one every time my client wants to add a new question.
$(document).ready(function() {
$('.slidebox*').hide();
// toggles the slidebox on clicking the noted link
$("#faqwrap1 a:not(div.slidebox a)").click(function() {
$("#faqbox1.slidebox").slideToggle('normal');
$('div.slidebox:not(#faqbox1)').slideUp('normal');
return false;
});
});
I thought of maybe doing something with a declared variable, like this:
for (var x = 0; x < 100; x++;) {
$('#[id^=faqwrap]'+ x 'a:not(div.slidebox a)')...
}
I hope this is clear enough for you! Again, I thank you in advance. :)
The best way to handle this is to not use the IDs, but use classes for the outer element. So your PHP would be altered like this:
<?php var $faqnum = 0; $faqnum++; ?>
<div id="faqwrap<?php $faqnum; ?>" class="question">
<h4>What data is shared?</h4>
<div id="faqbox<?php $faqnum; ?>" class="slidebox">
<p>Data sharing is defined by the type of service:</p>
<ul class="list">
<li>Third-party access to data (Enhanced Services only is strictly controlled and determined by the SDA)</li>
<li>All members must participate in points of contact and conjunction assessment but can choose whether to participate in other services</li>
<li>Participation in a service requires the member to provide associated data<br />
</ul>
</div>
</div>
Your JQuery would be rewritten with the selector for the class "question".
$(document).ready(function() {
$('.slidebox*').hide();
// toggles the slidebox on clicking the noted link
$(".question a:not(div.slidebox a)").click(function() {
/* close everything first */
$('div.slidebox').slideUp('normal');
/* selects and opens the the slidebox inside the div */
$(".slidebox", this).slideToggle('normal');
return false;
});
});
This will get you the effect you are looking for. The key differences in the JQuery is the way you get the slidebox inside the question that got clicked. I'm using the scoped selection $(".slidebox", this) to get just the slidebox inside the clicked ".question" element.
The subtle visual difference is that the slideUp() happens before the slideToggle(). This will essentially close any open queries before it opens the desired one. If you keep your animations fast, this will be more than fine. The advantage of this approach is that you don't have to worry about the count of questions on a page, and the selectors are most likely more optimized than the for loop.
Edit
I adjusted the PHP code to use a class for "slidetoggle" instead of an id. It's technically an HTML error to have multiple IDs that are the same. It can throw off some assistive technologies for people with dissabilities. I'm assuming that section of code was repeated several times on the page.
Without changing your current markup, this would work:
// toggles the slidebox on clicking the noted link
$("div[id=^faqwrap]").each(function () {
var $faqwrap= $(this);
$faqwrap.find("h4 > a").click(function () {
var $currentSlidebox = $faqwrap.children(".slidebox");
$currentSlidebox.slideToggle('normal');
$('div.slidebox').not($currentSlidebox).slideUp('normal');
return false;
});
});
Maybe you can find a few suggestions in the above code that help you.
Like #Berin, I'd also recommend giving a separate CSS class to the outer DIV and using that as a selector, instead of $("div[id=^faqwrap]").