I'm using the clever cms module to manage some basic cms pages. Now I want to add some custom behaviour to one of the routes the clever cms is using. An Example:
example.com/cms/page // <- a normale cms page
example.com/cms/category // <- another module should do it's stuff here
When I make a new module, I can only override the whole cms route, and not plug myself in the cms/category only.
Is there a way to do this?
If you are creating a new controller, this is easy; just make use of the native ability to map multiple controller directories to a frontname. In your module config:
<frontend>
<routers>
<cms>
<args>
<modules>
<your_module after="Mage_Cms">Your_Module</your_module>
</modules>
</args>
</cms>
</routers>
This will allow requests to map to Your/Module/controllers/CategoryController.php.
Related
I'm currently working on a custom module for Magento. I understand the basics of Packages, Modules and Routers, and I have built the front end part of my module.
However I am now moving on to the admin side of things. However I'm a little bit confused by how I add the admin part to my routers and get it to call the relevant controller.
Let's imagine I have created these routers...
<frontend>
<routers>
<slider>
<use>standard</use>
<args>
<module>Mypackage_Myodule</module>
<frontName>Mymodule</frontName>
</args>
</slider>
</routers>
</frontend>
<admin>
<routers>
<mymoduleadmin>
<use>admin</use>
<args>
<module>Mypackage_Myodule</module>
<frontName>Mymodule</frontName>
</args>
</mymoduleadmin>
</routers>
</admin>
I presume that both these routers will attempt to call controllers/IndexController.php and therefore the same functionality? Is it possible to set things up so my routers call different controllers depending on whether they are front end or admin? Is this even possible or do I need to set up a front end module and an admin module?
I apologise if this is a School Boy question, but this has me a bit confused and in reality I just want to know the most efficient way to set up a custom module with front end and admin functionality.
Depending upon the area(frontend or adminhtml), frontend or adminhtml router are dispatched.
So you need not need to worry about getting it messed up as long as you are using different controller files for frontend and adminhtml, frontend controller extending from Mage_Core_Controller_Front_Action & adminhtml extending from Mage_Adminhtml_Controller_Action.
Frontend / Adminhtml routers can be defined as (just a syntax):
<frontend>
<routers>
<[module]>
<use>standard</use>
<args>
<module>[Namespace]_[Module]</module>
<frontName>[module]</frontName>
</args>
</[module]>
</routers>
</frontend>
<admin>
<routers>
<[module]>
<use>admin</use>
<args>
<module>[Namespace]_[Module]</module>
<frontName>[module]</frontName>
</args>
</[module]>
</routers>
</admin>
And you can create frontend controllers under: app/code/[codePool]/[Namespace]/[Module]/controllers/
For example:
<?php
//file: app/code/local/MagePsycho/Testmodule/controllers/IndexController.php
class MagePsycho_Testmodule_IndexController extends Mage_Core_Controller_Front_Action
{
public function indexAction(){
}
}
In order to access it from url: http://your-magento-url/testmodule/index/index
and adminhtml controllers under:
app/code/[codePool]/[Namespace]/[Module]/controllers/Adminhtml/
For example:
<?php
//file: app/code/local/MagePsycho/Testmodule/controllers/Adminhtml/IndexController.php
class MagePsycho_Testmodule_Adminhtml_IndexController extends Mage_Adminhtml_Controller_Action
{
public function indexAction(){
}
}
In order to access it from url: http://your-magento-url/testmodule/adminhtml_index/index
(You can see the Adminhtml folder for separating adminhtml controllers)
Hope this gave you some info.
Thanks
Have a look at my similar question: Admin route in custom modules
I also would recommend using
<admin>
<routers>
<adminhtml>
<args>
<modules>
<modulename before="Mage_Adminhtml">Namespace_Module_Adminhtml</modulename>
</modules>
</args>
</adminhtml>
</routers>
</admin>
This will allow you to avoid using adminhtml part in the routes, so your module backend url will have simple and clean url like core modules e.g. admin/mymodule
I discovered an issue with Magento routing logic and I would like to see if anyone can confirm this.
Magento stacks routers admin, standard, then default and processes them one at a time. Magento gets the current module name based on the URL (see Mage_Core_Controller_Varien_Router_Standard::match()), then checks if the module should be handled by this router, based on a match to a frontName in the Magento config. If a match is found, it routes it. If not, it continues to the next router.
Config excerpt:
<admin>
<routers>
<myroute>
<use>admin</use>
<args>
<module>MyNamespace_MyModule</module>
<frontName>myroute</frontName>
</args>
</myroute>
</routers>
</admin>
<frontend>
<routers>
<myroute>
<use>admin</use>
<args>
<module>MyNamespace_MyModule</module>
<frontName>myroute</frontName>
</args>
</myroute>
</routers>
</frontend>
This means that if you use the same name for your frontend router as your admin router, the admin router will always be matched first, even on frontend pages. Your frontend page will now route as is if were an admin page, using the admin base_url, which may be different from your store's URL causing a broken redirect.
Note that this issue is not apparent in Magento instances where the admin base URL is the same as the frontend base URL.
Can anyone confirm that my assesment of the router logic is correct here?
You may want to look over Varien/Router/Standard.php as well in particular:
/**
* checking if this admin if yes then we don't use this router
*
* #return bool
*/
protected function _beforeModuleMatch()
{
if (Mage::app()->getStore()->isAdmin()) {
return false;
}
return true;
}
And this is called within the method match(Zend_Controller_Request_Http $request) as well as collectRoutes($configArea, $useRouterName) as $useRouterName will sometimes return admin and will also return standard for frontend requests. The assumption sounds correct as it all depends on how magento builds and stacks the _routes and _modules private array in this same class: Mage_Core_Controller_Varien_Router_Standard.
I believe in this case you would want to specify the <use> node as standard for frontend and admin for admin, or rewrite the controller action in the <global> node.
I think your best bet is to read over:
http://alanstorm.com/magento_dispatch_admin_cms_default_routers
and/or step through the logic with X-debug.
Even Alan Storm in his article writes how the same routers used for frontend and backend shouldn't be the same.
So it looks like this method is here to ensure the Standard router object bails if, for some reason, the store model object thinks its in admin mode. Like the Standard/Admin router relationship, the store object is another one of those things that points to certain parts of the Magento development process being focused on the frontend application first, and then tacking on an admin console later and having to back-port changes.
The store object is a model that really only applies to the frontend/cart application. However, because so much code in Magento assumes the store object exists, it needs to be available to the admin console application. This, in turn, creates trouble at the router level, which is what leads to a check like this. Many layers of abstraction, no defined contracts between classes/modules, and the fear of refactoring created by the lack of tests will always lead to these sorts of situations.
This is not a Magento bug, but is something to be aware of when writing modules or working with third party code. I have clarified the issue and resolution here. Essentially, the existing adminhtml route should always be used rather than creating new admin routes. This makes urls in the admin consistent and avoids conflicts. Thank you Alan and Jared for helping me understand Magento routing better.
Just to throw my 2 cents into this; I certainly noticed this issue tonight! I'm building a custom module and my config.xml routers are defined like so:
<admin>
<routers>
<namespace_module>
<use>admin</use>
<args>
<module>Namespace_Module</module>
<frontName>namespace_module</frontName>
</args>
</namespace_module>
</routers>
</admin>
<frontend>
<routers>
<namespace_module>
<use>standard</use>
<args>
<module>Namespace_Module</module>
<frontName>namespace_module</frontName>
</args>
</namespace_module>
</routers>
</frontend>
I was getting a 404 error on the frontend while the backend routers worked just fine. I changed the frontend name and voila:
<admin>
<routers>
<namespace_module>
<use>admin</use>
<args>
<module>Namespace_Module</module>
<frontName>namespace_module</frontName>
</args>
</namespace_module>
</routers>
</admin>
<frontend>
<routers>
<namespace_module>
<use>standard</use>
<args>
<module>Namespace_Module</module>
<frontName>namespace_module_front</frontName>
</args>
</namespace_module>
</routers>
</frontend>
Makes sense to use a unique name I guess!
I want to create an admin controller - without having any menu items associated with it.
Ideally I want to have my controller accessible via /index.php/admin/my_controller/.
So far I have rewritten the adminhtml controller as follows but i simnply get a 404 inside the admin console (i.e. not the main 404 page):
<admin>
<routers>
<my_module>
<use>admin</use>
<args>
<module>Me_Mymodule</module>
<frontName>my_controller</frontName>
</args>
</my_module>
<adminhtml>
<args>
<modules>
<my_module after="Mage_Adminhtml">Me_Mymodule</my_module>
</modules>
</args>
</adminhtml>
</routers>
</admin>
Your current config technique has been obsolete since version 1.4. Instead it is more convenient to structure it like this.
<admin>
<routers>
<adminhtml>
<args>
<modules>
<my_module before="Mage_Adminhtml">Me_Mymodule_Adminhtml</my_module>
</modules>
</args>
</adminhtml>
</routers>
</admin>
Then to get the /index.php/admin/mymodule/ path create the class Me_Mymodule_Adminhtml_MymoduleController extends Mage_Adminhtml_Controller_Action in Me/Mymodule/controllers/Adminhtml/MymoduleController.php. In your example you used an underscore in the controller name, be careful of that as it will be used as a directory separator when searching for the correct class.
Remember to generate URLs for your controller like Mage::getUrl('adminhtml/mymodule') so that it adds the secret key to paths, this is necessary when making an admin controller or it will refuse the page.
If there are no menu items then it will not be possible to add them to the ACL. You do not need an adminhtml.xml file in this case.
For you goal xml config is redundant.
Use following to add your controller to /admin frontname
Company_Module_Adminhtml
Now every controller that will be created in controller/Adminhtml folder will accessed through admin like
/admin/yourfilename/index
The class name of controller should be Module_Module_Adminhtml_YourfilenameController and should extend Mage_Adminhtml_Controller_Action
That's the trick.
Even if you do not add your controller to menu, you still have to add acl section for your controller adminhtml.xml. Do not forget to relog to admin after that.
I've encountered a problem in our Magento installations that I've been having a really hard time tracking down. Whenever a customer goes to the "My Account" page and clicks the "Address Book" link, it sends them to the default 404 CMS page. I searched around Google and found a few similar problems, but they weren't quite the same. However, one solution that was offered was that a custom login redirect module was the cause.
So I started disabling all of our custom modules one by one to see if that would fix the problem, and it turned out that disabling our custom Account Controller fixes the problem. I tried to track this down further by using Mage::log() in the overridden methods to see if they were getting called when trying to access /customer/address/, but nothing showed up in the logs.
The only lead I have left is that it's a problem with my controller configuration. This is the config.xml I have set up:
<?xml version="1.0"?>
<config>
<modules>
<mymodule_login>
<version>0.1.0</version>
</mymodule_login>
</modules>
<frontend>
<routers>
<mymodule_login>
<use>standard</use>
<args>
<module>MyModule_Login</module>
<frontName>customer</frontName>
</args>
</mymodule_login>
</routers>
</frontend>
<global>
<rewrite>
<mymodule_login>
<from><![CDATA[#^/account/#]]></from>
<to>/customer/account/</to>
</mymodule_login>
</rewrite>
<blocks>
<customer>
<rewrite>
<register-login>MyModule_Login_Block_View</register-login>
</rewrite>
</customer>
<login>
<class>RegisterLogin</class>
</login>
</blocks>
</global>
</config>
I think it might be a problem with the rewrite from #^/account/# to /customer/account/, but I don't know enough about Magento rewrites to determine if that's accurate or not. The methods I have overwritten are: loginPostAction, _loginPostRedirect, and _welcomeCustomer.
Dick Laurent was right - it was my configuration. All I did was add a second controller that overrode Mage_Customer_AddressController and now it works fine. The problem was that my frontend name was "customer" so it was expecting to find the corresponding controller in my custom module for when it went to the URL /customer/address/.
I had to override Mage_Customer_AccountController, however you can't simply just override just one controller, you have to override the whole module.
So here is how you solve in this situation. You create files that reference the parent.
NOTE: Replace {Namespace} with your custom namespace.
Here is my config.xml: (inside the folder app/code/local/{Namespace}/Customer/etc)
<?xml version="1.0"?>
<config>
<modules>
<{Namespace}_Customer>
<version>0.1.0</version>
</{Namespace}_Customer>
</modules>
<frontend>
<routers>
<customer>
<use>standard</use>
<args>
<module before="Mage_Customer">{Namespace}_Customer</module>
<frontName>customer</frontName>
</args>
</customer>
</routers>
</frontend>
</config>
Now overriding Mage_Customer_AccountController: (inside the folder app/code/local/{Namespace}/Customer/controllers)
<?php
require_once ('Mage/Customer/controllers/AccountController.php');
class {Namespace}_Customer_AccountController extends Mage_Customer_AccountController
{
protected function _loginPostRedirect()
{
$session = $this->_getSession();
if($session->getBeforeAuthUrl() == Mage::getUrl('checkout/onepage/index')){
$session->setBeforeAuthUrl(Mage::getUrl('checkout/cart'));
$this->_redirectUrl($session->getBeforeAuthUrl(true));
return;
}
return parent::_loginPostRedirect();
}
}
This redirects the customer back to their cart page after they login though checkout. We did this, because if there is an item in their cart from a previous session, Magento will combine the recent cart items with the ones from the previous session and proceed to payment without telling the customer. So to avoid customer complaints we simply redirect back to cart so the customer can see that this happens.
Now because we are overriding the entire module, we have to create the aforementioned files that reference the parent. The two other controllers under Mage_Customer are Mage_Customer_AddressController and Mage_Customer_ReviewController. Your Magento instance may vary as versions change, so make sure you check the parent folder, located at (app/code/core/Mage/Customer/controllers), and NEVER DIRECTLY EDIT CORE!
Here we reference the parent for Mage_Customer_AddressController: (inside the folder app/code/local/{Namespace}/Customer/controllers)
<?php
require_once ('Mage/Customer/controllers/AddressController.php');
class {Namespace}_Customer_AddressController extends Mage_Customer_AddressController
{
}
Likewise for Mage_Customer_ReviewController: (same file location)
<?php
require_once ('Mage/Customer/controllers/ReviewController.php');
class {Namespace}_Customer_ReviewController extends Mage_Customer_ReviewController
{
}
Without these files Magento simply can't find the controllers and throws a 404.
Lastly, create a file under app/etc/modules called {Namespace}_Customer.xml to enable your override, and address book should no longer 404.
I'm trying to add a custom action to a core controller by extending it in a local module. Below I have the class definition which resides in magento1_3_2_2/app/code/local/MyCompany/MyModule/controllers/Catalog/ProductController.php
class MyCompany_MyModule_Catalog_ProductController extends Mage_Adminhtml_Catalog_ProductController
{
public function massAttributeSetAction(){
...
}
}
Here is my config file at
magento1_3_2_2/app/code/local/MyCompany/MyModule/etc/config.xml:
...
<global>
<rewrite>
<mycompany_mymodule_catalog_product>
<from><![CDATA[#^/catalog_product/massAttributeSet/#]]></from>
<to>/mymodule/catalog_product/massAttributeSet/</to>
</mycompany_mymodule_catalog_product>
</rewrite>
<admin>
<routers>
<MyCompany_MyModule>
<use>admin</use>
<args>
<module>MyCompany_MyModule</module>
<frontName>MyModule</frontName>
</args>
</MyCompany_MyModule>
</routers>
</admin>
</global>
...
However, https://example.com/index.php/admin/catalog_product/massAttributeSet/ simply yields a admin 404 page. I know that the module is active - other code is executing fine. I feel it's simply a problem with my xml syntax. Am I going about this the write way? I'm hesitant because I'm not actually rewriting a controller method... I'm adding one entirely. However it does make sense in that, the original admin url won't respond to that action name and it will need to be redirected.
I'm using Magento 1.3.2.2
Thanks for any guidance.
I don't have access to my Magento installs at the moment, but two things pop out
First, your write rule
[#^/catalog_product/massAttributeSet/]
Is saying "match any URL that starts with /catalog_product" and your question indicates you want to match a URL that begins with /admin/catalog_product.
Second, if you're using 1.3+ consider skipping the URL rewrite method and trying a "real" controller override instead.