Magento: Extending Customer Account Controller to

2019-04-09 19:40发布

问题:

We are trying to add a couple of actions to the AccountController to add another action after the forgotpasswordpost action. The problem is if we add the action to the preDispatch logict to make sure you don't have to be logged in it still redirects back to the login page.

public function preDispatch()
    {
        // a brute-force protection here would be nice

        parent::preDispatch();

        if (!$this->getRequest()->isDispatched()) {
            return;
        }

        $action = $this->getRequest()->getActionName();
        if (!preg_match('/^(create|login|logoutSuccess|forgotpassword|forgotpasswordpost|confirm|confirmation|newactionhere)/i', $action)) {
            if (!$this->_getSession()->authenticate($this)) {
                $this->setFlag('', 'no-dispatch', true);
            }
        } else {
            $this->_getSession()->setNoReferer(true);
        }
    }

This doesn't work because we are calling the parent first which runs this as well but of course the preg_match doesn't match and it runs the authenticate method which runs the method $action->getResponse()->setRedirect($url) which of course sets the header and when it gets back to our code it doesn't matter and then redirects.

We could just remove the call to the parent but I am not sure that is the best approach since the parent calls its parent as well which runs some stuff to set the layout area and then also calls the parent method. I was thinking to just call the parent wtih Mage_Core_Controller_Front_Action but wasn't sure that was the right approach either.

回答1:

So this is what we did, we got the flag and checked if the action had the flag of no-dispatch. then we unset it, cleared the header and reset the response code.

public function preDispatch()
{
    // a brute-force protection here would be nice

    parent::preDispatch();

    $action = $this->getRequest()->getActionName();

    // The parent preDispatch call will set:
    // 1. the 'no-dispatch' flag and set a
    // 2. a 'Location' header for a 302 redirect to the login page
    //    for any actions which are not on the list.
    // 3. a HTTP Response Code of 302 (temporary redirect).
    // We add additional actions securityquestion and securityquestionpost in our override below, but
    // we need to undo the settings which get set by the call to the parent above.
    if (preg_match('/^(securityquestion|securityquestionpost)/i', $action))
    {
        $flag = 'no-dispatch';

        if ($this->getFlag($action, $flag))
        {
              unset($this->_flags[$action][$flag]); // Remove the flag to unset it
              $this->getResponse()->clearHeader('Location'); // Remove Location header for redirect
              $this->getResponse()->setHttpResponseCode(200); // Set HTTP Response Code to OK

        }
    }

    if (!$this->getRequest()->isDispatched()) {
        return;
    }


    if (!preg_match('/^(create|login|logoutSuccess|forgotpassword|forgotpasswordpost|confirm|confirmation|securityquestion|securityquestionpost)/i', $action)) {
        if (!$this->_getSession()->authenticate($this)) {
            $this->setFlag('', 'no-dispatch', true);
        }
    } else {
        $this->_getSession()->setNoReferer(true);
    }
}


回答2:

I needed to do something simmilar, I ended up skipping the original implementation of preDispatch of the Mage_Customer_AccountController.

so in my overriding class I have:

public function validActions() { return 'create|login|logoutSuccess|forgotpassword|forgotpasswordpost|confirm|confirmation|mynewaction'; }

public function preDispatch()
{
    //I override this to add my new Action as a valid one.
    //this is also the reason why I call the grand parent and not parent class's preDispatch()
    // becase the default implementation does not enable to simply override the list of valid actions.

    // a brute-force protection here would be nice

    $par = get_parent_class($this);
    $gpar = get_parent_class($par);  
    $gpar::preDispatch();

    if (!$this->getRequest()->isDispatched()) {
        return;
    }

    $action = $this->getRequest()->getActionName();
    if (!preg_match('/^(' . $this->validActions() . ')/i', $action)) {
        if (!$this->_getSession()->authenticate($this)) {
            $this->setFlag('', 'no-dispatch', true);
        }
    } else {
        $this->_getSession()->setNoReferer(true);
    }
}

as you can see I also added a validActions() that returns a pipe separated list of actions so if someone wants to override my own code it will be much easier to add another action.



回答3:

I have done it as follows

1) Overridden the function preDispatch

2) and replaced the code parent::preDispatch(); with Mage_Core_Controller_Front_Action::preDispatch(); . Its working fine now .But not sure if this is the proper method .



回答4:

I suggest that you add an Observer on postDispatch, filter for accountController and then set the _redirect. Let me know if you need more info.



回答5:

protected $_validActions = array('create','login','logoutSuccess','forgotpassword','forgotpasswordpost','confirm','confirmation');
protected $_customActions = array('customaction1', 'customaction2');

/**
 * Action predispatch
 *
 * Check customer authentication for some actions
 */
public function preDispatch()
{
    // a brute-force protection here would be nice

    $action = $this->getRequest()->getActionName();

    /**
     * If the requested action is a custom action, we can get around the parent class's 
     * action validation check by passing it an action it knows like 'login' for example.
     * 
     * We'll reset the action name in the request temporairily so that we don't have to deal with 
     * all the headers and flags that get set when an action check fails validation. This will also
     * allow us to avoid having the session->beforeAuthUrl getting set when session->authenticate(action) fails
     */
    if (preg_match('/^('.$this->_getCustomActions().')/i', $action))
    {
        $this->getRequest()->setActionName($this->_validActions[1]);
    }

    parent::preDispatch();

    /**
     * Parent check is complete, reset request action name to origional value
     */
    if ($action != $this->getRequest()->getActionName())
    {
        $this->getRequest()->setActionName($action);
    }

    if (!$this->getRequest()->isDispatched()) {
        return;
    }

    if (!preg_match('/^('.$this->_getValidActions().')/i', $action)) {
        if (!$this->_getSession()->authenticate($this)) {
            $this->setFlag('', 'no-dispatch', true);
        }
    } else {
        $this->_getSession()->setNoReferer(true);
    }

}

/**
 * Gets default action names and returns them as a pipe separated string
 *
 * @return string
 */
protected function _getValidActions()
{
    return implode("|", array_merge($this->_validActions, $this->_customActions));
}

/**
 * Gets custom action names and returns them as a pipe separated string
 *
 * @return string
 */
protected function _getCustomActions()
{
    return implode("|", $this->_customActions);
}