I currently work on a magento payment module which has to check if certain fields in a users profile/account are set.
If they do not exist/have no content, I want to either
1. redirect to /customer/account/edit/
2. display a notice that the fields in question has to be filled
or the other way around.
This is initialized by an AJAX call from Magentos core OnePage Checkout.
I tried several things like
Mage::app()->getFrontController()->getResponse()->setRedirect(Mage::getUrl('customer/account/edit/'));
Mage::app()->getResponse()->sendResponse();
exit;
and other things.
From the observer I somehow get a redirect but always end at /default/checkout/cart (which is default?)
In the model I see in chromes network tab that the correct page gets loaded but somehow it wont redirect to the page.
Please let me know if you need more Infos on the problem.
Edit 1: got rid of the second Mage::getURL, still no redirect.
Edit 2: I guess that if I could somehow 'hijack' the response for opcheckout.js it could do the redirect. Or should I maybe just add an extra js which listens as well? I think because the hole thing is triggered by an AJAX call the redirect just doesn't take place at the correct place. More ideas are welcome.
Edit 3: I try to figure at the moment out how to build a response fo the mentioned opcheckout.js which does not trigger the next step but does trigger redirect
Solved it
Solution:
Create an an Override for
/app/code/core/Mage/Checkout/controllers/OnepageController.php
This should be for example
app/code/local/{NameSpace}/{ModuleName}/controllers/OnepageController.php
Pay special attention to the "s" the folder is plural not singular. Took me ten minutes to see why my controller was not working.
The controller should be build like this:
require_once Mage::getModuleDir('controllers', "Mage_Checkout").DS."OnepageController.php";
class MOOD_BonimaScoreIdent_OnepageController extends Mage_Checkout_OnepageController {
public function indexAction(){
parent::indexAction();
}
public function savePaymentAction(){
//just an example
$result['redirect']=Mage::getUrl('customer/account/edit/');
$this->getResponse()->setBody(Mage::helper('core')->jsonEncode($result));
return;
}
}
The config.xml part to include the controller override is simply:
<frontend>
<routers>
<checkout>
<args>
<modules>
<MyModuleName before="Mage_Checkout">Namespace_MyModuleName</MyModuleName>
</modules>
</args>
</checkout>
</routers
</frontend>
Related
I have been trying events left and right trying to find one that fires right after updating the shipping address of an order via the magento admin.
I have tried a number of things I found on here, as well as sales_order_place_after (which is a hook I use for the frontend events), but for the adminhtml events, I cant seem to get my method to fire when I save the changes.
Can anyone point me in the direction of the event I am looking for?
I found the solution by doing a little hacking:
The event that you need to bind in this case is adminhtml_sales_order_addressSave.
I found this by creating a global event observer, which simply monitored all the events occurring on the server:
<adminhtml>
<events>
<controller_action_predispatch>
<observers>
<add_event>
<type>singleton</type>
<class>MGD_Shippingodbc_Model_Observer</class>
<method>trackEvents</method>
</add_event>
</observers>
</controller_action_predispatch>
</events>
</adminhtml>
Then I added this function to my class:
function trackEvents($observer)
{
Mage::log("track event:" . $observer->getEvent()->getControllerAction()->getFullActionName());
return $this;
}
Interestingly enough, I also realized I can use this same methodology to create my own event dispatchers as well, simply by monitoring the events as they come through this observer function, like this:
function trackEvents($observer)
{
if ($observer->getEvent()->getControllerAction()->getFullActionName() == "adminhtml_sales_order_addressSave")
Mage::dispatchEvent('my_custom_event_observer', []);
return $this;
}
Please try with
controller_action_predispatch_checkout_onepage_saveShiping
I have created one Magento module. It's working perfectly. But my problem is i want to create a module with more than one front page like:
Page 1:
www.magento.com/part/
Page 2:
www.magento.com/part/view/
In my part page there is one form and when i submit this it redirect to view page and i want to display some data whatever pass from part page.
Following is my directory structure:
app\code\local\HK\Part\Block
app\code\local\HK\Part\controllers
app\code\local\HK\Part\etc
app\code\local\HK\Part\Helper
app\code\local\HK\Part\Model
app\code\local\HK\Part\sql
app\design\frontend\default\default\layout
app\design\frontend\default\default\template
app\etc\modules
How can i achieve this?
Assuming you have setup your router correctly you just have to add another action in your controller. In your case it should be viewAction in PartController.php
A basic Magento URL structure can be broken down as follows:
<base url>/<module front-name>/<controller>/<action>/<any other params, optional>
So, from the provided URL structure
base url = http://www.magento.com/
module front name = part
controller = view
action = index (as its the default value, when no action name is provided)
A modules front name is defined in the config.xml file of the module, as follows:
<frontend>
<routers>
<namespace_custom_module_name>
<use>standard</use>
<args>
<module>Namespace_Custommodule</module>
<frontName>custom_frontname</frontName>
</args>
</namespace_custom_module_name>
</routers>
</frontend>
Replace custom_frontname inside frontName tag with part, and this should set you router correctly.
As per the first example, http://www.magento.com/part/, you have to create a IndexController.php with a function indexAction().
For the second example, http://www.magento.com/part/view/, you have to create a controller named PartController.php, with method name as indexAction().
Each action you create, inside a controller represents a page, so use it wisely. As a best practice, actions that works with similar data are always grouped inside a single controller.
For e.g., in CRUD applications, various operations like listing, add, edit, delete, save all actions are usually added to a single controller.
After trying to debug for hours I'm out of ideas and hope for some clarification (I guess I missunderstood a concept at some point).
The backstory: Some base categories need an "overview page" which should be generated automatically from child categories and products. So my approach was to add a sub category to every base category and create a custom page layout which is being used from all these sub categories. For my client this would be very easy to manage in the Magento backend since he would only need to change the value in one drop down. So I created a simple module defining the new page layout. Within the backend I was able to select this one as well.
The module config:
<?xml version="1.0"?>
<config>
<modules>
<Company_Layouts>
<version>0.1.0</version>
</Company_Layouts>
</modules>
<global>
<page>
<layouts>
<company_category_overview module="page" translate="label">
<label>Kategorie-Übersicht</label>
<template>page/1column.phtml</template>
<layout_handle>company_category_overview</layout_handle>
</company_category_overview>
</layouts>
</page>
</global>
<frontend>
<layout>
<updates>
<company_layouts>
<file>company_layouts.xml</file>
</company_layouts>
</updates>
</layout>
</frontend>
</config>
Since these special overview pages require some layout changes I was hoping to reference the layout in a specific layout file (company_layouts.xml)... and here my logic is leaving me:
With <layout_handle>company_category_overview</layout_handle> I was hoping to define a handle which I can use to change the layout only when this specific page template is being used. Exactly this is not the case. My layout updates which are inside the handle company_category_overview are just being ignored.
After digging deeper I realized, it doesn't seem to be my code but more like a general issue. In an old Magento 1.4 installation the page layout handle is being carried to all sites, like page_one_column. In Magento 1.7 and (what I'm using now) 1.8 this is only on the home page the case. I'm using Commerce Bug for debugging. I just tried this with a fresh 1.7 and a freh 1.8 installation.
Is this some concept I don't understand or just a plain bug?
Also, I'm aware that layout updates can be achieved within the backend but this would only be my last option since I feel it's much cleaner having this in a seperate file without the need of copy/pasting such stuff.
Is this some concept I don't understand or just a plain bug?
Both? Neither? The information in the <page><layout>...</layout></page> node is used by both the category pages and CMS pages, but each system uses the the information differently, and neither system uses it in a way you'd expect. Here's a rundown on how category pages use this information.
The category page is rendered by the following controller action
#File: app/code/core/Mage/Catalog/controllers/CategoryController.php
public function viewAction()
{
...
}
This controller action doesn't have the standard loadLayout and renderLayout method calls. Instead, there's a lot of extra code in this method for adding layout handles and doing things between generating the blocks and rendering the final layout. The section we're interested in is this
$design = Mage::getSingleton('catalog/design');
$settings = $design->getDesignSettings($category);
#...other stuff we don't care about...
if ($settings->getPageLayout()) {
$this->getLayout()->helper('page/layout')->applyTemplate($settings->getPageLayout());
}
When you save a category with your "Page Layout" in the Custom Design tab, the getPageLayout method call above should return company_category_overview. On category pages, Magento doesn't use this to apply a handle, instead it passes the values to the applyTemplate method. Here's that method in full.
#File: app/code/core/Mage/Page/Helper/Layout.php
public function applyTemplate($pageLayout = null)
{
if ($pageLayout === null) {
$pageLayout = $this->getCurrentPageLayout();
} else {
$pageLayout = $this->_getConfig()->getPageLayout($pageLayout);
}
if (!$pageLayout) {
return $this;
}
if ($this->getLayout()->getBlock('root') &&
!$this->getLayout()->getBlock('root')->getIsHandle()) {
// If not applied handle
$this->getLayout()
->getBlock('root')
->setTemplate($pageLayout->getTemplate());
}
return $this;
}
The pertinent parts are this line,
$pageLayout = $this->_getConfig()->getPageLayout($pageLayout);
which will load the information from your configuration
<label>Kategorie-Übersicht</label>
<template>page/1column.phtml</template>
<layout_handle>company_category_overview</layout_handle>
as a Varien_Object. Then, it will use this information to apply a template to the root block.
$this->getLayout()
->getBlock('root')
>setTemplate($pageLayout->getTemplate());
So, for category pages, the information in the <layout_handle/> node is never used. That's why your layout updates aren't being applied — Magento actually applies your handle.
I'm attempting to use an observer to modify the response of the add to cart controller action, but only in the context of an AJAX request.
My observer is called and my JS is retrieving data fine, I have verified this by putting a die() in my observer function cartAdd() and verifying the response developer console, which I am using to see the result of my response from Magento. So JS isn't the issue here.
My primary problem is that I can't seem to modify the response through the normal functions. I get the request by using $observer->getEvent()->getControllerAction()->getResponse() and then make changes to it by setHeader(), or setBody(), or any other function that modifies the response, but there is absolutely no effect to the response!
Does anybody have any clue as to why I'm not able to modify the response in my observer?
In /app/code/local/mynamespace/mymodule/etc/config.xml:
<frontend>
....
<events>
<controller_action_predispatch_checkout_cart_add>
<observers>
<mymodule_cart_add>
<type>singleton</type>
<class>mymodule/observer</class>
<method>cartAdd</method>
</mymodule_cart_add>
</observers>
</controller_action_predispatch_checkout_cart_add>
</events>
</frontend>
In /app/code/local/mynamespace/mymodule/Model/Observer.php:
public function cartAdd(Varien_Event_Observer $observer)
{
$controllerAction = $observer->getEvent()->getControllerAction();
if($controllerAction->getRequest()->isAjax()) {
$response = $controllerAction->getResponse();
// I've even tried using:
// $response = Mage::app()->getResponse();
$response->setHeader('HTTP/1.1','403 Forbidden'); //using this because i will need it in my final code and it will make it immediatly obvious the response has been changed
$response->setHeader('Content-type', 'application/json');
$response->setBody('hello world!!!!');
// this is to stop the product from being added to the cart
$controllerAction->setFlag('', Mage_Core_Controller_Varien_Action::FLAG_NO_DISPATCH, true);
}
}
Please note: I know this code isn't going to at all AJAXify adding to cart (which is my end goal). At the moment I am just trying to resolve this issue
I end up just getting the contents of the page that you would end up on as a result of running an add to cart action:
When a product is added to cart there is admin-configurable behavior to send a person to the cart page or to redirect them back to the product page. See the System > Configuration > Checkout > Shopping Cart: After Adding a Product Redirect to Shopping Cart field.
This redirect behavior is accomplished via a redirect which will displace any redirect set in the dynamic controller_action_predispatch_checkout_cart_add event; ref. the final bit of logic from the Mage_Checkout_CartController::addAction(). Have no fear, though! Magento core developers have the need to override this behavior as well, so it is possible to inform the Mage_Checkout cart controller's addAction() method to bypass the normal redirect behavior if a flag has been set on the checkout/session object. Not only is there a hook and supporting logic to make it work, but there is actually a working example from the core - always a good thing for developers.
Just prior to the final redirect logic in the addAction() method, the cart controller's addAction() method dispatches the checkout_cart_add_product_complete event. This event is observed by the Mage_Wishlist observer. A quick review of relevant final logic from the Mage_Wishlist_Model_Observer::processAddToCart() method shows how to prevent the cart controller's addAction() method from redirecting - namely by setting the no_cart_redirect flag on checkout/session object, which preserves the redirect set on the response object.
There is one more consideration in this case. It's likely that the Mage_Wishlist observers behavior should be preserved, namely: after adding a product to cart from the wishlist, a customer may be redirected to the next product in their wishlist. This is one of the instances when observer processing order matters. To make sure that the Mage_Wishlist module's add to cart behavior is preserved, other modules which consume the checkout_cart_add_product_complete event should fire before the Mage_Wishlist observer. In the declaration file for the custom module, the Mage_Wishlist module should be set as dependent on the custom module, which will ensure that the custom module's observer will fire before the Mage_Wishlist module:
<?xml version="1.0" encoding="UTF-8"?>
<config>
<modules>
<Custom_Module>
<active>true</active>
<codePool>local</codePool>
</Custom_Module>
<Mage_Wishlist>
<depends>
<Custom_Module />
</depends>
</Mage_Wishlist>
</modules>
</config>
If the Mage_Wishlist module were not a factor, the better targeted event to consume would be the dynamically-generated controller_action_postdispatch_checkout_cart_add event, which is the last targeted event before the generic controller_front_send_response_before event.
I would like to have some Magento product attributes that are not editable from the admin interface and some that are not visible at all in that interface (as a method of storing some persistent information about a product that should not be viewed by human users.. it's the only way of doing this that i can think of, any other suggestions are welcome).
So my question is: Do all Magento attributes have to be visible and editable from the admin interface? If not, how can they be made read-only or hidden?
I noticed that in the admin interface there are some read-only fields, so it must be possible to do this one way or another. After searching stackoverflow for this I found a possible solution involving JavaScript, but I would like to not go down that path if it's at all possible.
OK, it looks like it can be done after all. After adding an observer for the catalog_product_load_after event, the lockAttribute method of the Mage_Catalog_Model_Abstract class may be used to make a product attribute read-only. Here is the code for the observer method:
public function lockAttributes($observer) {
$event = $observer->getEvent();
$product = $event->getProduct();
$product->lockAttribute('attribute_code');
}
Since the catalog_product_load_after event is dispatched for every product load, the attributes supplied in the lock_attributes method are locked after every product load. This could have unexpected results: it is not possible to change the value of the attributes in the lock_attributes method without explicitly unlocking them.
Instead of using the catalog_product_load_after event, it suffices to add an observer for the catalog_product_edit_action event: this event is dispatched only when editing a product in the admin interface.
I think Aad Mathijssen and Epicurus combined have the best answer to the question, with a little clarification. As Aad points out, catalog_product_load_after is called after every product load and that means on the FrontEnd as well!
If we are looking to protect attribute fields only in the admin panels, catalog_product_edit_action is the more appropriate choice.
Your etc/config.xml will then be something like this:
<catalog_product_edit_action>
<observers>
<lock_attributes>
<class>yourmodule/observers</class>
<method>lockAttributes</method>
</lock_attributes>
</observers>
</catalog_product_edit_action>
No i guess its not possible from the attribute manager.
A easy quick and dirty solution would be to use css to hide the input and label.
I have developed exactly such extension that works for products, categories and CMS pages. You just have to define some rules and choose which attributes you want to show as read-only.
Extension URL: https://www.bubbleshop.net/magento-admin-readonly.html
Using this thread and some more digging around; the lockAttribute method is from an abstract class which means that it's possible to also be used on category attributes as well. I caught the 'catalog_category_load_after' observer and used it to lock my desired category attributes:
public function lockCategoryAttributes($observer) {
$event = $observer->getEvent();
$c = $event->getCategory();
$c->lockAttribute('attribute_code');
}
I'm not sure if that's the right observer to use but it works.
So yes it is possible to lock category attributes or make them readonly.
etc\adminhtml\events.xml
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd">
<event name="catalog_product_load_after">
<observer name="product_lock_attributes" instance="Vendor\Module\Observer\Lock"/>
</event>
</config>
Observer\Lock.php
namespace Vendor\Module\Observer;
class Lock implements \Magento\Framework\Event\ObserverInterface
{
public function execute(\Magento\Framework\Event\Observer $observer)
{
$event = $observer->getEvent();
$product = $event->getProduct();
$product->lockAttribute('attribute_code');
}
}