It is sometimes required to add buttons at the top of admin forms. The easy-but-not-so-good solution is to make a rewrite. The easy-and-good-trick elegant solution is to use an observer. Here is an example for the product edit page.


The trick in a nutshell

The idea is to:

  • Create a container of core/text_list type
  • Inject the content of the original Magento "Save and Continue Edit" button
  • Inject the content of our new button
  • Remplace the content of the original Magento "Save and Continue Edit" button by this container
  • The block of the "Save and Continue Edit" button will then be a core/text_list block containing both the original button and our new one

Making things happen

As a pre-requisit, a module must exist (in our example, this module is Herve_ProductEditButton) with:

  • Its declaration file (etc/modules/Herve_ProductEditButton.xml)
  • Its configuration file (app/code/community/Herve/Herve_ProductEditButton/etc/config.xml)
  • An admin controller (app/code/community/Herve/Herve_ProductEditButton/controllers/Adminhtml/ButtonController.php)
  • A Helper in order to manage translations (app/code/community/Herve/Herve_ProductEditButton/Helper/Data.php). Translations have not been done for this example

An observer (app/code/community/Herve/Herve_ProductEditButton/Model/Observer.php)

We will then observe the controller_action_layout_render_before_adminhtml_catalog_product_edit event for the adminthml area. The goal is to retrieve the layout right before it is planed to be rendered as HTML and inject our modifications.

The observer is declared like this:

<config>  
    ...
    <adminhtml>
        <events>
            <controller_action_layout_render_before_adminhtml_catalog_product_edit>
                <observers>
                    <herve_producteditbutton_add_button>
                        <class>Herve_ProductEditButton_Model_Observer</class>
                        <method>addButton</method>
                    </herve_producteditbutton_add_button>
                </observers>
            </controller_action_layout_render_before_adminhtml_catalog_product_edit>
        </events>
    </adminhtml>
    ...
</config>  

We must then implement the observer. Here is its commented code:

class Herve_ProductEditButton_Model_Observer  
{
    /**
     * Add a new button next to the existing "Save and Continue Edit" button
     *
     * @return void
     */
    public function addButton()
    {
        // Retrieve layout
        $layout = Mage::app()->getLayout();

        // Retrieve product_edit block
        $productEditBlock = $layout->getBlock('product_edit');

        // Retrieve original "Save and Continue Edit" button
        $saveAndContinueButton = $productEditBlock->getChild('save_and_edit_button');

        // Create new button
        $myButton = $layout->createBlock('adminhtml/widget_button')
            ->setData(array(
                'label'     => Mage::helper('producteditbutton')->__('My Button Label'),
                'onclick'   => 'setLocation(\'' . $this->getButtonUrl() . '\')',
                'class'  => 'save'
            ));

        // Create a container that will gather existing "Save and Continue Edit" button and the new button
        $container = $layout->createBlock('core/text_list', 'button_container');

        // Append existing "Save and Continue Edit" button and the new button to the container
        $container->append($saveAndContinueButton);
        $container->append($myButton);

        // Replace the existing "Save and Continue Edit" button with our container
        $productEditBlock->setChild('save_and_edit_button', $container);
    }

    /**
     * Retrieve the URL for button click
     *
     * @return string
     */
    public function getButtonUrl()
    {
        // The URL called fits to the controller of our module: Herve_ProductEditButton_Adminhtml_ButtonController
        return Mage::getModel('adminhtml/url')->getUrl('*/button/myButton', array(
            '_current'   => true,
            'back'       => 'edit',
            'tab'        => '{{tab_id}}',
            'active_tab' => null
        ));
    }
}

We will then code the controller that is in charge of our new button's behavior. As we have generated an URL in the previous observer with the code Mage::getModel('adminhtml/url')->getUrl('*/button/myButton'), the controller is then Herve_ProductEditButton_Adminhtml_ButtonController and its action method is myButtonAction()

Here is the commented code of the controller:

class Herve_ProductEditButton_Adminhtml_ButtonController extends Mage_Adminhtml_Controller_Action  
{
    /**
     * Behavior of our new button when it is clicked
     */
    public function myButtonAction()
    {
        // Retrieve product id from which the button has been clicked
        $productId = $this->getRequest()->getParam('id');

        /**
         *
         * All custom controller logic goes here
         *
         */

        $this->_getSession()->addSuccess($this->__('Congratulations, you clicked on a button!'));

        // Redirect to product edit page
        $this->_redirect('*/catalog_product/edit', array(
            'id'    => $productId,
            '_current'=>true
        ));
    }
}

Results

As expected, there is a new button labeled "My Button Label" next to the "Save and Continue Edit" original button.

New button

For other forms

The process is the same. What may be difficult is:

  • to retrieve the "edit" block that embeds the button to by modified with the container (product_edit in our example)
  • to retrieve the block name of the button to by modified with the container (save_and_edit_button in our example)

Example module

An example module is available here: https://github.com/herveguetin/Herve_ProductEditButton