Magento templates

Les Templates et le Design Magento

Https + Magento Cache + Internet Explorer = No image

Magento uses by default an advanced cache system in order to display templates content faster. But default configuration of this cache can sometime creates some issues.

Recently, we had some images that were not displayed in the top menu when we switch from an http page to a https page using Internet Explorer browser. Images in this menu still have a non secured (http) URI. Internet Explorer is not pleased with such content. By default, this browser does not display unsecured content on a secured page. We have this security warning displayed when some image have http URI on a https page:

Internet Explorer Security Warning

Block displaying top menu was using cache system but this cache was not regenerated when we switch from a http to a https page. That's why images were still having http URI.

For fixing this problem, we must rewrite getCacheKey method from Mage_Catalog_Block_Navigation class. A new parameter telling if page use http or https should be included in cache key. Cache will then be different for this 2 types of pages. This parameter is added with getSkinUrl method

Default code of getCacheKey

public function getCacheKey()
{
    return 'CATALOG_NAVIGATION_' . Mage::app()->getStore()->getId()
        . '_' . Mage::getDesign()->getPackageName()
        . '_' . Mage::getDesign()->getTheme('template')
        . '_' . Mage::getSingleton('customer/session')->getCustomerGroupId()
        . '_' . md5($this->getTemplate() . $this->getCurrenCategoryKey());
}

is then replaced by following code

public function getCacheKey()
{
    return 'CATALOG_NAVIGATION_' . Mage::app()->getStore()->getId()
        . '_' . Mage::getDesign()->getPackageName()
        . '_' . Mage::getDesign()->getTheme('template')
        . '_' . Mage::getSingleton('customer/session')->getCustomerGroupId()
        . '_' . md5($this->getTemplate() . $this->getCurrenCategoryKey())
        . '_' . md5($this->getSkinUrl()); /*** FIX IN THIS LINE ***/
}

This is it. You can now use peacefully https on Internet Explorer.

For more details about Magento cache system you can read this article.

Custom JavaScript form validators

Magento, thanks to Prototype framework, has nice set of JavaScript validators - so we can validate user input without reloading site. Of course, we cannot rely only on it, but it is good starting point - and for majority of users, it will have good look'n'feel.

But sometimes bulit-in validators are just not enough - for example, we can add custom fields for customer account, address etc. and we need some custom validation there. Magento together with Prototype gives us quick and easy way.

Little bit of theory - Prototype gives us Validation class. To check if form is valid, we have to call validate() method, which checks validation classes. I say classes, cause those are defined by css classes for input, for example:

<input name="someinput" class="required-entry" />  

will require to be not-empty. It would be nice to add our own classes to keep this process so simple as it is. So...

Step 1.

Create your own file, for example /skin/frontend/default/your_skin/js/myvalidation.js and add it to page.xml layout file.
Important note: it must be somewhere below adding script prototype/validation.js - usually, end of head block is good and safe place.
 

Step 2.

Editing myvalidation.js. Take a look on following code:

if(Validation) {        //   1.
    Validation.addAllThese([      //    2.
        [
            'validation-myown',       //     3.
            'Please insert proper word',     //    4.
            function(v,r){ return v.indexOf('valid')==-1?false:true }   //  5.
        ],
       [ ]    // 6.
    ])
}

Little explanations.

  1. We don't want to make any JavaScript error if for some reason validation script is not enabled, so we ensure it exists
  2. Adding a validators. Argument is array, each value of it is also array - so you can add multiple validators at once.
  3. This is css class that validator will be searching for, note that it must begin with validation-  ('-' sign at end) - otherwise it may not work as desired.
  4. This is error message displayed when validation is not passed.
  5. Finally, our most important function - validator itself.
  6. Next validators...

In this example validator function takes two arguments, but in fact usually first one is enough.
So first - called v here - is value of input at moment of validation
Second - called r here - can be useful in very custom validators - it is reference to validated object.
When validator function returns true, validation for field is passed, if false - opposite.

That's it. Quite simple if you will understand process ;)
 

Step 3. (optional)

We have nice error message - but what about case we need to translate it? Well, we can use Magento JavaScript class - Translate. Adding translations is quite simple, but has to be done from template level. We can use Jakub's module (blog.baobaz.com/en/blog/magento-translations-module):

<script type="text/javascript">
    Translator.add('Please insert proper word','<?php echo Mage::helper('translations')->__('Please insert proper word')?>');
</script>

Add this to template where you use your custom validators or create special block for it, so they can be easily reused.

Anything more on validators is just plain JavaScript and your needs :)

Magento checkout progress with custom layout

Recently, while working on Magento checkout process, I stumbled on two issues related to the "one page checkout" solution used on sites Magento. The problems are maybe not very difficult to solve, but as they repeat in different projects, I think they deserve mentioning. In this article I'll shortly explain the checkout progress mechanism in Magento, I'll investigate why it doesn't work with custom layout, and how to fix it.

As you can see, Magento checkout page contains two columns:

  1. main column, consisting of checkout steps
  2. side column with „checkout progress”, list of all currently entered data in checkout process (billing adress, shipping adress, shipping method, payment method)

The checkout progress column is updated by javascript each time any checkout step is completed. But if you decide to set different layout for checkout page than the default "two-columns-right" (default in Community Edition; in Enterprise Edition we've got "one column" layout here), the checkout progress column will not be updated any more. Why?

To find an answer on this mysterious riddle, open the file /skin/frontend/default/default/js/opcheckout.js. This is the javascript mechanism controlling behaviour of checkout process. About line 50 you can find function reloadProgressBlock:

reloadProgressBlock: function(){
    var updater = new Ajax.Updater(
        $$('.col-right')[0],
        this.progressUrl,  
        {method: 'get', onFailure: this.ajaxFailure.bind(this)}
    );
},

This function updates the content of the right column, filling it with the actual checkout progress status. So if the layout of checkout page is "two columns right", everything works fine. But in other case the function fails – it can't find the DOM element with class="col-right", specified by $$('.col-right')[0] javascript statement.

Now the solution is obvious – to replace $$('.col-right')[0] by the selector of your wrapper element containing <div class="one-page-checkout-progress">. So, for example, if your HTML structure looks like that:

<div id=”my-custom-right-column”>
        <div class=”one-page-checkout-progress”>
        ...
        </div>
</div>

then you have to copy the default opcheckout.js file, put it into your theme's appriopriate folder, and edit the reloadProgressBlock function, by changing the $$('.col-right')[0] into $('my-custom-right-column'):

reloadProgressBlock: function(){
    var updater = new Ajax.Updater(
        $('my-custom-right-column'),
        this.progressUrl,
        {method: 'get', onFailure: this.ajaxFailure.bind(this)}
    );
},

Moreover, the Ajax.Updater() accepts also an id of an object as first argument, so if you select wrapper by ID, you can simplify the code, removing the $() Prototype element selector:

reloadProgressBlock: function(){
    var updater = new Ajax.Updater(
        'my-custom-right-column',
        this.progressUrl,
        {method: 'get', onFailure: this.ajaxFailure.bind(this)}
    );
},

That's all. It works :)

Magento Checkout – How to customize the design

Customize checkout page!One of the most important parts of any e-commerce solution, including Magento, is checkout process. The number of orders is higly dependent on the simplicity, usability and even look of the checkout pages. That's why shop owners often decide to create custom design of those pages in their Magento website. With Magento templates system you can easily customize the look of any page, but in case of checkout, you have to keep some default constructions.

You can find the default template of checkout stages in app/design/frontend/.../.../templates/checkout/onepage.phtml. Here's the fragment of html output of that template:

<ol id="checkoutSteps" class="one-page-checkout">
    <li id="opc-login" class="section">
        <div class="head">
            ...<!-- head of  login section -->
        </div>
        <div id="checkout-step-login" class="box a-item" style="display:none;">
            ...<!-- body of login section -->
        </div>
    </li>
    ... <!-- next sections here -->
</ol>

As we can see, the checkout process is enclosed in an ordered list (<ol>...</ol>). It is quite a good idea, as the order of steps is very important here. Moreover, the default javaScript scripts are strongly related to the html structure (especially to the names of classes – we will look at them later). So it is suggested to preserve this default structure of an ordered list.

To improve the usability of the process, it is a good habit to give a different look to checkout steps headers, depending on the currently active stage. It is an easy task in Magento, because the included script dynamicaly adds or removes class names to the <li class="section"> elements of <ol id="checkoutSteps">. Those class names depend on the currently active step of checkout:

  • no additional class for all next steps after the current step
  • class="allow" for all previous steps
  • class="allow active" for the current step

A similar mechanism is applied in checkout progress - an additional column containing data of already passed checkout steps (billing address, shipping address, shipping method and payment method). The content of this column is changed dynamically, too, according to the current step. The example below show the checkout progress block's html structure after the "billing information" checkout step:

<div class="box one-page-checkout-progress">
    <h3>Your Checkout Progress</h3>
    <ol>
        <li>
            <h4 class="complete">Billing Address                  
                <span class="separator">|</span>                  
                <a href="#billing" onclick="checkout.accordion.openSection('opc-billing'); return false;">Change</a>
            </h4>
            <div class="content">
                ... <!-- billing address here -->
            </div>
        </li>
        <li>
            <h4>Shipping Address</h4>
        </li>
        <li>
            <h4>Shipping Method</h4>
        </li>
        <li>
            <h4>Payment Method</h4>
        </li>
    </ol>
</div>

The checkout progress is an ordered list, too. The steps that are not yet passed contain only <h4> title. In the other steps there is a class="complete" added to <h4> so we can make the titles of previous steps look different then the titles of next steps.

Those are the basis of the Magento checkout page structure. Understanding and remembering that strucutre can be very helpful during custom Magento shop development.

Prototype and jQuery in Magento

As we know, the Prototype JavaScript framework is included in standard magento installation. Together with script.aculo.us it is a powerful tool for programming client-side actions and effects. It also provides Ajax framework which makes any ajax requests easier then ever before. A lot of magento templates deploy Prototype functionality, e.g. to show/hide elements depending on user's choice or to display error messages with a nice 'fade' effect.

But Prototype is not the only JavaScript library to offer such an useful set of functions. One of other most popular libraries is jQuery – very lightweight (19KB minified and compressed version), simple to use and, to tell the truth, my favourite one. Created in 2006, now it has a large community and hudge set of plugins.

However, is it generally a good idea to use both libraries in one site magento? In my opinion: no, it isn't. At least in most cases. Why? There are a few main reasons:

  • They provide similar functionality so what you achieved by one of them, you can achieve by the other
  • More libraries included mean more data to download by a web browser and slower loading of page
  • They use some common structures, which may lead to conflicts

In some cases, though, you may be forced to include jQuery in your magento shop. For example, you need to use an additional JavaScript library which is created as a jQuery plugin. So how can you force Prototype and jQuery to work together without conflicts?

At first you have to download the recent jquery version (the current release is v.1.3.2) and put it in /js/jquery folder. Then you have to include it in all magento pages – the most logical way is to put the following piece of code in <block type="page/html_head"> section of /app/design/frontend/.../.../layout/page.xml:

<action method="addJs"><script>jquery/jquery-1.3.2.min.js</script></action>

Now you have to avoid conflicts with Prototype framework – they both use $ notation (in jQuery it is a shortcut for jQuery and in Prototype it is an alias for getElementById function). Fortunately, there is a mechanism in jQuery to override the $ function: jQuery.noConflict(). You can put such a construction at the end of your jquery-1.3.2.min.js file:

var $j = jQuery.noConflict();

and from now you have to use $j instead of $ function (or, if you want, you can still use jQuery). So be careful to change all instances of $ in your jquery plugins (but there are many plugins which have jQuery.noConflict() already implemented).

And it would be all, if there wasn't one web browser which still would have problems with coexisting Prototype and jQuery. And, surprisingly, it is not Internet Explorer ;) It is Firefox 2. It throws errors and refuses to execute the JavaScript. Of course currently there are newer versions of Firefox and the problem doesn't exist there, but there is still a little percent of net surfers who use Firefox 2 and they want to spend money in your shop, too;)

Fortunately there is an easy way to solve this problem. You have to include jQuery file before including Prototype. So you have to do the following in page.xml:

<action method="addJs"><script>jquery/jquery-1.3.2.min.js</script></action>
<action method="addJs"><script>prototype/prototype.js</script></action>

And now you can forget about the conflicts and enjoy those two magnificent libraries working together.

Understanding Magento Default Templates

Magento template system allows you to easily create custom html/css code according to design requirements. However, in some cases it would be a good idea to use some of the default magento templates. It may save some time spent on integrating new html structure with magento functionality.

For example, let's take a look at this location in standard magento structure: app\design\frontend\default\default\template\page. Here we have templates describing a basic structure of a page: 1column.phtml, 2colums-left.phtml, 2colums-right.phtml or 3colums.phtml. They all contain the same structure of the page (let's focus on the inside of 'body' section):

<div class="wrapper">

<!-- start header -->
        <div class="header">
            ...
        </div>
<!-- end header -->

<!-- start middle -->
        <div class="middle-container">
            ...
        </div>
<!-- end middle -->

<!-- start footer -->
        <div class="footer-container">
                <div class="footer">
                    ...
                </div>
        </div>
<!-- end footer -->

</div>

They differ only in part inside <div class=”middle-container”>, where a html code for specified layout is defined. As you can see, they present the most common way of building the pages – header, middle part, footer. So in most cases you can use those templates without any change in html and create only your own skin css, which will adapt the look of your page to your needs.

Of course, the more detailed a template is, the less chance that it can be used without change, but you can still start working on a default magento template and just modify the parts you need.

Display a BestSellers block on your Magento homepage

One of the frequent questions you ask when installing Magento is how to add Bestsellers block to your homepage

Indeed, Magento - in its standard version at least - does not provide this feature.

Nevertheless, even if the lack of this feature might be quite weird for an ecommerce platform, it is not a blocking issue. Quite the contrary, it allows you to realise how easy it is to create new blocks on Magento, or, at least, the flexibility of the architecture of Magento.

  1. Get the data

    This could be the most difficult step since you must know perfectly the Magento core object model.
    As soon as you know where to collect data (in this case, sold products) and how to request them (in this case, by descending order of sales), getting required data is pretty easy, it just take one line (actually several lines here, but just to allow a better readability).

    $_productCollection = Mage::getResourceModel('reports/product_collection')
        ->addAttributeToSelect('*')
        ->addOrderedQty()
        ->setOrder('ordered_qty', 'desc');

  2. Create your BestSellers block

    Remember to use MVC pattern: Zend framework, and therefore Magento, ensure that each object has its specific role.
    Creat your business object:
    In a file that could be app/code/local/ma_librairie/Bestseller/Model/Bestseller.php write your business rules (here: getting sold products by descending order of sales).
    Then in a file app/code/local/ma_librairie/Bestseller/Block/Bestseller.php extend class Mage_Core_Block_Template that will pass data to display block.
    At last, in file app/design/frontend/mon_theme/default/template/catalog/product/view/bestseller.phtml display block content.
    Of course these files have to be declared in the appropriate XML configuration files.

  3. Display your block

    Set up your block on yout active layout.
    Either change the XML file of your layout, or add it on your homepage from Magento backoffice.

For more information about Magento Templates, you may read the article Creating templates for your Magento website.

Creating templates for your Magento website

Creating a template for your Magento website is an important step in setting up your online store. Indeed, the visual apect of an e-store is as important as a shopwindow: it is the first thing the would-be customer will see. This shopwindow will retain a customer or on the contrary, make him leave.

(Of course there are other ways to bring prospects directly inside your store, using search engines for instance, but this will described in another article.)

Knowing this, the whole Magento design system was conceived and designed to help you creating pages on your Magento website, organizing them and decorating them as you wish. (And of course without specific developments which would prevent you from installing future Magento releases.)

In order to build a Magento design package, three types of objects are needed:

  1. templates
  2. skins
  3. layouts

Understanding how these three types of objects work together is essential in order to be able to create your deisgn package.

Templates

Templates are a set of blocks that will display your shop.

For instance, these blocks will:

  • display a product detail,
  • display the customer shopping cart,
  • display featured products,
  • display navigation menus,
  • display content informations such as "Terms of use",
  • et caetera .

Magento provides a default template that contains a variety of standard blocks. It is possible, using inheritance, to override blocks design, to create new ones, and so on...
Moreover, Magento being based on the Zend Framework, which provides components for the MVC design pattern, the business rules are defined elsewhere, allowing complete independence of your graphic with the core system.

Skins

Skins contain all the CSS files of your site and describe how the block should be displayed (colors, images, fonts...). Separated from the templates, it becomes extremely easy to customize the graphic style without changing the blocks.

Changing all or parts of the graphic can be easily achieved, for instance:

  • for specific parts of the website (specific category, refurb ...)
  • for a period of time (Christmas, sales ...),
  • for a particular category of user (committees, VIP ...),
  • et caetera .

Layouts

Layouts will determine which blocks will appear on which page (catalog page, product page, checkout page, customer account page ...) and define their attributes. In other words, they are the definition of the overall structure of your site. Again, a standard layout is proposed and it is easily customizable.