3
votes

I have created a custom Magento module which extends the core sales order functionality with some custom user input. After an order has been placed I would like to display this data in a custom tab on the order detail page of the admin area. I have managed to get the new tab displaying in the tab list however when I click on the tab it gives me a 404.

Here's my code:

app/code/local/Zac/Attack/etc/config.xml:

<?xml version="1.0" encoding="UTF-8"?>
<config>
    <modules>
        <Zac_Attack>
            <version>0.1.0</version>
        </Zac_Attack>
    </modules>
    <admin>
        <routers>
            <adminhtml>
                <args>
                    <modules>
                        <!-- Override Adminhtml module here. -->
                        <Zac_Attack_Adminhtml before="Mage_Adminhtml">Zac_Attack_Adminhtml</Zac_Attack_Adminhtml>
                    </modules>
                </args>
            </adminhtml>
        </routers>
    </admin>
    <adminhtml>
        <layout>
            <updates>
                <attack>
                    <file>attack.xml</file>
                </attack>
            </updates>
        </layout>
    </adminhtml>
    <global>
        <blocks>
            <attack>
                <class>Zac_Attack_Block</class>
            </attack>
        </blocks>
    <!-- models, resources, etc -->
    </global>
</config>

app/code/local/Zac/Attack/Block/Adminhtml/Sales/Order/View/Tab/Attack.php:

<?php

class Zac_Attack_Block_Adminhtml_Sales_Order_View_Tab_Design extends Mage_Adminhtml_Block_Template
    implements Mage_Adminhtml_Block_Widget_Tab_Interface
{
    protected function _construct()
    {
        parent::_construct();
        $this->setTemplate( 'attack/sales/order/view/tab/attack.phtml' );
    }

    public function getTabLabel()
    {
        return $this->__( 'Attack' );
    }

    public function getTabTitle()
    {
        return $this->__( 'Attack' );
    }

    public function getTabClass()
    {
        return '';
    }

    public function getClass()
    {
        return $this->getTabClass();
    }

    public function getTabUrl()
    {
        // Here the url gets rewritten to my custom name, throws 404 when called...
        // The url takes the form:
        // http://mydomain.com/admin/sales_order/attack/order_id/1/key/65cbb0c2956dd9413570a2ec8761bef5/
        return $this->getUrl('*/*/attack', array('_current' => true));
    }

    public function canShowTab()
    {
        return true;
    }

    public function isHidden()
    {
        return false;
    }

    public function getOrder()
    {
        return Mage::registry( 'current_order' );
    }
}

app/code/local/Zac/Attack/controllers/Adminhtml/Sales/OrderController.php:

<?php

require_once "Mage/Adminhtml/controllers/Sales/OrderController.php";

class Zac_Attack_Adminhtml_Sales_OrderController extends Mage_Adminhtml_Sales_OrderController
{
    public function viewAction()
    {
        // This doesn't get called when viewing the default order detail page.
        // I should see the <h1> output as the only content on the page but I don't.
        die( '<h1>viewAction()</h1>' );
    }

    public function attackAction()
    {
        // This should be called when the url has the pattern '*/*/attack' (as it does
        // when displaying my custom tab) however clicking this tab gives a 404.
        die('<h1>attackAction()</h1>');
    }
}

app/design/adminhtml/default/default/layout/attack.xml

<?xml version="1.0" encoding="UTF-8"?>
<layout>
    <adminhtml_sales_order_view>
        <reference name="sales_order_tabs">
            <action method="addTab">
                <name>order_design_details</name>
                <block>attack/adminhtml_sales_order_view_tab_design</block>
            </action>
        </reference>
    </adminhtml_sales_order_view>
</layout>

What appears to be failing is the controller override. Neither the overriden method "viewAction()" nor the custom action "attackAction()" get called. I can tell that the config is being picked up because when I print "Mage::getConfig()->getNode('admin/routers/adminhtml')" I can see the following output:

Mage_Core_Model_Config_Element Object
(
    [args] => Mage_Core_Model_Config_Element Object
        (
            [module] => Mage_Adminhtml
            [modules] => Mage_Core_Model_Config_Element Object
                (
                    [Mage_Index] => Mage_Index_Adminhtml
                    [Mage_Paygate] => Mage_Paygate_Adminhtml
                    [Mage_Paypal] => Mage_Paypal_Adminhtml
                    [widget] => Mage_Widget_Adminhtml
                    [Mage_GoogleOptimizer] => Mage_GoogleOptimizer_Adminhtml
                    [Mage_GoogleBase] => Mage_GoogleBase_Adminhtml
                    [Mage_Authorizenet] => Mage_Authorizenet_Adminhtml
                    [Mage_Bundle] => Mage_Bundle_Adminhtml
                    [Mage_Centinel] => Mage_Centinel_Adminhtml
                    [Mage_Compiler] => Mage_Compiler_Adminhtml
                    [connect] => Mage_Connect_Adminhtml
                    [Mage_Downloadable] => Mage_Downloadable_Adminhtml
                    [importexport] => Mage_ImportExport_Adminhtml
                    [Mage_PageCache] => Mage_PageCache_Adminhtml
                    [xmlconnect] => Mage_XmlConnect_Adminhtml
                    [EM_DeleteOrder_Adminhtml] => EM_DeleteOrder_Adminhtml
                    [find_feed] => Find_Feed_Adminhtml
                    [moneybookers] => Phoenix_Moneybookers
                    [Zac_Attack_Adminhtml] => Zac_Attack_Adminhtml
                )

            [frontName] => admin
        )

    [use] => admin
)

So, my first question is: Am I following the correct approach for adding a custom tab to the page?

If I am not following the correct approach can you please advise me what the correct approach is or provide a link which clearly outlines the whole approach (there are too many answer fragments when searching for Magento information, not enough whole answers).

If I am following the correct approach, why is my controller not overriding?

Well, I hope that I've provided enough detail to make the problem clear. If not, feel free to post follow up questions in the comments and I'll be happy to elaborate - if I know how.

Thanks in advance for any help offered.

Cheers, Zac

P.S. I noticed that there is another module in the community section overriding the same controller - however that override doesn't appear to be taking effect either. Regardless, I have completely removed the 3rd party module for the purposes of debugging to ensure that there is no interference.

1
what is the url that gives the 404?OSdave
I don't have access to the app where I am now, but off the top of my head it is (this may not be exactly right but it captures the issue): mydomain.com/admin/sales_order/attack/order_id/1/key/[key_value]. The built-in order detail url is mydomain.com/admin/sales_order/view/order_id/1/key/[key_value]. Note the difference is after '/sales_order/'.Zac Seth
it looks ok to me, I don't understand why the 404. The cache is disabled right? If you want to, upload the module somewhere and I'll give a look in a local installation.OSdave
Wow, I just figured out what the problem is. It turns there was another module overriding OrderController. If you look in the config element output you'll see "EM_DeleteOrder_Adminhtml" which is a community module in the project. Now I have to figure out how to get the inheritance tree right - whether the other two modules are essential or not and what to override or exclude them. This must be a pain for 3rd party module devs - it must be common for two community modules to override the same class. I wonder if there's a good design pattern to work around the lack of multiple inheritance.Zac Seth
Anyway, thanks for your time Dave. Hopefully someone can use my question as an example of how it should work (not that there aren't other good resources on the subject) and be aware of the potential hidden issue that I suffered from here.Zac Seth

1 Answers

1
votes

Well, this isn't the first time the solution wasn't what I was expecting it to be. As I mentioned in reply to OSDave's comments above, my module code was written exactly as it should be - the problem was with another module overriding the same controller.

For future reference, if you think you've got your controller override done right (admin or frontend - should be the same either way) but it isn't working, I highly recommend using "Mage::getConfig()->getNode('admin/routers/adminhtml')" to debug. Just make sure the XPath in the getNode method is appropriate for the module you're overriding and then look for any entries that aren't clearly Magento.

Hopefully this will save some others the hours I wasted on the problem.

Cheers, Zac