← Back to team overview

kolibri-discuss team mailing list archive

[Merge] lp:~asteinlein/kolibri/request+response into lp:kolibri

 

Anders Steinlein has proposed merging lp:~asteinlein/kolibri/request+response into lp:kolibri.

Requested reviews:
    Kolibri Dev (kolibri-dev)
-- 
https://code.launchpad.net/~asteinlein/kolibri/request+response/+merge/5529
Your team Kolibri Discuss is subscribed to branch lp:kolibri.
=== modified file 'examples/wishlist/actions/Index.php'
--- examples/wishlist/actions/Index.php	2009-04-07 22:06:43 +0000
+++ examples/wishlist/actions/Index.php	2009-04-14 16:50:50 +0000
@@ -4,7 +4,7 @@
  * page as the result. We also implement ModelAware so any validation errors when adding items
  * are displayed.
  */
-class Index extends ActionSupport implements ModelAware {
+class Index implements MessageAware, ModelAware {
 	public $model;
 	public $items;
 
@@ -16,14 +16,13 @@
 		$dbSetup = new DatabaseSetup();
 		if (!$dbSetup->isDone()) {
 			// Database tables is not set up, so redirect to setup page
-			return new RedirectResult($this, '/setup');
+			return new RedirectResponse('/setup');
 		}
 
-		$items = Models::init('Item');
 		// Notice that this calls findAll() in the ItemDao class
-		$this->items = $items->objects->findAll(); 
+		$this->items = Models::init('Item')->objects->findAll();
 		// Path is relative to views directory, extension omitted
-		return new XsltResult($this, '/index'); 
+		return new XsltResponse($this, '/index');
 	}
 }
 ?>

=== modified file 'examples/wishlist/actions/Setup.php'
--- examples/wishlist/actions/Setup.php	2008-10-20 16:41:10 +0000
+++ examples/wishlist/actions/Setup.php	2009-04-14 16:50:50 +0000
@@ -2,7 +2,7 @@
 /**
  * Action for the setup page -- prepares database tables.
  */
-class Setup extends ActionSupport {
+class Setup implements MessageAware {
 	/**
 	 * Displays the setup page if database is not already prepared.
 	 */
@@ -11,10 +11,10 @@
 
 		if ($dbSetup->isDone()) {
 			$this->msg->setMessage('Database is already prepared.');
-			return new RedirectResult($this, '/');
+			return new RedirectResponse('/');
 		}
 
-		return new XsltResult($this, '/setup');
+		return new XsltResponse($this, '/setup');
 	}
 
 	/**
@@ -24,7 +24,7 @@
 		$dbSetup = new DatabaseSetup();
 		$dbSetup->setup();
 		$this->msg->setMessage('Database prepared. All ready for wishes.');
-		return new RedirectResult($this, '/');
+		return new RedirectResponse('/');
 	}
 }
 ?>

=== modified file 'examples/wishlist/actions/items/Add.php'
--- examples/wishlist/actions/items/Add.php	2009-04-07 20:59:58 +0000
+++ examples/wishlist/actions/items/Add.php	2009-04-14 16:50:50 +0000
@@ -3,7 +3,7 @@
  * Action for adding new item. We implement ModelAware to have the model object populated by
  * request data, and ValidationAware to have it automatically validated.
  */
-class Add extends ActionSupport implements ModelAware, ValidationAware {
+class Add implements MessageAware, ModelAware, ValidationAware {
 	/**
 	 * Defines the model class to instantiate, which will be populated with request data and
 	 * put back into this property.
@@ -17,7 +17,7 @@
 	public function doPost () {
 		$this->model->save();
 		$this->msg->setMessage('Item successfully added.');
-		return new RedirectResult($this, '/');
+		return new RedirectResponse('/');
 	}
 
 	/**
@@ -29,7 +29,7 @@
 	public function validationFailed () {
 		// We could set a custom error message here if we want to override the default. I.e.:
 		// $this->msg->setMessage('The item could not be added to the wishlist', false);
-		return new RedirectResult($this, '/');
+		return new RedirectResponse('/');
 	}
 }
 ?>

=== modified file 'examples/wishlist/actions/items/Del.php'
--- examples/wishlist/actions/items/Del.php	2008-12-03 02:22:07 +0000
+++ examples/wishlist/actions/items/Del.php	2009-04-14 16:50:50 +0000
@@ -3,12 +3,13 @@
  * Action for deleting items. As the item to delete is specified by the last URI element (which does not have 
  * a matching action file), it is implicitly put in the "id" request parameter.
  */
-class Del extends ActionSupport {
+class Del implements MessageAware {
 	/**
 	 * TODO: Should really change to POST via form (and thus doPost()).
 	 */
-	public function doGet () {
-		$itemName = $this->request['id']; // We could also do $this->request->get('id'), whatever you prefer
+	public function doGet ($request) {
+		// We could also do $this->request->get('id'), whatever you prefer
+		$itemName = $request['id'];
 		$item = Models::init('Item');
 
 		// Tries to load the item (notice that this calls load() in the ItemDao class)
@@ -22,7 +23,8 @@
 			$this->msg->setMessage("Item with name $itemName not found.", false);
 		}
 
-		return new RedirectResult($this, '/'); // Redirect back to front page, notice messages are retained
+		// Redirect back to front page, notice messages are retained
+		return new RedirectResponse('/');
 	}
 }
 ?>

=== modified file 'examples/wishlist/actions/items/Have.php'
--- examples/wishlist/actions/items/Have.php	2008-12-03 02:22:07 +0000
+++ examples/wishlist/actions/items/Have.php	2009-04-14 16:50:50 +0000
@@ -2,11 +2,11 @@
 /**
  * Action for marking items as received.
  */
-class Have extends ActionSupport {
+class Have implements MessageAware {
 	// TODO: We should really POST instead...
-	public function doGet () {
+	public function doGet ($request) {
 		$item = Models::init('Item');
-		if ($item->objects->load($this->request['id'])) {
+		if ($item->objects->load($request['id'])) {
 			// Set received date (could of course be done in SQL, but just to show date library in use)
 			$df = DateFormat::getInstance(DateFormat::ISO_8601_DATE);
 			$item->received = $df->format(new Date());
@@ -19,7 +19,7 @@
 			$this->msg->setMessage("Item with name {$this->request['id']} not found.", false);
 		}
 
-		return new RedirectResult($this, '/');
+		return new RedirectResponse('/');
 	}
 }
 ?>

=== modified file 'examples/wishlist/conf/config.php'
--- examples/wishlist/conf/config.php	2009-04-07 23:17:37 +0000
+++ examples/wishlist/conf/config.php	2009-04-14 16:50:50 +0000
@@ -4,7 +4,7 @@
  * get by calling Config::get('key'), where key is the setting you want to return, i.e. 'mail'.
  */
 $config = array(
-		'webRoot'    => 'http://localhost', // Must be absolute URI including scheme. No trailing slash!
+		'webRoot'    => 'http://kolibri', // Must be absolute URI including scheme. No trailing slash!
 		'staticRoot' => '/static',          // URI of static resources (can be another host as http://static.example.com)
 		'locale'     => 'en_US.utf8',
 		'logging'    => array(

=== removed file 'src/actions/ActionSupport.php'
--- src/actions/ActionSupport.php	2008-12-09 23:12:48 +0000
+++ src/actions/ActionSupport.php	1970-01-01 00:00:00 +0000
@@ -1,38 +0,0 @@
-<?php
-require(ROOT . '/actions/SessionAware.php');
-require(ROOT . '/actions/MessageAware.php');
-
-/**
- * This class provides support-functionality needed by most actions. It is entirely optional for
- * actions to extend this class, but most will.
- */
-class ActionSupport implements SessionAware, MessageAware {
-	/**
-	 * The processing request.
-	 * @var Request
-	 */
-	protected $request;
-
-	/**
-	 * The HTTP session, if a <code>SessionInterceptor</code> is in use.
-	 * @var Session
-	 */
-	public $session;
-	
-	/**
-	 * Message facility which may be used to return a message to the user. This is only set if the
-	 * <code>MessageInterceptor</code> is invoked, else it is empty.
-	 * @var Message
-	 */
-	public $msg;
-	
-	/**
-	 * Constructor.
-	 *
-	 * @param Request $request	The request object representing the current HTTP request.
-	 */
-	public function __construct ($request) {
-		$this->request = $request;
-	}
-}
-?>

=== modified file 'src/actions/MessageAware.php'
--- src/actions/MessageAware.php	2008-10-20 16:41:10 +0000
+++ src/actions/MessageAware.php	2009-04-14 16:50:50 +0000
@@ -1,9 +1,9 @@
 <?php
 /**
- * This interface is used by actions that want to return messages to the user. They must also provide a
- * public <code>$msg</code> property to hold the actual <code>Message</code> instance.
- * 
- * @version		$Id: MessageAware.php 1493 2008-04-30 00:00:25Z anders $
+ * This interface is used by actions that want to return messages to the user. The
+ * <code>MessageInterceptor</code> must be configured for the action in order to prepare the
+ * message facility for use, which will be set in an implicit <code>$msg</code> property on
+ * the action.
  */
 interface MessageAware {}
 ?>

=== modified file 'src/actions/ParametersAware.php'
--- src/actions/ParametersAware.php	2008-10-20 16:41:10 +0000
+++ src/actions/ParametersAware.php	2009-04-14 16:50:50 +0000
@@ -1,8 +1,8 @@
 <?php
 /**
- * This interface is used by actions that want properties populated from session and/or request parameters.
- * 
- * @version		$Id: ParametersAware.php 1518 2008-06-30 23:43:38Z anders $
+ * This interface is used by actions that want properties populated from session and/or request
+ * parameters. The <code>ParametersInterceptor</code> must also be configured for the action
+ * for this to have any effect.
  */
 interface ParametersAware {}
 ?>

=== removed file 'src/actions/SessionAware.php'
--- src/actions/SessionAware.php	2008-10-20 16:41:10 +0000
+++ src/actions/SessionAware.php	1970-01-01 00:00:00 +0000
@@ -1,9 +0,0 @@
-<?php
-/**
- * This interface is used by actions that want access to session data. Actions implementing this interface
- * must also expose a public <code>session</code> property.
- * 
- * @version		$Id: SessionAware.php 1518 2008-06-30 23:43:38Z anders $
- */
-interface SessionAware {}
-?>

=== modified file 'src/actions/UploadAware.php'
--- src/actions/UploadAware.php	2008-10-20 16:41:10 +0000
+++ src/actions/UploadAware.php	2009-04-14 16:50:50 +0000
@@ -1,9 +1,8 @@
 <?php
 /**
- * This interface is used by actions that want uploaded files direct put into action instance variables.
- * The <code>UploadInterceptor</code> must be invoked for this to have any effect.
- * 
- * @version		$Id: UploadAware.php 1538 2008-08-03 22:10:45Z anders $
+ * This interface is used by actions that want uploaded files direct put into action instance
+ * variables, or a property on an exposed model. The <code>UploadInterceptor</code> must be
+ * configured for this to have any effect.
  */
 interface UploadAware {}
 ?>

=== modified file 'src/actions/ValidationAware.php'
--- src/actions/ValidationAware.php	2009-04-12 02:11:21 +0000
+++ src/actions/ValidationAware.php	2009-04-14 16:50:50 +0000
@@ -6,14 +6,14 @@
  * validate.
  *
  * If validation fails the <code>validationFailed()</code> method on the action is called for
- * it to return the response it deems appropriate, which is usually a RedirectResult back to
+ * it to return the response it deems appropriate, which is usually a SeeOtherResponse back to
  * the form. If however validation succeeds, normal request processing proceeds.
  */
 interface ValidationAware {
 	/**
-	 * Called when validation failed. A Result object must be returned, which will be rendered
-	 * instead of continuing request processing.
+	 * Called when validation failed. A <code>Response</code> object must be returned, which
+	 * will be rendered instead of continuing request processing.
 	 */
-	public function validationFailed () {}
+	public function validationFailed ();
 }
 ?>

=== modified file 'src/conf/autoload.php'
--- src/conf/autoload.php	2009-04-12 01:09:55 +0000
+++ src/conf/autoload.php	2009-04-14 16:50:50 +0000
@@ -1,52 +1,50 @@
 <?php
 /*
- * Defines classes that may be loaded upon its first use by __autoload(). They are not automatically
- * loaded unless they are actually used. See <code>UtilsInterceptor</code> for automatic loading of utils.
+ * Defines classes that may be loaded upon its first use by __autoload(). They are not
+ * automatically loaded unless they are actually used. See <code>UtilsInterceptor</code> for
+ * automatic loading of utils.
  */
 $autoloadClasses = array(
-		'DefaultActionMapper'    => '/core/DefaultActionMapper.php',
-		'ActionSupport'          => '/actions/ActionSupport.php',
-		'AuthAware'              => '/actions/AuthAware.php',
-		'MessageAware'           => '/actions/MessageAware.php',
-		'ModelAware'             => '/actions/ModelAware.php',
-		'ParametersAware'        => '/actions/ParametersAware.php',
-		'SessionAware'           => '/actions/SessionAware.php',
-		'UploadAware'            => '/actions/UploadAware.php',
-		'ValidationAware'        => '/actions/ValidationAware.php',
-		'DatabaseException'      => '/database/DatabaseException.php',
-		'DatabaseFactory'        => '/database/DatabaseFactory.php',
-		'ResultSet'              => '/database/ResultSet.php',
-		'ResultSetArray'         => '/database/ResultSetArray.php',
-		'SqlException'           => '/database/SqlException.php',
-		'AbstractInterceptor'    => '/interceptors/AbstractInterceptor.php',
-		'AuthInterceptor'        => '/interceptors/AuthInterceptor.php',
-		'ErrorInterceptor'       => '/interceptors/ErrorInterceptor.php',
-		'Interceptor'            => '/interceptors/Interceptor.php',
-		'MessageInterceptor'     => '/interceptors/MessageInterceptor.php',
-		'ModelInterceptor'       => '/interceptors/ModelInterceptor.php',
-		'ParametersInterceptor'  => '/interceptors/ParametersInterceptor.php',
-		'SessionInterceptor'     => '/interceptors/SessionInterceptor.php',
-		'UploadInterceptor'      => '/interceptors/UploadInterceptor.php',
-		'UtilsInterceptor'       => '/interceptors/UtilsInterceptor.php',
-		'ValidationInterceptor'  => '/interceptors/ValidationInterceptor.php',
-		'TransactionInterceptor' => '/interceptors/TransactionInterceptor.php',
-		'DataProvided'           => '/models/DataProvided.php',
-		'ModelProxy'             => '/models/ModelProxy.php',
-		'Models'                 => '/models/Models.php',
-		'Proxy'                  => '/models/Proxy.php',
-		'Validateable'           => '/models/Validateable.php',
-		'ValidateableModelProxy' => '/models/ValidateableModelProxy.php',
-		'AbstractResult'         => '/results/AbstractResult.php',
-		'FileResult'             => '/results/FileResult.php',
-		'ForwardResult'          => '/results/ForwardResult.php',
-		'JsonResult'             => '/results/JsonResult.php',
-		'NotFoundResult'         => '/results/NotFoundResult.php',
-		'PhpResult'              => '/results/PhpResult.php',
-		'RedirectResult'         => '/results/RedirectResult.php',
-		'Result'                 => '/results/Result.php',
-		'SmartyResult'           => '/results/SmartyResult.php',
-		'TextResult'             => '/results/TextResult.php',
-		'XsltResult'             => '/results/XsltResult.php',
-		'Validator'              => '/validation/Validator.php',
-		'ValidationHelper'       => '/validation/ValidationHelper.php');
+		'DefaultActionMapper'       => '/core/DefaultActionMapper.php',
+		'AuthAware'                 => '/actions/AuthAware.php',
+		'MessageAware'              => '/actions/MessageAware.php',
+		'ModelAware'                => '/actions/ModelAware.php',
+		'ParametersAware'           => '/actions/ParametersAware.php',
+		'UploadAware'               => '/actions/UploadAware.php',
+		'ValidationAware'           => '/actions/ValidationAware.php',
+		'DatabaseException'         => '/database/DatabaseException.php',
+		'DatabaseFactory'           => '/database/DatabaseFactory.php',
+		'ResultSet'                 => '/database/ResultSet.php',
+		'ResultSetArray'            => '/database/ResultSetArray.php',
+		'SqlException'              => '/database/SqlException.php',
+		'AbstractInterceptor'       => '/interceptors/AbstractInterceptor.php',
+		'AuthInterceptor'           => '/interceptors/AuthInterceptor.php',
+		'ErrorInterceptor'          => '/interceptors/ErrorInterceptor.php',
+		'Interceptor'               => '/interceptors/Interceptor.php',
+		'MessageInterceptor'        => '/interceptors/MessageInterceptor.php',
+		'ModelInterceptor'          => '/interceptors/ModelInterceptor.php',
+		'ParametersInterceptor'     => '/interceptors/ParametersInterceptor.php',
+		'SessionInterceptor'        => '/interceptors/SessionInterceptor.php',
+		'UploadInterceptor'         => '/interceptors/UploadInterceptor.php',
+		'UtilsInterceptor'          => '/interceptors/UtilsInterceptor.php',
+		'ValidationInterceptor'     => '/interceptors/ValidationInterceptor.php',
+		'TransactionInterceptor'    => '/interceptors/TransactionInterceptor.php',
+		'DataProvided'              => '/models/DataProvided.php',
+		'ModelProxy'                => '/models/ModelProxy.php',
+		'Models'                    => '/models/Models.php',
+		'Proxy'                     => '/models/Proxy.php',
+		'Validateable'              => '/models/Validateable.php',
+		'ValidateableModelProxy'    => '/models/ValidateableModelProxy.php',
+		'FileResponse'              => '/response/FileResponse.php',
+		'JsonResponse'              => '/response/JsonResponse.php',
+		'NotFoundResponse'          => '/response/NotFoundResponse.php',
+		'PermanentRedirectResponse' => '/response/PermanentRedirectResponse.php',
+		'PhpResponse'               => '/response/PhpResponse.php',
+		'RedirectResponse'          => '/response/RedirectResponse.php',
+		'Response'                  => '/response/Response.php',
+		'SeeOtherResponse'          => '/response/SeeOtherResponse.php',
+		'SmartyResponse'            => '/response/SmartyResponse.php',
+		'XsltResponse'              => '/response/XsltResponse.php',
+		'Validator'                 => '/validation/Validator.php',
+		'ValidationHelper'          => '/validation/ValidationHelper.php');
 ?>

=== modified file 'src/core/Dispatcher.php'
--- src/core/Dispatcher.php	2008-10-20 16:41:10 +0000
+++ src/core/Dispatcher.php	2009-04-10 01:43:31 +0000
@@ -39,7 +39,7 @@
 	 * 
 	 * The interceptors mapped to the target action is created and pushed to the dispatcher stack.
 	 *
-	 * @param Request $request				Request object for the interceptors.
+	 * @param Request $request				Request object.
 	 * @param ActionMapping $actionMapping	Mapping to the action to invoke.	
 	 * @return Dispatcher
 	 */
@@ -81,7 +81,7 @@
 		}
 
 		$class = $actionMapping->getActionClass();
-		$this->action = new $class($request);
+		$this->action = new $class();
 		$this->stack = InterceptorFactory::createInterceptors($stack);
 	}
 	
@@ -108,7 +108,7 @@
 				return $result;
 			}
 		}
-		return $this->action->{$this->actionMethod}();
+		return $this->action->{$this->actionMethod}($this->getRequest());
 	}
 
 	public function getRequest () {

=== modified file 'src/core/Request.php'
--- src/core/Request.php	2009-04-03 16:04:34 +0000
+++ src/core/Request.php	2009-04-12 16:32:29 +0000
@@ -22,15 +22,21 @@
 	 * @var string
 	 */
 	public $method;
+
+	/**
+	 * HTTP session if enabled by a <code>SessionInterceptor</code>.
+	 * @var Session
+	 */
+	public $session;
 	
 	/**
-	 * Creates an instance of this class. GET and POST parameters are merged. If any parameter keys
-	 * conflicts, POST parameters override GET parameters.
+	 * Creates an instance of this class. GET and POST parameters are merged. If any parameter
+	 * keys conflicts, POST parameters override GET parameters.
 	 * 
-	 * @param array $get_params		GET parameters for this request.
-	 * @param array $post_params	POST parameters for this request.
-	 * @param string $uri			URI of this request. Leave empty to use the actual request URI
-	 * 								from the client.
+	 * @param array $getParams  GET parameters for this request.
+	 * @param array $postParams POST parameters for this request.
+	 * @param string $uri       URI of this request. Leave empty to use the actual request URI
+	 *                          from the client.
 	 */
 	public function __construct ($getParams, $postParams, $uri = null) {
 		$this->params = array_merge($getParams, $postParams);
@@ -84,23 +90,28 @@
 	}
 
 	/**
-	 * Returns the value of the parameter with the specified key, or <code>NULL</code> if the parameter is
-	 * not found.
+	 * Returns the value of the parameter with the specified key, or <code>null</code> if the
+	 * parameter is not found.
 	 * 
 	 * @param string $key	Key to the parameter to return.
-	 * @return string		Value of the parameter, or <code>NULL</code>.
+	 * @return string		Value of the parameter, or <code>null</code>.
 	 */
 	public function get ($key) {
 		return (isset($this->params[$key]) ? $this->params[$key] : null);
 	}
 
 	/**
-	 * Returns all request parameters.
-	 *
-	 * @return array	Request parameters as key-value pairs.
+	 * Returns the value of the specified request header, or <code>null</code> if the header
+	 * isn't set set.
+	 *
+	 * Note that this is currently only a wrapper for $_SERVER, and thus contains more than
+	 * only the request headers.
+	 *
+	 * @param string $header Header to look for.
+	 * @param string         Value of header, or <code>null</code>.
 	 */
-	public function getAll () {
-		return $this->params;
+	public function getHeader ($header) {
+		return (isset($_SERVER[$header]) ? $_SERVER[$header] : null);
 	}
 
 	/**
@@ -122,8 +133,17 @@
 	}
 	
 	/**
-	 * Puts all the supplied parameters into the parameters for this request. Should only be used internally
-	 * by the framework.
+	 * Checks whether this request has a session associated with it.
+	 *
+	 * @return bool <code>true</code> if session exists, <code>false</code> if not.
+	 */
+	public function hasSession () {
+		return isset($this->session);
+	}
+	
+	/**
+	 * Puts all the supplied parameters into the parameters for this request. Should only be
+	 * used internally by the framework.
 	 *
 	 * @param array $params		An associated array with parameters.
 	 */

=== modified file 'src/core/RequestProcessor.php'
--- src/core/RequestProcessor.php	2008-10-20 16:41:10 +0000
+++ src/core/RequestProcessor.php	2009-04-11 23:30:29 +0000
@@ -37,8 +37,6 @@
 	 * Process the request.
 	 */
 	public function process () {
-		//echo "Processing request [uri: $this->uri] => [action: $this->action_path]...\n";
-
 		// Map the request to an action
 		$mapping = $this->mapper->map();
 		if ($mapping === null) {

=== modified file 'src/core/Session.php'
--- src/core/Session.php	2008-12-09 23:12:48 +0000
+++ src/core/Session.php	2009-04-10 01:43:31 +0000
@@ -30,10 +30,10 @@
 	}
 
 	/**
-	 * Retrieves a specific session parameter, or NULL if not set.
-	 *
-	 * @param mixed $key	Session parameter to retrieve.
-	 * @return mixed		The session parameter, or NULL if not set.
+	 * Returns the value of the session data with the specified key.
+	 * 
+	 * @param string $key	Key to the value to return.
+	 * @return string		Value of the data, or <code>null</code> if not found.
 	 */
 	public function offsetGet ($key) {
 		return (isset($_SESSION[$key]) ? $_SESSION[$key] : null);
@@ -71,10 +71,10 @@
 	 * Returns the value of the session data with the specified key.
 	 * 
 	 * @param string $key	Key to the value to return.
-	 * @return string		Value of the data, or <code>FALSE</code> if not found.
+	 * @return string		Value of the data, or <code>null</code> if not found.
 	 */
 	public function get ($key) {
-		return (isset($_SESSION[$key]) ? $_SESSION[$key] : false);
+		return (isset($_SESSION[$key]) ? $_SESSION[$key] : null);
 	}
 	
 	/**

=== modified file 'src/interceptors/AbstractInterceptor.php'
--- src/interceptors/AbstractInterceptor.php	2008-10-20 16:41:10 +0000
+++ src/interceptors/AbstractInterceptor.php	2009-04-10 01:43:31 +0000
@@ -1,17 +1,16 @@
 <?php
 /**
- * This is an abstract convenience class for interceptors. It implements empty <code>init()</code> and
- * <code>destroy()</code> methods so subclasses only have to implement <code>intercept()</code>.
- * 
- * @version		$Id: AbstractInterceptor.php 1492 2008-04-29 23:57:42Z anders $
+ * This is an abstract convenience class for interceptors. It implements empty
+ * <code>init()</code> and <code>destroy()</code> methods so subclasses only have to
+ * implement <code>intercept()</code>.
  */
 abstract class AbstractInterceptor implements Interceptor {
 	/**
-	 * Creates an interceptor instance. If any configuration is supplied, matching properties are set on
-	 * the instance. This requires that the concrete interceptor implementation defines the properties as
-	 * protected or public.
+	 * Creates an interceptor instance. If any configuration is supplied, matching properties
+	 * are set on the instance. This requires that the concrete interceptor implementation
+	 * defines the properties as protected or public.
 	 *
-	 * @param array $conf	Configuration for this interceptor, if any.
+	 * @param array $conf Configuration for this interceptor, if any.
 	 */
 	public function __construct ($conf = null) {
 		if ($conf !== null) {

=== modified file 'src/interceptors/AuthInterceptor.php'
--- src/interceptors/AuthInterceptor.php	2008-10-20 16:41:10 +0000
+++ src/interceptors/AuthInterceptor.php	2009-04-14 16:50:50 +0000
@@ -2,10 +2,9 @@
 /**
  * Interceptor which checks to see if the user making this request is authenticated.
  *
- * If the target action is <code>AuthAware</code> and implements the <code>allowedAccess()</code> method,
- * the user is passed to the action for it to determine whether to allow access.
- * 
- * @version		$Id: AuthInterceptor.php 1537 2008-08-02 19:22:57Z anders $
+ * If session is enabled, the target action is <code>AuthAware</code> and implements the
+ * <code>allowedAccess()</code> method, the user is passed to the action for it to determine
+ * whether to allow access.
  */
 class AuthInterceptor extends AbstractInterceptor {
 	/**
@@ -38,25 +37,30 @@
 	 */
 	public function intercept ($dispatcher) {
 		$action = $dispatcher->getAction();
-		$user = $action->session[$this->userKey];
-
-		if (!$this->isUserAuthenticated($user)) {
-			$action->session['target'] = $dispatcher->getRequest()->getUri();
-			if ($action instanceof MessageAware) {
-				$action->msg->setMessage('Vennligst logg inn for å komme til siden du forespurte.', false);
+		$request = $dispatcher->getRequest();
+
+		if ($request->hasSession()) {
+			$user = $request->session[$this->userKey];
+
+			if (!$this->isUserAuthenticated($user)) {
+				$request->session['target'] = $request->getUri();
+				if ($action instanceof MessageAware) {
+					$action->msg->setMessage('You must log in to access
+							the page you requested.', false);
+				}
+
+				return $this->denyAccess($dispatcher);
 			}
 
-			return $this->denyAccess($dispatcher);
-		}
+			if ($action instanceof AuthAware) {
+				if (method_exists($action, 'allowedAccess')) {
+					if (!$action->allowedAccess($user)) {
+						if (method_exists($action, 'denyAccess')) {
+							return $action->denyAccess($user);
+						}
 
-		if ($action instanceof AuthAware) {
-			if (method_exists($action, 'allowedAccess')) {
-				if (!$action->allowedAccess($user)) {
-					if (method_exists($action, 'denyAccess')) {
-						return $action->denyAccess($user);
+						return $this->denyAccess();
 					}
-
-					return $this->denyAccess($dispatcher);
 				}
 			}
 		}
@@ -66,8 +70,8 @@
 	/**
 	 * Makes sure the user value found in the session is of the correct type.
 	 *
-	 * @param mixed $user	Session value to make sure represents a user.
-	 * @return bool			TRUE if $user represents an authenticated user, FALSE if not.
+	 * @param mixed $user Session value to make sure represents a user.
+	 * @return bool       TRUE if $user represents an authenticated user, FALSE if not.
 	 */
 	private function isUserAuthenticated ($user) {
 		if ($user !== null) {
@@ -86,8 +90,8 @@
 	/**
 	 * Denies access by redirecting to the configured login URI.
 	 */
-	private function denyAccess ($dispatcher) {
-		return new RedirectResult($dispatcher->getAction(), $this->loginUri);
+	private function denyAccess () {
+		return new RedirectResponse($this->loginUri);
 	}
 }
 ?>

=== modified file 'src/interceptors/ErrorInterceptor.php'
--- src/interceptors/ErrorInterceptor.php	2008-12-17 15:50:47 +0000
+++ src/interceptors/ErrorInterceptor.php	2009-04-10 01:43:31 +0000
@@ -2,11 +2,9 @@
 require(ROOT . '/lib/ErrorHandler.php');
 
 /**
- * Interceptor which prepares and sets an error handler. Any errors triggered or exceptions thrown after
- * this interceptor is invoked will be handled by the error handler defined by this interceptor, unless
- * otherwised catched (for exceptions).
- *
- * TODO: Pluggable error handler. Specify class name by config option?
+ * Interceptor which prepares and sets an error handler. Any errors triggered or exceptions
+ * thrown after this interceptor is invoked will be handled by the error handler defined by
+ * this interceptor, unless otherwised catched (for exceptions).
  */
 class ErrorInterceptor extends AbstractInterceptor {
 	/**

=== modified file 'src/interceptors/Interceptor.php'
--- src/interceptors/Interceptor.php	2008-10-20 16:41:10 +0000
+++ src/interceptors/Interceptor.php	2009-04-10 01:43:31 +0000
@@ -1,13 +1,11 @@
 <?php
 /**
  * This interface defines the contract of an interceptor.
- * 
- * @version		$Id: Interceptor.php 1478 2008-04-02 14:04:52Z anders $
  */
 interface Interceptor {
 	/**
-	 * Initializes any resources required by the interceptor before use. This is called for all active
-	 * interceptors before the first one is invoked.
+	 * Initializes any resources required by the interceptor before use. This is called for all
+	 * active interceptors before the first one is invoked.
 	 */
 	public function init ();
 
@@ -17,12 +15,13 @@
 	public function destroy ();
 
 	/**
-	 * Executes this interceptor, by doing some processing before and/or after the rest of the request
-	 * processing. Interceptors can short-circuit the processing by returning a <code>Result</code> itself,
-	 * or delegate further processing of the request through <code>$dispatcher->invoke()</code>.
+	 * Executes this interceptor, by doing some processing before and/or after the rest of the
+	 * request processing. Interceptors can short-circuit the processing by returning a
+	 * <code>Result</code> itself, or delegate further processing of the request through
+	 * <code>$dispatcher->invoke()</code>.
 	 *
-	 * @param Dispatcher $dispatcher	Dispatcher handling the request processing flow.
-	 * @return Result					The result of the request.
+	 * @param Dispatcher $dispatcher Dispatcher handling the request processing flow.
+	 * @return Result                The result of the request.
 	 */
 	public function intercept ($dispatcher);
 }

=== modified file 'src/interceptors/MessageInterceptor.php'
--- src/interceptors/MessageInterceptor.php	2009-04-07 22:47:54 +0000
+++ src/interceptors/MessageInterceptor.php	2009-04-14 16:50:50 +0000
@@ -3,7 +3,8 @@
 
 /**
  * Interceptor which provides the target action, if <code>MessageAware</code>, with a facility
- * to give the user status messages.
+ * to give the user status messages. The facility is set in an implicit <code>$msg</code>
+ * property on the action.
  */
 class MessageInterceptor extends AbstractInterceptor {
 	/**
@@ -11,12 +12,13 @@
 	 */
 	public function intercept ($dispatcher) {
 		$action = $dispatcher->getAction();
+		$request = $dispatcher->getRequest();
 
 		if ($action instanceof MessageAware) {
 			// If a previous message is set in the session, put it into the action
-			if ($action instanceof SessionAware && isset($action->session['message'])) {
-				$action->msg = $action->session['message'];
-				$action->session->remove('message');
+			if ($request->hasSession() && isset($request->session['message'])) {
+				$action->msg = $request->session['message'];
+				$request->session->remove('message');
 			}
 			// Otherwise create a new instance
 			else {
@@ -28,13 +30,13 @@
 
 		/*
 		 * If we are about to redirect and a message has been set, save it temporarily in the
-		 * session so it can be retrieved in the new location.
+		 * session (if present) so it can be retrieved in the new location.
 		 */
-		if ($result instanceof RedirectResult
-				&& $action instanceof SessionAware
+		if ($result instanceof RedirectResponse
 				&& $action instanceof MessageAware
-				&& !$action->msg->isEmpty()) {
-			$action->session['message'] = $action->msg;
+				&& !$action->msg->isEmpty()
+				&& $request->hasSession()) {
+			$request->session['message'] = $action->msg;
 		}
 
 		return $result;

=== modified file 'src/interceptors/ModelInterceptor.php'
--- src/interceptors/ModelInterceptor.php	2009-04-12 01:09:55 +0000
+++ src/interceptors/ModelInterceptor.php	2009-04-14 16:50:50 +0000
@@ -24,6 +24,8 @@
 		$action = $dispatcher->getAction();
 
 		if ($action instanceof ModelAware) {
+			$request = $dispatcher->getRequest();
+
 			// We depend on a $model-property in ModelAware actions
 			if (!property_exists($action, 'model')) {
 				$class = get_class($action);
@@ -37,11 +39,12 @@
 			 * take precedence, to stop any invalid model in the session to override the newly
 			 * POSTed.
 			 */
-			if ($dispatcher->getRequest()->getMethod() == 'GET'
-					&& $action instanceof SessionAware && isset($action->session['model'])) {
-				$action->model = $action->session['model'];
+			if ($request->getMethod() == 'GET'
+					&& $request->hasSession()
+					&& isset($request->session['model'])) {
+				$action->model = $request->session['model'];
 				// Model has been extracted, remove it from session
-				$action->session->remove('model');
+				$request->session->remove('model');
 			}
 			// Otherwise prepare a model from request parameters
 			else {
@@ -55,7 +58,7 @@
 				}
 				
 				if ($model !== null) {
-					foreach ($dispatcher->getRequest()->params as $param => $value) {
+					foreach ($request->params as $param => $value) {
 						if (strpos($param, '::') !== false) {
 							/*
 							 * Parameter is a property path to inner models. Explode the path

=== modified file 'src/interceptors/ParametersInterceptor.php'
--- src/interceptors/ParametersInterceptor.php	2009-04-03 16:04:34 +0000
+++ src/interceptors/ParametersInterceptor.php	2009-04-10 01:43:31 +0000
@@ -1,9 +1,7 @@
 <?php
 /**
- * Interceptor which puts session and/or request parameters into corresponding properties of the target
- * action if <code>ParametersAware</code>.
- * 
- * @version		$Id: ParametersInterceptor.php 1518 2008-06-30 23:43:38Z anders $
+ * Interceptor which puts session and/or request parameters into corresponding properties of
+ * the target action if <code>ParametersAware</code>.
  */
 class ParametersInterceptor extends AbstractInterceptor {
 	/**
@@ -13,11 +11,13 @@
 		$action = $dispatcher->getAction();
 
 		if ($action instanceof ParametersAware) {
-			if ($action instanceof SessionAware) {
-				$this->populate($action, $action->session);
+			$request = $dispatcher->getRequest();
+
+			$this->populate($action, $request->params);
+
+			if ($request->hasSession()) {
+				$this->populate($action, $request->session);
 			}
-
-			$this->populate($action, $dispatcher->getRequest()->getAll());
 		}
 
 		return $dispatcher->invoke();

=== modified file 'src/interceptors/SessionInterceptor.php'
--- src/interceptors/SessionInterceptor.php	2008-12-08 18:45:30 +0000
+++ src/interceptors/SessionInterceptor.php	2009-04-10 01:43:31 +0000
@@ -2,26 +2,16 @@
 require(ROOT . '/core/Session.php');
 
 /**
- * Interceptor which prepares a session for use. A <code>Session</code> object is created and injected
- * into the <code>Request</code>.
- *
- * If the target action is <code>SessionAware</code> the session data is set on the action as well.
- * 
- * @version		$Id: SessionInterceptor.php 1518 2008-06-30 23:43:38Z anders $
+ * Interceptor which prepares a session for use. A <code>Session</code> object is created and
+ * injected into the <code>Request</code>.
  */
 class SessionInterceptor extends AbstractInterceptor {
 	/**
-	 * Instantiates a <code>Session</code> object and injects it into the request. If the action is
-	 * <code>SessionAware</code>, pass in session data.
+	 * Invokes and processes the interceptor.
 	 */
 	public function intercept ($dispatcher) {
-		$action = $dispatcher->getAction();
-
-		if ($action instanceof SessionAware) {
-			$session = new Session();
-			$action->session = $session;
-		}
-
+		$session = new Session();
+		$dispatcher->getRequest()->session = $session;
 		return $dispatcher->invoke();
 	}
 }

=== modified file 'src/interceptors/UploadInterceptor.php'
--- src/interceptors/UploadInterceptor.php	2009-02-22 03:41:01 +0000
+++ src/interceptors/UploadInterceptor.php	2009-04-10 01:43:31 +0000
@@ -2,14 +2,12 @@
 require(ROOT . '/lib/UploadFile.php');
 
 /**
- * Interceptor handling file uploads. For each file uploaded a <code>UploadFile</code> object representing
- * the uploaded file is created.
+ * Interceptor handling file uploads.
  *
- * If the target action is <code>ModelAware</code> and its model is prepared, each file is set on the
- * model. If on the other hand the action is <code>UploadAware</code>, the files are instead set directly
- * on the action.
- * 
- * @version		$Id: UploadInterceptor.php 1510 2008-06-17 05:45:50Z anders $
+ * For each file uploaded a <code>UploadFile</code> object representing the uploaded file is
+ * created. If the target action is <code>ModelAware</code> and its model is prepared, each
+ * file is set on the model. If on the other hand the action is <code>UploadAware</code>, the
+ * files are instead set directly on the action.
  */
 class UploadInterceptor extends AbstractInterceptor {
 	/**
@@ -29,13 +27,14 @@
 
 			foreach ($_FILES as $param => $file) {
 				/*
-				 * Only validate that file(s) were actually uploaded (they can still have errors,
-				 * which will be contained in the UploadFile).
+				 * Only validate that file(s) were actually uploaded (they can still have
+				 * errors, which will be contained in the UploadFile).
 				 */
 				if (is_array($file['error'])) {
 					foreach ($file['name'] as $i => $name) {
 						if ($file['error'][$i] != UPLOAD_ERR_NO_FILE) {
-							$files[] = new UploadFile($name, $file['tmp_name'][$i], $file['error'][$i]);
+							$files[] = new UploadFile($name, $file['tmp_name'][$i],
+									$file['error'][$i]);
 						}
 					}
 					if (isset($files)) {
@@ -43,7 +42,8 @@
 					}
 				}
 				else if ($file['error'] != UPLOAD_ERR_NO_FILE) {
-					$setOn->$param = new UploadFile($file['name'], $file['tmp_name'], $file['error']);
+					$setOn->$param = new UploadFile($file['name'], $file['tmp_name'],
+							$file['error']);
 				}
 			}
 		}

=== modified file 'src/interceptors/UtilsInterceptor.php'
--- src/interceptors/UtilsInterceptor.php	2008-10-20 16:41:10 +0000
+++ src/interceptors/UtilsInterceptor.php	2009-04-10 01:43:31 +0000
@@ -1,8 +1,6 @@
 <?php
 /**
  * Interceptor which loads utils listed in a <code>loadUtils</code> config setting.
- * 
- * @version		$Id: UtilsInterceptor.php 1510 2008-06-17 05:45:50Z anders $
  */
 class UtilsInterceptor extends AbstractInterceptor {
 	/**

=== modified file 'src/interceptors/ValidationInterceptor.php'
--- src/interceptors/ValidationInterceptor.php	2009-04-08 15:34:28 +0000
+++ src/interceptors/ValidationInterceptor.php	2009-04-14 16:50:50 +0000
@@ -6,7 +6,7 @@
  * which we are to validate in a public <code>$model</code> property. This model is usually
  * populated by a <code>ModelInterceptor</code>. If validation fails, the
  * <code>validationFailed()</code> method on the action is called for the action to determine
- * the result and set any custom error message.
+ * the response and set any custom error message.
  */
 class ValidationInterceptor extends AbstractInterceptor {
 	/**
@@ -40,11 +40,11 @@
 			}
 
 			/*
-			 * If the result is a redirect and sessions are enabled, store model for
+			 * If the response is a redirect and sessions are enabled, store model for
 			 * retrieval after redirect.
 			 */
-			if ($result instanceof RedirectResult && $action instanceof SessionAware) {
-				$action->session['model'] = $action->model;
+			if ($result instanceof RedirectResponse && $dispatcher->getRequest()->hasSession()) {
+				$dispatcher->getRequest()->session['model'] = $action->model;
 			}
 		}
 

=== renamed directory 'src/results' => 'src/response'
=== renamed file 'src/results/FileResult.php' => 'src/response/FileResponse.php'
--- src/results/FileResult.php	2008-12-17 15:50:47 +0000
+++ src/response/FileResponse.php	2009-04-11 23:30:29 +0000
@@ -1,60 +1,64 @@
 <?php
 /**
- * Implementation of a result set where a file is returned to the client. The contents may come
- * from an actual file on the file system or data passed to this class when it is instantiated.
+ * Response implementation where a file is returned to the client. The contents may come
+ * from an actual file or data passed to this class.
  */	
-class FileResult extends AbstractResult {
+class FileResponse extends Response {
 	private $outputName;
-	private $data;
 	private $dataIsFile;
 	
 	/**
-	 * Constructor.
+	 * Initialize this response.
 	 * 
-	 * @param object $action     Current action.
+	 * @param mixed $data        File data to return or a file name of the file.
 	 * @param string $mime       MIME-type of the file.
 	 * @param string $charset    Charset of the file.
-	 * @param string $data       Data contents to return directly or file name of the file.
-	 * @param bool $isFile       <code>TRUE</code> if $data is a file name, <code>FALSE</code> if not.
-	 * @param string $outputName Optional name to give the returned file, if different from $file when
-	 *                           referring to an actual file.
+	 * @param bool $isFile       <code>true</code> if $data is a file name,
+	 *                           <code>false</code> if not.
+	 * @param string $outputName Name to give the returned file. Optional when $file is a file
+	 *                           name.
 	 */
-	public function __construct ($action, $mime, $charset, $data, $isFile = false, $outputName = false) {
-		parent::__construct($action, $mime, $charset);
+	public function __construct ($data, $mime, $charset, $isFile = false, $outputName = null) {
+		parent::__construct($data, 200, $mime, $charset);
 
 		if (empty($data)) {
-			throw new Exception('No data supplied');
-		}
-
-		$this->data = $data;
-		$this->dataIsFile = $isFile;
-
-		if (empty($outputName)) {
+			throw new Exception('No file data or file path supplied.');
+		}
+
+		if ($isFile) {
+			if (!file_exists($data)) {
+				throw new Exeption("File $data to return to user does not exist.");
+			}
+		}
+
+		if ($outputName === null) {
 			// If actual file and no custom file name, set correct file name
 			if ($isFile) {
 				$this->outputName = basename($data);
 			}
 			else {
-				throw new Exception('No output file name given');
+				throw new Exception('No output file name given.');
+			}
 		}
 		else {
 			$this->outputName = $outputName;
 		}
+
+		$this->dataIsFile = $isFile;
 	}
 
 	/**
 	 * Sends the file to the client.
 	 */
 	public function render ($request) {
-		header("Content-Type: {$this->contentType}; charset={$this->charset}");
-		header("Content-Disposition: attachment; filename=\"{$this->outputName}\"");
+		$this->setHeader('Content-Disposition', "attachment; filename=\"$this->outputName\"");
 
-		if ($this->dataIsFile && is_file($this->data)) {
-			header('Content-Length: ' . filesize($this->data));
+		if ($this->dataIsFile) {
+			$this->setHeader('Content-Length', filesize($this->data));
 			readfile($this->data);
 		}
 		else {
-			header('Content-Length: ' . mb_strlen($this->data));
+			$this->setHeader('Content-Length', mb_strlen($this->data));
 			echo $this->data;
 		}
 	}

=== renamed file 'src/results/JsonResult.php' => 'src/response/JsonResponse.php'
--- src/results/JsonResult.php	2008-12-17 15:50:47 +0000
+++ src/response/JsonResponse.php	2009-04-11 23:30:29 +0000
@@ -1,29 +1,23 @@
 <?php
 /**
- * This result renders a JSON response with data to the client.
+ * This response renders the data as a JSON string to the client.
  */	
-class JsonResult implements Result {
-	private $data;
-	private $charset;
-
+class JsonResponse extends Response {
 	/**
-	 * Constructor.
+	 * Initialize this response.
 	 *
-	 * @param mixed $data     The data en encode in the JSON response..
+	 * @param mixed $data     The data en encode in the JSON response.
+	 * @param int $status     HTTP status code. Defaults to 200 OK.
 	 * @param string $charset Charset of the data. Defaults to utf-8.
 	 */
-	public function __construct ($data, $charset = 'utf-8') {	
-		$this->data    = $data;
-		$this->charset = $charset;
+	public function __construct ($data, $status = 200, $charset = 'utf-8') {
+		parent::__construct($data, $status, 'application/json', $charset);	
 	}
 
 	/**
-	 * Performs the JSON encoding and returns the result.
-	 *
-	 * @param Request $request	The request. Disregarded in this implementation.
+	 * Performs the JSON encoding and outputs the result.
 	 */
 	public function render ($request) {
-		header("Content-Type: application/json; charset=$this->charset");
 		echo json_encode($this->data);
 	}
 }

=== renamed file 'src/results/NotFoundResult.php' => 'src/response/NotFoundResponse.php'
--- src/results/NotFoundResult.php	2008-10-20 16:41:10 +0000
+++ src/response/NotFoundResponse.php	2009-04-11 23:30:29 +0000
@@ -1,21 +1,17 @@
 <?php
 /**
- * Provides the implementation of a result set which sends a 404 Not Found HTTP status header to
- * the client. A  XSL template is used to render a viewable response for the client.
- * 
- * @version		$Id: NotFoundResult.php 1542 2008-08-12 18:46:42Z anders $
+ * Response implementation which sends a 404 Not Found HTTP status code to the client. A XSL
+ * stylesheet is used to render a viewable response to the user.
  */	
-class NotFoundResult extends XsltResult {
-	
+class NotFoundResponse extends XsltResponse {
 	/**
-	 * Constructor.
+	 * Initialize this response.
 	 *
-	 * @param string $xsl_template	Name of XSL file (excluding the extension) to use, relative to
-	 * 								the views-directory.
+	 * @param string $stylesheet XSL stylesheet to use.
+	 * @param int $status        HTTP status code. Defaults to 200 OK.
 	 */
-	public function __construct ($action, $xslTemplate = '/404') {
-		parent::__construct($action, $xslTemplate);
-		header("HTTP/1.1 404 Not Found");
+	public function __construct ($stylesheet = '/404', 404) {
+		parent::__construct(null, $stylesheet);
 	}
 }
 ?>

=== added file 'src/response/PermanentRedirectResponse.php'
--- src/response/PermanentRedirectResponse.php	1970-01-01 00:00:00 +0000
+++ src/response/PermanentRedirectResponse.php	2009-04-14 16:50:50 +0000
@@ -0,0 +1,15 @@
+<?php
+/**
+ * This is a more specific redirect response, sending a 301 Moved Permanently status code.
+ */
+class PermanentRedirectResponse extends RedirectResponse {
+	/**
+	 * Initialize this response.
+	 * 
+	 * @param string $location Location of the redirect relative to the web root.
+	 */
+	public function __construct ($location) {
+		parent::__construct($location, 301);
+	}
+}
+?>

=== renamed file 'src/results/PhpResult.php' => 'src/response/PhpResponse.php'
--- src/results/PhpResult.php	2008-12-17 15:50:47 +0000
+++ src/response/PhpResponse.php	2009-04-11 23:30:29 +0000
@@ -1,39 +1,45 @@
 <?php
 /**
- * Provides the implementation for using a plain PHP file as a template when creating the result.
+ * Provides the implementation of a response using a PHP file as a template for creating
+ * the actual output.
  */	
-class PhpResult extends AbstractResult {
+class PhpResult extends Response {
 	private $phpTemplate;
-	
+
 	/**
-	 * Constructor.
+	 * Initializes this response.
 	 *
-	 * @param Object $action      The action processing the request.
-	 * @param string $phpTemplate Path to the template, relative to the VIEW_PATH, and excluding the extension.
+	 * @param mixed $data         Data to expose to the PHP template.
+	 * @param string $phpTemplate PHP template to use, relative to VIEW_PATH, omitting the
+	 *                            extension.
+	 * @param int $status         HTTP status code. Defaults to 200 OK.
+	 * @param string $contentType Content type of the response. Defaults to text/html.
 	 */
-	public function __construct ($action, $phpTemplate) {
-		parent::__construct($action);
+	public function __construct ($data, $phpTemplate, $status = 200,
+			$contentType = 'text/html') {
+		parent::__construct($data, $status, $contentType);
 		$this->phpTemplate = VIEW_PATH . "$phpTemplate.php";
-		
+
 		if (!file_exists($this->phpTemplate)) {
 			throw new Exception("PHP template ({$this->phpTemplate}) does not exist");
 		}
 	}
-	
+
 	/**
-	 * Extracts data from the current action into a sandboxed function scope, while providing
-	 * direct access to the request object and the application configuration.
-	 * This sandboxed scope is made available to the PHP template file by including it directly, and
-	 * collecting it's output which is thus used as the results output.
-	 *
-	 * @param Request $request Request object representing the current request.
+	 * Extracts the data into a sandboxed function scope, while providing direct access to the
+	 * request object and the application configuration. This sandboxed scope is made available
+	 * to the PHP template file by including it directly, and collecting it's output which is
+	 * thus used as the results output.
 	 */
 	public function render ($request) {
-		$data = $this->getActionData();
+		$data = array();
+		foreach ($this->data as $key => $value) {
+			$data[$key] = $value;
+		}
 
-		/**
-		 * Create a sandbox function which extracts all data to it's local scope,
-		 * instead of letting the view template run inside the PhpResult object scope.
+		/*
+		 * Create a sandbox function which extracts all data to it's local scope, instead of
+		 * letting the view template run inside the PhpResult object scope.
 		 */
 		$sandbox = create_function('$request, $config, $_d, $_t', 'extract($_d); include($_t);');
 		$sandbox($request, Config::get(), $data, $this->phpTemplate);

=== renamed file 'src/results/RedirectResult.php' => 'src/response/RedirectResponse.php'
--- src/results/RedirectResult.php	2009-04-07 23:17:37 +0000
+++ src/response/RedirectResponse.php	2009-04-14 16:50:50 +0000
@@ -1,29 +1,28 @@
 <?php
 /**
- * Provides the implementation of a result set which when rendered sends a redirect to the
- * client. It defaults to a 303 (See Other) status code, but this can be overridden.
- */	
-class RedirectResult extends AbstractResult {
+ * Provides the implementation of a response which sends a redirect to the client when rendered.
+ * This defaults to a 302 Found status code. While the status code can be overridden, it is
+ * recommended to use a more specific response class where one is availible.
+ */
+class RedirectResponse extends Response {
 	private $location;
-	private $code;
 
 	/**
-	 * Constructor.
+	 * Initialize this response.
 	 * 
 	 * @param string $location Location of the redirect relative to the web root.
-	 * @param int $code        HTTP status code to use. Defaults to 303.
+	 * @param int $status      HTTP status code. Defaults to 302 Found.
 	 */
-	public function __construct ($action, $location, $code = 303) {
-		parent::__construct($action);
+	public function __construct ($location, $status = 302) {
+		parent::__construct(null, $status);
 		$this->location = Config::get('webRoot') . $location;
-		$this->code = $code;
 	}
 
 	/**
 	 * Sends the redirect to the client.
 	 */
 	public function render ($request) {
-		header("Location: $this->location", true, $this->code);
+		$this->setHeader('Location', $this->location);
 		exit;
 	}
 }

=== renamed file 'src/results/AbstractResult.php' => 'src/response/Response.php'
--- src/results/AbstractResult.php	2009-04-03 16:04:34 +0000
+++ src/response/Response.php	2009-04-12 16:32:29 +0000
@@ -1,50 +1,84 @@
 <?php
 /**
- * Abstract class providing the basic implementation of a result. A result represents the outcome of the
- * request performed, and a response will usually be rendered to the client over HTTP.
+ * Provides a simple implementation of a response. This class can be used directly when
+ * outputting data manually, but a more specialized subclass is often used.
  */
-abstract class AbstractResult implements Result {
-	protected $action;
+class Response {
+	protected $data;
+	protected $status;
 	protected $contentType;
 	protected $charset;
 	
 	/**
-	 * Constructor.
+	 * Initializes this response with the supplied response data and meta data. When using this
+	 * class explicitly, <code>$data</code> must be a string with the response body, or added
+	 * through <code>output()</code>. Template implementations usually expects
+	 * <code>$data</code> to be an object or array.
 	 *
-	 * @param Request $request    The request object representing the current HTTP request.
-	 * @param string $contentType The content type of the rendered result. Default is text/html.
-	 * @param string $charset     The charset of the rendered result. Default is utf-8.
-	 * @return BaseResult
+	 * @param mixed $data         Data to use when rendering the output.
+	 * @param int $status         HTTP status code. Defaults to 200 OK.
+	 * @param string $contentType Content type of the response. Defaults to text/html.
+	 * @param string $charset     Charset of the response. Defaults to utf-8.
 	 */
-	public function __construct ($action, $contentType = 'text/html', $charset = 'utf-8') {
-		$this->action      = $action;
+	public function __construct ($data = '', $status = 200, $contentType = 'text/html',
+			$charset = 'utf-8') {
+		$this->data        = $data;
+		$this->status      = $status;
 		$this->contentType = $contentType;
 		$this->charset     = $charset;
-		
-		header("Content-Type: $contentType; charset=$charset");
-	}
-
-	/**
-	 * Returns the action that created this result.
-	 *
-	 * @return object
-	 */
-	public function getAction () {
-		return $this->action;
-	}
-
-	/**
-	 * Returns exposable data from the action.
-	 *
-	 * @return array Data from the action.
-	 */
-	public function getActionData () {
-		$data = array();
-		foreach ($this->action as $key => $value) {
-			$data[$key] = $value;
-		}
-
-		return $data;
+		$this->setHeader('Content-Type', "$contentType; charset=$charset", $status);
+	}
+
+	/**
+	 * Sets a header. If $status is supplied, the HTTP status code is changed to its value.
+	 *
+	 * @param string $header The header to set.
+	 * @param string $value  The value to set.
+	 * @param int $status    New HTTP status code to set, if any.
+	 */
+	public final function setHeader ($header, $value, $status = null) {
+		header("$header: $value", true, $status);
+	}
+
+	/**
+	 * Checks whether the supplied header has been sent or is about to be sent to the client.
+	 *
+	 * @param string $header The header to check for.
+	 * @return bool
+	 */
+	public final function isHeaderSet ($header) {
+		foreach (headers_list() as $headerSent) {
+			if (substr($headerSent, 0, strpos($headerSent, ':')) == $header) {
+				return true;
+			}
+		}
+		return false;
+	}
+
+	/**
+	 * Adds content to output. Any previously added data and the content supplied must both
+	 * be string values.
+	 *
+	 * @param string $content Content to add.
+	 * @throws Exception      If existing data or content added are not strings.
+	 */
+	public function output ($content) {
+		if (!is_string($this->data)) {
+			throw new Exception("Can't add output as previously submittet data
+					is not a string.");
+		}
+		if (!is_string($content)) {
+			throw new Exception("Content to output must be a string value.");
+		}
+
+		$this->data .= $content . "\n";
+	}
+
+	/**
+	 * Outputs the response body.
+	 */
+	public function render ($request) {
+		echo $this->data;
 	}
 }
 ?>

=== added file 'src/response/SeeOtherResponse.php'
--- src/response/SeeOtherResponse.php	1970-01-01 00:00:00 +0000
+++ src/response/SeeOtherResponse.php	2009-04-14 16:50:50 +0000
@@ -0,0 +1,17 @@
+<?php
+/**
+ * This is a more specific redirect response, sending a 303 See Other status code. This is
+ * most correctly used when redirecting to another GET location to display the result of a
+ * POST.
+ */
+class SeeOtherResponse extends RedirectResponse {
+	/**
+	 * Initialize this response.
+	 * 
+	 * @param string $location Location of the redirect relative to the web root.
+	 */
+	public function __construct ($location) {
+		parent::__construct($location, 303);
+	}
+}
+?>

=== renamed file 'src/results/SmartyResult.php' => 'src/response/SmartyResponse.php'
--- src/results/SmartyResult.php	2008-12-17 15:50:47 +0000
+++ src/response/SmartyResponse.php	2009-04-11 23:30:29 +0000
@@ -2,51 +2,51 @@
 require('Smarty/Smarty.class.php');
 
 /**
- * Provides the implementation of a result using the Smarty template engine to render the data as (X)HTML.
- * To use this result engine you'll need to have Smarty installed in a directory where PHP searches for
- * include files (include_path). You'll also need to add a 'smarty' section in your application configuration,
- * which defines compileDir at the very least. An example of the configuration section:
+ * Provides the implementation of a response using the Smarty template engine to render the
+ * data as (X)HTML. To use this response engine Smarty must be installed in a directory where
+ * PHP searches for include files (include_path). A 'smarty' section must also exist in your
+ * application configuration, which defines compileDir at the very least. An example of the
+ * configuration section:
  *
  *   'smarty' => array(
- *     'compileDir'  => '', // Full path to a directory writeable by PHP
- *     'cacheDir'    => '', // Full path to a directory writeable by PHP, if you want to use Smarty caching
- *     'configDir'   => '', // Full path to a directory containing Smarty-specific configuration files
- *     'templateDir' => ''  // Full path to a directory overriding the default Kolibri view directory
+ *     'compileDir'  => '', // Full path to a PHP-writeable dir
+ *     'cacheDir'    => '', // Full path to a PHP-writeable dir, if you want to use Smarty caching
+ *     'configDir'   => '', // Full path to a dir containing Smarty-specific configuration files
+ *     'templateDir' => ''  // Full path to a dir overriding the default Kolibri view directory
  *   )
  *
- * All configurable directories corresponds to similar Smarty directories, ie. compile_dir and cache_dir.
+ * All configurable directories corresponds to similar Smarty directories, ie. compile_dir and
+ * cache_dir.
  */	
-class SmartyResult extends AbstractResult {
+class SmartyResponse extends Response {
 	private $smartyTemplate;
 	
 	/**
-	 * Constructor.
+	 * Initialize this response.
 	 *
-	 * @param Object $action   The action processing the request.
-	 * @param string $template Path to the template, relative to the VIEW_PATH, and excluding the extension.
+	 * @param mixed $data         Data to pass on to the Smarty template.
+	 * @param string $template    Smarty template to use, relative to VIEW_DIR, omitting the
+	 *                            extension.
+	 * @param int $status         HTTP status code. Defaults to 200 OK.
+	 * @param string $contentType Content type of the response. Defaults to text/html.
 	 */
-	public function __construct ($action, $template) {
-		parent::__construct($action);
-		
-		// Remove prefixed / for Smarty, since it's standard in Kolibri
-		if ($template{0} == '/') $template = substr($template, 1);
-		
+	public function __construct ($data, $template, $status = 200, $contentType = 'text/html') {
+		parent::__construct($data, $status, $contentType);
 		$this->smartyTemplate = "$template.tpl";
+
 		if (!file_exists(VIEW_PATH . "/$template.tpl")) {
-			throw Exception("Smarty template ({$this->smartyTemplate}) does not exist");
+			throw Exception("Smarty template ({$this->smartyTemplate}) does not exist.");
 		}
 	}
 	
 	/**
-	 * Creates a Smarty template engine instance. Any exposable action data is made available, as well
-	 * as the request object and application configuration.
-	 *
-	 * @param Request $request Request object representing the current request.
+	 * Creates a Smarty template engine instance and uses it to render the output. The data
+	 * along with the request object and application configuration is exposed to the template.
 	 */
 	public function render ($request) {
 		$conf = Config::get('smarty');
 		if ($conf === null) {
-			throw new Exception('Smarty settings missing from application configuration');
+			throw new Exception('Smarty settings missing from application configuration.');
 		}
 
 		// Configure the Smarty engine
@@ -57,7 +57,11 @@
 		$smarty->config_dir = (isset($conf['configDir']) ? $conf['configDir'] : '');
 
 		// Assign data we want to expose to Smarty
-		$smarty->assign($this->getActionData());
+		$data = array();
+		foreach ($this->data as $key => $value) {
+			$data[$key] = $value;
+		}
+		$smarty->assign($data);
 		$smarty->assign('request', $request);
 		$smarty->assign('config', Config::get());
 

=== renamed file 'src/results/XsltResult.php' => 'src/response/XsltResponse.php'
--- src/results/XsltResult.php	2009-04-07 20:25:15 +0000
+++ src/response/XsltResponse.php	2009-04-12 16:32:29 +0000
@@ -1,36 +1,37 @@
 <?php
 /**
- * Provides the implementation of a result using XSLT to render the data.
- * 
- * @version		$Id$
- */	
-class XsltResult extends AbstractResult {
-	private $xslTemplate;
-	
+ * Provides the implementation of a response by using XSLT to transform the data server-side
+ * before outputting it.
+ */
+class XsltResponse extends Response {
+	private $stylesheet;
+
 	/**
-	 * Constructor.
+	 * Initialize this response.
 	 *
-	 * @param object $action      Current action.
-	 * @param string $xslTemplate Name of XSL file (excluding the extension) to use, relative to
-	 *                            the views-directory.
+	 * @param mixed $data         Data to transform.
+	 * @param string $stylesheet  XSL stylesheet to use, relative to VIEW_PATH, omitting the
+	 *                            extension.
+	 * @param int $status         HTTP status code. Defaults to 200 OK.
+	 * @param string $contentType Content type of the response. Defaults to text/html.
 	 */
-	public function __construct ($action, $xslTemplate) {
-		parent::__construct($action);
+	public function __construct ($data, $stylesheet, $status = 200,
+			$contentType = 'text/html') {
+		parent::__construct($data, $status, $contentType);
+		$this->stylesheet = VIEW_PATH . "$stylesheet.xsl";
+	}
 
-		$this->xslTemplate = VIEW_PATH . "$xslTemplate.xsl";
-	}
-	
 	/**
-	 * Performs the XSL transformations on the XML-data and outputs it.
+	 * Generates XML of the data, and performs XSL transformation on it before outputting.
 	 */
 	public function render ($request) {
 		$xmlGenerator = new XmlGenerator();
 		// Wrap request data in a containing element, <request />
 		$xmlGenerator->append($request, 'request');
-		// Append all action data directly to the root result element
-		$xmlGenerator->append($this->action);
-		
-		$transformer = new XslTransformer($this->xslTemplate);
+		// Append all data directly to the root result element
+		$xmlGenerator->append($this->data);
+
+		$transformer = new XslTransformer($this->stylesheet);
 
 		// Add scalar config params to XSLT as parameters
 		foreach (Config::get() as $key => $value) {

=== removed file 'src/results/ForwardResult.php'
--- src/results/ForwardResult.php	2008-12-17 15:50:47 +0000
+++ src/results/ForwardResult.php	1970-01-01 00:00:00 +0000
@@ -1,58 +0,0 @@
-<?php
-/**
- * Provides a result which forwards the request to another action. After the action has been
- * invoked, a final result is rendered. What final result to render can be specified when
- * instantiating this class. If no result was specified in the instantiation of this class, the
- * result of the action invoked will be rendered.
- *
- * FIXME: This is currently NOT rewritten for Kolibri. Do we really want this at all?
- */
-class ForwardResult extends BaseResult {
-	/** Result to render after action invocation, or NULL if the action result is to be rendered. */
-	var $end_result;
-	
-	/**
-	 * Constructor.
-	 *
-	 * @param Request &$request		The request object representing the current HTTP request.
-	 * @param string $uri			URI to forward the request to. An action mapper must be able
-	 * 								to map this URI to an action, or else nothing is rendered.
-	 * @param object $end_result	Optional result to render after the forward action is complete.
-	 * 								If not supplied, the result of the action is rendered.
-	 * @return BaseResult
-	 */
-	function ForwardResult (&$request, $uri, $end_result = null) {
-		parent::BaseResult($request);
-		
-		// Change the URI of the request
-		$this->request->uri = $uri;
-		
-		if (!empty($end_result)) {
-			$this->end_result = $end_result;
-		}
-	}
-	
-	/**
-	 * Maps the target URI specified in this result to an action and dispatches the request. When
-	 * the action returns we render our end result if provided, or else the result of the action.
-	 */
-	function render() {
-		// Map the action we want to forward to
-		$mapper = new ActionMapper();
-		$mapping = $mapper->map($this->request);
-		
-		if (!empty($mapping)) {
-			$dispatcher = new Dispatcher($this->request, $mapping);
-			$result = $dispatcher->invoke();
-			
-			// Render the end result if we have one, else the result of the invoked action
-			if (!empty($this->end_result)) {
-				$this->end_result->render();
-			}
-			else {
-				$result->render();
-			}
-		}
-	}
-}
-?>

=== removed file 'src/results/Result.php'
--- src/results/Result.php	2008-12-17 15:50:47 +0000
+++ src/results/Result.php	1970-01-01 00:00:00 +0000
@@ -1,8 +0,0 @@
-<?php
-/**
- * This interface defines the contract of a result.
- */
-interface Result {
-	public function render ($request);
-}
-?>

=== removed file 'src/results/TextResult.php'
--- src/results/TextResult.php	2008-12-17 15:50:47 +0000
+++ src/results/TextResult.php	1970-01-01 00:00:00 +0000
@@ -1,24 +0,0 @@
-<?php
-/**
- * Provides the implementation of a result set which sets the content-type to plain text. This
- * will ensure that all output will be sent to the client as-is.
- */	
-class TextResult extends AbstractResult {
-	private $text;
-	
-	/**
-	 * Constructor.
-	 *
-	 * @param object $action The current action.
-	 * @param string $text   Text to send to client.
-	 */
-	public function __construct ($action, $text) {
-		parent::__construct($action, 'text/plain');
-		$this->text = $text;
-	}
-
-	public function render ($request) {
-		echo $this->text;
-	}
-}
-?>


Follow ups