Matt's Blog
Splash screen and launcher icons - sizes and structure, for Appcelerator Titanium project

Being new to developing with Titanium Studio, I came across a lack of (clear) documentation as the resource formats and structure of files required to develop a cross platform application (and for it to look great on each device!).

Both Android and iOS have different approaches as to where they expect resources to be located in the build of an application (after Titanium has converted each to their native language). Therefore, I hope this article will help others as a starting point.

To begin with we need to create the images (at the correct aspect ratios) for the application…

Creating the Images Required

To ensure your application is optimised for all platforms, it’s essential you have all of the image dimensions (or more importantly aspect ratios) listed below.

Note: those mentioned in the right column are simply resized versions of the larger counterpart, therefore creative isn’t needed for these - just a resize and file save!

Dimensions Orientation Aspect ratio Platform Used to also make these image sizes:

Splash Screens

2048x1496 Landscape 256:187 iPad retina 1024x748
1024x768 Landscape 4:3 Android/Mobile web 320x240
800x480 Landscape 5:3 Android 400x240
480x320 Landscape 3:2 Android  
1536x2008 Portrait 192:251 iPad retina 768x1004
768x1024 Portrait 3:4 Mobile web  
640x1136 Portrait 40:71 iOS iPhone 5 retina  
640x960 Portrait 2:3 iOS iPhone 4S/iPod/Android 320x480
480x800 Portrait 3:5 Android 240x400
 

App Launcher Icons

512x512 [none] Android 128x128  
512x512 [none] iOS 144x144, 114x114, 72x72, 57x57, 50x50, 29x29

Splash Screens

Now we’ve created our images, it’s time to place them into the corresponding resource folders, with the appropriate filenames needed by each platform. You can do this manually (copying folder by folder and resizing as you go), but I recommend using a script mentioned to the foot of this article for quick deployment.

Dimensions Orientation Filename Notes Image to resize from Destination folder

iOS

2048x1496* Landscape Default-Landscape@2x.png iPad retina [original] Resources/iphone/
1024x748 Landscape Default-Landscape.png iPad 2048x1496 Resources/iphone/
1536x2008* Portrait Default-Portrait@2x.png iPad retina [original] Resources/iphone/
768x1004 Portrait Default-Portrait.png iPad 1536x2008 Resources/iphone/
640x1136* Portrait Default-568h@2x.png iPhone 5 [original] Resources/iphone/
640x960 Portrait Default@2x.png iPhone 4S/iPod [original] Resources/iphone/
320x480 Portrait Default.png iPhone/iPod 640x960 Resources/iphone/
 

Android

800x480 Landscape default.png Large screen, high density, long screen [original] Resources/android/images/res-long-land-hdpi/
800x480 Landscape default.png Large screen high, density [original] Resources/android/images/res-notlong-land-hdpi/
480x320 Landscape default.png Medium screen, medium density [original] Resources/android/images/res-notlong-land-mdpi/
400x240 Landscape default.png Small screen, low density, long screen 800x480 Resources/android/images/res-long-land-ldpi/
320x240 Landscape default.png Small screen, low density 1024x768 Resources/android/images/res-notlong-land-ldpi/
480x800 Portrait default.png Large screen, high density, long screen [original] Resources/android/images/res-long-port-hdpi/
480x800 Portrait default.png Large screen high, density [original] Resources/android/images/res-notlong-port-hdpi/
320x480 Portrait default.png Medium screen, medium density 640x960 Resources/android/images/res-notlong-port-mdpi/
320x480 Portrait default.png Default fallback splash screen 640x960 Resources/android/
240x400 Portrait default.png Small screen, low density, long screen 480x800 Resources/android/images/res-long-port-ldpi/
240x400 Portrait default.png Small screen, low density 480x800 Resources/android/images/res-notlong-port-ldpi/
 

Mobile Web

1024x768 Landscape Place in splash folder Specified in CSS in splash folder [original] Resoures/mobileweb/splash/
768x1024 Portrait Place in splash folder Specified in CSS in splash folder [original] Resoures/mobileweb/splash/

Launcher Icons

Finally, it’s time for our application launcher icons. Again, simply place in the corresponding folder ensuring the correct filename specified.

Dimensions Orientation Filename Notes Image to resize from Destination folder

iOS

512x512 [none] appicon@512.png App Store icon 512x512 Resources/iphone/
144x144 [none] appicon-144.png iPad Retina 512x512 Resources/iphone/
114x114 [none] appicon@2x.png iPhone/iPod Retina icon 512x512 Resources/iphone/
72x72 [none] appicon-72.png Low res iPads 512x512 Resources/iphone/
57x57 [none] appicon.png Low res iPhones 512x512 Resources/iphone/
50x50 [none] appicon-Small-50.png Spotlight search for lower iPads 512x512 Resources/iphone/
29x29 [none] appicon-Small.png Spotlight search for lower iPhones 512x512 Resources/iphone/
 

Android

128x128 [none] default.png Android app icon 512x512 Resources/android/

Android launcher icons: I had issues with specifying Android specific app launcher icons (again little documentation!). However having the default as 128x128, this meant (or at least from testing) that the platform was able to resize to the correct dimensions for each. If anyone’s got any further information on Android launcher icons I’d love to add this information to the article.

Useful Resources

Automated Deployment of Images

If you’d like a quick tool to help you generate these images, you can visit Bob Sims Gist page, who’s created a great script to help you out.

Note: the above script is run via a shell command prompt, but does not yet support the high density images for the iPhone 5 and iPad retina. These would need to be manually added as per the table above (marked by an *).

External Links

Magento checkout slow? It’s down to caching

Have you experienced any significant slowdowns during the checkout process of your Magento store? Most noticeably when customers place an order during the final stage of the checkout process, or when you login to your admin control panel.

Having come across this problem many-a-time, I’ve decided to write an in-depth article about the causes of this slowdown and a work around to speeding up your checkout process on our work blog. 

Read more here…

Adding your custom module to Magento’s Admin Global Search

When creating a custom module in Magento, have you ever wanted to add custom search functionality to the Magento admin area, allowing users to search your module? Today I will attempt to show you in the most simple of terms how you can achieve this and add to Magento’s global search.

For this example I will be using a custom module called Matt_Car, a fictitious module that may contain information on a car and output on the store front. We will be adding functionality to search by car name or brand within Magento’s admin area global search box.

I will assume you’ve already created a custom module, as this will be the module you are searching, but your module declaration should look something like…

app/etc/modules/Matt_Car.xml:

<?xml version="1.0"?>
<config>
    <modules>
        <Matt_Car>
            <active>true</active>
            <codePool>local</codePool>
        </Matt_Car>
    </modules>
</config>  

Next in our module config.xml file we need to add a global search XML node, to tell Magento of our ‘new’ search logic!

You’ll also notice I’ve included a model declaration, this is only required if you haven’t already told Magento your custom module has model classes, and the appropiate way of declaring them (in this case ‘matt_car’).

app/code/local/Matt/Car/etc/config.xml:

<?xml version="1.0"?>
<config>
    <modules>
        <Matt_Car>
            <version>0.1.0</version>
        </Matt_Car>
    </modules>
	<global>
        <models>
			<matt_car>
				<class>Matt_Car_Model</class>
			</matt_car>
		</models>
		...
	</global>
	...
    <adminhtml>
		...
        <global_search>
            <matt_car_test>
                <class>matt_car/search_test</class>
                <acl>matt_car</acl>
            </matt_car_test>
        </global_search>
		...
    </adminhtml>
	...
</config>

In our config.xml file you’ll notice we are defining “matt_car_test”, this is a unique name for our search, and can be anything you like. The “class” declaration tells Magento where to handle our search request (in this case Matt_Car_Model_Search_Test). 

The final “acl” declaration stops users who don’t have admin acces control level (ACL) permissions to access the Matt Car module, the ability to search our module either! The value should be the XML node name defined in your ACL declaration. Again ACL’s are something you should already have setup in your module, so I won’t cover that here. 

Now, lets create our search model class…

/app/code/local/Matt/Car/Model/Search/Test.php:

<?php
class Matt_Car_Model_Search_Test extends Varien_Object
{
    /**
     * Load search results
     *
     * @return Matt_Car_Model_Search_Test
     */
    public function load() {
        $arr = array();
		$searchText = $this->getQuery();
        $collection = Mage::getModel('matt_car/car')->getCollection()
            ->addFieldToFilter(
                array('name', 'brand'),
                array(
                    array('like'=>'%'.$searchText.'%'), 
                    array('like'=>'%'.$searchText.'%')
                    )
                )
            ->load();

        foreach ($collection as $model) {
            $description = strip_tags($model->getDescription());
            $arr[] = array(
                'id'            => 'matt_car/test/'.$model->getId(),
                'type'          => Mage::helper('adminhtml')->__('Car'),
                'name'          => $model->getName(),
                'description'   => Mage::helper('core/string')->substr($description, 0, 30),
                'url' => Mage::helper('adminhtml')->getUrl('*/search_test/edit', array('id'=>$model->getId())),
            );
        }

        $this->setResults($arr);
        return $this;
    }
}

Here we can see we are loading our “car” model collection class (something your module should already have, a collection that is). We then apply a filter to the collection, to search by database field names “name” and “brand”, using the like condition, passing in our search query string. 

For the collection results returned, we then loop through each and assign various values to our output array:

  • ID: A unique ID for the search link (can be anything you wish)
  • Type: This is the type of search result - shown in red in search results block, e.g. [Product]
  • Name: The text shown in bold in search results block
  • Description: This is the text shown below the bold heading in search element, you’ll note it is defined in a variable first to strip any nasties from WYSIWYG editors etc.
  • URL: It’s worth noting the URL should be the URL to your normal Magento admin page, in my example the full url would be index.php/admin/search_test/edit/id/19/key/2dfce21795ceba425f020b7a4a671181/. Simply copy the first section after the “/admin/” section in the modules URL. My example would map to app/code/local/Matt/Search/controllers/Adminhtml/Search/TestController.php but this is going off topic. 

The results are then set against our object. 

Simples! You should now be able to see your custom search results! You can add as many global search nodes as you wish, and of course make use of the standard Magento Collection methods on your search models.

How it works

If you’re interested in how this then gets used (feel free to ignore); In app/code/core/Mage/Adminhtml/controllers/IndexController.php in the globalSearchAction() Magento loops through each of the global search nodes from the XML config and returns the results (calling getResults() incidentally), merging them into a single array.

If you experience any errors, the chances are there is an error in your script… turn on debugging, and check Chrome/Firefox for your Ajax request responses!

Hope this helps and let me know how you get on!

Adding a custom layout XML handle in Magento via a custom observer

Today I am to show you how you can add a custom layout XML handle to apply layout updates to your Magento store.

This feature may allow you to apply a custom designs or add additional content dependant on, for example, content from your system (such as a category/product data). This is possible by the use of Magento’s powerful event mechanism, where you can intercept at certain points of the page request process, using an ‘observer’, and apply your own logic.

To illustrate this, in the example below I will attempt to create a custom design for category pages that are at the top level (root category level) of our store. The design changes will not be applied to categories who sit under their parent categories. To keep it simple, this example merely outputs an additional html block onto the page, but illustrates how you can add and remove blocks, change the template of a page or in fact anything else possible in Magento layout XML files by default.

So let’s begin…

<?xml version="1.0"?>
<config>
    <modules>
        <Company_Module>
            <active>true</active>
            <codePool>local</codePool>
        </Company_Module>
    </modules>
</config>

This is the new module declaration file and tells Magento our module exists.

Next we need to declare a few configuration options. Create the file app/code/local/Company/Module/etc/config.xml: 

<?xml version="1.0"?>
<config>
    <modules>
        <Company_Module>
            <version>0.1.0</version>
        </Company_Module>
    </modules>
    <global>
        <blocks>
	    <!-- Defines our blocks -->
            <company_module>
                <class>Company_Module_Block</class>
            </company_module>
        </blocks>
        <events>
            <!-- Before layout is loaded -->
            <controller_action_layout_load_before>
                <observers>
                    <!-- Add category layer handle -->
                    <category_layer_layout_observer>
                        <type>singleton</type>
                        <class>Company_Module_Model_Observer</class>
                        <method>addCategoryLayerLayoutHandle</method>
                    </category_layer_layout_observer>
                </observers>
            </controller_action_layout_load_before>
        </events>
    </global>
</config>

Here you can see we’ve defined our modules version number and created our block declaration (so Magento knows where to look when we wish to create one).

The final part of the file tells Magento we wish to call the addCategoryLayerLayoutHandle() method in our Observer file (defined below), on the Magento “controller_action_layout_load_before” system event (this is a system handle that is called just before the layout is loaded, perfect for adding new content). The handle “category_layer_layout_observer” is simply a name for our observer and can be anything you wish.

Now lets create our Observer class, create app/code/local/Company/Module/Model/Observer.php:

<?php
class Company_Module_Model_Observer {    
    public function addCategoryLayerLayoutHandle(Varien_Event_Observer $observer) {
        $category = Mage::registry('current_category');
        $product = Mage::registry('current_product');
    
        if ($category && !$product) {
            if($category->getLevel() == 1) {
                /* @var $update Mage_Core_Model_Layout_Update */
                $update = $observer->getEvent()->getLayout()->getUpdate();
                $update->addHandle('catalog_category_level1');
            }
        }
    }
}

Here you can see we are passing into our observer the Varien_Event_Observer object, that allows us to access the current layout, controller action etc. This is passed in by default to any observer method (you can define your own inputs incidentally), so therefore all we need to do is declare it in our method, in order to access its properties.

My function simply checks we are on a category page and not a product page (hence the session check), and then checks whether the category is a level 1 category. If so, we get the updated layout from the passed in event to our function, and add an additional handle “catalog_category_level1”.

Next we now define what would like to happen with our updated layout. We will assume your current theme package is = “company” and your design theme = “elegant”.

Add to or create to the file app/design/frontend/company/elegant/layout/local.xml:

<?xml version="1.0"?>
<layout version="0.1.0">
	...OMITTED OTHER LOCAL.XML CONFIG...
	<!--
	Shop top category
	-->
    <catalog_category_level1>
        <reference name="content">
            <block type="company_module/test" name="company.module.test" template="company/module/test.phtml"/>
        </reference>
    </catalog_category_level1>
	...
</layout>

Here we can see the same layout handle defined in our PHP is used, and tells Magento to include our new custom block in the content of the page!

Next we create our block file, app/code/local/Company/Module/Block/Test.php:

<?php
class Company_Module_Block_Test extends Mage_Core_Block_Template {
    public function getHelloWorld() {
        return "Hello World!";
    }
}

Here we’ve just created a simple function, returning a test Hello World text string. 

Finally, we need to create our block template file (we defined that in the layout local.xml file) that is associated with the block class we’ve just created above.

Create app/design/frontend/company/elegant/template/company/module/test.phtml:

<div class="test">
	<?php echo $this->getHelloWorld() ?>
</div>

Phew! All being well you should now see a friendly message when navigating to a level 1 category in your Magento store. 

The possibilities are endless (and this example doesn’t even touch the iceberg) with event Observers in Magento (you can even create your own events - post coming soon), and provides a great away of extending functionality custom to you and your store.

I hope this helps, any questions let me know!

Magento canonical category URL paths for products - an in depth look into the mysteries of missing category paths

An in depth look into the mysteries of missing category URL paths in Magento

Are you integued by how Magento decides whether to output category URL paths in your Magento category pages? Here I will try to explain the inner workings of Magento, and how the system determines the URL that is output for a given product. I will also breifly explain a workaround (all be it a little of a hack) to offer a more consistent approach to full URLs throughout your website.

Before I begin, we will take the following category structure as an example throughout this post:

  • Cars (1)
    • Honda (2)
      • Product: Honda Jazz (1)
      • Estates (3)
        • Product: Honda Accord - Black (2)
        • Product: Honda Accord - Red (3)
      • Hatchbacks (4)
        • Product: Honda Civic (4)
    • Volvo (5)…
  • Vans…

All categories have been set as anchors (to show sub category products as well as their own) and their ID is given in brackets above.

Lets begin…. When navigating to a category page (for example Honda > Estates), when Magento processes the layered navigation, the following code is called along the way:

/app/code/core/Mage/Catalog/Model/Layer.php 

public function prepareProductCollection($collection)
{
	$collection
	    ->addAttributeToSelect(Mage::getSingleton('catalog/config')->getProductAttributes())
	    ->addMinimalPrice()
	    ->addFinalPrice()
	    ->addTaxPercents()
	    //->addStoreFilter()
	    ->addUrlRewrite($this->getCurrentCategory()->getId()); // HERE FILTER IS APPLIED

	Mage::getSingleton('catalog/product_status')->addVisibleFilterToCollection($collection);
	Mage::getSingleton('catalog/product_visibility')->addVisibleInCatalogFilterToCollection($collection);

	return $this;
}

Above we can see the ->addUrlRewrite() method is called, with the current category (of the page you’re viewing - ‘Honda > Estates’), passed into it.

If a product is assigned to the current category page you are viewing (such as the Honda Accord - Black), this is fine… The current category’s URL path is output, along with the product path.

However if the category you’re on (e.g. ‘Honda’ - root category) is an anchor and shows products in lower sub categories too (Estates, Hatchbacks), this is where the problem occurs… 

In this situation when the various sub category products (such as the Accord and Civic) are output, as they are not directly assigned to the Honda root category, only the product URL path is output. The only exception to this in our example would be the Honda Jazz (ID 1) as that product is assigned to the Honda root category, and therefore matches.

So why does Magento do this… Magento has been designed this way as it has no way of knowing what sub category the other products are assigned to at a higher cartegory level. Magento also allows for the fact that any product can be assigned to multiple categories (and hence it wouldn’t know which category path to output in that case), therefore it cannot simply assume it’s assigned to one category and output its path.

Let’s delve further into the code and find out why this is so… 

When the collection is prepared by the Layered navigation (above), the addUrlRewrite() method stores the category passed into it (the current page category id), in our case  ’Honda’ - 2:

/app/code/core/Mage/Catalog/Model/Resource/Eav/Mysql4/Product/Collection.php

public function addUrlRewrite($categoryId = '')
{
	$this->_addUrlRewrite = true;
	if (Mage::getStoreConfig(Mage_Catalog_Helper_Product::XML_PATH_PRODUCT_URL_USE_CATEGORY, $this->getStoreId())) {
	    $this->_urlRewriteCategory = $categoryId;
	} else {
	    $this->_urlRewriteCategory = 0;
	}

	if ($this->isLoaded()) {
	    $this->_addUrlRewrite();
	}

	return $this;
}

As we can see this simply sets the category ID (e.g. 2) passed into the function, providing we have category URLs enabled in Magento. This then in turn calls _addUrlRewrite():

/app/code/core/Mage/Catalog/Model/Resource/Eav/Mysql4/Product/Collection.php 

protected function _addUrlRewrite()
{
	$urlRewrites = null;
	if ($this->_cacheConf) {
	    if (!($urlRewrites = Mage::app()->loadCache($this->_cacheConf['prefix'].'urlrewrite'))) {
		$urlRewrites = null;
	    } else {
		$urlRewrites = unserialize($urlRewrites);
	    }
	}

	if (!$urlRewrites) {
	    $productIds = array();
	    foreach($this->getItems() as $item) {
		$productIds[] = $item->getEntityId();
	    }
	    if (!count($productIds)) {
		return;
	    }

	    $select = $this->getConnection()->select()
		->from($this->getTable('core/url_rewrite'), array('product_id', 'request_path'))
		->where('store_id=?', Mage::app()->getStore()->getId())
		->where('is_system=?', 1)
		->where('category_id=? OR category_id is NULL', $this->_urlRewriteCategory) // HERE ASSIGNED
		->where('product_id IN(?)', $productIds)
		->order('category_id DESC'); // more priority is data with category id
	    $urlRewrites = array();

	    foreach ($this->getConnection()->fetchAll($select) as $row) {
		if (!isset($urlRewrites[$row['product_id']])) {
		    $urlRewrites[$row['product_id']] = $row['request_path'];
		}
	    }

	    ........
}

Here we can see (commented: “// HERE ASSIGNED”), that a filter is applied to the SQL where statement, using the ID we have just set above (2).

As we’re on the ‘Honda’ root category page, the above code generates an SQL statement similar too…

SELECT `core_url_rewrite`.`product_id`, `core_url_rewrite`.`request_path` FROM `core_url_rewrite` WHERE (store_id='1') AND (is_system=1) AND (category_id='2' OR category_id is NULL) AND (product_id IN('1', '2', '3', '4')) ORDER BY `category_id` DESC 

Here you note that the category id is 2 (for Honda). Therefore in the core_url_rewrite table it first checks for product ID 1,2,3 and 4 whether they are assigned to category id 2. If nothing is found, it simply outputs the product URL with no category prefix (category = NULL). The only one that would include a category path is product 1, as this is assigned to category 1 (Honda). Running the SQL on your system we give you a good idea as to what it is doing.

If we then navigated to a category page that product 2 and 3 were assigned to (say category id 3 - Honda > Estates), you would get something like…

SELECT `core_url_rewrite`.`product_id`, `core_url_rewrite`.`request_path` FROM `core_url_rewrite` WHERE (store_id='1') AND (is_system=1) AND (category_id='3' OR category_id is NULL) AND (product_id IN('2', '3')) ORDER BY `category_id` DESC 

As product 2 and 3 are assigned to category 3, the core_url_rewrite table will now output the category path aswell as the product path. And no problems! For reference, Product 1 (Honda Jazz) and 4 (Honda Civic) are no longer shown as these are assigned to the category above Estates.

Here you can now spot the problem we mentioned previously should you like full URL paths…

As product 2 and 3 are assigned to a sub category of Honda (Estates), when you’re viewing its parent category (Honda), as the products aren’t assigned to category 2 directly (as it’s assigned to 3), it defaults to using the product URL without the category prefix. Therefore as the category ‘Honda’ is an anchor, the products are shown with the non canonical category URL. This is because there was no match in the core_url_rewrite table for category 2 with product 1,2,3 or 4.

Removing the where condition (commented: “// HERE ASSIGNED”) solves the problem to an extent (all be it a hack). This is because the SQL is ordered by category id, so will therefore output the full path first, before the non prefixed URL. This providing the parent category was created before the child.

The only problem to this (and the reason Magento rightly have chosen to do it this way) is if the product is assigned to multiple categories, it will output the first in the database (the last category the product was assigned to), and this cannot be preempted, therefore should be used with caution.

If the condition above is removed, it would mean should a product be assigned to two categories (say Honda Estates and Hatchbacks), no matter which category you were viewing the URL would be the same, and not represent the category path you are viewing. E.g. on the estates category the URL would read /honda/estates/honda-accord-red and on the hatchbacks category it would also read /honda/estates/honda-accord-red - far from ideal!

This however is not a problem should you be happy with this weakness in single category URLs, or if you know for sure you’ll only be assigning them to one category each!

I hope this explination helps and would be great to hear if anyone else has come across this problem, or indeed has any other solutions!

Thanks for reading

An introduction to the Magento theming system

For those getting to grips with how Magento interprets its theming definitions, it can be a confusing playing field, therefore I have attempted to briefly explain some of the common pitfalls and issues you should be aware of when developing with Magento.

Theming is a generic term in Magento to encapsulate template files (such as the product page), styling (CSS, images, JavaScrpt), locale (translation files) and XML layout updates (block definitions and more). 

Below will will go through an example of creating a custom theme in Magento. We will use the package name of “matt” and a theme name of “glossy” for this example.

A “package” can contain multiple themes, therefore many companies create a “package” consisting of their company name and within this, create several “themes”, which may be applicable to certain websites, countries or campaigns.

Custom theme

In my example I will assume that you have created the following folders for your theme changes (here we can see the package is called “matt”, and the theme called “glossy”)….

  • app/design/frontend/matt/glossy/
  • skin/frontend/matt/glossy/

First Magento looks here for any template files (E.g. catalog/product/list.phtml - the product listing template), in addition to any images, JavaScript and locale updates rquested by the system.

Files should only be created or modified here if they need to be changed from it’s original state, more will become clear later.

If the template or file cannot be found in this theme, it moves on to the…

Default theme

  • app/design/frontend/matt/default/
  • skin/frontend/matt/default/

Here Magento falls back the check for a “default” folder in the newly created package. Magento always looks for a default folder in whichever package it resides, therefore it is important to always create one should you have a new package.

Tip: Should your website eventually consist of multiple themes, it is a good idea to modify or place new templates here in your “package”s default theme. This means all child theme’s will inherit it’s templates by default (as the name suggests, unless they are overridden), and leaves you to only modify theme specific themes in the first stage (above). Should you then need to update say the product view template you only need to change one file, rather than copy into all of your themes!

When creating new templates (for example with a new custom module), if the template is only package specific, you may wish to place the template file here, so it can be made use of in child themes. 

Should the template or file not be found in the default theme, Magento moves to it’s final stage, the…

Base theme

  • app/design/frontend/base/default/

These are the core theming files of Magento, do not modify these. Here contains all of the layout and template files necessary to support core Magento functionality. 

It’s worth noting however, the base skin theme doesn’t contain all images and CSS, as these are theme based.

When creating new templates (for example with a new custom module), if the template is a site wide change (say the module was going to be used across all your websites), base default would be an ideal location to place them. If you perform a Magento upgrade your template files will remain unchanged due to their unique folder structure. 

A different approach if using Enterprise

Here you may now see a floor with Enterprise and the changes of it’s templates over the core functionality offered from the base default theme (and there are many changes due to increased functionality)… 

As Magento Enterprise theming is done in an “enterprise” folder, this means should you create your own package instead of the “enterprise” folder, your site will loose Enterprise theme functionality, as in the inheritance order, the “enterprise” folder is ignored.

To combat this problem, if you are creating a custom package (such as “matt”, in the example above), you will need to copy the contents of the Enterprise folder into your new theme (almost always in a newly created “default” folder), not ideal, but a solution none the less.

Difference with local.xml

It’s worth noting that Magento doesn’t amalgamate your local.xml files along the way, therefore if you had a local.xml file in your custom theme (“matt”) and in default (“default”), you will need to make the same changes in both. This doesn’t affect base, as there is no local.xml file in the base structure (catalog.xml, catalogsearch.xml etc.). 

Difference with styles.css

As above, if styles.css if found in your custom theme (“matt”) and in default, it will use the first (“matt”), therefore if you wish to inherit CSS from your “default” theme it is advisable to define a new CSS file in your custom theme, eliminating the need to copy the CSS.

Recommendations

In your new themes, always put XML changes (such as block removals, additions, template changes) in local.xml, this keeps them in a central place and easy to maintain.

You should never need or, or want to, modify the base default theme, these are core files and should be left intact. Failure to do so may mean an upgrade would remove your changes or create a nightmare when debugging.

To summarise, packages allow you to essentially have a company theme, with various themes inside of it for sites you may have, for example that have a different look and feel. The way this works in Magento is great and offers a quick and easy way of running multiple sites which share similar functionality and style.

I hope this helps those new to Magento or wishing to find out more :)

Adding UK postcode form validation to Magento

Out of the box Magento doesn’t have the ability to, on the client side (using JS), validate UK postcodes (for example in the Checkout process). Therefore this article aims to show a workaround to achieve this.

Rather than some solutions, this doesn’t require you to edit each template file you have a postcode field in! All you need to do once the process below has been implemented, is add a specific CSS class to the input field… simples!

There is one caveat, it does modify a core Magento file! Perhaps make a back-up of this file for now, or if you’re using version control - no worries. The file being modified file is: /js/prototype/validation.js (which uses the Really Easy Field Validation library for those interested).

Open /js/prototype/validation.js and add the following lines:
Go to line 414 and you’ll notice the function “Validation.addAllThese”, just paste in the same way you see the others are formatted. 

['validate-postcode', 'Please enter a valid postcode', function(v) { return Validation.get('IsEmpty').test(v) || /(^[A-Z]{1,2}[0-9R][0-9A-Z]?[\s]?[0-9][ABD-HJLNP-UW-Z]{2}$)/i.test(v); }],

Then whenever you wish to use the validation, add a class of “validate-postcode” to your form text field. E.g:

<input type="text" name="postcode" value="" title="<?php echo $this->__('Postcode') ?>" class="input-text validate-postcode required-entry" id="postcode" />

Simple as that! Enjoy!