Tutorials/Understanding theme management in OXID eShop from 4.5.0

Understanding Theme management in OXID eShop from 4.5.0
From OXID eShop 4.5.0 theme handling will be moved from config.inc.php to back-end (Master Settings -&gt; Themes) showing list of all available templates, their descriptions, thumbnails and theme specific settings and also theme switching in back-end. Theme meta data file and thumbnail image laying in theme folder enables template appearance in list. To enable selected theme just click activate button.



Base theme folder structure, containing templates, translations, styles images and javascript files did not changed in this version. Changes are coming exceptionally to smarty template directory stucture, but this is not a subject of this tutorial.

Theme meta data
Azure theme files

Thumbnail out/azure/theme.jpg

Info out/azure/theme.php

$aTheme = array(  'id'           =&gt; 'azure',   'title'        =&gt; 'Azure',   'description'  =&gt; 'Azure theme for OXID eShop 4.5.0 Beta',   'thumbnail'    =&gt; 'theme.jpg',   'version'      =&gt; '0.5',   'author'       =&gt; 'OXID', );

It is enough to add these 2 files filled with your data to theme folder (/out/your_theme/) to see it in theme list. Theme id must match theme folder name.

Attention - starting from 4.7.x there are following changes: The theme.php needs to be placed into /application/views/your_theme/ where as well is the right place for your individualised template files (respecting the same folder structure like Azure). Within /out/mein_theme/ you need to place theme.jpg as well as folders /img and /src with all needed files.

Custom themes
To create a custom theme, which extends another one by overriding just some files, it is enough to add the id of the parent theme (parentTheme) together with compatible version numbers (parentVersions) to the $aTheme info array e.g.

$aTheme = array( 'title'          =&gt; 'Child of Azure',  'description'    =&gt; 'EXTENDED Azure theme for OXID eShop 4.5.0 Beta',  'thumbnail'      =&gt; 'theme.jpg',  'version'        =&gt; '0.5',  'author'         =&gt; 'OXID',  'parentTheme'    =&gt; 'azure',  'parentVersions' =&gt; array('0.5','0.6'), );

Such theme can only be activated if the parent theme ('azure' in this case) is present in the system and its version matches one of the predefined values ('0.5' or '0.6').

Such theme will take all files template file (and config options) from the parent theme. Except files, which are present in both the child and the parent themes, will be used from the child (custom) theme, effectively overriding the parent theme files.

Theme ID is stored in config variable named "sTheme". If activating custom theme with defined parent, parent theme ID will be saved to config variable "sTheme" and child theme ID will be saved to config variable "sCustomTheme". All custom theme config variables will override same parent themes config variables and if some option is missing - will take it value from parent.

In case any error happens, the theme can not be activated in the admin tool and instead of the 'Activate' button an error message is displayed e.g.



Script to copy only modified template-files
Normally it is way easier to work on a complete copy of the basic template-set while developing your own theme. Aggrosoft has developed a script to copy only the modified files afterwards to an extra folder. This new folder can be set as sCustomTheme in config-file then. Using the script is simple:

&lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"&gt; &lt;html xmlns="http://www.w3.org/1999/xhtml" xml:lang="de" lang="de"&gt; &lt;head&gt; &lt;title&gt;generate Template Override&lt;/title&gt; &lt;link rel="stylesheet" href="http://twitter.github.com/bootstrap/assets/css/bootstrap-1.1.1.min.css"&gt; &lt;meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"&gt; &lt;/head&gt; &lt;?php
 * 1) create "tploverride.php" in the shop root directory,
 * 2) paste the code below,
 * 3) open your browser and go to http://your-shop-url.com/tploverride.php
 * 4) choose the modified and original theme (there has to be an unmodified version of the original theme),
 * 5) choose target folder and press "Start".

function makeAll($dir, $mode = 0777, $recursive = true) { if( is_null($dir) || $dir === "" ){ return FALSE; }       if( is_dir($dir) || $dir === "/" ){ return TRUE; }       if( makeAll(dirname($dir), $mode, $recursive) ){ return mkdir($dir, $mode); }       return FALSE; }   function smartCopy($source, $dest, $options=array('folderPermission'=&gt;0755,'filePermission'=&gt;0755)) {       $result=false; //For Cross Platform Compatibility if (!isset($options['noTheFirstRun'])) { $source=str_replace('\\','/',$source); $dest=str_replace('\\','/',$dest); $options['noTheFirstRun']=true; }       if (is_file($source)) { if ($dest[strlen($dest)-1]=='/') { if (!file_exists($dest)) { makeAll($dest,$options['folderPermission'],true); }               $__dest=$dest."/".basename($source); } else { $__dest=$dest; }           if (!file_exists($__dest)) { $result=copy($source, $__dest); chmod($__dest,$options['filePermission']); }       } elseif(is_dir($source)) { if ($dest[strlen($dest)-1]=='/') { if ($source[strlen($source)-1]=='/') { //Copy only contents } else { //Change parent itself and its contents $dest=$dest.basename($source); @mkdir($dest); chmod($dest,$options['filePermission']); }           } else { if ($source[strlen($source)-1]=='/') { //Copy parent directory with new name and all its content @mkdir($dest,$options['folderPermission']); chmod($dest,$options['filePermission']); } else { //Copy parent directory with new name and all its content @mkdir($dest,$options['folderPermission']); chmod($dest,$options['filePermission']); }           }            $dirHandle=opendir($source); while($file=readdir($dirHandle)) {               if($file!="." &amp;&amp; $file!="..") {                   $__dest=$dest."/".$file; $__source=$source."/".$file; //echo "$__source ||| $__dest&lt;br /&gt;"; if ($__source!=$dest) { $result=smartCopy($__source, $__dest, $options); }               }            }            closedir($dirHandle); } else { $result=false; }       return $result; }

function compareDirectories( $path = '.', $changePath = '.', $level = 0 ){ $ignore = array( 'cgi-bin', '.', '..' ); $dh = @opendir( $path ); while( false !== ( $file = readdir( $dh ) ) ){ // Loop through the directory if( !in_array( $file, $ignore ) ){ if( is_dir( "$path/$file" ) ){ // Its a directory, so we need to keep reading down… compareDirectories( "$path/$file", "$changePath/$file", ($level+1),$time ); } else { compareFiles("$path/$file", "$changePath/$file"); }//elseif }//if in array }//while closedir( $dh ); }   function compareFiles( $path = '.', $changePath = '.' ){ $vanilla_path = $path; $branch_path = $changePath; if( !file_exists($branch_path) || strcmp(file_get_contents($vanilla_path),file_get_contents($branch_path)) ){ if($_REQUEST["displayonly"] == 1){ echo "$vanilla_path &gt;&gt; $branch_path&lt;br/&gt;"; }else{ $tPath = str_replace($_REQUEST['modifiedtpl']."/",'',$vanilla_path); makeAll(dirname("out/".$_REQUEST['targetfolder']."/$tPath")); smartCopy($vanilla_path, "out/".$_REQUEST['targetfolder']."/$tPath"); }       }           return true; }

$aTemplates = array; foreach ( glob("out/**",GLOB_ONLYDIR) as $tplfolder ) { if(file_exists($tplfolder."/theme.php")){ $aTemplates[] = $tplfolder; }   }    if($_REQUEST['fnc'] == 'create'){ compareDirectories( $_REQUEST['modifiedtpl'], $_REQUEST['originaltpl']); $blCopied = true; } ?&gt; &lt;body style="padding-top: 60px"&gt;

&lt;div class="topbar-wrapper noprint" style="z-index: 5"&gt; &lt;div class="topbar"&gt; &lt;div class="fill"&gt; &lt;div class="container"&gt; &lt;h3&gt;&lt;a href="#"&gt;Aggrosoft Override Generator&lt;/a&gt;&lt;/h3&gt; &lt;/div&gt; &lt;/div&gt; &lt;/div&gt; &lt;/div&gt;

&lt;div class="container"&gt;

&lt;div class="alert-message success"&gt; &lt;p&gt;&lt;strong&gt;Success!&lt;/strong&gt; You will find all modified files now in chosen the target folder.&lt;/p&gt; &lt;/div&gt;

&lt;form action="tploverride.php" method="post"&gt; &lt;input type="hidden" name="fnc" value="create"&gt; &lt;fieldset&gt; &lt;legend&gt;generate Custom Theme&lt;/legend&gt; &lt;div class="clearfix"&gt; &lt;label for="username"&gt;modified Template&lt;/label&gt; &lt;div class="input"&gt; &lt;select name="modifiedtpl"&gt; &lt;?php foreach ($aTemplates as $sTemplate) : ?&gt; &lt;option value="&lt;?php echo $sTemplate; ?&gt;"&gt;&lt;?php echo $sTemplate; ?&gt;&lt;/option&gt; &lt;?php endforeach; ?&gt; &lt;/select&gt; &lt;/div&gt; &lt;/div&gt;&lt;!-- /clearfix --&gt; &lt;div class="clearfix"&gt; &lt;label for="username"&gt;original Template&lt;/label&gt; &lt;div class="input"&gt; &lt;select name="originaltpl"&gt; &lt;?php foreach ($aTemplates as $sTemplate) : ?&gt; &lt;option value="&lt;?php echo $sTemplate; ?&gt;"&gt;&lt;?php echo $sTemplate; ?&gt;&lt;/option&gt; &lt;?php endforeach; ?&gt; &lt;/select&gt; &lt;/div&gt; &lt;/div&gt;&lt;!-- /clearfix --&gt; &lt;div class="clearfix"&gt; &lt;label for="username"&gt;target folder (in out)&lt;/label&gt; &lt;div class="input"&gt; &lt;input type="text" size="30" name="targetfolder" class="xlarge"&gt; &lt;/div&gt; &lt;/div&gt;&lt;!-- /clearfix --&gt; &lt;div class="clearfix"&gt; &lt;label for="username"&gt;simulate&lt;/label&gt; &lt;div class="input"&gt; &lt;div class="input-prepend"&gt; &lt;label class="add-on"&gt;&lt;input type="checkbox" name="displayonly" value="1"&gt;&lt;/label&gt; &lt;input class="small" readonly="readonly" value="Nur anzeigen" type="text"&gt; &lt;/div&gt; &lt;/div&gt; &lt;/div&gt;&lt;!-- /clearfix --&gt; &lt;div class="actions"&gt; &lt;button class="btn primary" type="submit"&gt;Start&lt;/button&gt;&amp;nbsp;&lt;button class="btn" type="reset"&gt;Cancel&lt;/button&gt; &lt;/div&gt; &lt;/fieldset&gt; &lt;/form&gt;

&lt;/div&gt; &lt;script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js"&gt;&lt;/script&gt; &lt;script type="text/javascript" src="http://autobahn.tablesorter.com/jquery.tablesorter.min.js"&gt;&lt;/script&gt; &lt;script type="text/javascript"&gt; $(function{         $("table").tablesorter({ sortList: 1,1 });      }); &lt;/script&gt; &lt;/body&gt; &lt;/html&gt; Thanks to Aggrosoft for sharing this script; thread in forums:http://www.oxid-esales.com/forum/showthread.php?p=70414

Theme specific config
OXID eShop themes always depended on different configuration parameters set in back-end. While developing new totally revamped Azure theme for OXID eShop 4.5.0 we decided to improve theme specific config handling.

OXID eShop 4.5.0 will come with separated theme config option handling allowing theme developers to add their own theme specific config options, without modifying back-end templates.

Theme config options will be stored in existing oxconfig database table, marked with dedicated oxmodule column containing value "theme:azure". Additional display parameters has a separate database table oxconfigdisplay allowing config option sorting, grouping and drop-down option constrain passing. Theme config language constants translation text has to be added to language files named theme_options.php.

Some theme specific config options are used in eShop source code to generate proper size images or calculate pagination so we recommend copying them from azure theme (search for "theme:azure" sql inserts in setup/sql/database.sql file).

Example
Adding new theme specific config option to azure theme to show/hide slogan in page header can be accomplished in following easy steps

1. Add option to oxconfig table

execute sql

INSERT INTO `oxconfig` (`OXID`,`OXSHOPID`,`OXMODULE`,`OXVARNAME`,`OXVARTYPE`,`OXVARVALUE`) VALUES ('azure.blShowSlogan.1', 1, 'theme:azure', 'blShowSlogan', 'bool', ); ''

2. Configure option display parameters in oxconfigdisplay table

Config variable display depends on variable type (oxconfig.OXVARTYPE)

To add drop-down selects for predefined values, set constraint value(oxconfigdisplay.OXVARCONSTRAINT)

You can always invent you own theme option groups, when specifying grouping value (oxconfigdisplay.OXGROUPING) and adding translations to theme_options.php file ( constant name 'SHOP_THEME_GROUP_mygroup').

execute sql

INSERT INTO `oxconfigdisplay` (`OXID,`OXCFGMODULE`,`OXCFGVARNAME`,`OXGROUPING`,`OXVARCONSTRAINT`,`OXPOS`) VALUES ('azure.blShowSlogan.1', 'theme:azure', 'blShowSlogan', 'display', '', 0);

3. Add language translations to out/azure/[en|de]/theme_options.php

'SHOP_THEME_blShowSlogan'  =&gt; 'Display slogan',

4. Add slogan with option check to out/azure/tpl/layout/header.tpl template

[{if $oViewConf-&gt;getViewThemeParam('blShowSlogan') }] &lt;h1 style="margin:0 10px 15px;padding:10px;color:#fff;background:#000;"&gt;Slogan text&lt;/h1&gt; [{/if}]





Clone a theme with independent config
To get an own theme with all config-parameters without any relation to a parent theme, you need to copy all files from the initial theme to a new folder ( /myTheme). Then run following script to copy all config-parameters:


 * for shop versions 4.5.x: http://wiki.oxidforge.org/Snippet_collection/clone_theme_with_config_4-5


 * for shop versions 4.7.x : http://wiki.oxidforge.org/Snippet_collection/clone_theme_with_config_4-7_5-0

Template Structure
out/azure/tpl ├── custom ├── email │  ├── html │   │   ├── footer.tpl │   │   ├── forgotpwd.tpl │   │   ├── header.tpl │   │   ├── invite.tpl │   │   ├── newsletteroptin.tpl │   │   ├── order_cust.tpl │   │   ├── order_owner.tpl │   │   ├── owner_reminder.tpl │   │   ├── pricealarm_owner.tpl │   │   ├── register.tpl │   │   ├── senddownloadlinks.tpl │   │   ├── sendednow.tpl │   │   ├── suggest.tpl │   │   └── wishlist.tpl │   └── plain │       ├── forgotpwd.tpl │       ├── invite.tpl │       ├── newsletteroptin.tpl │       ├── order_cust.tpl │       ├── order_owner.tpl │       ├── pricealarm_owner.tpl │       ├── register.tpl │       ├── senddownloadlinks.tpl │       ├── sendednow.tpl │       ├── suggest.tpl │       └── wishlist.tpl ├── form │   ├── account_newsletter.tpl │   ├── contact.tpl │   ├── fieldset │   │   ├── order_newsletter.tpl │   │   ├── order_remark.tpl │   │   ├── salutation.tpl │   │   ├── state.tpl │   │   ├── user_account.tpl │   │   ├── user_billing.tpl │   │   ├── user_email.tpl │   │   ├── user_noaccount.tpl │   │   └── user_shipping.tpl │   ├── forgotpwd_change_pwd.tpl │   ├── forgotpwd_email.tpl │   ├── formparams.tpl │   ├── guestbook.tpl │   ├── login_account.tpl │   ├── login.tpl │   ├── newsletter.tpl │   ├── pricealarm.tpl │   ├── privatesales │   │   ├── accept_terms.tpl │   │   ├── basketexcl.tpl │   │   └── invite.tpl │   ├── recommendation_add.tpl │   ├── recommendation_edit.tpl │   ├── register.tpl │   ├── suggest.tpl │   ├── user_checkout_change.tpl │   ├── user_checkout_noregistration.tpl │   ├── user_checkout_registration.tpl │   ├── user_password.tpl │   ├── user.tpl │   ├── wishlist_publish.tpl │   ├── wishlist_search.tpl │   └── wishlist_suggest.tpl ├── layout │   ├── base.tpl │   ├── footer.tpl │   ├── header.tpl │   ├── page.tpl │   ├── popup.tpl │   └── sidebar.tpl ├── message │   ├── err_404.tpl │   ├── errors.tpl │   ├── error.tpl │   ├── err_setup.tpl │   ├── err_unknown.tpl │   ├── exception.tpl │   ├── inputvalidation.tpl │   ├── notice.tpl │   ├── success.tpl │   └── user_blocked.tpl ├── page │   ├── account │   │   ├── dashboard.tpl │   │   ├── downloads.tpl │   │   ├── forgotpwd.tpl │   │   ├── inc │   │   │   ├── account_menu.tpl │   │   │   └── file_attributes.tpl │   │   ├── login.tpl │   │   ├── newsletter.tpl │   │   ├── noticelist.tpl │   │   ├── order.tpl │   │   ├── password.tpl │   │   ├── recommendationadd.tpl │   │   ├── recommendationedit.tpl │   │   ├── recommendationlist.tpl │   │   ├── register_confirm.tpl │   │   ├── register_success.tpl │   │   ├── register.tpl │   │   ├── user.tpl │   │   └── wishlist.tpl │   ├── checkout │   │   ├── basket.tpl │   │   ├── inc │   │   │   ├── basketcontents.tpl │   │   │   ├── options.tpl │   │   │   ├── payment_other.tpl │   │   │   ├── payment_oxidcashondel.tpl │   │   │   ├── payment_oxidcreditcard.tpl │   │   │   ├── payment_oxiddebitnote.tpl │   │   │   ├── steps.tpl │   │   │   ├── trustedshops.tpl │   │   │   └── wrapping.tpl │   │   ├── order.tpl │   │   ├── payment.tpl │   │   ├── thankyou.tpl │   │   └── user.tpl │   ├── compare │   │   ├── compare.tpl │   │   └── inc │   │       └── compareitem.tpl │   ├── details │   │   ├── ajax │   │   │   ├── fullproductinfo.tpl │   │   │   └── productmain.tpl │   │   ├── details.tpl │   │   └── inc │   │       ├── attributes.tpl │   │       ├── compare_links.tpl │   │       ├── deliverytime.tpl │   │       ├── editTags.tpl │   │       ├── fullproductinfo.tpl │   │       ├── media.tpl │   │       ├── morepics.tpl │   │       ├── priceinfo.tpl │   │       ├── productmain.tpl │   │       ├── related_products.tpl │   │       ├── tabs.tpl │   │       ├── tags.tpl │   │       └── zoompopup.tpl │   ├── guestbook │   │   └── guestbook.tpl │   ├── info │   │   ├── contact.tpl │   │   ├── content_plain.tpl │   │   ├── content.tpl │   │   ├── links.tpl │   │   ├── newsletter.tpl │   │   ├── news.tpl │   │   └── suggest.tpl │   ├── list │   │   ├── list.tpl │   │   └── morecategories.tpl │   ├── privatesales │   │   ├── invite.tpl │   │   └── login.tpl │   ├── recommendations │   │   ├── inc │   │   │   └── list.tpl │   │   └── recommlist.tpl │   ├── review │   │   ├── review_login.tpl │   │   └── review.tpl │   ├── search │   │   └── search.tpl │   ├── shop │   │   ├── mallstart.tpl │   │   └── start.tpl │   ├── tags │   │   └── tags.tpl │   └── wishlist │       └── wishlist.tpl ├── rdfa │   ├── content │   │   ├── content.tpl │   │   └── inc │   │       ├── business_entity.tpl │   │       ├── delivery_charge.tpl │   │       └── payment_charge.tpl │   ├── details │   │   ├── details.tpl │   │   └── inc │   │       ├── delivery.tpl │   │       ├── object.tpl │   │       ├── payment.tpl │   │       └── price.tpl │   └── rdfa.tpl └── widget    ├── address    │   ├── billing_address.tpl    │   └── shipping_address.tpl    ├── breadcrumb.tpl    ├── dynscript.tpl    ├── facebook    │   ├── comments.tpl    │   ├── connect.tpl    │   ├── enable.tpl    │   ├── facepile.tpl    │   ├── init.tpl    │   ├── invite.tpl    │   ├── like.tpl    │   └── share.tpl    ├── footer    │   ├── categorieslist.tpl    │   ├── info.tpl    │   ├── manufacturers.tpl    │   ├── newsletter.tpl    │   ├── services.tpl    │   └── vendors.tpl    ├── header    │   ├── currencies.tpl    │   ├── languages.tpl    │   ├── loginbox.tpl    │   ├── search.tpl    │   ├── servicebox.tpl    │   └── topcategories.tpl    ├── locator    │   ├── attributes.tpl    │   ├── itemsperpage.tpl    │   ├── listdisplaytype.tpl    │   ├── listlocator.tpl    │   ├── paging.tpl    │   └── sort.tpl    ├── manufacturersslider.tpl    ├── minibasket    │   ├── countdown.tpl    │   ├── minibasketmodal.tpl    │   ├── minibasket.tpl    │   └── newbasketitemmsg.tpl    ├── product    │   ├── bargainitems.tpl    │   ├── boxproducts.tpl    │   ├── compare_links.tpl    │   ├── listitem_grid.tpl    │   ├── listitem_infogrid.tpl    │   ├── listitem_line.tpl    │   ├── list.tpl    │   └── selectbox.tpl    ├── promoslider.tpl    ├── reviews    │   ├── rating.tpl    │   └── reviews.tpl    ├── rss.tpl    ├── shoplupe    │   └── ratings.tpl    ├── sidebar    │   ├── adminbanner.tpl    │   ├── categoriestree.tpl    │   ├── news.tpl    │   ├── partners.tpl    │   ├── recommendation.tpl    │   └── tags.tpl    └── trustedshops        ├── info.tpl        └── ratings.tpl