
Product page tabs like 1.5 on Prestashop 1.6
Have you been missing the traditional product page tabs in the new Prestashop 1.6 Template? If so, see how to easily take them back.
Let’s get some Prestashop 1.5 template code
To start off, get some 1.5 template code and drop it into our 1.6. I chose 1.5.6.2 as it’s the latest 1.5 so far. This is how it looks like, you can simply copy/paste it:
{if (isset($product) && $product->description) || (isset($features) && $features) || (isset($accessories) && $accessories) || (isset($HOOK_PRODUCT_TAB) && $HOOK_PRODUCT_TAB) || (isset($attachments) && $attachments) || isset($product) && $product->customizable} <div id="more_info_block" class="clear"> <ul id="more_info_tabs" class="idTabs idTabsShort clearfix"> {if $product->description}<li><a id="more_info_tab_more_info" href="#idTab1">{l s='More info'}</a></li>{/if} {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} {if isset($accessories) AND $accessories}<li><a href="#idTab4">{l s='Accessories'}</a></li>{/if} {if isset($product) && $product->customizable}<li><a href="#idTab10">{l s='Product customization'}</a></li>{/if} {$HOOK_PRODUCT_TAB} </ul> <div id="more_info_sheets" class="sheets align_justify"> {if isset($product) && $product->description} <!-- full description --> <div id="idTab1" class="rte">{$product->description}</div> {/if} {if isset($features) && $features} <!-- product's features --> <ul id="idTab2" class="bullet"> {foreach from=$features item=feature} {if isset($feature.value)} <li><span>{$feature.name|escape:'htmlall':'UTF-8'}</span> {$feature.value|escape:'htmlall':'UTF-8'}</li> {/if} {/foreach} </ul> {/if} {if isset($attachments) && $attachments} <ul id="idTab9" class="bullet"> {foreach from=$attachments item=attachment} <li><a href="{$link->getPageLink('attachment', true, NULL, "id_attachment={$attachment.id_attachment}")|escape:'html'}">{$attachment.name|escape:'htmlall':'UTF-8'}</a><br />{$attachment.description|escape:'htmlall':'UTF-8'}</li> {/foreach} </ul> {/if} {if isset($accessories) AND $accessories} <!-- accessories --> <div id="idTab4" class="bullet"> <div class="block products_block accessories_block clearfix"> <div class="block_content"> <ul> {foreach from=$accessories item=accessory name=accessories_list} {if ($accessory.allow_oosp || $accessory.quantity_all_versions > 0 || $accessory.quantity > 0) AND $accessory.available_for_order AND !isset($restricted_country_mode)} {assign var='accessoryLink' value=$link->getProductLink($accessory.id_product, $accessory.link_rewrite, $accessory.category)} <li class="ajax_block_product{if $smarty.foreach.accessories_list.first} first_item{elseif $smarty.foreach.accessories_list.last} last_item{else} item{/if} product_accessories_description"> <p class="s_title_block"> <a href="{$accessoryLink|escape:'htmlall':'UTF-8'}">{$accessory.name|escape:'htmlall':'UTF-8'}</a> {if $accessory.show_price AND !isset($restricted_country_mode) AND !$PS_CATALOG_MODE} - <span class="price">{if $priceDisplay != 1}{displayWtPrice p=$accessory.price}{else}{displayWtPrice p=$accessory.price_tax_exc}{/if}</span>{/if} </p> <div class="product_desc"> <a href="{$accessoryLink|escape:'htmlall':'UTF-8'}" title="{$accessory.legend|escape:'htmlall':'UTF-8'}" class="product_image"><img src="{$link->getImageLink($accessory.link_rewrite, $accessory.id_image, 'medium_default')|escape:'html'}" alt="{$accessory.legend|escape:'htmlall':'UTF-8'}" width="{$mediumSize.width}" height="{$mediumSize.height}" /></a> <div class="block_description"> <a href="{$accessoryLink|escape:'htmlall':'UTF-8'}" title="{l s='More'}" class="product_description">{$accessory.description_short|strip_tags|truncate:400:'...'}</a> </div> <div class="clear_product_desc"> </div> </div> <p class="clearfix" style="margin-top:5px"> <a class="button" href="{$accessoryLink|escape:'htmlall':'UTF-8'}" title="{l s='View'}">{l s='View'}</a> {if !$PS_CATALOG_MODE && ($accessory.allow_oosp || $accessory.quantity > 0)} <a class="exclusive button ajax_add_to_cart_button" href="{$link->getPageLink('cart', true, NULL, "qty=1&id_product={$accessory.id_product|intval}&token={$static_token}&add")|escape:'html'}" rel="ajax_id_product_{$accessory.id_product|intval}" title="{l s='Add to cart'}">{l s='Add to cart'}</a> {/if} </p> </li> {/if} {/foreach} </ul> </div> </div> </div> {/if} <!-- Customizable products --> {if isset($product) && $product->customizable} <div id="idTab10" class="bullet customization_block"> <form method="post" action="{$customizationFormTarget}" enctype="multipart/form-data" id="customizationForm" class="clearfix"> <p class="infoCustomizable"> {l s='After saving your customized product, remember to add it to your cart.'} {if $product->uploadable_files}<br />{l s='Allowed file formats are: GIF, JPG, PNG'}{/if} </p> {if $product->uploadable_files|intval} <div class="customizableProductsFile"> <h3>{l s='Pictures'}</h3> <ul id="uploadable_files" class="clearfix"> {counter start=0 assign='customizationField'} {foreach from=$customizationFields item='field' name='customizationFields'} {if $field.type == 0} <li class="customizationUploadLine{if $field.required} required{/if}">{assign var='key' value='pictures_'|cat:$product->id|cat:'_'|cat:$field.id_customization_field} {if isset($pictures.$key)} <div class="customizationUploadBrowse"> <img src="{$pic_dir}{$pictures.$key}_small" alt="" /> <a href="{$link->getProductDeletePictureLink($product, $field.id_customization_field)|escape:'html'}" title="{l s='Delete'}" > <img src="{$img_dir}icon/delete.gif" alt="{l s='Delete'}" class="customization_delete_icon" width="11" height="13" /> </a> </div> {/if} <div class="customizationUploadBrowse"> <label class="customizationUploadBrowseDescription">{if !empty($field.name)}{$field.name}{else}{l s='Please select an image file from your computer'}{/if}{if $field.required}<sup>*</sup>{/if}</label> <input type="file" name="file{$field.id_customization_field}" id="img{$customizationField}" class="customization_block_input {if isset($pictures.$key)}filled{/if}" /> </div> </li> {counter} {/if} {/foreach} </ul> </div> {/if} {if $product->text_fields|intval} <div class="customizableProductsText"> <h3>{l s='Text'}</h3> <ul id="text_fields"> {counter start=0 assign='customizationField'} {foreach from=$customizationFields item='field' name='customizationFields'} {if $field.type == 1} <li class="customizationUploadLine{if $field.required} required{/if}"> <label for ="textField{$customizationField}">{assign var='key' value='textFields_'|cat:$product->id|cat:'_'|cat:$field.id_customization_field} {if !empty($field.name)}{$field.name}{/if}{if $field.required}<sup>*</sup>{/if}</label> <textarea name="textField{$field.id_customization_field}" id="textField{$customizationField}" rows="1" cols="40" class="customization_block_input">{if isset($textFields.$key)}{$textFields.$key|stripslashes}{/if}</textarea> </li> {counter} {/if} {/foreach} </ul> </div> {/if} <p id="customizedDatas"> <input type="hidden" name="quantityBackup" id="quantityBackup" value="" /> <input type="hidden" name="submitCustomizedDatas" value="1" /> <input type="button" class="button" value="{l s='Save'}" onclick="javascript:saveCustomization()" /> <span id="ajax-loader" style="display:none"><img src="{$img_ps_dir}loader.gif" alt="loader" /></span> </p> </form> <p class="clear required"><sup>*</sup> {l s='required fields'}</p> </div> {/if} {if isset($HOOK_PRODUCT_TAB_CONTENT) && $HOOK_PRODUCT_TAB_CONTENT}{$HOOK_PRODUCT_TAB_CONTENT}{/if} </div> </div> {/if}
Copy the previous code. Then, open up product.tpl, which you can find in the theme’s folder, and locate this line:
{if isset($features) && $features} <!-- Data sheet --> <section class="page-product-box">
Right before that, paste in the 1.5 template code. We will need to make minor adjustments to it, but before diving deep into coding, let’s comment out the 1.6 code.
Comment out everything from here:
{if isset($features) && $features}
To right before
{if isset($HOOK_PRODUCT_FOOTER) && $HOOK_PRODUCT_FOOTER}{$HOOK_PRODUCT_FOOTER}{/if}
The result should be as follows:
{*if isset($features) && $features} <!-- Data sheet --> <section class="page-product-box"> <h3 class="page-product-heading">{l s='Data sheet'}</h3> <table class="table-data-sheet"> {foreach from=$features item=feature} <tr class="{cycle values="odd,even"}"> {if isset($feature.value)} <td>{$feature.name|escape:'html':'UTF-8'}</td> <td>{$feature.value|escape:'html':'UTF-8'}</td> {/if} </tr> {/foreach} </table> </section> <!--end Data sheet --> // WHOLE CODE BLOCK HERE... </div> </div> </section> <!--end Accessories --> {/if*} {if isset($HOOK_PRODUCT_FOOTER) && $HOOK_PRODUCT_FOOTER}{$HOOK_PRODUCT_FOOTER}{/if}
With the whole block commented out. Then, comment out what comes after the product footer hook as well
<!-- description & features --> {*if (isset($product) && $product->description) || (isset($features) && $features) || (isset($accessories) && $accessories) || (isset($HOOK_PRODUCT_TAB) && $HOOK_PRODUCT_TAB) || (isset($attachments) && $attachments) || isset($product) && $product->customizable} {if isset($attachments) && $attachments} // WHOLE CODE BLOCK HERE... <p class="clear required"><sup>*</sup> {l s='required fields'}</p> </section> <!--end Customization --> {/if} {/if*}
Pay extra attention to this step, there are a bunch of IF statements at the end of the block, which might cause a bad error if not commented out in the right place.
Adapting the code
At this point, we need to adapt a couple of sections, to make sure they are compatible with 1.6. We will simply examine each section and replace the content with its newer counterpart when needed.
The first block is the product description, and there is really nothing to change here, as it’s the same across the two templates.
Features (data sheet) is another one that can be left as it is.
THe following one would be attachments, looking like this in the 1.5 snippet:
{foreach from=$attachments item=attachment} <li><a href="{$link->getPageLink('attachment', true, NULL, "id_attachment={$attachment.id_attachment}")|escape:'html'}">{$attachment.name|escape:'htmlall':'UTF-8'}</a><br />{$attachment.description|escape:'htmlall':'UTF-8'}</li> {/foreach}
Let’s compare it to the 1.6 version:
{foreach from=$attachments item=attachment name=attachements} {if $smarty.foreach.attachements.iteration %3 == 1}<div class="row">{/if} <div class="col-lg-4"> <h4><a href="{$link->getPageLink('attachment', true, NULL, "id_attachment={$attachment.id_attachment}")|escape:'html':'UTF-8'}">{$attachment.name|escape:'html':'UTF-8'}</a></h4> <p class="text-muted">{$attachment.description|escape:'html':'UTF-8'}</p> <a class="btn btn-default btn-block" href="{$link->getPageLink('attachment', true, NULL, "id_attachment={$attachment.id_attachment}")|escape:'html':'UTF-8'}"> <i class="icon-download"></i> {l s="Download"} ({Tools::formatBytes($attachment.file_size, 2)}) </a> <hr /> </div> {if $smarty.foreach.attachements.iteration %3 == 0 || $smarty.foreach.attachements.last}</div>{/if} {/foreach}
Replacing it sounds like a good idea, so go ahead and do it. Simply paste over everything inside the foreach (make sure you also add the name=attachments part!).
Accessories is another one we need to replace, so delete everything within the condition:
{if isset($accessories) AND $accessories} <!-- accessories --> <div id="idTab4" class="bullet"> // Delete everything inside </div> {/if} <!-- Customizable products -->
Still making sure to retain the #idTab4 div
The last block of the 1.5 template concerns customizable data, and it’s better to replace the whole content here as well. Therefore, once more:
{if isset($product) && $product->customizable} <div id="idTab10" class="bullet customization_block"> // Delete everything inside </div> {/if}
Remove everything inside that div with idTab10 as ID.
We should be done…almost! There is a leftover of the 1.6 section, Pack Items, which used to be elsewhere in 1.5. If you don’t want it to be part of tabbed data, you can simply uncomment the section as it is. Otherwise, read on.
Dealing with the Pack Items block
We cannot simply copy and paste the pack items block, as that would result in a div having no parent link to call it among tabs. The first thing we therefore have to do is add a link pointing to it, in the tabs navigation bar:
<ul id="more_info_tabs" class="idTabs idTabsShort clearfix"> {if $product->description}<li><a id="more_info_tab_more_info" href="#idTab1">{l s='More info'}</a></li>{/if} {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} {if isset($accessories) AND $accessories}<li><a href="#idTab4">{l s='Accessories'}</a></li>{/if} {if isset($product) && $product->customizable}<li><a href="#idTab10">{l s='Product customization'}</a></li>{/if} {if isset($packItems) && $packItems|@count > 0}<li><a href="#blockpack">{l s='Pack Items'}</a></li>{/if} {$HOOK_PRODUCT_TAB} </ul>
As you can see, I added a whole new line to handle the pack tab, which is referencing to a (still non existing) div with the id #blockpack. You can really choose any ID you want here, just making sure it matches in the following step.
Let’s not scroll back after the customization block:
{/if} {if isset($HOOK_PRODUCT_TAB_CONTENT) && $HOOK_PRODUCT_TAB_CONTENT}{$HOOK_PRODUCT_TAB_CONTENT}{/if}
Right before {if isset($HOOK_… } but after that closing {/if} (which was the customization condition), so in that only one empty line, add the following:
{if isset($packItems) && $packItems|@count > 0} <div id="blockpack"> {include file="$tpl_dir./product-list.tpl" products=$packItems} </div> {/if}
Here is how it should look
{/if} {if isset($packItems) && $packItems|@count > 0} <div id="blockpack"> {include file="$tpl_dir./product-list.tpl" products=$packItems} </div> {/if} {if isset($HOOK_PRODUCT_TAB_CONTENT) && $HOOK_PRODUCT_TAB_CONTENT}{$HOOK_PRODUCT_TAB_CONTENT}{/if}
You can now breathe. The worst part is done. Save and refresh, you should have a working tabs system, even if navigation links are stack one on top of the other. Let’s give it some style!
Styling Tabs in the Product page
There is not much to do at this point. Open up product.css, located in the theme folder, css/, and paste in the following
#more_info_tabs li { float:left; } #more_info_tabs li a { padding: 0 20px; color:white; line-height: 40px; height: 40px; display:block; background: #555; } #more_info_tabs li a:active {text-decoration:none;} #more_info_tabs li a.selected { background:black; text-decoration:none; }
I gave it some generic styling to have them look like tabs, so feel free to expand over the tutorial and make it prettier! We’re done!