I have wrote 2 Magento observers and they both do exactly what I want with the exception that they end on the wrong page. In other words, they write the log files, modify the databases, and talk with other servers, but they modify the page to page routing. For example, I have an observer that I used at login that modifies a database, writes a cookie, and writes to a log, but it changes the post log-in page to
http://www.my-web-site.com/index.php/customer/login/post/
and then gives me a 404 error. If I hit "Ctrl" + 'r' then I am logged in at
http://www.my-web-site.com/index.php/customer/account/index/
which is correct. If I change, app/code/local/my_module/my_model/etc/config.xml to
app/code/local/my_module/my_model/etc/config.xml.1 (in other words take out the observer), then Magento routes to the correct page,
I'm thinking that I need router information in config.xml. My current config.xml is:
<?xml version="1.0" encoding="UTF-8"?>
<!-- The root node for Magento module configuration -->
<config>
<!-- The module's node contains basic information about each Magento module -->
<modules>
<!-- This must exactly match the namespace and module's folder
names, with directory separators replaced by underscores -->
<MyCompany_LogIn>
<!-- The version of our module, starting at 0.0.0 -->
<version>0.0.0</version>
</MyCompany_LogIn>
</modules>
<!-- Configure our module's behavior in the global scope -->
<global>
<!-- Defining models -->
<models>
<!-- Unique identifier in the model's node.
By convention, we put the module's name in lowercase. -->
<mycompany_login>
<!-- The path to our models directory,
with directory separators replaced by underscores -->
<class>MyCompany_LogIn_Model</class>
</mycompany_login>
</models>
</global>
<frontend>
<!-- Defining an event observer -->
<events>
<!-- The code of the event we want to observe -->
<customer_login>
<!-- Defining an observer for this event -->
<observers>
<!-- Unique identifier within the catalog_product_save_after node.
By convention, we write the module's name in lowercase. -->
<mycompany_login>
<!-- The model to be instantiated -->
<class>mycompany_login/observer</class>
<!-- The method of the class to be called -->
<method>wrtLogInCookie</method>
<!-- The type of class to instantiate -->
<type>singleton</type>
</mycompany_login>
</observers>
</customer_login>
</events>
</frontend>
</config>
I'm guessing that the login inside Magento uses an observer, and I'm interfering with it.
Besides the , I'm guessing that I could also accomplish a similar thing in the PHP Observer code. My observer is:
<?php
/**
* Our class name should follow the directory structure of
* our Observer.php model, starting from the namespace,
* replacing directory separators with underscores.
* i.e. /www/app/code/local/MyCompany/LogIn/Model/Observer.php
*/
class MyCompany_LogIn_Model_Observer extends Varien_Event_Observer
{
/**
* Magento passes a Varien_Event_Observer object as
* the first parameter of dispatched events.
*/
public function wrtLogInCookie(Varien_Event_Observer $observer)
{
// Retrieve the product being updated from the event observer
$customer = $observer->getEvent()->getCustomer();
$email = $customer->getEmail();
Mage::log('The E-mail is: ' . $email);
$ran_nmbr = rand();
Mage::log('The random number is: ' . $ran_nmbr);
$crnt_dat = date("m-d-Y::H:i:s");
Mage::log('The date is: ' . $crnt_dat);
return $this;
}
}
?>
I have read about routers, but the articles discussed it in terms of landing on some page before the extension is executed. As you can see, I need to land on the right page after the extension is executed.
Inside the PHP observer, I also tried redirects. For example,
Mage::app()->getResponse()->setRedirect(Mage::getUrl('customer/account/login'));
Maybe I need a full URL address or something. I'm sure this is easy to fix, but my ignorance seems to be following me around. Please help if you know something about this.
Unfortunately, it's not quite as simple as an easy fix. You see, if we look at app/code/core/Mage/Customer/controllers/AccountController.php, in the loginPostAction(), we see that the customer/session singleton triggers the customer_login call. However, what is causing the trip-up here is that after that is called, back in the controller, the controller calls $this->_loginPostRedirect(), so all of your rerouting work that you did is overwritten.
How to fix:
After saying it isn't all that simple, I did happen to see a cheat that we can take advantage of:
$session = Mage::getSingleton('customer/session');
$session->setBeforeAuthUrl($forwardToUrl);
You're partially right. The problem you're running into is Magento's redirect mechanism works with a "last one to say something" wins philosophy. If you look at the standard login code in
app/code/core/Mage/Customer/controllers/AccountController.php
You'll see the loginPostAction method ends with a call to
$this->_loginPostRedirect();
which (ultimately) ends up calling some code that looks like this
$this->getResponse()->setRedirect($url);
This may be the code that's causing you a problem, or it may be something else. The general problem is the final call to the response object's setRedirect method will win.
My usual solution to this is setting some sort of global flag (static variable on a class, a flag set with Mage::register) when I want to perform a redirect, and then creating an additional observer for controller_action_postdispatch. In this observer I look for the global flag I set, and if I find it, set the redirect there.
This handled 99% of redirect situations, and should handle yours. The times this won't work are with some admin login cases, as well as URL rewrites.
The admin login contains some redirect code that doesn't use Magento response object
#File: app/code/core/Mage/Admin/Model/Session.php
...
header('Location: ' . $requestUri);
exit;
...
If this redirect is causing you a problem, create a listener for admin_session_user_login_success that uses PHP header redirects before Magento's does.
Similarly, if Magento's using a rewrite object, the following code might run
#File: app/code/core/Mage/Core/Model/Url/Rewrite.php
if ($isPermanent) {
header('HTTP/1.1 301 Moved Permanently');
}
header('Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0');
header('Pragma: no-cache');
header('Location: ' . $url);
exit;
(that said, the rewrite code will rarely be your problem, as in standard Magento operation is runs before controller dispatch)
Related
I'm attempting to subscribe to the sales_order_place_after observer in Magento so that I can output order data. Unfortunately, anytime I try to log output to Mage::log, print_r, or even var_dump, I receive a User Error: Some transactions have not been committed or rolled back error.
It's probably also important to note that I'm using one step checkout and the checkout usually hangs (confirmation email still comes through). Shortly after the hang, I receive a PHP Fatal error: Allowed memory size of 268435456 bytes exhausted (tried to allocate 234881024 bytes) error. Can't seem to track down where or what is causing the memory leak... but it this error is only spit out if I am trying to output data.
config.xml
<?xml version="1.0" encoding="UTF-8"?>
<config>
<modules>
<InfinitelyWhite_OrderEvent>
<version>0.0.1</version>
</InfinitelyWhite_OrderEvent>
</modules>
<!-- Configure our module's behavior in the global scope -->
<global>
<!-- Defining models -->
<models>
<!--
Unique identifier in the model's node.
By convention, we put the module's name in lowercase.
-->
<infinitelywhite_orderevent>
<!--
The path to our models directory, with directory
separators replaced by underscores
-->
<class>InfinitelyWhite_OrderEvent_Model</class>
</infinitelywhite_orderevent>
</models>
<events>
<sales_order_place_after>
<observers>
<infinitelywhite_orderevent>
<class>infinitelywhite_orderevent/observer</class>
<method>exportNewOrder</method>
<type>singleton</type>
</infinitelywhite_orderevent >
</observers>
</sales_order_place_after>
</events>
</global>
Observer.php
class InfinitelyWhite_OrderEvent_Model_Observer
{
/**
* Magento passes a Varien_Event_Observer object as
* the first parameter of dispatched events.
*/
public function exportNewOrder(Varien_Event_Observer $observer)
{
Mage::log('reached export_new_order');
$order = $observer->getOrder();
Mage::log($order->getData());
return $this;
}
}
Any help would be greatly appreciated.
Nick Parson .....please removed singleton
<type>singleton</type>
Turns out the error was coming up because I was attempting to pull order information with $observer->getOrder(), when the correct approach is to use $observer->getEvent()->getOrder(). I was simply missing the getEvent().
I'm now able to extract data like so:
$order = $observer->getEvent()->getOrder();
$billing = $order->getBillingAddress();
$shipping = $order->getShippingAddress();
$customer = $order->getCustomer();
And then I can do something like:
$order->getData() or $customer->getData()
It's also important to note that because I switched up my observer event and I'm not using sales_model_service_quote_submit_success which stopped the nasty User Error: Some transactions have not been committed or rolled back error.
Can't comment yet, but what happens if you disable your observer? (Just to make sure it is the observer causing this issue).
If it is the observer, you could try replacing $order->getData() with $order->debug() and see if that helps
I have set up a default 'no modifications' copy of magento on a local machine to replicate the results on our production machine.
When I try to catch the event 'adminhtml_customer_save_after' in a module, it always returns / stops on customer_save_before. This is also true for any of the customer_save_after type events.
Adding
file_put_contents('/tmp/events.log','Dispatching '. $name. "\n",FILE_APPEND);
to the dispatchEvent function in /var/www/app/Mage.php verifies that it indeed does return on customer_save_before, even though that is not the event i'm asking for.
Please validate this and let me know if this is intended functionality. I must have access to the entity_id in magento admin section when our call center / order team creates new customers in the admin side of the site (for placing phone-in orders) for synchronization with our company's database.
PHP from module
<?php
class NKI_CustomerSync_Model_Observer
{
public function AddCustomerToQueue($observer)
{
$event = $observer->getEvent();
$customer = $event->getCustomer();
$model=$event->getModel();
echo "<PRE>";
var_dump($event->getName());
var_dump($event->getData());
var_dump($event);
var_dump(get_class_methods($event));
die();
}.....
XML
<config>
<modules>
<NKI_CustomerSync>
<version>0.1.0</version>
</NKI_CustomerSync>
</modules>
<global>
<events>
<adminhtml_customer_save_after>
<observers>
<NKI_customersync_model_observer>
<type>singleton</type>
<class>NKI_CustomerSync_Model_Observer</class>
<method>AddCustomerToQueue</method>
</NKI_customersync_model_observer>
</observers>
</adminhtml_customer_save_after>
First, since your post betrays a few misunderstandings about how Magento's event system works, which in turn is leading you to diagnose the problem incorrectly, here's a quick review of the event system.
The dispatchEvent method in Mage.php is the wrong place to check if Magento "catches" your event. This method receives all events. It's not until deeper in the calling chain, in Mage_Core_Model_App's identically named dispatchEvent method
#File: app/code/core/Mage/Core/Model/App.php
public function dispatchEvent($eventName, $args)
{
foreach ($events[$eventName]['observers'] as $obsName=>$obs) {
//...
}
}
where Magento will look for any event observers (or in your parlance, event catchers)
Magento issues all sorts of events on every request. The customer_save_before event is issued whenever a customer model object is saved. This includes saving both on the frontend and the backend. However, the adminhtml_customer_save_after event is fired here
#File: app/code/core/Mage/Adminhtml/controllers/CustomerController.php
public function saveAction()
{
//...
$customer->save();
//...
Mage::dispatchEvent('adminhtml_customer_prepare_save', array(
'customer' => $customer,
'request' => $this->getRequest()
));
//..
}
In other words, this event fires in the saveAction of the admin customer controller. In other other words, this event fires after a user clicks "save" in the Magento admin console when looking at an individual customer.
So, both the customer_save_before and the adminhtml_customer_save_after event will fire when a customer gets saved in the Magento admin. The customer_save_before event fires first, and then the adminhtml_customer_save_after event fires.
As for your specific code, what you've shown looks correct. That assuming you have the closing } on the observer class, and that it's in the correct location.
app/code/community/NKI/CustomerSync/Model/Observer.php
//or, if your module is configured in the local code pool
app/code/local/NKI/CustomerSync/Model/Observer.php
and that your module's config.xml has a closing </config>, is valid XML, and is in your module's etc/config.xml file. This also assumes Magento can see your module.
I used your code to throw together a skeleton module and your event fired when I saved a customer on the backend of Magento. That skeleton module is here. Compare it to what you have to see where your module may be subtly incorrect.
The Event Should be put in adminhtml and is called customer_save_after
Let's say I use a custom controller to have a url path/frontend name of
/customcategory
Well, obviously if I have a controller file named 'TestController.php' and indexAction
the url path would be
/customcategory/test/index
What I am trying to figure out is how I do re-name the Test Controller, or modify the config xml file, so I can have a hyphenated url from a controller file such as
/customcategory/test-section/index
I know that if I want /customcategory to be hyphenated, I can just modify the frontend tag in the config file. But the site I am building would benefit from a hyphenated controller route, the part that comes after /customcategory with keywords and I cannot get it to work nor can I find an example on google - as crazy as that may seem.
Thanks for your time.
What you are trying to do is possible using global rewrite in your custom module. You could pass all incoming request for /customcategory/* to a specific controller action. But you would have to manage your own route (base on the depth of your url path).
e.g www.MageIgniter.com/customcategory/path1/path2
config.xml
<global>
<rewrite>
<fancy_url>
<from><![CDATA[/customcategory\/(.*)/]]></from>
<to><![CDATA[customcategory/index/processroute/tagname/$1/]]></to>
<complete>1</complete>
</fancy_url>
<rewrite>
</global>
<frontend>
<routers>
<tagseo>
<use>standard</use>
<args>
<frontName>customcategory</frontName>
</args>
</tagseo>
</routers>
class MageIgniter_Customcategory_IndexController extends Mage_Core_Controller_Front_Action
{
public function processRoute(){
print_r($requestUri = Mage::app()->getRequest()->getRequestUri()); //path1/path2
print_r($this->getRequest()->getParam('tagname')); // path1
print_r($this->getRequest())
// do you custom logic here base on about request path explode('/', trim($requestUri,'/'))
}
...
For a working example see "Product Tags" section # http://www.contempospace.com/bedroom-furniture/wardrobe-closets/custom-closet-systems/isa-closet-system-shelves-hanging-walk-in-reach-in-closet.html
As far as I am aware you cannot add hypens in the url to match up to a filename. If you are trying to get a folder structure you can just add more paths to it.
For example if you wanted:
Namespace/CustomCategory/controller/test/SectionController.php
you could do:
/customcategory/test_section/index
If I want to do something extra when a particular configuration field for my custom module is saved (over and above saving to the Magento core config table), I can just specify a backend_model for that field in my system.xml, and have that backend model class extend Mage_Core_Model_Config_Data, override _afterSave, and put my extra stuff in that method.
But what if I have several fields I want to do this for. I don't want the behaviour to be to save field1 and call my afterSave for that field, save field2 and call my afterSave for that field, etc. I'd rather that all the fields were saved to the Magento core config table, and then I do my extra stuff.
Is that possible? I thought I might be able to achieve that using event/observer. So in my config.xml, <adminhtml> section, I added an observer as follows:
<events>
<admin_system_config_changed_mysection>
<observers>
<mypfx_admin_system_config_changed_mysection>
<class>mymodule/adminhtml_system_config_backend_configSaveObserver</class>
<method>myConfigSaved</method
</mypfx_admin_system_config_changed_mysection>
</observers>
</admin_system_config_changed_mysection>
</events>
but my observer method is not called when the config is saved. Maybe I have the wrong event name? The "mysection" bit on the end of the event name I was guessing had to match the section from system.xml:
<sections>
<mysection translate="label" module="mymodule">
...
<groups>
...
</groups>
</mysection>
</sections>
Thanks.
The event you're trying to listen for doesn't exist. Here's what you want to do, and some tips for picking the right event in the future.
First, every event is fired in Magento by the Mage::dispatchEvent method. Search the core code for these calls and you'll always know the name of the event you want to listen for.
$ ack 'admin_system_config_changed_'
Adminhtml/controllers/System/ConfigController.php
136: Mage::dispatchEvent("admin_system_config_changed_section_{$section}",
From the above, you can see the name of the event vs. what you thought it was
admin_system_config_changed_section_{$section}
admin_system_config_changed_mysection
So, it looks like you're missing the section before your own section name.
Second, while working on a development box, the best way to find the event you're looking for is to log things at the source. Temporarily add some debugging code to the dispatchEvent function.
#File: app/Mage.php
public static function dispatchEvent($name, array $data = array())
{
//either one of the lines below should do it. One uses Magento's
//built in logging, the other uses something more crude
#Mage::Log($name);
#file_put_contents('/tmp/test.log',"$name\n",FILE_APPEND);
Varien_Profiler::start('DISPATCH EVENT:'.$name);
$result = self::app()->dispatchEvent($name, $data);
#$result = self::registry('events')->dispatch($name, $data);
Varien_Profiler::stop('DISPATCH EVENT:'.$name);
return $result;
}
This will dump a huge list of event names out to your log. I typically use OS X's Console.app to view the log file during the request, copy the lines out, sort and remove duplicates, and then end up with a list like this
admin_system_config_changed_section_commercebug
admin_user_load_after
admin_user_load_before
adminhtml_block_html_before
adminhtml_controller_action_predispatch_start
application_clean_cache
controller_action_layout_generate_blocks_after
controller_action_layout_generate_blocks_before
controller_action_layout_generate_xml_before
controller_action_layout_load_before
controller_action_layout_render_before
controller_action_layout_render_before_adminhtml_system_config_edit
controller_action_postdispatch
controller_action_postdispatch_adminhtml
controller_action_postdispatch_adminhtml_system_config_edit
controller_action_postdispatch_adminhtml_system_config_save
controller_action_predispatch
controller_action_predispatch_adminhtml
controller_action_predispatch_adminhtml_system_config_edit
controller_action_predispatch_adminhtml_system_config_save
controller_front_init_before
controller_front_init_routers
controller_front_send_response_after
controller_front_send_response_before
core_abstract_load_after
core_abstract_load_before
core_block_abstract_prepare_layout_after
core_block_abstract_prepare_layout_before
core_block_abstract_to_html_after
core_block_abstract_to_html_before
core_collection_abstract_load_after
core_collection_abstract_load_before
core_config_data_load_after
core_config_data_save_after
core_config_data_save_before
core_config_data_save_commit_after
core_layout_block_create_after
core_locale_set_locale
core_session_abstract_add_message
core_session_abstract_clear_messages
http_response_send_before
model_load_after
model_load_before
model_save_after
model_save_before
model_save_commit_after
resource_get_tablename
store_load_after
store_load_before
You still need to use some intelligence guessing to figure out which event you want, but they're named intuitively enough that you can usually find what you're looking for.
You need to tie your observer method to a specific Magento event (you can add your own, but need to find when you want it to be fired and add your own dispatchEvent call). If Magento has an event built in, use that event name in the config.
There's a pdf of the built in event lists on the web - google for it & you'll find it.
I am using extension
http://www.magentocommerce.com/magento-connect/eitai2001/extension/1468/order-status/reviews#reviews
of magento which provide a lot of order status..but my need is limited ..so i want to remove some status from my order-status list... how can i do this..please help
Thanks!
Edited
I made change in config.xml
If I comment any order-status ... like this
<!--<processing_cc_settled translate="label"><label>Processing - Credit Card has been Settled</label></processing_cc_settled>-->
but in combobox(where status shows in admin site )still processing_cc_settled appears at the same place where Processing - Credit Card has been Settled showing up before commenting
If this is the same extension that I have seen installed one client's site then it should have a config.xml file included in its /etc subdirectory (NOTE: not the global /app/etc but somwhere in the /app/code/community/ folder). In this file you will find definitions for all the additional statuses. Just comment out the ones you don't want. You can also change the ones that you leave so that they are better suited to your needs.
CLARIFICATION:
Here's a piece of my config.xml:
<config>
<modules>
<Mage_Sales_Community>
<version>0.1.2</version>
</Mage_Sales_Community>
</modules>
<global>
<sales>
<order>
<statuses>
<!--Complete Status Descriptions-->
<complete translate="label"><label>Dispatched</label></complete>
<!--Cancelled Status Descriptions-->
<canceled translate="label"><label>Suspended</label></canceled>
<!--<canceled_discontinued translate="label"><label>Suspended - No stock</label></canceled_discontinued> -->
<!-- /* Additional custom statuses will go here */ -->
<!-- Format of a status description is <name_of_status translate="label"><label>Name of Status</label?</name_of_status> -->
</statuses>
</order>
</sales>
</global>
</config>
Note that complete and canceled have a different label defined. There is also a canceled_discontinued status, but it is not used as the whole entry is commented out. If the entry stays on the order comments dropdown after you have commented it make sure that you have refreshed the cache...
I add/remove magento statuses in install scripts, using something like below:
<?php
$installer = $this;
/*
Possible states:
Mage_Sales_Model_Order::STATE_CANCELED
Mage_Sales_Model_Order::STATE_CLOSED
Mage_Sales_Model_Order::STATE_COMPLETE
Mage_Sales_Model_Order::STATE_HOLDED
Mage_Sales_Model_Order::STATE_NEW
Mage_Sales_Model_Order::STATE_PAYMENT_REVIEW
Mage_Sales_Model_Order::STATE_PENDING_PAYMENT
Mage_Sales_Model_Order::STATE_PROCESSING
*/
$installer->startSetup();
// Get status model
$status = Mage::getModel('sales/order_status');
// Delete some statuses
$status->setStatus('processing_cc_settled')->delete();
$status->setStatus('another_status_code_to_delete')->delete();
//Add a new status
$status->setStatus('holded_cc_error')
->setLabel('On Hold: CC Error')
->assignState(Mage_Sales_Model_Order::STATE_HOLDED)
//for example, use any available existing state from above
->save();
//To set an order to this status:
//$order->setData('state', "holded");
//$order->setStatus("holded_cc_error");
//$order->save();
$installer->endSetup();