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);
else $url = "./";
Director::redirect($url);
die();
return;
}
singleton('SiteTree')->extend('contentcontrollerInit', $this);

View File

@ -23,8 +23,8 @@ class ContentNegotiator {
}
static function process($content) {
if(self::$disabled) return $content;
static function process(HTTPResponse $response) {
if(self::$disabled) return;
$mimes = array(
"xhtml" => "application/xhtml+xml",
@ -56,35 +56,39 @@ class 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
if(substr($content,0,5) == '<' . '?xml' /*|| $_REQUEST['ajax']*/ ) {
header("Content-type: application/xhtml+xml; charset=" . self::$encoding);
header("Vary: Accept");
if(substr($content,0,5) == '<' . '?xml' ) {
$response->addHeader("Content-type", "application/xhtml+xml; charset=" . self::$encoding);
$response->addHeader("Vary" , "Accept");
$content = str_replace('&nbsp;','&#160;', $content);
$content = str_replace('<br>','<br />', $content);
$content = eregi_replace('(<img[^>]*[^/>])>','\\1/>', $content);
return $content;
$response->setBody($content);
} else {
return $this->html($content);
return $this->html($response);
}
}
function html($content) {
if(!headers_sent()) {
header("Content-type: text/html; charset=" . self::$encoding);
header("Vary: Accept");
}
function html(HTTPResponse $response) {
$response->addHeader("Content-type", "text/html; charset=" . self::$encoding);
$response->addHeader("Vary", "Accept");
$content = $response->getBody();
$content = ereg_replace("<\\?xml[^>]+\\?>\n?",'',$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('<html xmlns="[^"]+"','<html ', $content);
return $content;
$response->setBody($content);
}
protected static $disabled;

View File

@ -20,6 +20,11 @@ class Controller extends ViewableData {
protected $basicAuthEnabled = true;
/**
* The HTTPResponse object that the controller returns
*/
protected $response;
function setURLParams($urlParams) {
$this->urlParams = $urlParams;
}
@ -38,8 +43,11 @@ class Controller extends ViewableData {
protected $baseInitCalled = false;
function run($requestParams) {
if(isset($_GET['debug_profile'])) Profiler::mark("Controller", "run");
Controller::$currentController = $this;
$this->response = new HTTPResponse();
$this->requestParams = $requestParams;
$this->action = isset($this->urlParams['Action']) ? str_replace("-","_",$this->urlParams['Action']) : "index";
// Init
@ -47,6 +55,9 @@ class Controller extends ViewableData {
$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 we had a redirection or something, halt processing.
if($this->response->isFinished()) return $this->response;
// Look at the action variables for forms
foreach($this->requestParams as $paramName => $paramVal) {
if(substr($paramName,0,7) == 'action_') {
@ -93,7 +104,7 @@ class Controller extends ViewableData {
// disregard validation if a single field is called
if(!isset($_REQUEST['action_callfieldmethod'])) {
$valid = $form->beforeProcessing();
if(!$valid) exit();
if(!$valid) return $this->response;
}
// 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_profile'])) Profiler::mark("$this->class::$funcName (controller action)");
$result = $this->$funcName($this->urlParams);
if(isset($_GET['debug_profile'])) Profiler::unmark("$this->class::$funcName (controller action)");
@ -164,13 +176,16 @@ class Controller extends ViewableData {
$result = $viewer->process($extended);
}
if($result) $result = ContentNegotiator::process($result);
$this->response->setBody($result);
if($result) ContentNegotiator::process($this->response);
// Set up HTTP cache headers
HTTP::add_cache_headers();
HTTP::add_cache_headers($this->response);
if(isset($_GET['debug_profile'])) Profiler::unmark("Controller", "run");
return $result;
return $this->response;
}
function defaultAction($action) {
@ -234,8 +249,6 @@ class Controller extends ViewableData {
Cookie::set("PastMember", true);
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->baseInitCalled = true;
@ -245,6 +258,13 @@ class Controller extends ViewableData {
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.
* @param perm The permission to be checked, such as 'View'.
@ -303,6 +323,19 @@ class Controller extends ViewableData {
function PastMember() {
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) {
if(isset($_GET['debug_profile'])) Profiler::mark("Director","direct");
$controllerObj = Director::getControllerForURL($url);
if(is_string($controllerObj) && substr($controllerObj,0,9) == 'redirect:') {
Director::redirect(substr($controllerObj, 9));
} 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");
echo $output;
$response->output();
if(isset($_GET['debug_profile'])) Profiler::unmark("Outputting to browser");
}
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.
*/
static 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;
}
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();
Controller::curr()->redirect($url);
}
/**

View File

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

View File

@ -6,12 +6,16 @@
class RootURLController extends Controller {
protected static $is_at_root = false;
/**
* Marks at that we are actually at the root URL before handing control over to another controller
*/
function index() {
public function run($requestParams) {
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();
die();
}
}
?>

View File

@ -637,4 +637,14 @@ class Form extends ViewableData {
static function set_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 {
Director::redirect("Security/login");
}
exit();
return;
}
function LoginForm() {