Jump to:

23491 Posts in 18996 Topics by 2878 members

General Questions

SilverStripe Forums » General Questions » RedirectorPage and Director rules conflict?

General questions about getting started with SilverStripe that don't fit in any of the categories above.

Moderators: martimiz, Sean, biapar, Willr, Ingo, swaiba, simon_w

Page: 1 2
Go to End
Author Topic: 2721 Views
  • aragonne
    Avatar
    Community Member
    26 Posts

    RedirectorPage and Director rules conflict? Link to this post

    Hi there,

    I have a weird situation where a request for non-existent page doesn't send the visitor to the 404 Error page but instead to a parent page.

    For example, a request is made for:

    /understanding/analytes/xyz.

    'xyz' is a non-existent (or a unpublished page). Normally the visitor should be sent to the 404 page, but instead is sent to the 'analytes' page, which is an existing page that is, of course, the parent of 'xyz'. I think the reason for this is that the Director rule:

    '$URLSegment//$Action/$ID/$OtherID' => 'ModelAsController

    in sapphire/_config.php, is matched, where

    $URLSegment = /understanding/analytes/
    $Action = xyz
    $ID = null
    $OtherID = null

    Instead of redirecting to the 404 page, I think SilverStripe is looking for a method called 'xyz' in the page type for /understanding/analytes. However, /understanding/analytes is a RedirectorPage and since its init() method is the first thing executed, the user is sent to the redirect page.

    I need to figure out how to create a subclass of RedirectorPage that when an Action param exists in the url, send the user to the 404 page instead of redirecting to the intended page. If an Action param doesn't exist, execute the normal redirect behavior.

    I tried the following subclass code but got the error below:

    class Redirector404Page extends RedirectorPage {}

    class Redirector404Page_Controller extends RedirectorPage_Controller {
       function init() {
          $action = $this->request->param('Action');
          if ($action) {
             Director::redirect('/page-not-found', 404);
          }
          parent::init();
       }
    }

    Error message:

    [User Warning] Already directed to /page-not-found; now trying to direct to /map/aindex/
    GET /understanding/analytes/abc/def?debug_request=1

    Line 464 in /Users/steve/__projects/lto_ss/us/silverstripe-us/sapphire/core/control/Controller.php
    Source

    455       }
    456    }
    457    
    458    /**
    459     * Redirct to the given URL.
    460     * It is generally recommended to call Director::redirect() rather than calling this function directly.
    461     */
    462    function redirect($url, $code=302) {
    463       if($this->response->getHeader('Location')) {
    464          user_error("Already directed to " . $this->response->getHeader('Location') . "; now trying to direct to $url", E_USER_WARNING);
    465          return;
    466       }
    467
    468       // Attach site-root to relative links, if they have a slash in them
    469       if($url == "" || $url[0] == '?' || (substr($url,0,4) != "http" && $url[0] != "/" && strpos($url,'/') !== false)){
    470          $url = Director::baseURL() . $url;

    Trace

    Already directed to /page-not-found; now trying to direct to /map/aindex/
    Line 464 of Controller.php
    Controller->redirect(/map/aindex/,301)
    Line 410 of Director.php
    Director::redirect(/map/aindex/,301)
    Line 159 of RedirectorPage.php
    RedirectorPage_Controller->init()
    Line 26 of Redirector404Page.php
    Redirector404Page_Controller->init()
    Line 136 of Controller.php
    Controller->handleRequest(SS_HTTPRequest)
    Line 199 of ContentController.php
    ContentController->handleRequest(SS_HTTPRequest)
    Line 184 of ContentController.php
    ContentController->handleRequest(SS_HTTPRequest)
    Line 67 of ModelAsController.php
    ModelAsController->handleRequest(SS_HTTPRequest)
    Line 282 of Director.php
    Director::handleRequest(SS_HTTPRequest,Session)
    Line 125 of Director.php
    Director::direct(/understanding/analytes/xyz)
    Line 127 of main.php

    Can anyone help with advice on what code I can use in this subclass' init() method to send the user to a 404 page if an Action param exists?

    Also, if there is a solution, is there away to specify the 404 error page, without hardcoding it with its URLSegment, /page-not-found?

    thanks!

  • martimiz
    Avatar
    Forum Moderator
    1086 Posts

    Re: RedirectorPage and Director rules conflict? Link to this post

    Something like this maybe?


    if (!empty($action)) {
    $this->httpError(404, 'The requested page could not be found.');
    }

    [EDIT] Possibly better yet:

    if($response = ErrorPage::response_for(404)) {
       return $response;
    } else {
       $this->httpError(404, 'The requested page could not be found.');
    }

  • aragonne
    Avatar
    Community Member
    26 Posts

    Re: RedirectorPage and Director rules conflict? Link to this post

    Thanks for the tip martimiz!

    I updated the subclass' code to:

    <?php

    class Redirector404Page extends RedirectorPage {

    }

    class Redirector404Page_Controller extends RedirectorPage_Controller {
       function init() {
          $action = $this->request->param('Action');
          if ($action) {
             if(response = ErrorPage::response_for(404)) {
              return response;
             } else {
              $this->httpError(404, 'The requested page could not be found.');
             }
          }
          parent::init();
       }
    }

    but now getting this error when requesting the page:

    [User Warning] init() method on class 'Redirector404Page_Controller' doesn't call Controller::init(). Make sure that you have parent::init() included.

    However, I don't want to call RedirectorPage's (the parent class) init() method unless the if block fails. How can I get around this?

    thanks!

  • martimiz
    Avatar
    Forum Moderator
    1086 Posts

    Re: RedirectorPage and Director rules conflict? Link to this post

    I would think you could add it at the bottom of the function, because if a 404 is generated beforehand, it won't get to that point anyway - [EDIT] but I see you already did that...

  • martimiz
    Avatar
    Forum Moderator
    1086 Posts

    Re: RedirectorPage and Director rules conflict? Link to this post

    I'm beginning to think you shouldn't use the init() method at all, but instead extend the handleRequest method. The rest would stay almost the same:

    function handleRequest(SS_HTTPRequest $request) {

       $action = $request->param('Action');
       if ($action) {
          if (response = ErrorPage::response_for(404)) {
             return response;
          } else {
             return $this->httpError(404, 'The requested page could not be found.');
          }
       }
       return parent::handleRequest($request);
    }

    Again - didn't test it. What do you think?

  • aragonne
    Avatar
    Community Member
    26 Posts

    Re: RedirectorPage and Director rules conflict? Link to this post

    Hi martimiz,

    Wow, thanks, this gets me a lot closer now and helps me understand the order of execution in which methods are called. I initially thought init() was the first method called, but now realize handleRequest() comes before that.

    A request for an unpublished/non-existent child page of this new Redirector404Page now correctly returns the 404 page. However, a request for any existing/published child page of Redirector404Page now also returns a 404, which should not be the case. The check for the 'Action' param doesn't appear to be the correct check as I initially thought. I think this is because SilverStripe tries to match the Director rules first before trying to find the actual page?

    For example, let's say an existing/published page '/understanding/analytes/sodium' is requested. Before page type reassignment to this new subclass, SilverStripe would have correctly returned this page. Now, what I think what is happening is:

    1. SilverStripe receives the request
    2. the url matches a Director rule '$URLSegment//$Action/$ID/$OtherID' first instead of finding if there is actually a page with this Link pattern
    3. SS resolves the URLSegment as '/understanding/analytes' (a Redirector404Page) and that an Action param ('sodium') exists.
    4. Of course, now in the handleRequest() method of Redirector404Page, SS sees the Action param exists and returns the 404 page.

    I was thinking of changing from the check for the existence of the Action param to a check for the existence of a published page associated with the requested url, ie, /understanding/analytes/sodium. I found that the requested url can be fetched via request->getURL(). But after this, I'm somewhat lost as how to check if the page exists and if it does, return the page instead of the 404.

    In pseudo-code:

    $url = $request->getURL();
    if (any page object Link matches with $url) {
    return the page
    }
    else {
    # same code you suggested goes below
    if (action param exists) {
    return 404
    }
    else {
    redirect as usual
    }
    }

    thanks for helping out martimiz!

    (p.s. Is there a fresher way to think about this problem that I'm missing?)

  • martimiz
    Avatar
    Forum Moderator
    1086 Posts

    Re: RedirectorPage and Director rules conflict? Link to this post

    Hi aragonne,

    I've just now installed your redirectorpage to reproduce things. I think this really is a bug, since other pagetypes don't behave this way at all. But I can't seem to grasp what causes it - I guess it would take some serious core knowledge So I wouldn't delve too miserably deep in HandleRequest after all - unless for finding the real bugfix.

    Your init() version almost works - if not for the Controller::init() warning I've see other bits of core code where they manually disable init() at some point to avoid loops... So I tried manually 'fooling' Controller::init() in thinking it has been called. Not nice, but it does work. And as it's only your own 404redirector page that is affected, we might be in the clear for now?

       function init() {
          $action = $this->request->param('Action');
          if ($action) {
              
              // fool Controller::init() - brrrrr...
              $this->baseInitCalled = true;

             if($response = ErrorPage::response_for(404)) {
                return $response;
             } else {
                Director::redirect('/page-not-found', 404);
             }
             return;
          }
          parent::init();
       }

    Btw: $this->httpError(404, 'The requested page could not be found!!'); doesn't seem to work at all in this spot...

  • aragonne
    Avatar
    Community Member
    26 Posts

    Re: RedirectorPage and Director rules conflict? Link to this post

    Hi martimiz,

    I totally agree with you that this is a bug of RedirectorPage. The use case is that any http request for a descendant unpublished (or non-existing) page of RedirectorPage will always return the redirect to page instead of the 404 page. I've submitted a bug ticket at http://open.silverstripe.org/ticket/6832.

    Your recent suggestion appears to be good temporary workaround and I will use it until a patch is hopefully delivered. Here is the working code:

    class Redirector404Page extends RedirectorPage {}

    class Redirector404Page_Controller extends RedirectorPage_Controller {

       function init() {
          $action = $this->request->param('Action');
          if ($action) {

             // NOTE: fool Controller::init() by setting baseInitCalled, otherwise will
             //          get following error:
             //          [User Warning] init() method on class
             //          'Redirector404Page_Controller'
             //          doesn't call Controller::init(). Make sure that you have
             //          parent::init() included.
             $this->baseInitCalled = true;

             if ($response = ErrorPage::response_for(404)) {
                return $response;
             }
             else {
                return $this->httpError(404, 'The requested page could not be found.');
             }
          }
          parent::init();
       }
    }

    thanks again for the gracious help!
    Steve

    2721 Views
Page: 1 2
Go to Top

Want to know more about the company that brought you SilverStripe? Then check out SilverStripe.com

Comments on this website? Please give feedback.