views:

6135

answers:

6

Hi there,

I want to add a redirection URL to my login forms action (as a query) in login page, so after loging-in, one can visit the previous page he or she was surfing.

First I thought about using Zend Session and save the url of each page in a variable. but I read in the documentation that it has overhead. So, is there a better way to do so? or is there an other way to use zend session with no overhead?

A: 

I'm sure there is some built in method for doing this somewhere in ZF, but I'm lazy, so I did it this way:

Create your own *App_Controller_Action* class (create /library/App/Controller/Action.php). Extend all of your controllers off of this class

In each of my controllers, I call $this->_initAuth(), function pasted below:

protected function _initAuth()
{
    $auth = Zend_Auth::getInstance();
    if (!$auth->hasIdentity() && strcmp($_SERVER['REQUEST_URI'], '/auth/login') <> 0)
        $this->_redirect('/auth/login' . $_SERVER['REQUEST_URI']);
    else
        $this->_identity = $auth->getIdentity();
}

In my AuthController, I do the following to make sure my form points to the full url:

$uri = str_replace('/auth/login','',$_SERVER['REQUEST_URI']);
if (strlen($uri) > 0)
    $form->setAction($this->_helper->url('login') . $uri);
else
    $form->setAction($this->_helper->url('login'));

If the login validates, then I do the following:

if (strlen($uri) > 0)
 $this->_redirect($uri);
else
 $this->_redirect('/');
Mark
obviously before your application goes live, you'll want to sanitize $uri, which I haven't done yet, once you know all possible accepted inputs
Mark
+3  A: 

What I have found as a simple method to accomplish this is just to have a hidden field in your login form.

Now, im not sure if your login form is a generic HTML element or is actually an instance of Zend_Form, but if its an instance of Zend_Form, you could simple add the following:

$this->addElement('hidden', 'return', array(
     'value' => Zend_Controller_Front::getInstance()->getRequest()->getRequestUri(),    
      ));

Then in my authentication script, like the comment above, I have a simple redirect that uses the value passed in to return them to the same page.

$this->_redirect($this->_request->getPost('return'));

Obviously in these two examples, they are just written to compact the code and probably do not represent the best way to accomplish it. The two methods using the getRequest() in my code are actually not embedded in the redirect or the addElement, but for example purposes I just slid them in there.

The answer above will obviously work as well, unless you have some massive page redirection going on. The main reason I am running mine this way right now is because not all of my forms are running in Zend_Form and its also nice being able to change the value of the hidden return input text box for testing purposes.

Jesta
Thank you Mark and Jesta,I prefer Jesta Answer. because am doing lots of routing, forwarding, preDispatch functions and so on!!! Thank you anyway. I wait for more answers.
Morteza M.
This solution doesn't work always.
sanders
+2  A: 

I have a predispatch hook in plugin for authorization. In it if (and only if) user needs to be logged I save the request URI to session and after logging in I redirect there. There is no overhead except when redirecting to login form. But that's a case where overhead is no problem. :)

if(!$auth->hasIdentity()){
  $this->_insertLastUrlToSession();
  $this->redirect('/index/login');
} else {
  //no overhead
}
Tomáš Fejfar
Thank you all,So, I will use new Zend_Session_Namespace instead of Zend_Session::start() in _insertLastUrlToSession helper,ha? Thank you.
Morteza M.
+3  A: 

Basically the same thing that Jesta is doing in his answer, but I added the following functions to my "MW_Form" class - which is a superclass of all my forms - easy enough to $form->trackReferrer($this->getRequest()); from the controller with any form. The getReferrer() function takes a "default" argument (which if the user has REFERER headers disabled, or there is no referrer - your going to want a default place to redirect back to)

  /**
   * Adds a form element named "referrer" and sets its default value to either
   * the 'referrer' param from the request, or the HTTP_REFERER header.
   *
   * @param Zend_Controller_Request_Abstract $request 
   * @return MW_Form
   * @author Corey Frang
   */
  public function trackReferrer(Zend_Controller_Request_Abstract $request)
  {
    $this->addElement('hidden', 'referrer');
    $this->setDefault('referrer', 
      $request->getParam('referrer', 
        $request->getServer('HTTP_REFERER')));
        // HTTP_REFERER not HTTP_REFERRER - grrr HTTP spec misspellings

    // use no decorator for the actual form element
    $this->referrer->setDecorators(array()); 

    // use our custom "referrer" decorator to stick the hidden before the <dl>
    $decorators = $this->getDecorators();
    $this->clearDecorators();
    foreach ($decorators as $class=>$decorator)
    {
      if (substr($class,-5) == '_Form') {
        $this->addDecorator('Referrer');
        $added = true;
      }
      $this->addDecorator($decorator);
    }
    if (!$added) $this->addDecorator('Referrer');

    return $this;
  }

  /**
   * Returns the referrer field if it exists.
   *
   * @return string | false
   * @param mixed $default The value to return if referrer isn't set
   * @author Corey Frang
   **/
  public function getReferrer($default = false)
  {
    if (!isset($this->referrer)) return $default;
    $val = $this->referrer->getValue();
    if ($val) return $val;
    return $default;
  }

The Decorator Used - gives you the added benifit of not using up any rows in the <dl> created by zend_form:

class MW_Form_Decorator_Referrer extends Zend_Form_Decorator_Abstract  {
  /**
   * Attaches the standard "ViewHelper" decorator for the 'referrer' element
   * prepended on the content
   *
   * @return void
   * @author Corey Frang
   **/
  public function render($content)
  {
    $form = $this->getElement();
    if ($form instanceOf MW_Form)
    {
      $referrer = $form->referrer;
      if ($referrer)
      {
        $decorator = new Zend_Form_Decorator_ViewHelper(array('placement'=>self::PREPEND));
        $decorator->setElement($referrer);
        return $decorator->render($content);
      }
    }
    return "Error - No Referrer Found".$content;
  }
}

Example Usage (from a controller):

$form = $description->getEditForm();
$form->trackReferrer($this->_request);
if ($this->_request->isPost())
{
  if ($form->process($this->_request->getPost()))
  {
    return $this->_redirect($form->getReferrer('/page'));
  }
}
gnarf
Gnarf looks nice. But in which file does the "trackReferrer" function go? What type of class is it? Is it a helper or what?
sanders
It's in my `My_Form` class which extends `Zend_Form` and all my forms extend from this....
gnarf
A: 

I see this already has an answer, but i'd like to throw mine in too, just as a different way to skin the cat sort of deal, using static methods.

class App_Helpers_LastVisited {
    /**
     * Example use:
     * App_Helpers_LastVisited::saveThis($this->_request->getRequestUri());
     */
    public static function saveThis($url) {
        $lastPg = new Zend_Session_Namespace('history');
        $lastPg->last = $url;
        //echo $lastPg->last;// results in /controller/action/param/foo
    }

    /**
     * I typically use redirect:
     * $this->_redirect(App_Helpers_LastVisited::getLastVisited());
     */
    public static function getLastVisited() {
        $lastPg = new Zend_Session_Namespace('history');
        if(!empty($lastPg->last)) {
            $path = $lastPg->last;
            $lastPg->unsetAll();
            return $path;
        }

        return ''; // Go back to index/index by default;
     }
}

This doesn't run all the time, only on a need to basis.

That's the whole code, part of my blog post here (http://hewmc.blogspot.com/2010/08/simple-way-to-store-last-visited-url-in.html)

doingsitups
Thank you. I read your blog post and this answer. you know, accepted answer in this question is really simple when using forms in the case application doesn't use sessions at all. but it is usual for web applications to use sessions, so adding another key/value pair to store previous url doesn't make sense and your answer is helpful.
Morteza M.
Thanks for your reply, very kind of you. And thanks for visiting my blog, which I normally have ahem, no visitors :) . Actually I have updated that blog post, but only that my attitude is more inclusive of other use cases, the code is not changed. Have a good day!
doingsitups
A: 

In addition to gnarfs answer, i modified it to making it validate - for those of you that get a kick of it.

$this->addDecorator(array('WrapClose' => 'HtmlTag'), array('tag' => 'div', 'placement' => 'PREPEND', 'closeOnly' => true));
$this->addDecorator('Referrer'); 
$this->addDecorator(array('WrapOpen' => 'HtmlTag'), array('tag' => 'div', 'placement' => 'PREPEND', 'openOnly' => true));
Phliplip