12
votes

I have problem with magento messages. I am building custom module which in theory should be able to restrict access to some parts of the store. I have created an observer which hook into controller_action_predispatch event and checks if current request can be accessed by the user. If the action cannot be accessed the observer redirects user and sets the error info. I want to set the redirect url to the page the customer is coming from in order to avoid clicking through entire shop. I am looking at the HTTP_REFERER and use it if it is set, otherwise I redirect customer to homepage. The problem is that in the later case (homepage redirect) everything works great but when I set url based on the referer I do not see error message in message box.

The code from the observer ($name variable is a string):

Mage::getSingleton('core/session')->addError('Acces to '.$name.' section is denied');
$url = Mage::helper('core/http')->getHttpReferer() ? Mage::helper('core/http')->getHttpReferer()  : Mage::getUrl();
Mage::app()->getResponse()->setRedirect($url);

What I found interesting is that if I do any change in the observer file and save it, then the next request which fails and gets redirected to referer url shows the error information but any subsequent loses the messages.

I was thinking that the problem is in the full url and my local instalation (I am using .local domain) but so I tried adding

$url = str_replace(Mage::getBaseUrl(), '/', $url);

but this did not helped.

I also tried redirect using php header() function without any result as well.

All cache is disabled. The workflow which triggers the problem is as follows:

  1. I'm going to any accessible page (for example /customer/account)
  2. Click on cart link (cart for this account is disabled)
  3. Return to /customer/account and the error message is displayed
  4. Click on cart link again
  5. Return to /customer/account but no error message

Any hint on where to look will be appreciated.

3
Can you deactivate all your caches (+ FPC if magento EE) to see if it's a cache problem ? By the way, i didn't understand all your explications, you understood that session error messages are deleted after the first display ?Jscti
I expanded the description, hope it is clear now.Zefiryn

3 Answers

24
votes
//A Success Message
Mage::getSingleton('core/session')->addSuccess("Some success message");

//A Error Message
Mage::getSingleton('core/session')->addError("Some error message");

//A Info Message (See link below)
Mage::getSingleton('core/session')->addNotice("This is just a FYI message...");

//These lines are required to get it to work
session_write_close(); //THIS LINE IS VERY IMPORTANT!
$this->_redirect('module/controller/action');

// or
$url = 'path/to/your/page';
$this->_redirectUrl($url);

This will work in a controller, but if you're trying to redirect after output has already been sent, then you can only do that through javascript:

<script language=”javascript” type=”text/javascript”>
window.location.href=”module/controller/action/getparam1/value1/etc";
</script>    
4
votes

Your messages get lost because you use an unfavorably way for a redirect in controller_action_predispatch. Your solution causes on the one hand the "message lost" and on the other hand, it wastes processing power of your server.

When you take a look at Mage_Core_Controller_Varien_Action::dispatch(), you'll see that your solution doesn't stop the execution of the current action, but it should do that with a redirect. Instead Magento executes the current action to its end, including the rendering of the message you had added before. So no wonder why the message gets lost with the next client request, Magento had it already rendered before, with the server response which includes your redirect.

Further you'll see in Mage_Core_Controller_Varien_Action::dispatch() only one possibility to stop the execution of the current action and skip directly to the redirect, which is in line 428 catch (Mage_Core_Controller_Varien_Exception $e) [...]. So you have to use Mage_Core_Controller_Varien_Exception which is quite unpopular, but the only right solution for your purpose. The only problem is, this class has a bug since it was introduced in Magento 1.3.2. But this can be easily fixed.

Just create your own class which is derived from Mage_Core_Controller_Varien_Exception:

/**
 * Controller exception that can fork different actions, 
 * cause forward or redirect
 */
class Your_Module_Controller_Varien_Exception 
    extends Mage_Core_Controller_Varien_Exception
{
    /**
     * Bugfix
     * 
     * @see Mage_Core_Controller_Varien_Exception::prepareRedirect()
     */
    public function prepareRedirect($path, $arguments = array())
    {
        $this->_resultCallback = self::RESULT_REDIRECT;
        $this->_resultCallbackParams = array($path, $arguments);
        return $this;
    }
}

So you can now implement your solution realy clean with that:

/**
 * Your observer
 */
class Your_Module_Model_Observer
{
    /**
     * Called before frontend action dispatch
     * (controller_action_predispatch)
     * 
     * @param Varien_Event_Observer $observer
     */
    public function onFrontendActionDispatch($observer)
    {
        // [...]

        /* @var $action Mage_Core_Model_Session */
        $session = Mage::getSingleton('core/session');
        /* @var $helper Mage_Core_Helper_Http */
        $helper = Mage::helper('core/http');
        // puts your message in the session
        $session->addError('Your message');
        // prepares the redirect url
        $params = array();
        $params['_direct'] = $helper->getHttpReferer() 
            ? $helper->getHttpReferer() : Mage::getHomeUrl();
        // force the redirect
        $exception = new Your_Module_Controller_Varien_Exception();
        $exception->prepareRedirect('', $params);
        throw $exception;
    }
}
0
votes

this will work , so try it:

$url = 'path/to/your/page';
$this->_redirectUrl($url);
return false;

This means you are not allowing again to execute anything else.