$Images not loaded properly on one product - prestashop

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 :)

Related

Xpath following the for the next thumbnail - optimized solution? (for selenium automated solution)

At demo store we have a list of thumbnail as given below :
<ul class="product_list grid row">
<li class="ajax_block_product....">
<div class="product-container">
<!-- at this level ist the thumbnail number 1 -->
</div>
</li>
<li class="ajax_block_product....">
<div class="product-container">
<!-- at this level ist the thumbnail number 2 -->
</div>
</li>
<li class="ajax_block_product....">
<div class="product-container">
<!-- at this level ist the thumbnail number 3 -->
</div>
</li>
</ul>
I can navigate through thumbnails using the xpath mentioned below:
thumbnail number 1 = //div[#class="product-container"]
thumbnail number 2 = //div[#class="product-container"]/following::div[#class="product-container"][1]
thumbnail number 3 = //div[#class="product-container"]/following::div[#class="product-container"][1]/following::div[#class="product-container"][1]
Through, above xpath are working fine for me but not an optimized solution.
Update 1: The objective is leave the xpath in a closed "form" at web page object library, for using by automated tests.
Get all the thumbnails in a list and then navigate through. Use below CSS selector for the same. Not sure which language you are using. I write this using Java.
List<WebElement> products = driver.findElements(By.cssSelector("div.product-container"));
for(WebElement product : products){
String productName = product.findElement(By.cssSelector(".product-name")).getText();
String productPrice = product.findElement(By.cssSelector(".right-block .price")).getText();
...
}
OR using the xpath mentioned by you.
List<WebElement> allThumbnails = driver.findElements(By.xpath("//div[#class='product-container']"));
for(WebElement thumbnails:allThumbnails){
String productName = product.findElement(By.xpath(".//a[#class='product-name']")).getText();
String productPrice = product.findElement(By.xpath("//div[#class='right-block']//span[#class='price product-price']")).getText();
...
}
UPDATED
As per your comment if it is require to mentioned fix element in your xpath then using indexes would be the right approach.
there are total 7 product present on the page you shared the URL and xpath so you can write it like-
thumbnail number 1 = //ul[#class='product_list grid row']/li[1]
thumbnail number 2 = //ul[#class='product_list grid row']/li[2]
...
thumbnail number 7 = //ul[#class='product_list grid row']/li[7]
If all the products share the same class you can just have Selenium pick up all matching elements, as opposed to explicitly stating one after the other like you're currently trying.
You've not said what language you're using, but here's what it'd look like in C#:
// Setup your web driver...
var thumbnails = driver.FindElements(By.XPath("//div[#class=\"product-container\"]"));
foreach (var thumbnail in thumbnails)
{
// Do work
}
Or
for(int i = 0; i < thumbnails.Count; i++)
{
// Do work by index
}
Note the plural FindElements, it returns a collection of all matching elements. Selenium does a fairly good job of matching methods across languages so that should point you in the right direction at least.

How to integrate my code into a sitefinity project

We have a sitefinity Customer Portal. Now we need to add MVC pages to it. I understand how to add a page, and how to drag e.g. a list to the page's content. But I don't understand how I can create a controller and other c# code to populate the list and do other custom things. We cannot open the project in Visual Studio, and we have no access to the existing code.
First of all, you must sure your project run success on your local. You can check it by login to back end page.
Then you can create the MVC component like this: (you should create all of this in root/MVC folder)
Create controller first:
[ControllerToolboxItem(Name = "ImportCSV", Title = "ImportCSV", SectionName = "ImportCSV")]
public class ImportCSVController : Controller
{
// GET: ImportCSV
public ActionResult Index()
{
return View();
}
}
SectionName is title of content group for you custom
Title is the title of component
Name is used for code behind
Then you can create the views to show in page: (you have to create the views in MVC/Views/ImportCSV, sitefinity will recognize folder name to map in BE)
<h2>Upload File</h2>
<div class="form-group">
<input type="file" id="dataFile" name="upload" />
</div>
<div class="form-group">
<a onclick="upload()" class="button" id="btnupload">Upload</a>
</div>
You need to get access to the code then, controllers\models need to be compiled. You can get away with a lot directly in a cshtml file though which DOESN'T need to be compiled.
Could you download a new blank SF project that's on your version and start from scratch pointed at your DB? Copy over /App_Data and /ResourcePackages to the new project and just run it. Should work fine, but any page that has a custom widget on it that uses custom code would tank. Sorry I'm just not sure why you don't have the code. Could use JustDecompile to retrieve the actual code for custom widgets too I suppose.

Need assistance with Prestashop label code

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}

How does the data flow from webserver to webpage.

I am in process of creating a new website. I have done my basic HTML/CSS/JQuery code to generate the webpage. The website is going to display images. Now my questions are around where the images are supposed to be stored and how to retrieve them. I did research but I am all over the place with the architecture.
My understanding is that HTML page will make a query to a web server (like Apache) and get the data/images back and display it? The function of the web server is to provide the data based on the query, is that right? Where is the data like jpeg images, their metadata, link between gallery and images would be stored? Is there another layer of DB somewhere? Would the architecture be HTML<-->Apache<-->DB ?
Or do I just put my images in a database and host the data their. Basically taking out Apache from the architecture? The queries are going to depend only on the current stage in the navigation tree (nothing user specific).
There is no need for DB to use images. It works more this way:
HTML <-> Apache <-> Image
because apache has the ability to deliver files.
Now, there are several differents way of working.
For example, the image can be load dynamically in a php files with images header. In this case, the scheme will be :
HTML <-> Apache <-> PHP <-> Image
To do it, you simply put your images in a folder where apache's user can access.
For example you can have the following structure in /var/www/sitename:
index.html
img / my_image.jpg
And in index.html
<img src="img/my_image.jpg" ... />
Edit to answer you question :
create a php script that will generate the json array, for example :
page.php
<?php
switch($_GET['link']){
case 'link1':
images_links = array(
'path/to/img1',
'path/to/img2',
...
);
break;
case 'link2':
images_links = array(
'path/to/img3',
'path/to/img4',
...
);
break;
}
echo json_encode(images_links);
?>
Let's guess your html is
<a>link1</a>
<a>link2</a>
<img class="imgToChange" src="..."/>
<img class="imgToChange" src="..."/>
...
Then you will add this javascript function to your html
function updateImages(clicked_link){
// get the text of the link
link_text = clicked_link.innerHTML;
// send a request to page.php to get images's urls
$.get( "path/to/page.php?link="+link_text, function( data ) {
// data will be your json array
images_links = data;
// get a table of all images elements that can be changed
var images = document.getElementsByClassName("imgToChange");
// for each image in the json array
for(var k=0; k<images_links.length; k++){
images[k].src = images_links[k];
}
});
}
And you just have to call this function when a link is clicked
<a onclick="updateImages(this)">link1</a>
<a onclick="updateImages(this)">link2</a>
<img class="imgToChange" src="..."/>
<img class="imgToChange" src="..."/>
...

MVC4 C# - How to submit list of object that are being displayed to the user?

I'm working on an MVC4 C# project in VS2010.
I would like to allow the user to upload the contents of a .csv file to a database but there is a requirement to first echo the contents of the file to screen (as a final visual check) before submitting. What would be the best approach of submitting to the database as I am struggling to find a way of persisting the complex object in the view?
Here is the view where I am using a form to allow the user to upload the csv file:
#model IEnumerable<MyNamespace.Models.MyModel>
#{
ViewBag.Title = "Upload";
WebGrid grid = new WebGrid(Model, rowsPerPage: 5);
}
<h2>Upload</h2>
<form action="" method="post" enctype="multipart/form-data">
<label for="file">Filename:</label>
<input type="file" name="file" id="file" />
<input type="submit" />
</form>
<h2>Grid</h2>
#grid.GetHtml(
//Displaying Grid here)
<p>
#Html.ActionLink("Submit", "Insert")
</p>
Here is the action in the controller that processes the csv file:
[HttpPost]
public ActionResult Upload(HttpPostedFileBase file)
{
var fileName = Path.GetFileName(file.FileName);
var path = Path.Combine(Server.MapPath("~/App_Data"), fileName);
file.SaveAs(path);
//Stream reader will read test.csv file in current folder
StreamReader sr = new StreamReader(path);
//Csv reader reads the stream
CsvReader csvread = new CsvReader(sr);
List<MyModel> listMyModele = new List<MyModel>(); // creating list of model.
csvread.Configuration.RegisterClassMap<MyModelMap>(); // use mapping specified.
listMyModel = csvread.GetRecords<MyModel>().ToList();
sr.Close();
//return View();
return View(listMyModel);
}
Up until this point everything is simple, I can upload the csv to the controller, read using CsvHelper, produce a list of MyModel objects and display in the view within a grid. To reiterate my initial question, is it now possible to submit the complex object (the list of MyModel) from the view as I can't figure out a way of making it available to an action within the controller.
Thank you.
Yes it's possible, It's "easier" if you have a Model with the IEnumerable in it so you can use the naming convention like this:
Property[index].ItemProperty
for every Html input/select field.
If you want to keep the IEnumerable as Model I think the naming convention is something like this:
ItemProperty[index]
So translated in code:
#Html.TextBoxFor(t => t.Property, new { name = "Property[" + i + "]" })
where i comes from a for loop to render all items or something like that.
I have already done it but I can't find the code at the moment. KendoUI uses this scheme for its multirows edit in the grid component.
You can check their POST AJAX requests for the right naming convention.
EDIT 1:
Otherwise you can think about store the model somewhere temporarily and retrieve it every time and updating with user inputs. It's a little more expensive but probably easier to write. Something like an updated csv file or a temporary db table.