Category Archives: Uncategorized

Reports Primer

Reports in Magento provide Administrator views of transaction data recorded by the system.

When viewing reports the data can sometimes be outdated, and return no data. Which is terribly confusing especially when you know you have data, but also can be very misleading if the data reported is inaccurate but you do not realise. For this reason it is recommended that whenever using the reports that first go to Administrator -> Reports -> Refresh Statistics. Once on that page define the reports you need or simply Select All and then from the dropdown choose to Refresh Lifetime Statistics, although apply some level of caution here especially in a production environment, in which a large amount of data may be processed.

Adminhtml – An Overview of addColumn

When creating or manipulating an Administrator grid it may be necessary to introduce a new column into the grid. Let’s consider this situation.

The grid page will be an extension of the Mage_Adminhtml_Block_Sales_Order_Grid class, and will be defined similar to the following:

class {Namespace}_{Module}_Block_Adminhtml_{Module}_Grid extends Mage_Adminhtml_Block_Sales_Order_Grid

Having defined the class, it will be necessary to overwrite the class protected function _prepareColumns(), and this will be the function that will hold the definition of the new addColumn method.

    protected function _prepareColumns() {
        $this->addColumn('{column_name}', array(
            'header' => Mage::helper('{module}')->__('{Column Title}'),
            'index' => '{column_name}',
            'type' => 'datetime',
            'format' => 'dd-MMMM-yyyy'
        ));
        return parent::_prepareColumns();
    }

Options and their Definitions:
header – This is the title that will be displayed at the top of the column.

index

type – Data type related to the data to be displayed in the column, based not he DB column from which it is extracted;
boolean, smallint, integer, bigint, float, numeric, decimal, date, timestamp, date time, text, var, binary

format – If type allows formatting option, then use this as to define the format. E.g. for a type of datetime, it is posible to use ‘format’ => ‘dd-MMMM-yyyy’ to output the datetime value according to that format.

width – Is a definition of the column width on the grid page in pixels.

renderer – This is used to link the column to a function and will take the structure of

new {Namespace}_{Module}_Block_Adminhtml_{Title}()

. This will then use the renderer to determine the correct value.

options

Admin – Remove Button From Container Page

When using the admin pages to display functionality the containers often include buttons, and sometimes those buttons are simply more than is needed.

In the grid container page, this is identified by the fact that the page contains the following after the class definition: extends Mage_Adminhtml_Block_Widget_Grid_Container. Personally, I created a page that had a class definition of the following

class {Namespace}_{Module}_Block_Adminhtml_{Module} extends Mage_Adminhtml_Block_Widget_Grid_Container

Based on the classname this will define the file location as app/code/local/{Namespace}/{Module}/Block/Adminhtml/{Module}.php

To remove the button, an overwrite of the __construct is necessary. Consider the following

    public function __construct() {
        $this->_controller = 'adminhtml_{module}';
        $this->_blockGroup = '{module}';
        $this->_headerText = Mage::helper('{module}')->__('{header text}');
        parent::__construct();
        $this->_removeButton('add');
    }

This worked well on the grid container, but not all containers contain a button with the name ‘add’. The great thing is that you should be able to swap this out for the name of the button on the container page that you working on.

Consider a page which extends Mage_Adminhtml_Block_Widget_Form_Container, for example a page of the following class definition

class {Namespace}_{Module}_Block_Adminhtml_{Module}_Edit extends Mage_Adminhtml_Block_Widget_Form_Container

The contents may look similar to:

    public function __construct() {
        $this->_objectId = 'id';
        $this->_blockGroup = '{module}';
        $this->_controller = 'adminhtml_{module}';
        parent::__construct();
        $this->_removeButton('save');

In order to identify the button names I went to the class that is being extended, in this case Mage_Adminhtml_Block_Widget_Form_Container. The file location for this is app/code/core/Mage/Adminhtml/Block/Widget/Form/Container.php. By considering the public function __construct(), the following code is visible:

    public function __construct()
    {
        parent::__construct();

        if (!$this->hasData('template')) {
            $this->setTemplate('widget/form/container.phtml');
        }

        $this->_addButton('back', array(
            'label'     => Mage::helper('adminhtml')->__('Back'),
            'onclick'   => 'setLocation(\'' . $this->getBackUrl() . '\')',
            'class'     => 'back',
        ), -1);
        $this->_addButton('reset', array(
            'label'     => Mage::helper('adminhtml')->__('Reset'),
            'onclick'   => 'setLocation(window.location.href)',
        ), -1);

        $objId = $this->getRequest()->getParam($this->_objectId);

        if (! empty($objId)) {
            $this->_addButton('delete', array(
                'label'     => Mage::helper('adminhtml')->__('Delete'),
                'class'     => 'delete',
                'onclick'   => 'deleteConfirm(\''. Mage::helper('adminhtml')->__('Are you sure you want to do this?')
                    .'\', \'' . $this->getDeleteUrl() . '\')',
            ));
        }

        $this->_addButton('save', array(
            'label'     => Mage::helper('adminhtml')->__('Save'),
            'onclick'   => 'editForm.submit();',
            'class'     => 'save',
        ), 1);
    }

This shows the buttons that will be continued by this container, and the name to use to remove it.

Creating A Frontend Page**

The idea here is to create a new page and then provide a link to that page in the template menu.

Firstly page creation. This is primarily achieved thought the backend administration.

So Admin -> CMS -> Pages

Admin-CMS-Pages Menu Definition

Then create a new page with the Add New Page button

Admin-CMS-Add New Page Button

Creating the Menu Entry

Once the page has been created, it can be helpful for the page to be present on the menu of the site so that visitors can click through to that page.

This can be achieved by following the steps.
Go to Admin -> Catalog -> Manage Categories

Admin-Catalog-Manage Categories Menu Option

Click on the Add Subcategory button displayed in the top left of the page

Admin-Catalog-Manage Categories-Add Subcategory

Complete the form

Admin-Catalog-Manage Categories-Add Subcategory Form

Note that the key fields to complete the process
Name – Admin page name for easy identification
Is Active – Set to Yes
URL Key – Title that makes up the page URL

Save the category.

Associate the Page and the Menu Item (Subcategory)

It is then required to link these two elements to each other. Go to:
Admin -> Catalog -> URL Rewrite Management

Admin-Catalog-URL Rewrite Management

In the grid select the category entry that you made, this will be identified by the Request Path value equalling the value defined in the URL Key specified in the subcategory definition above.


This will take you to a screen similar to

Admin-Catalog-URL Rewrite Management-Grid Selection-Open

Copy and store the Target Path value, in the above screenshot this value is catalog/category/view/id/48

Change the catalog/category/view/id/48 to Permanent (301)

Admin-Catalog-URL Rewrite Management-Grid Selection-Modified

Save this and when returned to the URL Rewrite Management grid, click on the Add URL Rewrite button

Admin-Catalog-URL Rewrite Management-Add URL Rewrite

On the new page that opens click on the Create URL Rewrite dropdown list

Admin-Catalog-Add URL Rewrite-Create URL Rewrite

Select Custom from the list. This will update the lower portion of the page with a range of fields that require definition

Admin-Catalog-Add URL Rewrite-Create URL Rewrite-Custom

Type – Custom, defined from the dropdown choice made previously
Store – Depends on how many stores exist in installation. This example contains an English and Spanish store.
ID Path – Enter the URL Key used when creating the page in the initial step.
Request Path – Use the stored Target Path value that was defined earlier.
Target Path – Same as ID Path (defined above).
Redirect – Permanent (301).

Click the save button.

Reload the home page, and the new menu item should now be visible and this should click through to the page that has been created.

Resources:
http://www.magentocommerce.com/knowledge-base/entry/add-a-new-page

http://www.magentocommerce.com/knowledge-base/entry/adding-page-links-in-the-navigation-bar

Setting a Default Shipping Method**

To set a default shipping method you will need to modify a range of files in the one page checkout process.

Here are the steps to consider:

** THIS NEEDS MORE EXPLORATION OUTSIDE OF OF THE SITUATION DEFINED HERE **
app/code/local/{Namespace}/{Module}/controllers/{Module}Controller.php

<?php

require_once 'Mage/Checkout/controllers/OnepageController.php';

class {Namespace}_OnepageController extends Mage_Checkout_OnepageController {

    public function saveDeliverytimesAction() {
        if ($this->_expireAjax()) {
            return;
        }

        if ($this->getRequest()->isPost()) {
            $data = $this->getRequest()->getPost('deliverytimes', array());

            $result = $this->getOnepage()->saveDeliverytimes($data);

            if (!isset($result['error'])) {
                $result['goto_section'] = 'payment';
                $result['update_section'] = array(
                    'name' => 'payment-method',
                    'html' => $this->_getPaymentMethodsHtml()
                );
            }
        }
        $this->getOnepage()->getQuote()->collectTotals()->save();
        $this->getResponse()->setBody(Mage::helper('core')->jsonEncode($result));
    }

    public function saveBillingAction() {
        if ($this->_expireAjax()) {
            return;
        }
        if ($this->getRequest()->isPost()) {
            $data = $this->getRequest()->getPost('billing', array());
            $customerAddressId = $this->getRequest()->getPost('billing_address_id', false);

            $method = 'jardinorganico_jardinorganico';
            $this->getOnepage()->saveShippingMethod($method);

            if (isset($data['email'])) {
                $data['email'] = trim($data['email']);
            }
            $result = $this->getOnepage()->saveBilling($data, $customerAddressId);

            if (!isset($result['error'])) {
                /* check quote for virtual */
                if ($this->getOnepage()->getQuote()->isVirtual()) {
                    $result['goto_section'] = 'payment';
                    $result['update_section'] = array(
                        'name' => 'payment-method',
                        'html' => $this->_getPaymentMethodsHtml()
                    );
                } elseif (isset($data['use_for_shipping']) && $data['use_for_shipping'] == 1) {
                    $result['goto_section'] = 'deliverytimes';

                    $result['allow_sections'] = array('shipping');
                    $result['duplicateBillingInfo'] = 'true';
                } else {
                    $result['goto_section'] = 'shipping';
                }
            }
            $this->getOnepage()->getQuote()->collectTotals()->save();
            $this->getResponse()->setBody(Mage::helper('core')->jsonEncode($result));
        }
    }

    public function saveShippingAction() {
        if ($this->_expireAjax()) {
            return;
        }
        if ($this->getRequest()->isPost()) {
            $data = $this->getRequest()->getPost('shipping', array());
            $customerAddressId = $this->getRequest()->getPost('shipping_address_id', false);
            $result = $this->getOnepage()->saveShipping($data, $customerAddressId);


            if (!isset($result['error'])) {
                $method = 'jardinorganico_jardinorganico';
                $result = $this->getOnepage()->saveShippingMethod($method); 

                if (!isset($result['error'])) {
                    $result['goto_section'] = 'deliverytimes'; //Go to our step
                }
            }

            $this->getOnepage()->getQuote()->collectTotals()->save();
            $this->getResponse()->setBody(Mage::helper('core')->jsonEncode($result));
        }
    }
}

The key lines to consider here are:

            $method = 'freeshipping_freeshipping';
            $this->getOnepage()->saveShippingMethod($method);

The method variable would be changed to the method of choice given that Free Shipping is not desired.

Controllers – Backend Introduction

Controlling the flow of a Magento store using the controller mechanism has some subtle distinctions in the backend than in the frontend, and while not incredibly difficult to comprehend, is easier explained rather than trying to discover alone.

An area that often implements controller use is the definition of menus. Presented here are two ways to define the same thing using a config.xml and an adminhtml.xml file, both of these would be located in the /app/code/{code pool – local/community}/{Namespace}/{Module}/etc/ folder, if creating a new module.

    <admin>
        <routers>
            <adminhtml>
                <args>
                    <modules>
                        <{module} before="Mage_Adminhtml">{Namespace}_{Module}_Adminhtml</{module}>
                    </modules>
                </args>
            </adminhtml>
        </routers>
    </admin>

An associated menu definition that will reference this controller definition could be seen in an adminhtml.xml file:

<?xml version="1.0" encoding="ISO-8859-1"?>
<config>
    <menu>
        <googlerouting translate="title" module="googlerouting">
            <title>Google Routing</title>
            <sort_order>15</sort_order>
            <children>
                <items module="googlerouting">
                    <title>Get Next Delivery Routes</title>
                    <action>adminhtml/googlerouting</action>
                </items>
            </children>
        </googlerouting>
    </menu>
</config>

The main aspect to consider here is the line adminhtml/googlerouting as this specifies the URL string that will be translated by the config.xml file using the tags & <{module} before="Mage_Adminhtml">.

Alternatively, it is possible to see the following syntax:

    <admin>
        <routers>
            <{module}>
                <use>admin</use>
                <args>
                    <module>{Namespace}_{Module}</module>
                    <frontName>{module}</frontName>
                </args>
            </{module}>
        </routers>
    </admin>

The associated adminhtml.xml menu definition will be:

<?xml version="1.0" encoding="ISO-8859-1"?>
<config>
    <menu>
        <{module} translate="title" module="{module}">
            <title>Menu Title</title>
            <sort_order>15</sort_order>
            <children>
                <items module="{module}">
                    <title>Item menu child</title>
                    <action>{module}/adminhtml_{module}</action>
                </items>
            </children>
        </{module}>
    </menu>
</config>

The difference in the config.xml definition is that has been replaced with <{module}> and that a frontname has been specified. So the action call has a slightly different flavour here {module}/adminhtml_{module}.

Both of these ways of defining the location will call the default index action of the controller located at app/code/{code pool – local/community}/{Namespace}/{Module}/controllers/Adminhtml/{Module}Controller.php. This file would have a structure similar, but in no way limited to:

<?php
class {Namespace}_{Module}_{Module}Controller extends Mage_Adminhtml_Controller_Action {
    public function indexAction() {
        $this->loadLayout();
        $this->renderLayout();
        return $this;
    }
}

I wrote this due to the fact that I have had the hardest time trying to get to grips with controllers. One of the ways that I’d recommend testing that your controller works is by adding a first line in the action function that consists of die (‘{classname} {functionname}’);. So in the basic example shown above this would look like:

<?php
class {Namespace}_{Module}_{Module}Controller extends Mage_Adminhtml_Controller_Action {
    public function indexAction() {
        die ('{Namespace}_{Module}_{Module}Controller indexAction');
        $this->loadLayout();
        $this->renderLayout();
        return $this;
    }
}

This will terminate operation of the site and output the details of the terminating function. Obviously this is not to be encouraged as a part of debugging on a production live site, and for that I would recommend the use of Mage::log to get feedback on a site which will not affect system operation.

Adding An Action To Administrator Grid Dropdown

Follow the steps outlined below to add an additional massAction option to the dropdown list displayed on an Administrator grid layout page.

Administrator -> Grid -> MassActions Dropdown

Recreate the following folders, pages and content:

/app/code/etc/modules/{Namespace}_{Module}.xml
Content:

<?xml version="1.0" encoding="ISO-8859-1"?>
<config>
    <modules>
        <{Namespace}_{Module}>
            <active>true</active>
            <codePool>local</codePool>
        </{Namespace}_{Module}>
    </modules>
</config>

/app/code/local/{Namespace}/{Module}/etc/config.xml
Content:

<?xml version="1.0"?>
<config>
    <modules>
        <{Namespace}_{Module}>
            <version>0.1.0</version>
        </{Namespace}_{Module}>
    </modules>
    <admin>
        <routers>
            <adminhtml>
                <args>
                    <modules>
                        <{module} before="Mage_Adminhtml">{Namespace}_{Module}_Adminhtml</{module}>
                    </modules>
                </args>
            </adminhtml>
        </routers>
    </admin>
    <global>
        <helpers>
            <{module}>
                <class>{Namespace}_{Module}_Helper</class>
            </{module}>
        </helpers>
    </global>
</config>

Consider the following section of the xml file

    <admin>
        <routers>
            <adminhtml>
                <args>
                    <modules>
                        <{module} before="Mage_Adminhtml">{Namespace}_{Module}_Adminhtml</{module}>
                    </modules>
                </args>
            </adminhtml>
        </routers>
    </admin>

This defines that the controller that will be used in the admin area of the site will be found at <{module}>. So the URL will read {domain}/admin(front name)/{module}(controller)/{action}

/app/code/local/{Namespace}/{Module}/etc/adminhtml.xml
Content:

<?xml version="1.0"?>
<config>
    <menu>
        <{module} translate="title" module="{module}">
            <title>{module} Menu Item</title>
            <sort_order>15</sort_order>
            <children>
                <{module}_adminform module="{module}">
                    <title>the most important menu child</title>
                    <action>adminhtml/{module}/index</action>
                </{module}_adminform>
            </children>
        </{module}>
    </menu>
</config>

/app/code/local/{Namespace}/{Module}/Helper/Data.php
Content:

<?php
class {Namespace}_{Module}_Helper_Data extends Mage_Core_Helper_Abstract {

}

/app/code/local/{Namespace}/{Module}/Block/Adminhtml/Grid.php
Content:

<?php

class {Namespace}_{Module}_Block_Adminhtml_{Module}_Grid extends Mage_Adminhtml_Block_Widget_Grid
{

    public function __construct()
    {
        parent::__construct();
        $this->setId('sales_order_grid');
        $this->setUseAjax(true);
        $this->setDefaultSort('created_at');
        $this->setDefaultDir('DESC');
        $this->setSaveParametersInSession(true);
    }

    /**
     * Retrieve collection class
     *
     * @return string
     */
    protected function _getCollectionClass()
    {
        return 'sales/order_grid_collection';
    }

    protected function _prepareCollection()
    {
        $collection = Mage::getResourceModel($this->_getCollectionClass());
        $this->setCollection($collection);
        return parent::_prepareCollection();
    }

    protected function _prepareColumns()
    {


        $this->addColumn('real_order_id', array(
            'header'=> Mage::helper('sales')->__('Order #'),
            'width' => '80px',
            'type'  => 'text',
            'index' => 'increment_id',
        ));

        if (!Mage::app()->isSingleStoreMode()) {
            $this->addColumn('store_id', array(
                'header'    => Mage::helper('sales')->__('Purchased From (Store)'),
                'index'     => 'store_id',
                'type'      => 'store',
                'store_view'=> true,
                'display_deleted' => true,
            ));
        }

        $this->addColumn('created_at', array(
            'header' => Mage::helper('sales')->__('Purchased On'),
            'index' => 'created_at',
            'type' => 'datetime',
            'width' => '100px',
        ));

        $this->addColumn('billing_name', array(
            'header' => Mage::helper('sales')->__('Bill to Name'),
            'index' => 'billing_name',
        ));

        $this->addColumn('shipping_name', array(
            'header' => Mage::helper('sales')->__('Ship to Name'),
            'index' => 'shipping_name',
        ));

        $this->addColumn('base_grand_total', array(
            'header' => Mage::helper('sales')->__('G.T. (Base)'),
            'index' => 'base_grand_total',
            'type'  => 'currency',
            'currency' => 'base_currency_code',
        ));

        $this->addColumn('grand_total', array(
            'header' => Mage::helper('sales')->__('G.T. (Purchased)'),
            'index' => 'grand_total',
            'type'  => 'currency',
            'currency' => 'order_currency_code',
        ));

        $this->addColumn('status', array(
            'header' => Mage::helper('sales')->__('Status'),
            'index' => 'status',
            'type'  => 'options',
            'width' => '70px',
            'options' => Mage::getSingleton('sales/order_config')->getStatuses(),
        ));

        if (Mage::getSingleton('admin/session')->isAllowed('sales/order/actions/view')) {
            $this->addColumn('action',
                array(
                    'header'    => Mage::helper('sales')->__('Action'),
                    'width'     => '50px',
                    'type'      => 'action',
                    'getter'     => 'getId',
                    'actions'   => array(
                        array(
                            'caption' => Mage::helper('sales')->__('View'),
                            'url'     => array('base'=>'*/sales_order/view'),
                            'field'   => 'order_id'
                        )
                    ),
                    'filter'    => false,
                    'sortable'  => false,
                    'index'     => 'stores',
                    'is_system' => true,
            ));
        }
        $this->addRssList('rss/order/new', Mage::helper('sales')->__('New Order RSS'));

        $this->addExportType('*/*/exportCsv', Mage::helper('sales')->__('CSV'));
        $this->addExportType('*/*/exportExcel', Mage::helper('sales')->__('Excel XML'));

        return parent::_prepareColumns();
    }

    protected function _prepareMassaction() {
        $this->setMassactionIdField('entity_id');
        $this->getMassactionBlock()->setFormFieldName('order_ids');
        $this->getMassactionBlock()->setUseSelectAll(false);

        $this->getMassactionBlock()->addItem('{massActionName}', array(
            'label' => Mage::helper('{module}')->__('label'),
            'url' => $this->getUrl('adminhtml/{module}', array('' => '')),
            'confirm' => Mage::helper('{module}')->__('Are you sure?')
        ));

        if (Mage::getSingleton('admin/session')->isAllowed('sales/order/actions/cancel')) {
            $this->getMassactionBlock()->addItem('cancel_order', array(
                'label' => Mage::helper('sales')->__('Cancel'),
                'url' => $this->getUrl('*/sales_order/massCancel'),
            ));
        }

        if (Mage::getSingleton('admin/session')->isAllowed('sales/order/actions/hold')) {
            $this->getMassactionBlock()->addItem('hold_order', array(
                'label' => Mage::helper('sales')->__('Hold'),
                'url' => $this->getUrl('*/sales_order/massHold'),
            ));
        }

        if (Mage::getSingleton('admin/session')->isAllowed('sales/order/actions/unhold')) {
            $this->getMassactionBlock()->addItem('unhold_order', array(
                'label' => Mage::helper('sales')->__('Unhold'),
                'url' => $this->getUrl('*/sales_order/massUnhold'),
            ));
        }

        $this->getMassactionBlock()->addItem('pdfinvoices_order', array(
            'label' => Mage::helper('sales')->__('Print Invoices'),
            'url' => $this->getUrl('*/sales_order/pdfinvoices'),
        ));

        $this->getMassactionBlock()->addItem('pdfshipments_order', array(
            'label' => Mage::helper('sales')->__('Print Packingslips'),
            'url' => $this->getUrl('*/sales_order/pdfshipments'),
        ));

        $this->getMassactionBlock()->addItem('pdfcreditmemos_order', array(
            'label' => Mage::helper('sales')->__('Print Credit Memos'),
            'url' => $this->getUrl('*/sales_order/pdfcreditmemos'),
        ));

        $this->getMassactionBlock()->addItem('pdfdocs_order', array(
            'label' => Mage::helper('sales')->__('Print All'),
            'url' => $this->getUrl('*/sales_order/pdfdocs'),
        ));

        $this->getMassactionBlock()->addItem('print_shipping_label', array(
            'label' => Mage::helper('sales')->__('Print Shipping Labels'),
            'url' => $this->getUrl('*/sales_order_shipment/massPrintShippingLabel'),
        ));

        return $this;
    }

    public function getRowUrl($row)
    {
        if (Mage::getSingleton('admin/session')->isAllowed('sales/order/actions/view')) {
            return $this->getUrl('*/sales_order/view', array('order_id' => $row->getId()));
        }
        return false;
    }

    public function getGridUrl()
    {
        return $this->getUrl('*/*/grid', array('_current'=>true));
    }

}

The key to adding additional elements to the grid drop down is through the use of protected function _prepareMassaction() within the Grid.php file.

The line $this->getMassactionBlock()->setFormFieldName(‘order_ids’); will call the massAction specified passing the order_ids values as a parameter.

So, it can be observed that the massAction is specified as:

        $this->getMassactionBlock()->addItem('{massActionName}', array(
            'label' => Mage::helper('{module}')->__('label'),
            'url' => $this->getUrl('adminhtml/{module}', array('' => '')),
            'confirm' => Mage::helper('{module}')->__('Are you sure?')
        ));

with a URL routing value defined as

$this->getUrl('adminhtml/{module}', array('' => '')),

which basically specifies that the controller, will call the action of the name {massActionName} defined in the code.

A look of and example controller:

<?php

class Webholism_Googlerouting_Adminhtml_GoogleroutingController extends Mage_Adminhtml_Controller_Action {

    protected function _initAction()
    {
        $this->loadLayout()
            ->_setActiveMenu('googlerouting/items')
            ->_addBreadcrumb(Mage::helper('adminhtml')->__('Items Manager'), Mage::helper('adminhtml')->__('Item Manager'));
        return $this;
    }
   
    public function indexAction() {
        $this->_initAction();       
        $this->_addContent($this->getLayout()->createBlock('{module}/adminhtml_{module}'));
        $this->renderLayout();
    }    
    
    public function {massActionName} () {
        $orderIds = $this->getRequest()->getParam('order_ids');

        foreach ($orderIds as $orderId) {
            echo ('<br />');
            var_dump ($orderId);
            echo ('<br />');
        }
        $this->_initAction();
        $this->_addContent($this->getLayout()->createBlock('googlerouting/adminhtml_massgroutes'));
        $this->renderLayout();
    }
}

Resources:
http://www.bubblecode.net/en/2012/02/08/magento-create-your-own-admin-controller-in-a-new-tab/
http://www.andreiboar.com/magento/magento-adminhtml-make-your-controller-work-under-the-admin-route/

Controllers – Frontend Introduction

In order to get an understanding on how controllers operate, here is a simple module with some accompanying explanations on what is going on.

Create the following folder and file structure and insert the content as defined below:

app/code/etc/modules/{Namespace}_{Module].xml

<?xml version="1.0" encoding="ISO-8859-1"?>
<config>
    <modules>
        <{Namespace}_{Module}>
            <active>true</active>
            <codePool>local</codePool>
        </{Namespace}_{Module}>
    </modules>
</config>

app/code/local/{Namespace}/{Module}/etc/config.xml

<?xml version="1.0" encoding="ISO-8859-1"?>
<config>
    <modules>
        <{Namespace}_{Module}>
            <version>0.1.0</version>
        </{Namespace}_{Module}>
    </modules>
    
    <frontend>
        <routers>
            <{module} module="{module}">
                <use>standard</use>
                <args>
                    <module>{Namespace}_{Module}</module>
                    <frontName>{module}</frontName>
                </args>
            </{module}>
        </routers>
    </frontend>
</config>

Note that the convention is for the frontname to mirror the module name, although this does not have to be the case.

app/code/local/{Namespace}/{Module}/controllers/IndexController.php

<?php
class {Namespace}_{Module}_IndexController extends Mage_Core_Controller_Front_Action {
    public function indexAction () {
        $this->loadLayout();
        $this->renderLayout();
    }
}

To access the routing that has just been created it is necessary to enter into the browser address bar the frontname that was defined in the config.xml file. So enter {domain, e.g. localhost/magento or www.magentoexample.com/}/{frontname}.

The result will show an empty page, as you are routing to content that does not exist.

Resources:
Packt Publishing – Magento PHP Developer’s Guide
http://alanstorm.com/magento_controller_hello_world
http://alanstorm.com/magento_dispatch_standard_router

Installing Magento with Sample Data

It can be tedious to create products and customers in an attempt to test Magento functionality, and this is where the nice developers have provided some assistance.

Download the version of Magento that you require, and download the relevant version of the sample data from http://www.magentocommerce.com/download.

Create a database and user, and make note of these details.

Copy the uncompressed Magento install files into the appropriate directory, based on the system that is being worked with. See our other articles on setting up Magento based on whether this is a local or remote installation. Also, consideration may need to be made in reference to changing file ownership and permissions.

Expand the compressed sample data file, and copy the folder into the Magento media folder. Then run the SQL file on the Magento DB, this will install all the Magento tables without any prefix. The SQL will need to be altered manually if a prefix is required.

Install Magento as through the usual process, and when the install has completed it will be seen that products are available in the shop.

Resources:
http://www.magentocommerce.com/knowledge-base/entry/ce18-and-ee113-installing#install-sample
http://www.magentocommerce.com/download

Transferring a Remote Site To Local

Export the DB
Copy the files

Reproduce the DB locally
Move the folders/files into the htdocs directory (change file permissions, my experience suggests that changing to 777 is the most suitable from a functionality standpoint, and as the transfer is to a local system this should rarely introduce security issues).

Modify the app/etc/local.xml file replacing the details related to the DB connection (if this is different)

                <connection>
                    <host><![CDATA[localhost/127.0.0.1]]></host>
                    <username><![CDATA[{username}]]></username>
                    <password><![CDATA[{password}]]></password>
                    <dbname><![CDATA[{dbname}]]></dbname>
                    <initStatements><![CDATA[SET NAMES utf8]]></initStatements>
                    <model><![CDATA[mysql4]]></model>
                    <type><![CDATA[pdo_mysql]]></type>
                    <pdoType><![CDATA[1]]></pdoType>
                    <active>1</active>
                </connection>

DB tables:
core_config_data
-> web/unsecure/base_url = {local_server_URI, e.g. localhost/magento or 127.0.0.1/magento}
-> web/secure/base_url = {local_server_URI, e.g. localhost/magento or 127.0.0.1/magento}

That appears to do the trick. 🙂