Added HTTPResponse object, to encapsulate Controller responses for aid testing and other 'quirky' uses of Controllers

git-svn-id: svn://svn.silverstripe.com/silverstripe/open/modules/sapphire/trunk@40390 467b73ca-7a2a-4603-9d3b-597d59a354a9
This commit is contained in:
Sam Minnee 2007-08-17 03:09:46 +00:00
parent 801b94b3bf
commit 8a0688aa5d
9 changed files with 91 additions and 49 deletions

View File

@ -88,7 +88,7 @@ class ContentController extends Controller {
if($getVars) $url = "./?" . http_build_query($getVars); if($getVars) $url = "./?" . http_build_query($getVars);
else $url = "./"; else $url = "./";
Director::redirect($url); Director::redirect($url);
die(); return;
} }
singleton('SiteTree')->extend('contentcontrollerInit', $this); singleton('SiteTree')->extend('contentcontrollerInit', $this);

View File

@ -23,8 +23,8 @@ class ContentNegotiator {
} }
static function process($content) { static function process(HTTPResponse $response) {
if(self::$disabled) return $content; if(self::$disabled) return;
$mimes = array( $mimes = array(
"xhtml" => "application/xhtml+xml", "xhtml" => "application/xhtml+xml",
@ -56,35 +56,39 @@ class ContentNegotiator {
} }
$negotiator = new ContentNegotiator(); $negotiator = new ContentNegotiator();
return $negotiator->$chosenFormat($content); $negotiator->$chosenFormat( $response );
} }
function xhtml($content) { function xhtml(HTTPResponse $response) {
$content = $response->getBody();
// Only serve "pure" XHTML if the XML header is present // Only serve "pure" XHTML if the XML header is present
if(substr($content,0,5) == '<' . '?xml' /*|| $_REQUEST['ajax']*/ ) { if(substr($content,0,5) == '<' . '?xml' ) {
header("Content-type: application/xhtml+xml; charset=" . self::$encoding); $response->addHeader("Content-type", "application/xhtml+xml; charset=" . self::$encoding);
header("Vary: Accept"); $response->addHeader("Vary" , "Accept");
$content = str_replace('&nbsp;','&#160;', $content); $content = str_replace('&nbsp;','&#160;', $content);
$content = str_replace('<br>','<br />', $content); $content = str_replace('<br>','<br />', $content);
$content = eregi_replace('(<img[^>]*[^/>])>','\\1/>', $content); $content = eregi_replace('(<img[^>]*[^/>])>','\\1/>', $content);
return $content;
$response->setBody($content);
} else { } else {
return $this->html($content); return $this->html($response);
} }
} }
function html($content) { function html(HTTPResponse $response) {
if(!headers_sent()) { $response->addHeader("Content-type", "text/html; charset=" . self::$encoding);
header("Content-type: text/html; charset=" . self::$encoding); $response->addHeader("Vary", "Accept");
header("Vary: Accept");
} $content = $response->getBody();
$content = ereg_replace("<\\?xml[^>]+\\?>\n?",'',$content); $content = ereg_replace("<\\?xml[^>]+\\?>\n?",'',$content);
$content = str_replace(array('/>','xml:lang','application/xhtml+xml'),array('>','lang','text/html'), $content); $content = str_replace(array('/>','xml:lang','application/xhtml+xml'),array('>','lang','text/html'), $content);
$content = ereg_replace('<!DOCTYPE[^>]+>', '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">', $content); $content = ereg_replace('<!DOCTYPE[^>]+>', '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">', $content);
$content = ereg_replace('<html xmlns="[^"]+"','<html ', $content); $content = ereg_replace('<html xmlns="[^"]+"','<html ', $content);
return $content; $response->setBody($content);
} }
protected static $disabled; protected static $disabled;

View File

@ -19,6 +19,11 @@ class Controller extends ViewableData {
protected static $currentController; protected static $currentController;
protected $basicAuthEnabled = true; protected $basicAuthEnabled = true;
/**
* The HTTPResponse object that the controller returns
*/
protected $response;
function setURLParams($urlParams) { function setURLParams($urlParams) {
$this->urlParams = $urlParams; $this->urlParams = $urlParams;
@ -38,14 +43,20 @@ class Controller extends ViewableData {
protected $baseInitCalled = false; protected $baseInitCalled = false;
function run($requestParams) { function run($requestParams) {
if(isset($_GET['debug_profile'])) Profiler::mark("Controller", "run"); if(isset($_GET['debug_profile'])) Profiler::mark("Controller", "run");
Controller::$currentController = $this;
$this->response = new HTTPResponse();
$this->requestParams = $requestParams; $this->requestParams = $requestParams;
$this->action = isset($this->urlParams['Action']) ? str_replace("-","_",$this->urlParams['Action']) : "index"; $this->action = isset($this->urlParams['Action']) ? str_replace("-","_",$this->urlParams['Action']) : "index";
// Init // Init
$this->baseInitCalled = false; $this->baseInitCalled = false;
$this->init(); $this->init();
if(!$this->baseInitCalled) user_error("init() method on class '$this->class' doesn't call Controller::init(). Make sure that you have parent::init() included.", E_USER_WARNING); if(!$this->baseInitCalled) user_error("init() method on class '$this->class' doesn't call Controller::init(). Make sure that you have parent::init() included.", E_USER_WARNING);
// If we had a redirection or something, halt processing.
if($this->response->isFinished()) return $this->response;
// Look at the action variables for forms // Look at the action variables for forms
foreach($this->requestParams as $paramName => $paramVal) { foreach($this->requestParams as $paramName => $paramVal) {
@ -93,7 +104,7 @@ class Controller extends ViewableData {
// disregard validation if a single field is called // disregard validation if a single field is called
if(!isset($_REQUEST['action_callfieldmethod'])) { if(!isset($_REQUEST['action_callfieldmethod'])) {
$valid = $form->beforeProcessing(); $valid = $form->beforeProcessing();
if(!$valid) exit(); if(!$valid) return $this->response;
} }
// If the action wasnt' set, choose the default on the form. // If the action wasnt' set, choose the default on the form.
@ -143,6 +154,7 @@ class Controller extends ViewableData {
if(isset($_GET['debug_controller'])) Debug::show("Found function $funcName on the $this->class controller"); if(isset($_GET['debug_controller'])) Debug::show("Found function $funcName on the $this->class controller");
if(isset($_GET['debug_profile'])) Profiler::mark("$this->class::$funcName (controller action)"); if(isset($_GET['debug_profile'])) Profiler::mark("$this->class::$funcName (controller action)");
$result = $this->$funcName($this->urlParams); $result = $this->$funcName($this->urlParams);
if(isset($_GET['debug_profile'])) Profiler::unmark("$this->class::$funcName (controller action)"); if(isset($_GET['debug_profile'])) Profiler::unmark("$this->class::$funcName (controller action)");
@ -163,14 +175,17 @@ class Controller extends ViewableData {
$result = $viewer->process($extended); $result = $viewer->process($extended);
} }
$this->response->setBody($result);
if($result) $result = ContentNegotiator::process($result); if($result) ContentNegotiator::process($this->response);
// Set up HTTP cache headers // Set up HTTP cache headers
HTTP::add_cache_headers(); HTTP::add_cache_headers($this->response);
if(isset($_GET['debug_profile'])) Profiler::unmark("Controller", "run"); if(isset($_GET['debug_profile'])) Profiler::unmark("Controller", "run");
return $result;
return $this->response;
} }
function defaultAction($action) { function defaultAction($action) {
@ -234,8 +249,6 @@ class Controller extends ViewableData {
Cookie::set("PastMember", true); Cookie::set("PastMember", true);
DB::query("UPDATE Member SET LastVisited = NOW() WHERE ID = $member->ID", null); DB::query("UPDATE Member SET LastVisited = NOW() WHERE ID = $member->ID", null);
} }
Controller::$currentController = $this;
// This is used to test that subordinate controllers are actually calling parent::init() - a common bug // This is used to test that subordinate controllers are actually calling parent::init() - a common bug
$this->baseInitCalled = true; $this->baseInitCalled = true;
@ -244,6 +257,13 @@ class Controller extends ViewableData {
public static function currentController() { public static function currentController() {
return Controller::$currentController; return Controller::$currentController;
} }
/**
* Returns the current controller
*/
public static function curr() {
return Controller::$currentController;
}
/** /**
* Returns true if the member is allowed to do the given action. * Returns true if the member is allowed to do the given action.
@ -303,6 +323,19 @@ class Controller extends ViewableData {
function PastMember() { function PastMember() {
return Cookie::get("PastMember") ? true : false; return Cookie::get("PastMember") ? true : false;
} }
/**
* Handle redirection
*/
function redirect($url) {
// Attach site-root to relative links, if they have a slash in them
if(substr($url,0,4) != "http" && $url[0] != "/" && strpos($url,'/') !== false){
$url = Director::baseURL() . $url;
}
$this->response->redirect($url);
}
} }
?> ?>

View File

@ -42,13 +42,17 @@ class Director {
function direct($url) { function direct($url) {
if(isset($_GET['debug_profile'])) Profiler::mark("Director","direct"); if(isset($_GET['debug_profile'])) Profiler::mark("Director","direct");
$controllerObj = Director::getControllerForURL($url); $controllerObj = Director::getControllerForURL($url);
if(is_string($controllerObj) && substr($controllerObj,0,9) == 'redirect:') { if(is_string($controllerObj) && substr($controllerObj,0,9) == 'redirect:') {
Director::redirect(substr($controllerObj, 9)); Director::redirect(substr($controllerObj, 9));
} else if($controllerObj) { } else if($controllerObj) {
$output = $controllerObj->run(array_merge((array)$_GET, (array)$_POST, (array)$_FILES)); $response = $controllerObj->run(array_merge((array)$_GET, (array)$_POST, (array)$_FILES));
if(isset($_GET['debug_profile'])) Profiler::mark("Outputting to browser"); if(isset($_GET['debug_profile'])) Profiler::mark("Outputting to browser");
echo $output; $response->output();
if(isset($_GET['debug_profile'])) Profiler::unmark("Outputting to browser"); if(isset($_GET['debug_profile'])) Profiler::unmark("Outputting to browser");
} }
if(isset($_GET['debug_profile'])) Profiler::unmark("Director","direct"); if(isset($_GET['debug_profile'])) Profiler::unmark("Director","direct");
} }
@ -158,20 +162,7 @@ class Director {
* - if it is just a word without an slashes, then it redirects to another action on the current controller. * - if it is just a word without an slashes, then it redirects to another action on the current controller.
*/ */
static function redirect($url) { static function redirect($url) {
// Attach site-root to relative links, if they have a slash in them Controller::curr()->redirect($url);
if(substr($url,0,4) != "http" && $url[0] != "/" && strpos($url,'/') !== false){
$url = Director::baseURL() . $url;
}
if(headers_sent($file, $line)) {
echo
"<p>Redirecting to <a href=\"$url\" title=\"Please click this link if your browser does not redirect you\">$url... (output started on $file, line $line)</a></p>
<meta http-equiv=\"refresh\" content=\"1; url=$url\" />
<script type=\"text/javascript\">setTimeout('window.location.href = \"$url\"', 50);</script>";
} else {
header("Location: $url");
}
die();
} }
/** /**

View File

@ -9,7 +9,8 @@ class ModelAsController extends Controller implements NestedController {
public function run($requestParams) { public function run($requestParams) {
$this->init(); $this->init();
return $this->getNestedController()->run($requestParams); $nested = $this->getNestedController();
return $nested->run($requestParams);
} }
public function init() { public function init() {

View File

@ -5,13 +5,17 @@
*/ */
class RootURLController extends Controller { class RootURLController extends Controller {
protected static $is_at_root = false; protected static $is_at_root = false;
/** public function run($requestParams) {
* Marks at that we are actually at the root URL before handing control over to another controller
*/
function index() {
self::$is_at_root = true; self::$is_at_root = true;
Director::direct(self::get_homepage_urlsegment() . '/');
$controller = new ModelAsController();
$controller->setUrlParams(array(
'URLSegment' => self::get_homepage_urlsegment(),
'Action' => '',
));
return $controller->run($requestParams);
} }
/** /**

View File

@ -87,7 +87,6 @@ class RedirectorPage_Controller extends Page_Controller {
} }
parent::init(); parent::init();
die();
} }
} }
?> ?>

View File

@ -637,4 +637,14 @@ class Form extends ViewableData {
static function set_current_action($action) { static function set_current_action($action) {
self::$current_action = $action; self::$current_action = $action;
} }
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// TESTING HELPERS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
function testSubmission($action, $data) {
$data['action_' . $action] = true;
//$this->controller->run()
}
} }

View File

@ -70,7 +70,7 @@ class Security extends Controller {
} else { } else {
Director::redirect("Security/login"); Director::redirect("Security/login");
} }
exit(); return;
} }
function LoginForm() { function LoginForm() {