21489 Posts in 5783 Topics by 2621 members
| Go to End | Next > | |
| Author | Topic: | 1438 Views |
-
RedirectorPage and Director rules conflict?

8 January 2012 at 3:02pm Last edited: 8 January 2012 3:04pm
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 = nullInstead 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=1Line 464 in /Users/steve/__projects/lto_ss/us/silverstripe-us/sapphire/core/control/Controller.php
Source455 }
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.phpCan 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!
-
Re: RedirectorPage and Director rules conflict?

9 January 2012 at 12:28am Last edited: 9 January 2012 2:52am
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.');
} -
Re: RedirectorPage and Director rules conflict?

9 January 2012 at 10:42pm
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!
-
Re: RedirectorPage and Director rules conflict?

9 January 2012 at 11:51pm Last edited: 9 January 2012 11:55pm
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...
-
Re: RedirectorPage and Director rules conflict?

10 January 2012 at 1:00am Last edited: 10 January 2012 1:02am
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?
-
Re: RedirectorPage and Director rules conflict?

10 January 2012 at 1:05pm Last edited: 11 January 2012 1:37am
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?)
-
Re: RedirectorPage and Director rules conflict?

11 January 2012 at 1:20am
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...
-
Re: RedirectorPage and Director rules conflict?

11 January 2012 at 2:31am Last edited: 11 January 2012 10:47am
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
| 1438 Views | ||
| Go to Top | Next > |

