Prestashop and ImageKit

Published June 11, 2021, 8:21 p.m.

Recently we found a really useful service - imagekit.io.

Image CDN with automatic optimization, real-time transformation, and storage that you can integrate with existing setup in minutes.

What is the most amazing thing about this service, it has a free plan with very a generous set of features and allowances.

Let's use it for a Prestashop theme.

First of all, we need to create an account on imagekit.io. After verifying your email, you'll be able to login to your account. At the very first step you will be asked for IMAGEKIT ID. By default this is a random string value. It make sense to change this random string to something meaningful, like the main portion of your website domain name. After setting all up, you will be unable to change your IMAGEKIT ID, so please do it right now.

Next, the imagekit terminology becomes a somewhat confusing. We need to "Attach existing origin in ImageKit.io". Click on the Attach existing origin in ImageKit.io link and then click on ADD NEW ORIGIN button. Origin Name may be any string, for Origin Type select Web Folder - HTTP(S) server and Magento, Shopify, Wordpress, etc.. For Base URL use base URL, like https://www.example.com (without /images subfolder as it advise you). Leave the bottom checkboxes unchecked (unless you know what they are), and click Submit.

What has confused me a little - where can you find this newly created "origin" after it has been set up? Under "URL endpoins" menu at the left. You will find "Default URL endpoint" there. Click on it, here you can edit your "origins". You might need a new "origin", if you are using a multi-store feature of Prestashop. For example, we have 2 shops and 2 domains for these shops in our Prestashop - gellifique.co.uk for UK shop and gellifique.eu for Spanish shop. Both domains should be added to Imagekit Default URL endpoins as 2 separate "origins". One endpoint, two origins. This was somewhat confusing.

Basically, all done. Now we can use ImageKit URLs instead of original URLs for images in your theme. I wanted to pinpoint all templates in Classic theme which needs to be updated, but as it happened, the Classic theme has changed a lot since we used it to create a custom theme for our webshop. In our case, I had to modify these files (under /themes/mytheme/ folder):

templates/catalog/listing/category.tpl 
templates/catalog/_partials/product-cover-thumbnails.tpl 
templates/catalog/_partials/product-images-modal.tpl 
templates/catalog/_partials/miniatures/product.tpl 
modules/ps_imageslider/views/templates/hook/slider.tpl 

In the latest version of the Classic theme some of these files became split into two or more files and some references may have moved somewhere else... So I'll show an example of what I did to my, probably outdated version. Let's take this file:

templates/catalog/_partials/miniatures/product.tpl 

It looks like it didn't change much. Let's find a reference to a product image (lines 33-37):

<img
  src="{$product.cover.bySize.home_default.url}"
  alt="{if !empty($product.cover.legend)}{$product.cover.legend}{else}{$product.name|truncate:30:'...'}{/if}"
  data-full-size-image-url="{$product.cover.large.url}"
/>

We need to change src attribute - with what? PHP parse_url() function will help us:

src = "https://ik.imagekit.io/ourimagekitid{parse_url($product.cover.bySize.home_default.url, PHP_URL_PATH)}"

where ourimagekitid is the IMAGEKIT ID we have set up earlier. If we know the exact size of the image here, we may specify it for the Imagekit, for example:

src = "https://ik.imagekit.io/ourimagekitid/tr:w-250,h-250{parse_url($product.cover.bySize.home_default.url, PHP_URL_PATH)}"

Do similar exercises to remaining templates, and the task is completed! However...

We had to hardcode IMAGEKIT ID into several files. Not good. Next issue - sometimes you may want to switch off ImageKit completely, for example in development environment. And the last one (at least for me) - although ImageKit gives us an ability to Purge cache, we have to specify it file by file, and even so it doesn't occure immediately. Sometimes you have to wait a few minutes before it have an effect... Would be nice to implement some feature like file versioning, like attaching ? to the end of the URL.

So let's write another Prestashop module.

Like before, we will create a module skeleton, using (see my first article):

https://validator.prestashop.com/generator

We will register two hooks:

public function install()
{
    Configuration::updateValue('IMAGEKIT_LIVE_MODE', false);
    return parent::install() &&
            $this->registerHook('actionDispatcher') && 
            $this->registerHook('backOfficeHeader');
}

"backOfficeHeader" hook is needed only for attaching some js file to back office (not really important):

public function hookBackOfficeHeader()
{
    if (Tools::getValue('configure') == $this->name || Tools::getValue('module_name') == $this->name) {
        $this->context->controller->addJS($this->_path.'views/js/back.js');
    }
}

And the important one is hookActionDispatcher. It allows us to define a custom plugin for Smarty templates. This plugin can be used as a function in a Smarty template

public function hookActionDispatcher(){
    $this->context->smarty->registerPlugin('function', 'imagekit', array('Imagekit', 'imagekitUrl'));
}

And the function implementation construct the needed URL for ImageKit image:

public static function imagekitUrl($params,$smarty) {
    if ($params['url']) {
        if ( Configuration::get('IMAGEKIT_LIVE_MODE', false) && Configuration::get('IMAGEKIT_ENDPOINT', '')) {
            $tr = $params['tr'] ? 'tr:'.$params['tr'] : '';
            $v = $params['v'] ? '?'.Configuration::get('IMAGEKIT_TS', '') : '';
              return Configuration::get('IMAGEKIT_ENDPOINT', '') . $tr . parse_url($params['url'],PHP_URL_PATH) . $v;
        }

        return $params['url'];
    }
    return '';
}

It takes configuration parameters from the module configuration. IMAGEKIT_LIVE_MODE allows us to switch using ImageKit on/off with a single click. Parameters passed to the function are "url", "tr", and "v" (only "url" parameter is mandatory). "tr" set required transformation in ImageKit format, and v=1 (0 by default) append timestamp "?" querystring to the URL.

So now the src attribute in templates/catalog/_partials/miniatures/product.tpl template will look like this:

src = "{imagekit tr='w-250,h-250' url=$product.cover.bySize.home_default.url}"

What about "v" (versioning) parameter and why all the buzz? As I found out, the only place where it makes sense is templates/catalog/listing/category.tpl template. This is the only place where a newer image is being saved by the same name as a previous one. If you upload a new image for a category with id 25, for example, the image URL will be /img/c/25.jpg (if you upload a jpg file). If you upload a new image, it still will have this name. Thus ImageKit will think it is the same image. You can Purge cache from ImageKit dashboard (providing you know the exact URL), wait some time and the change will happen... But for me it was easier to introduce "versioning". You have to go to my module configuration, double-click on Version (Timestamp) field, ans save configuration. And image URL:

/img/c/25.jpg

will become something like this

/img/c/25.jpg?1623440107030

and ImageKit will treat it as a new image.

In all other templates Prestashop generates a unique image name, using either the actual file name, or some random values, and such problem doesn't raise.

So in the file templates/catalog/listing/category.tpl

the line

<img class="replace-2x" 
    src="{$link->getCatImageLink($subcategory.link_rewrite,$subcategory.id_image,'')}"
    alt="{$subcategory.name|escape:'html':'UTF-8'}"/>

became

<img class="replace-2x" 
    src="{imagekit tr='w-230,h-230' v=1 url=$link->getCatImageLink($subcategory.link_rewrite,$subcategory.id_image,'')}"
    alt="{$subcategory.name|escape:'html':'UTF-8'}"/>

I cannot find a similar line in the modern version of Classic theme. Maybe subcategories images are not shown at all in the modern Classic theme?

Ok, everything is working now as I wanted it. Except for a small problem what I couldn't solve. Maybe some of the readers will point me to a right direction ?

If, for some reason, I will unregister or disable my module, all images will go! Because Smarty plugin "imagekit" doens't exist any more. I wanted to do something like this in my templates:

<img class="replace-2x" 
        {if function_exists("imagekitUrl")}
            src="{imagekit tr='w-230,h-230' v=1 url=$link->getCatImageLink($subcategory.link_rewrite,$subcategory.id_image,'')}"
        {else}
            src="{$link->getCatImageLink($subcategory.link_rewrite,$subcategory.id_image,'')}"
     {/if}
         />

but for some reason it doesn't seem to work. Anyone can help? (Maybe I had to use "modifer" instead of "function" in Smarty plugin, it would be easier than?)

As usual, the source of the module is on GitHub:

https://github.com/vallka/prestamodule_imagekit.git

Laybuy against Prestashop

Published May 22, 2021, 7:40 p.m.

Recently we subscribed to Laybuy with our Prestashop-based eCommerce website. It provides Prestashop module out of a box. The module is easy to install and setup. However, immediately we have found a problem. In certain circumstances Laybuy module reported an error - totals didn't sum up:

Read more...

Prestashop Module - Enhanced Product List in Admin

Published May 5, 2021, 8:05 p.m.

Today we are returning to Prestashop modules programming. What game shall we play today?

Read more...

Prestashop Modules Programming. Bcc outgoing emails

Published Feb. 28, 2021, 8:10 p.m.

What is the idea?

Let's start programming Prestashop modules. Where to start from? Here is the documentation:

https://devdocs.prestashop.com/1.7/modules/

Ok. But what would be our first module? It should be very simple, and yet it should be useful. Here is the idea: add BCC to all outgoing emails, that we will have copies of all outgoing emails in out mailbox. Why would we want it? Actually this is what many people want, just try to google. For newly set up website it may be useful. You may want to see how actually the emails look like. And find all versions of emails which Prestashop sends to your customers. Probably after looking to these emails you would want to change them a bit, maybe remove a default "Powered by Prestashop" line at the bottom... You may want to keep monitoring outgoing emails to ensure that next update of Prestashop do not revert your custom emails to default ones (you probably forgot to check "Keep email templates" while updating Prestashop). Ok, let's do it.

Among Prestashop hooks there is one which can be used for our purpose: actionEmailSendBefore

actionEmailSendBefore

Before sending an email This hook is used to filter the content 
or the metadata of an email before sending it or even prevent its sending

Located in: /classes/Mail.php

https://devdocs.prestashop.com/1.7/modules/concepts/hooks/list-of-hooks/

If we have a chance to look inside /classes/Mail.php file, we will see that all the parameters are prefixed with "&", what means we can change them:

$hookBeforeEmailResult = Hook::exec(
        'actionEmailSendBefore',
        [
            'idLang' => &$idLang,
            'template' => &$template,
            'subject' => &$subject,
            'templateVars' => &$templateVars,
            'to' => &$to,
            'toName' => &$toName,
            'from' => &$from,
            'fromName' => &$fromName,
            'fileAttachment' => &$fileAttachment,
            'mode_smtp' => &$mode_smtp,
            'templatePath' => &$templatePath,
            'die' => &$die,
            'idShop' => &$idShop,
            'bcc' => &$bcc,
            'replyTo' => &$replyTo,
        ],
        null,
        true
    );

I couldn't find any place in Prestashop Back Office which could potentially use BCC. So we can assume (for simplicity) it is always empty and we can just set it to our own value in the hook.

Let's create a module!

Luckily, there is a simple way to create a module skeleton. There is a module generator supplied by Prestashop itself:

https://validator.prestashop.com/generator

I wouldn't say it works perfectly but it works. Probably, it is not updated as ofter as new Prestashop versions are released, and contains some bugs. Also, as mentioned in the documentation, if you want to create a Payments module for Prestashop 1.7 you should not use this generator. But for our purpose it as just invaluable.

So put some values to start a module - name, version, author name etc.:

Read more...