Rewrite of Horde's MVC system to use controllers built around the single responsibili...
authorChuck Hagenbuch <chuck@horde.org>
Sun, 24 Jan 2010 01:58:33 +0000 (20:58 -0500)
committerChuck Hagenbuch <chuck@horde.org>
Tue, 20 Jul 2010 14:28:54 +0000 (10:28 -0400)
43 files changed:
framework/Controller/lib/Horde/Controller.php [new file with mode: 0644]
framework/Controller/lib/Horde/Controller/Base.php
framework/Controller/lib/Horde/Controller/Dispatcher.php [deleted file]
framework/Controller/lib/Horde/Controller/FileUpload.php [deleted file]
framework/Controller/lib/Horde/Controller/Filter/Gzip.php [new file with mode: 0644]
framework/Controller/lib/Horde/Controller/FilterCollection.php [new file with mode: 0644]
framework/Controller/lib/Horde/Controller/FilterRunner.php [new file with mode: 0644]
framework/Controller/lib/Horde/Controller/Mime/Type.php [deleted file]
framework/Controller/lib/Horde/Controller/Null.php [new file with mode: 0644]
framework/Controller/lib/Horde/Controller/PostFilter.php [new file with mode: 0644]
framework/Controller/lib/Horde/Controller/PreFilter.php [new file with mode: 0644]
framework/Controller/lib/Horde/Controller/Request.php [new file with mode: 0644]
framework/Controller/lib/Horde/Controller/Request/Base.php [deleted file]
framework/Controller/lib/Horde/Controller/Request/Cli.php [deleted file]
framework/Controller/lib/Horde/Controller/Request/Http.php
framework/Controller/lib/Horde/Controller/Request/Mock.php [deleted file]
framework/Controller/lib/Horde/Controller/Request/Null.php [new file with mode: 0644]
framework/Controller/lib/Horde/Controller/RequestConfiguration.php [new file with mode: 0644]
framework/Controller/lib/Horde/Controller/Response.php [new file with mode: 0644]
framework/Controller/lib/Horde/Controller/Response/Base.php [deleted file]
framework/Controller/lib/Horde/Controller/Response/Cli.php [deleted file]
framework/Controller/lib/Horde/Controller/Response/Http.php [deleted file]
framework/Controller/lib/Horde/Controller/Response/Mock.php [deleted file]
framework/Controller/lib/Horde/Controller/ResponseWriter.php [new file with mode: 0644]
framework/Controller/lib/Horde/Controller/ResponseWriter/Web.php [new file with mode: 0644]
framework/Controller/lib/Horde/Controller/ResponseWriter/WebDebug.php [new file with mode: 0644]
framework/Controller/lib/Horde/Controller/Runner.php [new file with mode: 0644]
framework/Controller/lib/Horde/Controller/Scanner.php [deleted file]
framework/Controller/lib/Horde/Controller/SettingsExporter.php [new file with mode: 0644]
framework/Controller/lib/Horde/Controller/SettingsExporter/Default.php [new file with mode: 0644]
framework/Controller/lib/Horde/Controller/StatusCodes.php [deleted file]
framework/Controller/lib/Horde/Controller/UrlWriter.php [deleted file]
framework/Controller/package.xml
framework/Controller/test/Horde/Controller/FilterRunnerTest.php [new file with mode: 0644]
framework/Core/lib/Horde/Core/Binder/Mapper.php [new file with mode: 0644]
framework/Core/lib/Horde/Core/Controller/NotFound.php [new file with mode: 0644]
framework/Core/lib/Horde/Core/Controller/RequestConfiguration.php [new file with mode: 0644]
framework/Core/lib/Horde/Core/Controller/RequestMapper.php [new file with mode: 0644]
framework/Core/lib/Horde/Core/Controller/SettingsFinder.php [new file with mode: 0644]
framework/Core/lib/Horde/Core/Factory/Request.php [new file with mode: 0644]
framework/Core/lib/Horde/Registry.php
framework/Core/package.xml
horde/rampage.php

diff --git a/framework/Controller/lib/Horde/Controller.php b/framework/Controller/lib/Horde/Controller.php
new file mode 100644 (file)
index 0000000..877b4d5
--- /dev/null
@@ -0,0 +1,13 @@
+<?php
+/**
+ * Interface for all controller objects
+ *
+ * @category Horde
+ * @package  Horde_Controller
+ * @author   James Pepin <james@bluestatedigital.com>
+ * @license  http://opensource.org/licenses/bsd-license.php BSD
+ */
+interface Horde_Controller
+{
+    public function processRequest(Horde_Controller_Request $request, Horde_Controller_Response $response);
+}
index fc4e430..8c034d4 100644 (file)
 <?php
 /**
- * Copyright 2007 Maintainable Software, LLC
- * Copyright 2008-2010 The Horde Project (http://www.horde.org/)
+ * Base class for controllers that implements the Logged, Injected, and Viewed
+ * interfaces.
  *
- * @author     Mike Naberezny <mike@maintainable.com>
- * @author     Derek DeVries <derek@maintainable.com>
- * @author     Chuck Hagenbuch <chuck@horde.org>
- * @license    http://opensource.org/licenses/bsd-license.php
- * @category   Horde
- * @package    Horde_Controller
- */
-
-/**
- * @author     Mike Naberezny <mike@maintainable.com>
- * @author     Derek DeVries <derek@maintainable.com>
- * @author     Chuck Hagenbuch <chuck@horde.org>
- * @license    http://opensource.org/licenses/bsd-license.php
- * @category   Horde
- * @package    Horde_Controller
+ * This class is for convenience, if you decide you wish to use only logging or
+ * the injector or views, or neither, you do not have to use it.  As long as
+ * your controllers implement Horde_Controller, they are runnable.
+ *
+ * @category Horde
+ * @package  Horde_Controller
+ * @author   James Pepin <james@bluestatedigital.com>
+ * @license  http://opensource.org/licenses/bsd-license.php BSD
  */
-abstract class Horde_Controller_Base
+abstract class Horde_Controller_Base implements Horde_Controller
 {
     /**
-     * Params is the list of variables set through routes.
-     * @var Horde_Support_Array
-     */
-    protected $params;
-
-    /**
-     * Have we performed a render on this controller
-     * @var boolean
-     */
-    protected $_performedRender = false;
-
-    /**
-     * Have we performed a redirect on this controller
-     * @var boolean
-     */
-    protected $_performedRedirect = false;
-
-    /**
-     * The request object we are processing
-     * @var Horde_Controller_Request_Base
-     * @todo Assign default value.
-     */
-    protected $_request;
-
-    /**
-     * The response object we are returning
-     * @var Horde_Controller_Response
-     * @todo Assign default value.
-     */
-    protected $_response;
-
-    /**
-     * The current action being performed
-     * @var string
-     * @todo Assign default value.
-     */
-    protected $_action;
-
-    /**
-     * Normal methods available as action requests.
-     * @var array
-     */
-    protected $_actionMethods = array();
-
-    /**
-     * @var string
-     */
-    protected $_viewsDir = '';
-
-    /**
-     * @var
-     */
-    protected $_urlWriter;
-
-    /**
-     * New controller instance
-     */
-    public function __construct($options = array())
-    {
-        foreach ($options as $key => $val) {
-            $this->{'_' . $key} = $val;
-        }
-    }
-
-    /**
-     * Access to resources (request) and lazy loading of some (view).
+     * This is marked private on purpose, so that you have to use the
+     * getInjector() method to access it in derived classes.  This is done so
+     * that you don't assume its always set, since its set via setter-injection
+     * to save on having to define a constructor param for it
      *
-     * @param string $name  Property to access
+     * @var Horde_Injector
      */
-    public function __get($name)
-    {
-        switch ($name) {
-        case 'request':
-            return $this->_request;
-
-        case '_view':
-            $this->_view = new Horde_View;
-            return $this->_view;
-        }
-    }
+    private $_injector;
 
     /**
-     * Process the {@link Horde_Controller_Request_Base} and return
-     * the {@link Horde_Controller_Response}. This is the method that is called
-     * for every request to be processed. It then determines which action to call
-     * based on the parameters set within the {@link Horde_Controller_Request_Base}
-     * object.
-     *
-     * <code>
-     *  <?php
-     *  ...
-     *  $request  = new Horde_Controller_Request_Base();
-     *  $response = new Horde_Controller_Response_Base();
-     *
-     *  $response = $controller->process($request, $response);
-     *  ...
-     *  ?>
-     * </code>
+     * Private on purpose so you have to use getLogger().
      *
-     * @param   Horde_Controller_Request_Base   $request
-     * @param   Horde_Controller_Response_Base  $response
-     * @return  Horde_Controller_Response_Base
-     */
-    public function process(Horde_Controller_Request_Base $request, Horde_Controller_Response_Base $response)
-    {
-        $this->_request = $request;
-        $this->_response = $response;
-
-        $this->_initParams();
-
-        $this->_shortName = str_replace('Controller', '', $this->params[':controller']);
-
-        try {
-            // templates
-            $this->_initActionMethods();
-            $this->_initViewPaths();
-            $this->_initViewHelpers();
-
-            // Initialize application logic used through all actions
-            $this->_initializeApplication();
-            if ($this->_performed()) {
-                return $this->_response;
-            }
-
-            // Initialize sub-controller logic used through all actions
-            if (is_callable(array($this, '_initialize'))) {
-                $this->_initialize();
-            }
-
-            // pre filters
-
-            // execute action & save any changes to sessionData
-            $this->{$this->_action}();
-
-            // post filters
-
-            // render default if we haven't performed an action yet
-            if (!$this->_performed()) {
-                $this->render();
-            }
-        } catch (Exception $e) {
-            // error handling
-            throw $e;
-        }
-
-        return $this->_response;
-    }
-
-    /**
+     * @var Horde_Log_Logger
      */
-    protected function interpretStatus($status)
-    {
-        return Horde_Controller_StatusCodes::interpret($status);
-    }
+    private $_logger;
 
     /**
-     * Generate a URL
-     * @see Horde_Controller_UrlWriter
+     * Private on purpose so you have to use getView().
      *
-     * @param  string|array  $first   named route, string, or options array
-     * @param  array         $second  options array (if not in $first)
-     * @return string                 generated url
+     * @var Horde_View
      */
-    protected function urlFor($first = array(), $second = array())
-    {
-        return $this->getUrlWriter()->urlFor($first, $second);
-    }
+    private $_view;
 
     /**
-     * Get an instance of UrlWriter for this controller.
+     * Set the injector for this controller
      *
-     * @return Horde_Controller_UrlWriter
-     */
-    public function getUrlWriter()
-    {
-        // instantiate UrlWriter that will generate URLs for this controller
-        if (!$this->_urlWriter) {
-            $defaults = array('controller' => $this->getControllerName());
-            $this->_urlWriter = new Horde_Controller_UrlWriter($defaults);
-        }
-        return $this->_urlWriter;
-    }
-
-    /**
-     * Get the current controller's name.
+     * @inject
      *
-     * @return string
+     * @param Horde_Injector  The injector that this controller should use to create objects
      */
-    protected function getControllerName()
+    public function setInjector(Horde_Injector $injector)
     {
-        if (empty($this->params)) {
-            $this->_initParams();
-        }
-
-        return $this->params[':controller'];
+        $this->_injector = $injector;
     }
 
     /**
-     * Render the response to the user. Actions are automatically rendered if no other
-     * action is specified.
-     *
-     * <code>
-     *  <?php
-     *  ...
-     *  $this->render(array('text'    => 'some text to render'));
-     *  $this->render(array('action'  => 'actionName'));
-     *  $this->render(array('nothing' => 1));
-     *  ...
-     *  ?>
-     * </code>
-     *
-     * @see renderText()
-     * @see renderAction()
-     * @see renderNothing()
+     * Get the injector for this controller
      *
-     * @param array $options
-     *
-     * @throws  Horde_Controller_Exception
+     * @return Horde_Injector  The injector previously set for this controller,
+     * or a new Horde_Injector_TopLevel
      */
-    protected function render($options = array())
+    public function getInjector()
     {
-        // should not render/redirect more than once.
-        if ($this->_performed()) {
-            throw new Horde_Controller_Exception("Double render error: \"$this->_action\"");
-        }
-
-        // validate options
-
-        // set response status
-        if (!empty($options['status'])) {
-            $header = $this->interpretStatus($options['status']);
-            $this->_response->setStatus($header);
-        }
-
-        // set response location
-        if (!empty($options['location'])) {
-            $url = $this->urlFor($options['location']);
-            $this->_response->setHeader("Location: $url", $replace = true);
-        }
-
-        // render text
-        if (!empty($options['text'])) {
-            $this->renderText($options['text']);
-
-        // render xml
-        } elseif (!empty($options['xml'])) {
-            $this->_response->setContentType('application/xml');
-
-            $xml = $options['xml'];
-            if (is_object($xml) && method_exists($xml, 'toXml')) {
-                $xml = $xml->toXml();
-            }
-
-            $this->renderText($xml);
-
-        // render template file
-        } elseif (!empty($options['action'])) {
-            $this->renderAction($options['action']);
-
-        // render empty body
-        } elseif (!empty($options['nothing'])) {
-            $this->renderText('');
-
-        // render default template
-        } else {
-            $this->renderAction($this->_action);
+        if ($this->_injector) {
+            return $this->_injector;
         }
+        return new Horde_Injector_TopLevel();
     }
 
     /**
-     * Render text directly to the screen without using a template
+     * Set the Logger for this controller
      *
-     * <code>
-     *  <?php
-     *  ...
-     *  $this->renderText('some text to render to the screen');
-     *  ...
-     *  ?>
-     * </code>
+     * @inject
      *
-     * @param   string  $text
+     * @param Horde_Log_Logger The logger to use for this controller
      */
-    protected function renderText($text)
+    public function setLogger(Horde_Log_Logger $logger)
     {
-        $this->_response->setBody($text);
-        $this->_performedRender = true;
+        $this->_logger = $logger;
     }
 
     /**
-     * The name of the action method will render by default.
-     *
-     * render 'listDocuments' template file
-     * <code>
-     *  <?php
-     *  ...
-     *  $this->renderAction('listDocuments');
-     *  ...
-     *  ?>
-     * </code>
+     * Get the logger assigned to this controller
      *
-     * @param   string  $name
+     * @return Horde_Log_Logger  The logger for this controller
      */
-    protected function renderAction($name)
+    public function getLogger()
     {
-        // current url
-        $this->_view->currentUrl = '/' . $this->_request->getUri();
-
-        // copy instance variables
-        foreach (get_object_vars($this) as $key => $value) {
-            $this->_view->$key = $value;
-        }
-
-        // add suffix
-        if ($this->_actionConflict) {
-            $name = str_replace('Action', '', $name);
-        }
-        if (strpos($name, '.') === false) {
-            $name .= '.html.php';
-        }
-
-        // prepend this controller's "short name" only if the action was
-        // specified without a controller "short name".
-        // e.g. index           -> Shortname/index
-        //      Shortname/index -> Shortname/index
-        if (strpos($name, '/') === false) {
-            // $name = $this->_shortName . '/' . $name;
+        if ($this->_logger) {
+            return $this->_logger;
         }
-
-        if ($this->_useLayout) {
-            $this->_view->contentForLayout = $this->_view->render($name);
-            $text = $this->_view->render($this->_layoutName);
-        } else {
-            $text = $this->_view->render($name);
-        }
-        $this->renderText($text);
-    }
-
-    /**
-     * Render blank content. This can be used anytime you want to send a 200 OK
-     * response back to the user, but don't need to actually render any content.
-     * This is mostly useful for ajax requests.
-     *
-     * <code>
-     *  <?php
-     *  ...
-     *  $this->renderNothing();
-     *  ...
-     *  ?>
-     * </code>
-     */
-    protected function renderNothing()
-    {
-        $this->renderText('');
+        return new Horde_Log_Logger(new Horde_Log_Handler_Null());
     }
 
     /**
-     * Set the layout template for the controller. Specify the name of the file in
-     * the /app/views/layouts directory without the .html extension
+     * Set the Horde_View object to be used for this controller
      *
-     * <code>
-     *  <?php
-     *  ...
-     *  public function _initialize()
-     *  {
-     *      $this->setLayout('application');
-     *  }
-     *  ...
-     *  ?>
-     * </code>
+     * @inject
      *
-     * @param   string  $layoutName
+     * @param Horde_View_Base  The view object
      */
-    protected function setLayout($layoutName)
+    public function setView(Horde_View_Base $view)
     {
-        $this->_useLayout  = true;
-        $this->_layoutName = $layoutName;
+        $this->_view = $view;
     }
 
     /**
-     * Check if a render or redirect has been performed
-     * @return  boolean
-     */
-    protected function _performed()
-    {
-        return $this->_performedRender || $this->_performedRedirect;
-    }
-
-    /**
-     * Each variable set through routing {@link Horde_Routes_Mapper} is
-     * available in controllers using the $params array.
+     * Gets the current view for this controller
      *
-     * The controller also has access to GET/POST arrays using $params
-     *
-     * The action method to be performed is stored in $this->params[':action'] key
-     */
-    protected function _initParams()
-    {
-        $this->params = new Horde_Support_Array($this->_request->getParameters());
-        $this->_action = $this->params->get(':action');
-    }
-
-    /**
-     * Set the list of public actions that are available for this Controller.
-     * Subclasses can remove methods from being publicly called by calling
-     * {@link hideAction()}.
-     *
-     * @throws  Horde_Controller_Exception
-     */
-    protected function _initActionMethods()
-    {
-        // Perform reflection to get the list of public methods
-        $reflect = new ReflectionClass($this);
-        $methods = $reflect->getMethods();
-        foreach ($methods as $m) {
-            if ($m->isPublic() && !$m->isConstructor() && !$m->isDestructor()  &&
-                $m->getName() != 'process' && substr($m->getName(), 0, 1) != '_') {
-                $this->_actionMethods[$m->getName()] = 1;
-            }
-        }
-
-        // try action suffix.
-        if (!isset($this->_actionMethods[$this->_action]) &&
-            isset($this->_actionMethods[$this->_action.'Action'])) {
-            $this->_actionConflict = true;
-            $this->_action = $this->_action.'Action';
-        }
-        // action isn't set, but there is a methodMissing() catchall method
-        if (!isset($this->_actionMethods[$this->_action]) &&
-            isset($this->_actionMethods['methodMissing'])) {
-            $this->_action = 'methodMissing';
-
-        // make sure we have an action set, and that there is no methodMissing() method
-        } elseif (!isset($this->_actionMethods[$this->_action]) &&
-                  !isset($this->_actionMethods['methodMissing'])) {
-            $msg = 'Missing action: '.get_class($this)."::".$this->_action;
-            throw new Horde_Controller_Exception($msg);
-        }
-    }
-
-    /**
-     * Initialize the view paths where the templates reside for this controller.
-     * These are added in FIFO order, so if we do $this->renderAction('foo'),
-     * in the BarController, the order it will search these directories will be:
-     *  1. /views/Bar/foo.html
-     *  2. /views/shared/foo.html
-     *  3. /views/layouts/foo.html
-     *  4. /views/foo.html (the default)
+     * @note This method will create an empty Horde_View if none has been set.
      *
-     * We can specify a directory to look in instead of relying on the default order
-     * by doing $this->renderAction('shared/foo').
+     * @return Horde_View_Base  The view for this controller, or a new empty
+     * Horde_View if none is set
      */
-    protected function _initViewPaths()
+    public function getView()
     {
-        $this->_view->addTemplatePath($this->_viewsDir . 'layouts');
-        $this->_view->addTemplatePath($this->_viewsDir . 'shared');
-        $this->_view->addTemplatePath($this->_viewsDir . $this->_shortName);
-    }
-
-    /**
-     * Initialize the default helpers for use in the views
-     */
-    protected function _initViewHelpers()
-    {
-        $controllerHelper = $this->_shortName . 'Helper';
-        if (class_exists($controllerHelper)) {
-            new $controllerHelper($this->_view);
+        if ($this->_view) {
+            return $this->_view;
         }
+        return new Horde_View();
     }
-
-    /**
-     * This gets called before action is performed in a controller.
-     * Override method in subclass to setup filters/helpers
-     */
-    protected function _initializeApplication()
-    {
-    }
-
 }
diff --git a/framework/Controller/lib/Horde/Controller/Dispatcher.php b/framework/Controller/lib/Horde/Controller/Dispatcher.php
deleted file mode 100644 (file)
index fa3e25a..0000000
+++ /dev/null
@@ -1,266 +0,0 @@
-<?php
-/**
- * Copyright 2007 Maintainable Software, LLC
- * Copyright 2008-2010 The Horde Project (http://www.horde.org/)
- *
- * @author     Mike Naberezny <mike@maintainable.com>
- * @author     Derek DeVries <derek@maintainable.com>
- * @author     Chuck Hagenbuch <chuck@horde.org>
- * @license    http://opensource.org/licenses/bsd-license.php
- * @category   Horde
- * @package    Horde_Controller
- */
-
-/**
- * Dispatch a request to the appropriate controller and execute the response.
- *
- * @author     Mike Naberezny <mike@maintainable.com>
- * @author     Derek DeVries <derek@maintainable.com>
- * @author     Chuck Hagenbuch <chuck@horde.org>
- * @license    http://opensource.org/licenses/bsd-license.php
- * @category   Horde
- * @package    Horde_Controller
- */
-class Horde_Controller_Dispatcher
-{
-    /** @var Horde_Controller_Dispatcher */
-    private static $_instance;
-
-    /** @var Horde_Routes_Mapper */
-    protected $_mapper;
-
-    /** @var Horde_Log_Logger */
-    protected $_logger;
-
-    /** @var Horde_Support_Inflector */
-    protected $_inflector;
-
-    /** @var string */
-    protected $_controllerDir = '';
-
-    /** @var string */
-    protected $_viewsDir = '';
-
-    /**
-     * Singleton method. This should be the only way of instantiating a
-     * Horde_Controller_Dispatcher object.
-     *
-     * @return Horde_Controller_Dispatcher
-     */
-    public static function singleton($context = array())
-    {
-        if (self::$_instance === null) {
-            self::$_instance = new self($context);
-        }
-        return self::$_instance;
-    }
-
-    /**
-     * Class constructor. Client code should use the singleton method to
-     * instantiate objects.
-     */
-    protected function __construct($context)
-    {
-        if (!isset($context['mapper']) || ! $context['mapper'] instanceof Horde_Routes_Mapper) {
-            $context['mapper'] = new Horde_Routes_Mapper();
-            $context['mapper']->connect('/:controller/:action/:id');
-        }
-
-        foreach ($context as $key => $val) {
-            $this->{'_' . $key} = $val;
-        }
-
-        // Make sure controller directory, if set, ends in a /.
-        if ($this->_controllerDir && substr($this->_controllerDir, -1) != '/') {
-            $this->_controllerDir .= '/';
-        }
-
-        // Set the mapper's controller directory and controllerScan
-        if ($this->_controllerDir && !$this->_mapper->directory) {
-            $this->_mapper->directory = $this->_controllerDir;
-        }
-        $scanner = new Horde_Controller_Scanner($this->_mapper);
-        $this->_mapper->controllerScan = $scanner->getCallback();
-
-        // Make sure views directory, if set, ends in a /.
-        if ($this->_viewsDir && substr($this->_viewsDir, -1) != '/') {
-            $this->_viewsDir .= '/';
-        }
-
-        // Make sure we have an inflector
-        if (!$this->_inflector) {
-            $this->_inflector = new Horde_Support_Inflector();
-        }
-    }
-
-    /**
-     * Get the route utilities for this dispatcher and its mapper.
-     *
-     * @return  Horde_Routes_Utils
-     */
-    public function getRouteUtils()
-    {
-        return $this->_mapper->utils;
-    }
-
-    /**
-     * Dispatch the request to the correct controller.
-     *
-     * @param Horde_Controller_Request_Base $request
-     */
-    public function dispatch(Horde_Controller_Request_Base $request, $response = null)
-    {
-        $t = new Horde_Support_Timer();
-        $t->push();
-
-        if (! $response instanceof Horde_Controller_Response_Base) {
-            $response = new Horde_Controller_Response_Http();
-            // $response = new Horde_Controller_Response_Base();
-        }
-
-        // Recognize routes and process request
-        $controller = $this->recognize($request);
-        $response = $controller->process($request, $response);
-
-        // Send response and log request
-        $time = $t->pop();
-        $this->_logRequest($request, $time);
-        $response->send();
-    }
-
-    /**
-     * Check if request path matches any Routes to get the controller
-     *
-     * @return  Horde_Controller_Base
-     * @throws  Horde_Controller_Exception
-     */
-    public function recognize($request)
-    {
-        $path = $request->getPath();
-        if (substr($path, 0, 1) != '/') {
-            $path = '/' . $path;
-        }
-
-        $matchdata = $this->_mapper->match($path);
-        if ($matchdata) {
-            $hash = $this->formatMatchdata($matchdata);
-        }
-
-        if (empty($hash) || !isset($hash[':controller'])) {
-            $msg = 'No routes match the path: "' . $request->getPath() . '"';
-            throw new Horde_Controller_Exception($msg);
-        }
-
-        $request->setPathParams($hash);
-
-        // try to load the class
-        $controllerName = $hash[':controller'];
-        if (!class_exists($controllerName, false)) {
-            $path = $this->_controllerDir . str_replace('_', '/', $controllerName) . '.php';
-            if (file_exists($path)) {
-                require $path;
-            } else {
-                $msg = "The Controller \"$controllerName\" does not exist at " . $path;
-                throw new Horde_Controller_Exception($msg);
-            }
-        }
-
-        $options = array(
-            'viewsDir' => $this->_viewsDir,
-        );
-        return new $controllerName($options);
-    }
-
-    /**
-     * Take the $matchdata returned by a Horde_Routes_Mapper match and add
-     * in :controller and :action that are used by the rest of the framework.
-     *
-     * Format controller names: my_stuff => MyStuffController
-     * Format action names:     action_name => actionName
-     *
-     * @param   array   $matchdata
-     * @return  mixed   false | array
-     */
-    public function formatMatchdata($matchdata)
-    {
-        $ret = array();
-        foreach ($matchdata as $key => $val) {
-            if ($key == 'controller') {
-                $ret[':controller'] = $this->_inflector->camelize($val) . 'Controller';
-            } elseif ($key == 'action') {
-                $ret[':action'] = $this->_inflector->camelize($val, 'lower');
-            } elseif ($key == 'module') {
-                $ret[':module'] = $this->_inflector->camelize($val);
-            }
-
-            $ret[$key] = $val;
-        }
-
-        if (!empty($ret[':module'])) {
-            $ret[':controller'] = $ret[':module'] . '_' . $ret[':controller'];
-        }
-
-        return !empty($ret) && isset($ret['controller']) ? $ret : false;
-    }
-
-    /**
-     * Log the http request
-     *
-     * @todo - get total query times
-     *
-     * @param   Horde_Controller_Request_Base $request
-     * @param   int $totalTime
-     */
-    protected function _logRequest(Horde_Controller_Request_Base $request, $totalTime)
-    {
-        if (!is_callable(array($this->_logger, 'info'))) {
-            return;
-        }
-
-        $queryTime  = 0; // total time to execute queries
-        $queryCount = 0; // total queries performed
-        $phpTime = $totalTime - $queryTime;
-
-        // embed user info in log
-        $uri    = $request->getUri();
-        $method = $request->getMethod();
-
-        $paramStr = 'PARAMS=' . $this->_formatLogParams($request->getParameters());
-
-        $msg = "$method $uri $totalTime ms (DB=$queryTime [$queryCount] PHP=$phpTime) $paramStr";
-        $msg = wordwrap($msg, 80, "\n\t  ", 1);
-
-        $this->_logger->info($msg);
-    }
-
-    /**
-     * Formats the request parameters as a "key => value, key => value, ..." string
-     * for the log file.
-     *
-     * @param array $params
-     * @return string
-     */
-    protected function _formatLogParams($params)
-    {
-        $paramStr = '{';
-        $count = 0;
-        foreach ($params as $key => $value) {
-            if ($key != 'controller'  && $key != 'action' &&
-                $key != ':controller' && $key != ':action') {
-                if ($count++ > 0) { $paramStr .= ', '; }
-
-                $paramStr .= $key.' => ';
-
-                if (is_array($value)) {
-                    $paramStr .= $this->_formatLogParams($value);
-                } elseif (is_object($value)) {
-                    $paramStr .= get_class($value);
-                } else {
-                    $paramStr .= $value;
-                }
-            }
-        }
-        return $paramStr . '}';
-    }
-
-}
diff --git a/framework/Controller/lib/Horde/Controller/FileUpload.php b/framework/Controller/lib/Horde/Controller/FileUpload.php
deleted file mode 100644 (file)
index 06695b8..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-<?php
-/**
- * Copyright 2007 Maintainable Software, LLC
- * Copyright 2008-2010 The Horde Project (http://www.horde.org/)
- *
- * @author     Mike Naberezny <mike@maintainable.com>
- * @author     Derek DeVries <derek@maintainable.com>
- * @author     Chuck Hagenbuch <chuck@horde.org>
- * @license    http://opensource.org/licenses/bsd-license.php
- * @category   Horde
- * @package    Horde_Controller
- */
-
-/**
- * A file upload from multipart form
- *
- * @author     Mike Naberezny <mike@maintainable.com>
- * @author     Derek DeVries <derek@maintainable.com>
- * @author     Chuck Hagenbuch <chuck@horde.org>
- * @license    http://opensource.org/licenses/bsd-license.php
- * @category   Horde
- * @package    Horde_Controller
- */
-class Horde_Controller_FileUpload
-{
-    public $originalFilename = null;
-    public $length           = null;
-    public $contentType      = null;
-    public $path             = null;
-
-    public function __construct($options)
-    {
-        $this->originalFilename = isset($options['name'])     ? $options['name']     : null;
-        $this->length           = isset($options['size'])     ? $options['size']     : null;
-        $this->contentType      = isset($options['type'])     ? $options['type']     : null;
-        $this->path             = isset($options['tmp_name']) ? $options['tmp_name'] : null;
-    }
-
-}
diff --git a/framework/Controller/lib/Horde/Controller/Filter/Gzip.php b/framework/Controller/lib/Horde/Controller/Filter/Gzip.php
new file mode 100644 (file)
index 0000000..9994cc4
--- /dev/null
@@ -0,0 +1,37 @@
+<?php
+/**
+ * Filter to gzip content before being served
+ *
+ * @category Horde
+ * @package  Horde_Controller
+ * @author   James Pepin <james@bluestatedigital.com>
+ * @license  http://opensource.org/licenses/bsd-license.php BSD
+ */
+class Horde_Controller_Filter_Gzip implements Horde_Controller_PostFilter
+{
+    public function processResponse(Horde_Controller_Request $request, Horde_Controller_Response $response, Horde_Controller $controller)
+    {
+        $body = $response->getBody();
+        $body = gzencode($body);
+
+        $response->setHeader('Content-Encoding', 'gzip');
+        $response->setHeader('Content-Length', $this->_byteCount($body));
+        $response->setBody($body);
+
+        return $response;
+    }
+
+    /**
+     * If mbstring is set to overload str* function then we could be counting
+     * multi-byte chars as single bytes so we need to treat the string like its
+     * 8-bit encoded to get an accurate byte count.
+     */
+    protected function _byteCount($string)
+    {
+        if (ini_get('mbstring.func_overload') > 0) {
+            return mb_strlen($string, '8bit');
+        }
+
+        return strlen($string);
+    }
+}
diff --git a/framework/Controller/lib/Horde/Controller/FilterCollection.php b/framework/Controller/lib/Horde/Controller/FilterCollection.php
new file mode 100644 (file)
index 0000000..829c39a
--- /dev/null
@@ -0,0 +1,20 @@
+<?php
+/**
+ * Interface for an object that houses a collection of pre/post filters.
+ *
+ * @category Horde
+ * @package  Horde_Controller
+ * @author   James Pepin <james@bluestatedigital.com>
+ * @author   Bob McKee <bob@bluestatedigital.com>
+ * @license  http://opensource.org/licenses/bsd-license.php BSD
+ */
+interface Horde_Controller_FilterCollection
+{
+    /**
+     */
+    public function addPreFilter(Horde_Controller_PreFilter $filter);
+
+    /**
+     */
+    public function addPostFilter(Horde_Controller_PostFilter $filter);
+}
diff --git a/framework/Controller/lib/Horde/Controller/FilterRunner.php b/framework/Controller/lib/Horde/Controller/FilterRunner.php
new file mode 100644 (file)
index 0000000..75cbd00
--- /dev/null
@@ -0,0 +1,97 @@
+<?php
+/**
+ * Collects filters and executes them around a controller
+ *
+ * @category Horde
+ * @package  Horde_Controller
+ * @author   Bob McKee <bob@bluestatedigital.com>
+ * @author   James Pepin <james@bluestatedigital.com>
+ * @license  http://opensource.org/licenses/bsd-license.php BSD
+ */
+class Horde_Controller_FilterRunner implements Horde_Controller_FilterCollection
+{
+    /**
+     * @var Horde_Controller
+     */
+    protected $_controller;
+
+    /**
+     * @var array
+     */
+    protected $_preFilters = array();
+
+    /**
+     * @var array
+     */
+    protected $_postFilters = array();
+
+    /**
+     */
+    public function __construct(Horde_Controller $controller)
+    {
+        $this->_controller = $controller;
+    }
+
+    /**
+     * Append filter to prefilters array
+     *
+     * @param Horde_Controller_PreFilter $filter
+     */
+    public function addPreFilter(Horde_Controller_PreFilter $filter)
+    {
+        array_push($this->_preFilters, $filter);
+    }
+
+    /**
+     * Prepend fitler to postfilters array
+     *
+     * @param Horde_Controller_PostFilter $filter
+     */
+    public function addPostFilter(Horde_Controller_PostFilter $filter)
+    {
+        array_unshift($this->_postFilters, $filter);
+    }
+
+    /**
+     * Executes filters and controller method. Execution happens in the following order:
+     *
+     * - Run processRequest() on prefilters in first-in-first-out order
+     * - Run processRequest() on controller
+     * - Run processResponse() on postfilters in first-in-last-out order
+     *
+     * @param Horde_Controller_Request $request
+     * @param Horde_Controller_Response $response
+     *
+     * @return Horde_Controller_Response
+     */
+    public function processRequest(Horde_Controller_Request $request, Horde_Controller_Response $response)
+    {
+        if ($this->_applyPreFilters($request, $response) !== Horde_Controller_PreFilter::REQUEST_HANDLED) {
+            $this->_controller->processRequest($request, $response);
+            $this->_applyPostFilters($request, $response);
+        }
+        return $response;
+    }
+
+    /**
+     */
+    protected function _applyPreFilters(Horde_Controller_Request $request, Horde_Controller_Response $response)
+    {
+        foreach ($this->_preFilters as $filter) {
+            if ($filter->processRequest($request, $response, $this->_controller) === Horde_Controller_PreFilter::REQUEST_HANDLED) {
+                return Horde_Controller_PreFilter::REQUEST_HANDLED;
+            }
+        }
+
+        return Horde_Controller_PreFilter::REQUEST_CONTINUE;
+    }
+
+    /**
+     */
+    protected function _applyPostFilters(Horde_Controller_Request $request, Horde_Controller_Response $response)
+    {
+        foreach ($this->_postFilters as $filter) {
+            $filter->processResponse($request, $response, $this->_controller);
+        }
+    }
+}
diff --git a/framework/Controller/lib/Horde/Controller/Mime/Type.php b/framework/Controller/lib/Horde/Controller/Mime/Type.php
deleted file mode 100644 (file)
index a5df481..0000000
+++ /dev/null
@@ -1,136 +0,0 @@
-<?php
-/**
- * Copyright 2007 Maintainable Software, LLC
- * Copyright 2008-2010 The Horde Project (http://www.horde.org/)
- *
- * @author     Mike Naberezny <mike@maintainable.com>
- * @author     Derek DeVries <derek@maintainable.com>
- * @author     Chuck Hagenbuch <chuck@horde.org>
- * @license    http://opensource.org/licenses/bsd-license.php
- * @category   Horde
- * @package    Horde_Controller
- * @subpackage Response
- */
-
-/**
- * Handles managing of what types of responses the client can handle and which
- * one was requested.
- *
- * @author     Mike Naberezny <mike@maintainable.com>
- * @author     Derek DeVries <derek@maintainable.com>
- * @author     Chuck Hagenbuch <chuck@horde.org>
- * @license    http://opensource.org/licenses/bsd-license.php
- * @category   Horde
- * @package    Horde_Controller
- * @subpackage Response
- */
-class Horde_Controller_Mime_Type
-{
-    public $symbol;
-    public $synonyms;
-    public $string;
-
-    public static $set             = array();
-    public static $lookup          = array();
-    public static $extensionLookup = array();
-    public static $registered      = false;
-
-    public function __construct($string, $symbol = null, $synonyms = array())
-    {
-        $this->string   = $string;
-        $this->symbol   = $symbol;
-        $this->synonyms = $synonyms;
-    }
-
-    public function __toString()
-    {
-        return $this->symbol;
-    }
-
-    public static function lookup($string)
-    {
-        if (!empty(self::$lookup[$string])) {
-            return self::$lookup[$string];
-        } else {
-            return null;
-        }
-    }
-
-    public static function lookupByExtension($ext)
-    {
-        if (!empty(self::$extensionLookup[$ext])) {
-            return self::$extensionLookup[$ext];
-        } else {
-            return null;
-        }
-    }
-
-    public static function register($string, $symbol, $synonyms = array(), $extSynonyms = array())
-    {
-        $type = new Horde_Controller_Mime_Type($string, $symbol, $synonyms);
-        self::$set[] = $type;
-
-        // add lookup strings
-        foreach (array_merge((array)$string, $synonyms) as $string) {
-            self::$lookup[$string] = $type;
-        }
-
-        // add extesnsion lookups
-        foreach (array_merge((array)$symbol, $extSynonyms) as $ext) {
-            self::$extensionLookup[$ext] = $type;
-        }
-    }
-
-    /**
-     * @todo - actually parse the header. This is simply mocked out
-     * with common types for now
-     */
-    public static function parse($acceptHeader)
-    {
-        $types = array();
-
-        if (strstr($acceptHeader, 'text/javascript')) {
-            if (isset(self::$extensionLookup['js'])) {
-                $types[] = self::$extensionLookup['js'];
-            }
-
-        } elseif (strstr($acceptHeader, 'text/html')) {
-            if (isset(self::$extensionLookup['html'])) {
-                $types[] = self::$extensionLookup['html'];
-            }
-
-        } elseif (strstr($acceptHeader, 'text/xml')) {
-            if (isset(self::$extensionLookup['xml'])) {
-                $types[] = self::$extensionLookup['xml'];
-            }
-
-        // default to html
-        } else {
-            if (isset(self::$extensionLookup['html'])) {
-                $types[] = self::$extensionLookup['html'];
-            }
-        }
-        return $types;
-    }
-
-    /**
-     * Register mime types
-     * @todo - move this elsewhere?
-     */
-    public static function registerTypes()
-    {
-        if (!self::$registered) {
-            Horde_Controller_Mime_Type::register('*/*',                  'all');
-            Horde_Controller_Mime_Type::register('text/plain',           'text', array(), array('txt'));
-            Horde_Controller_Mime_Type::register('text/html',            'html', array('application/xhtml+xml'), array('xhtml'));
-            Horde_Controller_Mime_Type::register('text/javascript',      'js',   array('application/javascript', 'application/x-javascript'), array('xhtml'));
-            Horde_Controller_Mime_Type::register('application/json',     'json');
-            Horde_Controller_Mime_Type::register('text/xml',             'rss');
-            Horde_Controller_Mime_Type::register('application/atom+xml', 'atom');
-            Horde_Controller_Mime_Type::register('text/csv',             'csv');
-            Horde_Controller_Mime_Type::register('application/xml',      'xml',  array('text/xml', 'application/x-xml'));
-            self::$registered = true;
-        }
-    }
-
-}
diff --git a/framework/Controller/lib/Horde/Controller/Null.php b/framework/Controller/lib/Horde/Controller/Null.php
new file mode 100644 (file)
index 0000000..a8bbf01
--- /dev/null
@@ -0,0 +1,16 @@
+<?php
+/**
+ * Null controller object.  Useful for filter tests that don't use the
+ * controller object.
+ *
+ * @category Horde
+ * @package  Horde_Controller
+ * @author   Bob McKee <bob@bluestatedigital.com>
+ * @license  http://opensource.org/licenses/bsd-license.php BSD
+ */
+class Horde_Controller_Null implements Horde_Controller
+{
+    public function processRequest(Horde_Controller_Request $request, Horde_Controller_Response $response)
+    {
+    }
+}
diff --git a/framework/Controller/lib/Horde/Controller/PostFilter.php b/framework/Controller/lib/Horde/Controller/PostFilter.php
new file mode 100644 (file)
index 0000000..7dd88b2
--- /dev/null
@@ -0,0 +1,14 @@
+<?php
+/**
+ * Interface for filters that are executed after the controller has generated the response
+ *
+ * @category Horde
+ * @package  Horde_Controller
+ * @author   James Pepin <james@bluestatedigital.com>
+ * @author   Bob McKee <bob@bluestatedigital.com>
+ * @license  http://opensource.org/licenses/bsd-license.php BSD
+ */
+interface Horde_Controller_PostFilter
+{
+    public function processResponse(Horde_Controller_Request $request, Horde_Controller_Response $response, Horde_Controller $controller);
+}
diff --git a/framework/Controller/lib/Horde/Controller/PreFilter.php b/framework/Controller/lib/Horde/Controller/PreFilter.php
new file mode 100644 (file)
index 0000000..1bdb886
--- /dev/null
@@ -0,0 +1,17 @@
+<?php
+/**
+ * Interface for filers to be run before the controller is executed
+ *
+ * @category Horde
+ * @package  Horde_Controller
+ * @author   James Pepin <james@bluestatedigital.com>
+ * @author   Bob McKee <bob@bluestatedigital.com>
+ * @license  http://opensource.org/licenses/bsd-license.php BSD
+ */
+interface Horde_Controller_PreFilter
+{
+    const REQUEST_HANDLED = true;
+    const REQUEST_CONTINUE = false;
+
+    public function processRequest(Horde_Controller_Request $request, Horde_Controller_Response $response, Horde_Controller $controller);
+}
diff --git a/framework/Controller/lib/Horde/Controller/Request.php b/framework/Controller/lib/Horde/Controller/Request.php
new file mode 100644 (file)
index 0000000..a5f0307
--- /dev/null
@@ -0,0 +1,47 @@
+<?php
+/**
+ * Interface for a request object
+ *
+ * @category Horde
+ * @package  Horde_Controller
+ * @author   James Pepin <james@bluestatedigital.com>
+ * @license  http://opensource.org/licenses/bsd-license.php BSD
+ */
+interface Horde_Controller_Request
+{
+    /**
+     */
+    public function getPath();
+
+    /**
+     */
+    public function getMethod();
+
+    /**
+     */
+    public function getGetVars();
+
+    /**
+     */
+    public function getFileVars();
+
+    /**
+     */
+    public function getServerVars();
+
+    /**
+     */
+    public function getPostVars();
+
+    /**
+     */
+    public function getCookieVars();
+
+    /**
+     */
+    public function getRequestVars();
+
+    /**
+     */
+    public function getSessionId();
+}
diff --git a/framework/Controller/lib/Horde/Controller/Request/Base.php b/framework/Controller/lib/Horde/Controller/Request/Base.php
deleted file mode 100644 (file)
index 01dc766..0000000
+++ /dev/null
@@ -1,212 +0,0 @@
-<?php
-/**
- * Copyright 2007 Maintainable Software, LLC
- * Copyright 2008-2010 The Horde Project (http://www.horde.org/)
- *
- * @author     Mike Naberezny <mike@maintainable.com>
- * @author     Derek DeVries <derek@maintainable.com>
- * @author     Chuck Hagenbuch <chuck@horde.org>
- * @license    http://opensource.org/licenses/bsd-license.php
- * @category   Horde
- * @package    Horde_Controller
- * @subpackage Request
- *
- * http://pythonpaste.org/webob/
- * http://usrportage.de/archives/875-Dojo-and-the-Zend-Framework.html
- * http://framework.zend.com/manual/en/zend.filter.input.html
- * http://www.lornajane.net/posts/2009/Adding-PUT-variables-to-Request-Object-in-Zend-Framework
- */
-
-/**
- * @author     Mike Naberezny <mike@maintainable.com>
- * @author     Derek DeVries <derek@maintainable.com>
- * @author     Chuck Hagenbuch <chuck@horde.org>
- * @license    http://opensource.org/licenses/bsd-license.php
- * @category   Horde
- * @package    Horde_Controller
- * @subpackage Request
- */
-abstract class Horde_Controller_Request_Base
-{
-    /**
-     * Request timestamp
-     *
-     * @var integer
-     */
-    protected $_timestamp;
-
-    /**
-     * The SAPI
-     *
-     * @var string
-     */
-    protected $_sapi;
-
-    /**
-     * Unique id per request.
-     * @var string
-     */
-    protected $_requestId;
-
-    protected $_server;
-    protected $_env;
-
-    protected $_body;
-
-    /**
-     */
-    public function __construct($options = array())
-    {
-        $this->_initRequestId();
-
-        $this->_server  = isset($options['server'])  ? $options['server']  : $_SERVER;
-        $this->_env     = isset($options['env'])     ? $options['env']     : $_ENV;
-
-        if (isset($_SERVER['REQUEST_TIME'])) {
-            $this->_timestamp = $_SERVER['REQUEST_TIME'];
-        } else {
-            $this->_timestamp = time();
-        }
-
-        $this->_sapi = php_sapi_name();
-    }
-
-    /**
-     * Get request timestamp
-     *
-     * @return integer
-     */
-    public function getTimestamp()
-    {
-        return $this->_timestamp;
-    }
-
-    /**
-     * Get server variable with the specified $name
-     *
-     * @param   string  $name
-     * @return  string
-     */
-    public function getServer($name)
-    {
-        if ($name == 'SCRIPT_NAME' && strncmp($this->_sapi, 'cgi', 3) === 0) {
-            $name = 'SCRIPT_URL';
-        }
-        return isset($this->_server[$name]) ? $this->_server[$name] : null;
-    }
-
-    /**
-     * Get environment variable with the specified $name
-     *
-     * @param   string  $name
-     * @return  string
-     */
-    public function getEnv($name)
-    {
-        return isset($this->_env[$name]) ? $this->_env[$name] : null;
-    }
-
-    /**
-     * Get a combination of all parameters. We have to do
-     * some wacky loops to make sure that nested values in one
-     * param list don't overwrite other nested values
-     *
-     * @return  array
-     */
-    public function getParameters()
-    {
-        $allParams = array();
-        $paramArrays = array($this->_pathParams, $this->_formattedRequestParams);
-
-        foreach ($paramArrays as $params) {
-            foreach ((array)$params as $key => $value) {
-                if (!is_array($value) || !isset($allParams[$key])) {
-                    $allParams[$key] = $value;
-                } else {
-                    $allParams[$key] = array_merge($allParams[$key], $value);
-                }
-            }
-        }
-        return $allParams;
-    }
-
-    /**
-     * Get entire list of parameters set by {@link Horde_Controller_Route_Path} for
-     * the current request
-     *
-     * @return  array
-     */
-    public function getPathParams()
-    {
-        return $this->_pathParams;
-    }
-
-    /**
-     * When the {@link Horde_Controller_Dispatcher} determines the
-     * correct {@link Horde_Controller_Route_Path} to match the url, it uses the
-     * Routing object data to set appropriate variables so that they can be passed
-     * to the Controller object.
-     *
-     * @param   array   $params
-     */
-    public function setPathParams($params)
-    {
-        $this->_pathParams = !empty($params) ? $params : array();
-    }
-
-    /**
-     * Get the unique ID generated for this request
-     * @see     _initRequestId()
-     * @return  string
-     */
-    public function getRequestId()
-    {
-        return $this->_requestId;
-    }
-
-    /**
-     * The request body
-     *
-     * @TODO Allow overriding php://input, and make the default interface to
-     * return an SplFileObject, or a (doesn't currently exist) Horde_File
-     * object, instead of the raw data.
-     *
-     * @return  string
-     */
-    public function getBody()
-    {
-        if (!isset($this->_body)) {
-            $this->_body = file_get_contents('php://input');
-        }
-        return $this->_body;
-    }
-
-    /**
-     * Return the request content length
-     *
-     * @return  int
-     */
-    public function getContentLength()
-    {
-        return strlen($this->getBody());
-    }
-
-    /**
-     * Turn this request into a URL-encoded query string.
-     */
-    public function __toString()
-    {
-        return http_build_query($this);
-    }
-
-    abstract public function getPath();
-
-    /**
-     * Uniquely identify each request from others. This aids in threading
-     * related log requests during troubleshooting on a busy server
-     */
-    private function _initRequestId()
-    {
-        $this->_requestId = (string)new Horde_Support_Uuid;
-    }
-}
diff --git a/framework/Controller/lib/Horde/Controller/Request/Cli.php b/framework/Controller/lib/Horde/Controller/Request/Cli.php
deleted file mode 100644 (file)
index 349308c..0000000
+++ /dev/null
@@ -1,121 +0,0 @@
-<?php
-/**
- * Copyright 2007 Maintainable Software, LLC
- * Copyright 2008-2010 The Horde Project (http://www.horde.org/)
- *
- * @author     Mike Naberezny <mike@maintainable.com>
- * @author     Derek DeVries <derek@maintainable.com>
- * @author     Chuck Hagenbuch <chuck@horde.org>
- * @license    http://opensource.org/licenses/bsd-license.php
- * @category   Horde
- * @package    Horde_Controller
- * @subpackage Request
- */
-
-/**
- * Represents a command line request invocation.
- *
- * @author     Mike Naberezny <mike@maintainable.com>
- * @author     Derek DeVries <derek@maintainable.com>
- * @author     Chuck Hagenbuch <chuck@horde.org>
- * @license    http://opensource.org/licenses/bsd-license.php
- * @category   Horde
- * @package    Horde_Controller
- * @subpackage Request
- */
-class Horde_Controller_Request_Cli extends Horde_Controller_Request_Base
-{
-    /**
-     * Command line arguments
-     */
-    protected $_argv;
-
-    /**
-     * Constructor
-     *
-     * In addition to the $options from Horde_Controller_Request_Base, you can
-     * pass a 'path' argument as a string, or a Horde_Argv_Parser object as
-     * 'argvParser' to override the path of the cli request.
-     */
-    public function __construct($options = array())
-    {
-        parent::__construct($options);
-
-        $pathArgs = null;
-        if (isset($options['path'])) {
-            $pathArgs = $options['path'];
-        } elseif (isset($options['argvParser'])) {
-            $pathArgs = $options['argvParser'];
-        }
-
-        $this->setPath($pathArgs);
-    }
-
-    public function getUri()
-    {
-        return $this->getPath();
-    }
-
-    public function getMethod()
-    {
-        return 'CLI';
-    }
-
-    public function setArgv($args)
-    {
-        $this->_argv = $args;
-    }
-
-    public function getPath()
-    {
-        return $this->_path;
-    }
-
-    public function setPath($args = null)
-    {
-        if (is_string($args)) {
-            $this->_path = $args;
-        } else {
-            if ($args instanceof Horde_Argv_Parser) {
-                $parser = $args;
-            } else {
-                $parser = new Horde_Argv_Parser(array(
-                    'allowUnknownArgs' => true,
-                    'addHelpOption' => false,
-                ));
-            }
-
-            list($this->_argv, $args) = $parser->parseArgs();
-            if (!count($args)) {
-                throw new Horde_Controller_Exception('unknown command: ' . implode(' ', $args));
-            }
-            $this->_path = $args[0];
-            $this->_path = str_replace('-', '_', $this->_path);
-        }
-    }
-
-    /**
-     * Get all command line parameters.
-     * some wacky loops to make sure that nested values in one
-     * param list don't overwrite other nested values
-     *
-     * @return  array
-     */
-    public function getParameters()
-    {
-        $allParams = array();
-        $paramArrays = array($this->_pathParams, $this->_argv);
-
-        foreach ($paramArrays as $params) {
-            foreach ((array)$params as $key => $value) {
-                if (!is_array($value) || !isset($allParams[$key])) {
-                    $allParams[$key] = $value;
-                } else {
-                    $allParams[$key] = array_merge($allParams[$key], $value);
-                }
-            }
-        }
-        return $allParams;
-    }
-
-}
index ef411bb..24f1ca0 100644 (file)
 <?php
 /**
- * Copyright 2007 Maintainable Software, LLC
- * Copyright 2008-2010 The Horde Project (http://www.horde.org/)
- *
- * @author     Mike Naberezny <mike@maintainable.com>
- * @author     Derek DeVries <derek@maintainable.com>
- * @author     Chuck Hagenbuch <chuck@horde.org>
- * @license    http://opensource.org/licenses/bsd-license.php
- * @category   Horde
- * @package    Horde_Controller
- * @subpackage Request
+ * @category Horde
+ * @package  Horde_Controller
+ * @author   James Pepin <james@bluestatedigital.com>
+ * @license  http://opensource.org/licenses/bsd-license.php BSD
  */
-
-/**
- * Represents an HTTP request to the server. This class handles all
- * headers/cookies/session data so that it all has one point of entry for being
- * written/retrieved.
- *
- * @author     Mike Naberezny <mike@maintainable.com>
- * @author     Derek DeVries <derek@maintainable.com>
- * @author     Chuck Hagenbuch <chuck@horde.org>
- * @license    http://opensource.org/licenses/bsd-license.php
- * @category   Horde
- * @package    Horde_Controller
- * @subpackage Request
- */
-class Horde_Controller_Request_Http extends Horde_Controller_Request_Base
+class Horde_Controller_Request_Http implements Horde_Controller_Request
 {
+    protected $_path;
+
     /**
      * All the headers.
      * @var array
      */
     protected $_headers = null;
 
-    /**
-     * PHPSESSID
-     * @var string
-     */
-    protected $_sessionId;
-
-    // superglobal arrays
-    protected $_get;
-    protected $_post;
-    protected $_files;
-    protected $_request;
-
-    // cookie/session info
-    protected $_cookie;
-    protected $_session;
-
-    protected $_contentType;
-    protected $_accepts;
-    protected $_format;
-    protected $_method;
-    protected $_remoteIp;
-    protected $_port;
-    protected $_https;
-    protected $_isAjax;
-
-    protected $_domain;
-    protected $_uri;
-    protected $_pathParams;
-
     /*##########################################################################
     # Construct/Destruct
     ##########################################################################*/
 
-    /**
-     * Request is populated with all the superglobals from page request if
-     * data is not passed in.
-     *
-     * @param   array   $options  Associative array with all superglobals
-     */
-    public function __construct($options = array())
-    {
-        try {
-            if (!empty($options['session_control']) && $options['session_control'] != 'none') {
-                $this->_initSessionData();
-            }
-            
-            // register default mime types
-            Horde_Controller_Mime_Type::registerTypes();
-
-            // superglobal data if not passed in thru constructor
-            $this->_get     = isset($options['get'])     ? $options['get']     : $_GET;
-            $this->_post    = isset($options['post'])    ? $options['post']    : $_POST;
-            $this->_cookie  = isset($options['cookie'])  ? $options['cookie']  : $_COOKIE;
-            $this->_request = isset($options['request']) ? $options['request'] : $_REQUEST;
-
-            parent::__construct($options);
-
-            $this->_pathParams = array();
-            //$this->_formattedRequestParams = $this->_parseFormattedRequestParameters();
-
-            // use FileUpload object to store files
-            $this->_setFilesSuperglobals();
-
-            // disable all superglobal data to force us to use correct way
-            //@TODO
-            //$_GET = $_POST = $_FILES = $_COOKIE = $_REQUEST = $_SERVER = array();
-
-            $this->_domain   = $this->getServer('SERVER_NAME');
-            $this->_uri      = trim($this->getServer('REQUEST_URI'), '/');
-            $this->_method   = $this->getServer('REQUEST_METHOD');
-            // @TODO look at HTTP_X_FORWARDED_FOR, handling multiple addresses: http://weblogs.asp.net/james_crowley/archive/2007/06/19/gotcha-http-x-forwarded-for-returns-multiple-ip-addresses.aspx
-            $this->_remoteIp = $this->getServer('REMOTE_ADDR');
-            $this->_port     = $this->getServer('SERVER_PORT');
-            $this->_https    = $this->getServer('HTTPS') || $this->getServer('SSL_PROTOCOL') || $this->getServer('HTTP_X_CLUSTER_SSL');
-            $this->_isAjax   = $this->getServer('HTTP_X_REQUESTED_WITH') == 'XMLHttpRequest';
-        } catch (Exception $e) {
-            $this->_malformed = true;
-            $this->_exception = $e;
-        }
-    }
-
-
-    /*##########################################################################
-    # Public Methods
-    ##########################################################################*/
-
-    /**
-     * Get the http request method:
-     *  eg. GET, POST, PUT, DELETE
-     *
-     * @return  string
-     */
-    public function getMethod()
-    {
-        $methods = array('GET', 'HEAD', 'PUT', 'POST', 'DELETE', 'OPTIONS');
-
-        if ($this->_method == 'POST') {
-            $params = $this->getParameters();
-            if (isset($params['_method'])) {
-                $faked = strtoupper($params['_method']);
-                if (in_array($faked, $methods)) return $faked;
-            }
-        }
-
-        return $this->_method;
-    }
-
-    /**
-     * Get list of all superglobals to pass into a different request
-     *
-     * @return  array
-     */
-    public function getGlobals()
-    {
-        return array('get'     => $this->_get,
-                     'post'    => $this->_post,
-                     'cookie'  => $this->_cookie,
-                     'session' => $this->_session,
-                     'files'   => $this->_files,
-                     'request' => $this->_request,
-                     'server'  => $this->_server,
-                     'env'     => $this->_env);
-    }
-
-    /**
-     * Get the domain for the current request
-     * eg. https://www.maintainable.com/articles/show/123
-     *     $domain is -> www.maintainable.com
-     *
-     * @return  string
-     */
-    public function getDomain()
+    public function __construct($path)
     {
-        return $this->_domain;
+        $this->_path = $path;
     }
 
-    /**
-     * Get the host for the current request
-     * eg. http://www.maintainable.com:3000/articles/show/123
-     *     $host is -> http://www.maintainablesoftware.com:3000
-     *
-     * @param   boolean $usePort
-     * @return  string
-     */
-    public function getHost($usePort = false)
-    {
-        $scheme = 'http' . ($this->_https == 'on' ? 's' : null);
-        $port   = $usePort && !empty($this->_port) && $this->_port != '80' ? ':' . $this->_port : null;
-        return "{$scheme}://{$this->_domain}$port";
-    }
 
-    /**
-     * @todo    add ssl support
-     * @return  string
-     */
-    public function getProtocol()
-    {
-        // return $this->getServer('SERVER_PROTOCOL');
-        return 'http://';
-    }
-
-    /**
-     * Get the uri for the current request
-     * eg. https://www.maintainable.com/articles/show/123?page=1
-     *     $uri is -> articles/show/123?page=1
-     *
-     * @return  string
-     */
-    public function getUri()
-    {
-        return $this->_uri;
-    }
-
-    /**
-     * Get the path from the URI. (strip get params)
-     * eg. https://www.maintainable.com/articles/show/123?page=1
-     *     $path is -> articles/show/123
-     *
-     * @return  string
-     */
     public function getPath()
     {
-        $path = $this->_uri;
-        if (strstr($path, '?')) {
-            $path = trim(substr($path, 0, strpos($path, '?')), '/');
-        }
-        return $path;
-    }
-
-    public function getContentType()
-    {
-        if (!isset($this->_contentType)) {
-            $type = $this->getServer('CONTENT_TYPE');
-            // strip parameters from content-type like "; charset=UTF-8"
-            if (is_string($type)) {
-                if (preg_match('/^([^,\;]*)/', $type, $matches)) {
-                    $type = $matches[1];
-                }
-            }
-
-            $this->_contentType = Horde_Controller_Mime_Type::lookup($type);
-        }
-        return $this->_contentType;
-    }
-
-    /**
-     * @return  array
-     */
-    public function getAccepts()
-    {
-        if (!isset($this->_accepts)) {
-            $accept = $this->getServer('HTTP_ACCEPT');
-            if (empty($accept)) {
-                $types = array();
-                $contentType = $this->getContentType();
-                if ($contentType) { $types[] = $contentType; }
-                $types[] = Horde_Controller_Mime_Type::lookupByExtension('all');
-                $accepts = $types;
-            } else {
-                $accepts = Horde_Controller_Mime_Type::parse($accept);
-            }
-            $this->_accepts = $accepts;
-        }
-        return $this->_accepts;
+        return $this->_path;
     }
 
-
-    /**
-     * Returns the Mime type for the format used in the request. If there is no
-     * format available, the first of the
-     *
-     * @return  string
-     */
-    public function getFormat()
-    {
-        if (!isset($this->_format)) {
-            $params = $this->getParameters();
-            if (isset($params['format'])) {
-                $this->_format = Horde_Controller_Mime_Type::lookupByExtension($params['format']);
-            } else {
-                $this->_format = current($this->getAccepts());
-            }
-        }
-        return $this->_format;
-    }
-
-    /**
-     * Get the remote Ip address as a dotted decimal string.
-     *
-     * @return  string
-     */
-    public function getRemoteIp()
+    public function getMethod()
     {
-        return $this->_remoteIp;
+        $serverVars = $this->getServerVars();
+        return $serverVars['REQUEST_METHOD'];
     }
 
     /**
-     * Get cookie value from specified $name OR get All when $name isn't passed in
+     * Gets the request variables GET, POST, COOKIE, SERVER, REQUEST etc.
      *
-     * @param   string  $name
-     * @param   string  $default
-     * @return  string
+     * @param string $name  The name of the superglobal whose vars to return
      */
-    public function getCookie($name=null, $default=null)
+    protected function getVars($name)
     {
-        if (isset($name)) {
-            return isset($this->_cookie[$name]) ? $this->_cookie[$name] : $default;
-        } else {
-            return $this->_cookie;
-        }
+        return $GLOBALS['_' . $name];
     }
 
-    /**
-     * Get session value from session data by $name or ALL when $name isn't passed in
-     *
-     * @param   string  $name
-     * @param   string  $default
-     * @return  mixed
-     */
-    public function getSession($name=null, $default=null)
+    public function getGetVars()
     {
-        if (isset($name)) {
-            return isset($this->_session[$name]) ? $this->_session[$name] : $default;
-        } else {
-            return $this->_session;
-        }
+        return $this->getVars('GET');
     }
 
-    /**
-     * Get entire list of $_COOKIE parameters
-     *
-     * @return  array
-     */
-    public function getCookieParams()
+    public function getFileVars()
     {
-        return $this->_cookie;
+        return $this->getVars('FILES');
     }
 
-    /**
-     * Get entire list of $_SERVER parameters
-     *
-     * @return  array
-     */
-    public function getServerParams()
+    public function getServerVars()
     {
-        return $this->_server;
+        return $this->getVars('SERVER');
     }
 
-    /**
-     * Get a combination of all parameters. We have to do
-     * some wacky loops to make sure that nested values in one
-     * param list don't overwrite other nested values
-     *
-     * @return  array
-     */
-    public function getParameters()
+    public function getPostVars()
     {
-        $allParams = array();
-        $paramArrays = array($this->_pathParams, /*$this->_formattedRequestParams, */
-                             $this->_get, $this->_post, $this->_files);
-
-        foreach ($paramArrays as $params) {
-            foreach ((array)$params as $key => $value) {
-                if (!is_array($value) || !isset($allParams[$key])) {
-                    $allParams[$key] = $value;
-                } else {
-                    $allParams[$key] = array_merge($allParams[$key], $value);
-                }
-            }
-        }
-        return $allParams;
+        return $this->getVars('POST');
     }
 
-    /**
-     * Get entire list of $_GET parameters
-     * @return  array
-     */
-    public function getGetParams()
+    public function getCookieVars()
     {
-        return $this->_get;
+        return $this->getVars('COOKIE');
     }
 
-    /**
-     * Get entire list of $_POST parameters
-     *
-     * @return  array
-     */
-    public function getPostParams()
+    public function getRequestVars()
     {
-        return $this->_post;
+        return $this->getVars('REQUEST');
     }
 
-    /**
-     * Get entire list of $_FILES parameters
-     *
-     * @return  array
-     */
-    public function getFilesParams()
-    {
-        return $this->_files;
-    }
-
-    /**
-     * Get the session ID of this request (PHPSESSID)
-     * @see    _initSession()
-     * @return string
-     */
     public function getSessionId()
     {
-        return $this->_sessionId;
-    }
-
-
-    /*##########################################################################
-    # Modifiers
-    ##########################################################################*/
-
-    /**
-     * Set the uri and parse it for useful info
-     *
-     * @param   string  $uri
-     */
-    public function setUri($uri)
-    {
-        $this->_uri = trim($uri, '/');
-    }
-
-    /**
-     * Set the session array.
-     *
-     * @param   string  $name
-     * @param   mixed   $value
-     */
-    public function setSession($name, $value=null)
-    {
-        if (is_array($name)) {
-            $this->_session = $name;
-        } else {
-            $this->_session[$name] = $value;
-        }
-    }
-
-
-    /*##########################################################################
-    # Private Methods
-    ##########################################################################*/
-
-    /**
-     * Start up default session storage, and get stored data.
-     *
-     * @todo    further investigate session_cache_limiter() on ie6 (see below)
-     * @todo    implement active record session store
-     */
-    protected function _initSessionData()
-    {
-        $this->_sessionId = session_id();
-
-        if (! strlen($this->_sessionId)) {
-            // internet explorer 6 will ignore the filename/content-type during
-            // sendfile over ssl unless session_cache_limiter('public') is set
-            // http://joseph.randomnetworks.com/archives/2004/10/01/making-ie-accept-file-downloads/
-            $agent = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : '';
-            if (strpos($agent, 'MSIE') !== false) {
-                session_cache_limiter("public");
-            }
-
-            session_start();
-            $this->_sessionId = session_id();
-        }
-
-        // Important: Setting "$this->_session = $_SESSION" does NOT work.
-        $this->_session = array();
-        if (is_array($_SESSION)) {
-            foreach ($_SESSION as $key => $value) {
-                $this->_session[$key] = $value;
-            }
-        }
-    }
-
-    /**
-     * Initialize the File upload information
-     */
-    protected function _setFilesSuperglobals()
-    {
-        if (empty($_FILES)) {
-            $this->_files = array();
-            return;
-        }
-        $_FILES = array_map(array($this, '_fixNestedFiles'), $_FILES);
-
-        // create FileUpload object of the file options
-        foreach ((array)$_FILES as $name => $options) {
-            if (isset($options['tmp_name'])) {
-                $this->_files[$name] = new Horde_Controller_FileUpload($options);
-            } else {
-                foreach ($options as $attr => $data) {
-                    $this->_files[$name][$attr] = new Horde_Controller_FileUpload($data);
-                }
-            }
-        }
-    }
-
-    /**
-     * fix $_FILES superglobal array. (PHP mungles data when we use brackets)
-     *
-     * @link http://www.shauninman.com/archive/2006/11/30/fixing_the_files_superglobal
-     * @param   array   $group
-     */
-    protected function _fixNestedFiles($group)
-    {
-        // only rearrange nested files
-        if (!is_array($group['tmp_name'])) { return $group; }
-
-        foreach ($group as $property => $arr) {
-            foreach ($arr as $item => $value) {
-                $result[$item][$property] = $value;
-            }
-        }
-        return $result;
+        //TODO: how do we get session ID?
+        //should probably be passing it in the constructor, or via setSession
+        //we should definitely lazy-load sessions though cause we don't always care about it
+        //perhaps a preFilter to start the session if the controller requests it, and then call setSession on the request
+        //object
+        return 0;
     }
 
     /**
@@ -596,5 +168,4 @@ class Horde_Controller_Request_Http extends Horde_Controller_Request_Base
 
         return $result;
     }
-
 }
diff --git a/framework/Controller/lib/Horde/Controller/Request/Mock.php b/framework/Controller/lib/Horde/Controller/Request/Mock.php
deleted file mode 100644 (file)
index 381ebaf..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-<?php
-/**
- * Copyright 2007 Maintainable Software, LLC
- * Copyright 2008-2010 The Horde Project (http://www.horde.org/)
- *
- * @author     Mike Naberezny <mike@maintainable.com>
- * @author     Derek DeVries <derek@maintainable.com>
- * @author     Chuck Hagenbuch <chuck@horde.org>
- * @license    http://opensource.org/licenses/bsd-license.php
- * @category   Horde
- * @package    Horde_Controller
- * @subpackage Request
- */
-
-/**
- * @author     Mike Naberezny <mike@maintainable.com>
- * @author     Derek DeVries <derek@maintainable.com>
- * @author     Chuck Hagenbuch <chuck@horde.org>
- * @license    http://opensource.org/licenses/bsd-license.php
- * @category   Horde
- * @package    Horde_Controller
- * @subpackage Request
- */
-class Horde_Controller_Request_Mock extends Horde_Controller_Request_Base
-{
-    public function getPath()
-    {
-    }
-}
diff --git a/framework/Controller/lib/Horde/Controller/Request/Null.php b/framework/Controller/lib/Horde/Controller/Request/Null.php
new file mode 100644 (file)
index 0000000..7803705
--- /dev/null
@@ -0,0 +1,73 @@
+<?php
+/**
+ * Null request object.
+ *
+ * Useful for filter tests that don't use the request object.
+ *
+ * @category Horde
+ * @package  Horde_Controller
+ * @author   Bob McKee <bob@bluestatedigital.com>
+ * @license  http://opensource.org/licenses/bsd-license.php BSD
+ */
+class Horde_Controller_Request_Null implements Horde_Controller_Request
+{
+    /**
+     */
+    public function getMethod()
+    {
+    }
+
+    /**
+     */
+    public function getPath()
+    {
+    }
+
+    /**
+     */
+    public function getParameters()
+    {
+    }
+
+    /**
+     */
+    public function getGetVars()
+    {
+    }
+
+    /**
+     */
+    public function getFileVars()
+    {
+    }
+
+    /**
+     */
+    public function getServerVars()
+    {
+    }
+
+    /**
+     */
+    public function getPostVars()
+    {
+    }
+
+    /**
+     */
+    public function getCookieVars()
+    {
+    }
+
+    /**
+     */
+    public function getRequestVars()
+    {
+    }
+
+    /**
+     */
+    public function getSessionId()
+    {
+    }
+}
diff --git a/framework/Controller/lib/Horde/Controller/RequestConfiguration.php b/framework/Controller/lib/Horde/Controller/RequestConfiguration.php
new file mode 100644 (file)
index 0000000..19e40f2
--- /dev/null
@@ -0,0 +1,18 @@
+<?php
+/**
+ * @category Horde
+ * @package  Horde_Controller
+ * @author   Bob McKee <bob@bluestatedigital.com>
+ * @author   James Pepin <james@bluestatedigital.com>
+ * @license  http://opensource.org/licenses/bsd-license.php BSD
+ */
+interface Horde_Controller_RequestConfiguration
+{
+    public function getControllerName();
+
+    public function setControllerName($controllerName);
+
+    public function getSettingsExporterName();
+
+    public function setSettingsExporterName($settingsName);
+}
diff --git a/framework/Controller/lib/Horde/Controller/Response.php b/framework/Controller/lib/Horde/Controller/Response.php
new file mode 100644 (file)
index 0000000..39581d8
--- /dev/null
@@ -0,0 +1,62 @@
+<?php
+/**
+ * @category Horde
+ * @package  Horde_Controller
+ * @author   James Pepin <james@bluestatedigital.com>
+ * @license  http://opensource.org/licenses/bsd-license.php BSD
+ */
+class Horde_Controller_Response
+{
+    protected $_headers = array();
+    protected $_body;
+    protected $_requestConfiguration;
+
+    public function __construct()
+    {
+    }
+
+    public function setHeaders(array $headers)
+    {
+        $this->_headers = array_merge($this->_headers, $headers);
+    }
+
+    public function setHeader($name, $value)
+    {
+        $this->_headers[$name] = $value;
+    }
+
+    public function setBody($body)
+    {
+        $this->_body = $body;
+    }
+
+    public function getHeaders()
+    {
+        return $this->_headers;
+    }
+
+    public function getBody()
+    {
+        return $this->_body;
+    }
+
+    public function internalRedirect()
+    {
+        return $this->_requestConfiguration != null;
+    }
+
+    public function setRedirectUrl($url)
+    {
+        $this->_headers['Location'] = $url;
+    }
+
+    public function getRedirectConfiguration()
+    {
+        return $this->_requestConfiguration;
+    }
+
+    public function setRedirectConfiguration(Horde_Controller_RequestConfiguration $config)
+    {
+        $this->_requestConfiguration = $config;
+    }
+}
diff --git a/framework/Controller/lib/Horde/Controller/Response/Base.php b/framework/Controller/lib/Horde/Controller/Response/Base.php
deleted file mode 100644 (file)
index 11bc946..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-<?php
-/**
- * Copyright 2007 Maintainable Software, LLC
- * Copyright 2008-2010 The Horde Project (http://www.horde.org/)
- *
- * @author     Mike Naberezny <mike@maintainable.com>
- * @author     Derek DeVries <derek@maintainable.com>
- * @author     Chuck Hagenbuch <chuck@horde.org>
- * @license    http://opensource.org/licenses/bsd-license.php
- * @category   Horde
- * @package    Horde_Controller
- * @subpackage Response
- */
-
-/**
- * @author     Mike Naberezny <mike@maintainable.com>
- * @author     Derek DeVries <derek@maintainable.com>
- * @author     Chuck Hagenbuch <chuck@horde.org>
- * @license    http://opensource.org/licenses/bsd-license.php
- * @category   Horde
- * @package    Horde_Controller
- * @subpackage Response
- */
-class Horde_Controller_Response_Base
-{
-    protected $_body = '';
-
-    /**
-     */
-    public function send()
-    {
-        echo $this->_body;
-    }
-
-    public function setBody($body)
-    {
-        $this->_body = $body;
-    }
-}
diff --git a/framework/Controller/lib/Horde/Controller/Response/Cli.php b/framework/Controller/lib/Horde/Controller/Response/Cli.php
deleted file mode 100644 (file)
index 8b1d778..0000000
+++ /dev/null
@@ -1,78 +0,0 @@
-<?php
-/**
- * Copyright 2007 Maintainable Software, LLC
- * Copyright 2008-2010 The Horde Project (http://www.horde.org/)
- *
- * @author     Mike Naberezny <mike@maintainable.com>
- * @author     Derek DeVries <derek@maintainable.com>
- * @author     Chuck Hagenbuch <chuck@horde.org>
- * @license    http://opensource.org/licenses/bsd-license.php
- * @category   Horde
- * @package    Horde_Controller
- * @subpackage Response
- */
-
-/**
- * @TODO Allow specifying the stream where output is going instead of assuming
- * STDOUT.
- *
- * @author     Mike Naberezny <mike@maintainable.com>
- * @author     Derek DeVries <derek@maintainable.com>
- * @author     Chuck Hagenbuch <chuck@horde.org>
- * @license    http://opensource.org/licenses/bsd-license.php
- * @category   Horde
- * @package    Horde_Controller
- * @subpackage Response
- */
-class Horde_Controller_Response_Cli extends Horde_Controller_Response_Base
-{
-    /**
-     * @var stream
-     */
-    protected $_stream;
-
-    public function construct()
-    {
-        $this->_stream = fopen('php://stdout');
-    }
-
-    /**
-     * Writes a string to the Response stream
-     *
-     * Can be called with an array of parameters or with a variable number of
-     * parameters like printf.
-     *
-     * @param string $string The string to write to the reponse stream
-     * @param array|params $params The parameters to replace in the string (think printf)
-     */
-    public function write($string, $params = null)
-    {
-        if (!is_array($params)) {
-            $params = func_get_args();
-            array_shift($params);
-        }
-        fwrite($this->_stream, vsprintf($string, $params));
-    }
-
-    /**
-     * Writes a newline-terminated string to the Response stream
-     *
-     * Can be called with an array of parameters or with a variable number of
-     * parameters like printf.
-     *
-     * @param string $string The string to write to the reponse stream
-     * @param array|params $params The parameters to replace in the string (think printf)
-     */
-    public function writeLn($string, $params = array())
-    {
-        if (!is_array($params)) {
-            $params = func_get_args();
-            array_shift($params);
-        }
-        $line = vsprintf($string, $params);
-        if (substr($line, -1) != "\n") {
-            $line .= "\n";
-        }
-        fwrite($this->_stream, $line);
-    }
-}
diff --git a/framework/Controller/lib/Horde/Controller/Response/Http.php b/framework/Controller/lib/Horde/Controller/Response/Http.php
deleted file mode 100644 (file)
index 0675e4c..0000000
+++ /dev/null
@@ -1,305 +0,0 @@
-<?php
-/**
- * Copyright 2007 Maintainable Software, LLC
- * Copyright 2008-2010 The Horde Project (http://www.horde.org/)
- *
- * @author     Mike Naberezny <mike@maintainable.com>
- * @author     Derek DeVries <derek@maintainable.com>
- * @author     Chuck Hagenbuch <chuck@horde.org>
- * @license    http://opensource.org/licenses/bsd-license.php
- * @category   Horde
- * @package    Horde_Controller
- * @subpackage Response
- */
-
-/**
- * @author     Mike Naberezny <mike@maintainable.com>
- * @author     Derek DeVries <derek@maintainable.com>
- * @author     Chuck Hagenbuch <chuck@horde.org>
- * @license    http://opensource.org/licenses/bsd-license.php
- * @category   Horde
- * @package    Horde_Controller
- * @subpackage Response
- */
-class Horde_Controller_Response_Http extends Horde_Controller_Response_Base
-{
-    /**
-     * Cookies sent with response
-     * @var array
-     */
-    protected $_cookie = array();
-
-    /**
-     * Stored session data
-     * @var array
-     */
-    protected $_session = array();
-
-    /**
-     * The url to redirect the request to
-     * @var string
-     */
-    protected $_redirectUrl = null;
-
-    /**
-     * The http status code. Default to OK
-     * @var string
-     */
-    protected $_status = '200 OK';
-
-    /**
-     * HTTP headers to send
-     * @var array
-     */
-    protected $_headers = array();
-
-    /**
-     * prevent cached content (ie)
-     * @var array
-     */
-    protected $_preventCache = true;
-
-    /**
-     * Body of the rendered page
-     * @var string
-     */
-    protected $_body = null;
-
-
-    /*##########################################################################
-    # Construct
-    ##########################################################################*/
-
-    /**
-     * Construct response
-     */
-    public function __construct(){}
-
-
-    /*##########################################################################
-    # Instance methods
-    ##########################################################################*/
-
-    /**
-     * Set the url for redirection
-     *
-     * @param   string  $toUrl
-     */
-    public function redirect($toUrl)
-    {
-        /* Alternate 301 branch - should allow choosing status:
-           $this->_status = '301 Moved Permanently';
-           $this->_redirectUrl = $toUrl;
-           $this->_headers["Location: $toUrl"] = true;
-           $this->_headers["Connection: close"] = true;
-        */
-
-        $this->_status = '302 Found';
-        $this->_redirectUrl = $toUrl;
-        $this->_headers["Location: $toUrl"] = true;
-    }
-
-    /**
-     * Page was not found
-     */
-    public function pageNotFound()
-    {
-        $this->_status = '404 Page Not Found';
-    }
-
-    /**
-     * Send content to the browser.
-     *
-     * After the body content has been sent, terminates the execution of the
-     * PHP script.
-     */
-    public function send()
-    {
-        // send all headers
-        foreach ($this->getHeaders() as $header => $replace) {
-            header($header, $replace);
-        }
-
-        // set cookies
-        foreach ($this->_cookie as $name => $options) {
-            setcookie($name, $options['value'], $options['expiration'], $options['path']);
-        }
-
-        // set session data
-        foreach ($this->_session as $name => $value) {
-            $_SESSION[$name] = $value;
-        }
-
-        // send body
-        print $this->getBody();
-    }
-
-
-    /*##########################################################################
-    # Accessors
-    ##########################################################################*/
-
-    /**
-     * Set a cookie
-     *
-     * @param   string  $name
-     * @param   string  $value
-     * @param   int     $expiration
-     * @param   string  $path
-     */
-    public function setCookie($name, $value, $expiration=0, $path=null)
-    {
-        // only set cookies for this matter by default
-        $this->_cookie[$name] = array('value'      => $value,
-                                      'expiration' => $expiration,
-                                      'path' => isset($path) ? $path : '/');
-    }
-
-    /**
-     * Set a session variable OR all session variables (by array).
-     *
-     * <code>
-     *  // set single session var
-     *  $this->setSession('NAME', 'my session');
-     *
-     *  // set all session vars (overwrites previous single sessions set)
-     *  $this->setSession(array('NAME 1' => 'my session 1',
-     *                          'NAME 2' => 'my session 2'));
-     * </code>
-     *
-     * @param   mixed   $name
-     * @param   mixed   $value
-     */
-    public function setSession($name, $value=null)
-    {
-        // Set by name or all at once
-        if (is_string($name)) {
-            $this->_session[$name] = $value;
-        } elseif (is_array($name)) {
-            $this->_session = $name;
-        }
-    }
-
-    /**
-     * Add header information
-     *
-     * @param   string  $header
-     * @param   boolean $replace
-     */
-    public function setHeader($header, $replace=true)
-    {
-        $this->_headers[$header] = $replace;
-    }
-
-    /**
-     * Set the body of the response
-     *
-     * @param   string  $body
-     */
-    public function setBody($body)
-    {
-        $this->_body = $body;
-    }
-
-    /**
-     * Set the HTTP status code
-     *
-     * @param   string  $status
-     */
-    public function setStatus($status)
-    {
-        $this->_status = $status;
-    }
-
-    /**
-     * Get the headers of the response
-     *
-     * @return  array
-     */
-    public function getHeaders()
-    {
-        $headers["HTTP/1.1 $this->_status"] = true;
-
-        if ($this->_status == '200 OK') {
-            $headers["Connection: close"] = true;
-            $headers["Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT"] = true;
-
-            // Try to keep browser from caching any screen to ensure current data.
-            if ($this->_preventCache && !isset($this->_headers["Expires: 0"])) {
-                $headers["Expires: Mon, 26 Jul 1997 05:00:00 GMT"]             = true;
-                $headers["Cache-Control: no-store, no-cache, must-revalidate"] = true;
-                $headers["Pragma: no-cache"]                                   = true;
-            }
-        }
-        return array_merge($this->_headers, $headers);
-    }
-
-    /**
-     * Get the body of the response
-     *
-     * @return  string
-     */
-    public function getBody()
-    {
-        return $this->_body;
-    }
-
-    /**
-     * Get the HTTP status of the response
-     *
-     * @return  string
-     */
-    public function getStatus()
-    {
-        return $this->_status;
-    }
-
-    /**
-     * Get  3 digit http code from the status
-     *
-     * @return  int
-     */
-    public function getStatusCode()
-    {
-        preg_match("/(\d\d\d)/", $this->_status, $matches);
-        return isset($matches[1]) ? (int) $matches[1] : 0;
-    }
-
-    /**
-     * @todo charset
-     */
-    public function setContentType($mimeType)
-    {
-        $this->setHeader("Content-Type: $mimeType", $replace=true);
-    }
-
-    /**
-     * Get if the response is a 200 OK
-     *
-     * @return  boolean
-     */
-    public function isOk()
-    {
-        return substr($this->_status, 0, 1) == '2';
-    }
-
-    /**
-     * Get if the response is a redirection
-     *
-     * @return  boolean
-     */
-    public function isRedirect()
-    {
-        return substr($this->_status, 0, 1) == '3';
-    }
-
-    /**
-     * Get where the page is redirecting to
-     *
-     * @return  string
-     */
-    public function getRedirectUrl()
-    {
-        return $this->_redirectUrl;
-    }
-}
diff --git a/framework/Controller/lib/Horde/Controller/Response/Mock.php b/framework/Controller/lib/Horde/Controller/Response/Mock.php
deleted file mode 100644 (file)
index eabd257..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-<?php
-/**
- * Copyright 2007 Maintainable Software, LLC
- * Copyright 2008-2010 The Horde Project (http://www.horde.org/)
- *
- * @author     Mike Naberezny <mike@maintainable.com>
- * @author     Derek DeVries <derek@maintainable.com>
- * @author     Chuck Hagenbuch <chuck@horde.org>
- * @license    http://opensource.org/licenses/bsd-license.php
- * @category   Horde
- * @package    Horde_Controller
- * @subpackage Response
- */
-
-/**
- * @author     Mike Naberezny <mike@maintainable.com>
- * @author     Derek DeVries <derek@maintainable.com>
- * @author     Chuck Hagenbuch <chuck@horde.org>
- * @license    http://opensource.org/licenses/bsd-license.php
- * @category   Horde
- * @package    Horde_Controller
- * @subpackage Response
- */
-class Horde_Controller_Response_Mock extends Horde_Controller_Response_Base
-{
-}
diff --git a/framework/Controller/lib/Horde/Controller/ResponseWriter.php b/framework/Controller/lib/Horde/Controller/ResponseWriter.php
new file mode 100644 (file)
index 0000000..9bc4234
--- /dev/null
@@ -0,0 +1,11 @@
+<?php
+/**
+ * @category Horde
+ * @package  Horde_Controller
+ * @author   James Pepin <james@bluestatedigital.com>
+ * @license  http://opensource.org/licenses/bsd-license.php BSD
+ */
+interface Horde_Controller_ResponseWriter
+{
+    public function writeResponse(Horde_Controller_Response $response);
+}
diff --git a/framework/Controller/lib/Horde/Controller/ResponseWriter/Web.php b/framework/Controller/lib/Horde/Controller/ResponseWriter/Web.php
new file mode 100644 (file)
index 0000000..864a6cc
--- /dev/null
@@ -0,0 +1,19 @@
+<?php
+/**
+ * @category Horde
+ * @package  Horde_Controller
+ * @author   James Pepin <james@bluestatedigital.com>
+ * @license  http://opensource.org/licenses/bsd-license.php BSD
+ */
+class Horde_Controller_ResponseWriter_Web implements Horde_Controller_ResponseWriter
+{
+    /**
+     */
+    public function writeResponse(Horde_Controller_Response $response)
+    {
+        foreach ($response->getHeaders() as $key => $value) {
+            header("$key: $value");
+        }
+        echo $response->getBody();
+    }
+}
diff --git a/framework/Controller/lib/Horde/Controller/ResponseWriter/WebDebug.php b/framework/Controller/lib/Horde/Controller/ResponseWriter/WebDebug.php
new file mode 100644 (file)
index 0000000..562434c
--- /dev/null
@@ -0,0 +1,30 @@
+<?php
+/**
+ * @category Horde
+ * @package  Horde_Controller
+ * @author   James Pepin <james@bluestatedigital.com>
+ * @license  http://opensource.org/licenses/bsd-license.php BSD
+ */
+class Horde_Controller_ResponseWriter_WebDebug implements Horde_Controller_ResponseWriter
+{
+    public function writeResponse(Horde_Controller_Response $response)
+    {
+        $headerHtml = '<div><strong>Headers:</strong><pre>';
+        $headers = $response->getHeaders();
+        foreach ($headers as $key => $value) {
+            $headerHtml .= "$key: $value\n";
+        }
+        echo htmlspecialchars($headerHtml) . '</pre></div>';
+
+        if ($headers['Location']) {
+            echo '<p>Redirect To: <a href="' . htmlspecialchars($headers['Location']) . '">' . htmlspecialchars($headers['Location']) . '</a></p>';
+        }
+
+        if (isset($headers['Content-Encoding']) && $headers['Content-Encoding'] == 'gzip') {
+            // Strip off the header and inflate it
+            echo gzinflate(substr($response->getBody(), 10));
+        } else {
+            echo $response->getBody();
+        }
+    }
+}
diff --git a/framework/Controller/lib/Horde/Controller/Runner.php b/framework/Controller/lib/Horde/Controller/Runner.php
new file mode 100644 (file)
index 0000000..82d188a
--- /dev/null
@@ -0,0 +1,46 @@
+<?php
+/**
+ * Class to execute the controller request
+ *
+ * @category Horde
+ * @package  Horde_Controller
+ * @author   James Pepin <james@bluestatedigital.com>
+ * @license  http://opensource.org/licenses/bsd-license.php BSD
+ */
+class Horde_Controller_Runner
+{
+    protected $_logger;
+
+    public function __construct(Horde_Log_Logger $logger)
+    {
+        $this->_logger = $logger;
+    }
+
+    public function execute(Horde_Injector $injector, Horde_Controller_Request $request, Horde_Controller_RequestConfiguration $config)
+    {
+        $this->_logger->debug('RequestConfiguration in Horde_Controller_Runner: ' . print_r($config, true));
+
+        $exporter = $injector->getInstance($config->getSettingsExporterName());
+        $exporter->exportBindings($injector);
+
+        $controller = $config->getControllerName();
+        if (!$controller) {
+            throw new Horde_Controller_Exception('No controller defined');
+        }
+
+        $implementationBinder = new Horde_Injector_Binder_Implementation($controller);
+        $injector->addBinder('Horde_Controller', new Horde_Injector_Binder_AnnotatedSetters($implementationBinder));
+
+        $filterRunner = $injector->createInstance('Horde_Controller_FilterRunner');
+        $exporter->exportFilters($filterRunner, $injector);
+
+        $response = $filterRunner->processRequest($request, $injector->createInstance('Horde_Controller_Response'));
+        if ($response->internalRedirect()) {
+            $this->_logger->debug('Internal redirect');
+            return $this->execute($injector->createChildInjector(), $request, $response->getRedirectConfiguration());
+        }
+
+        $this->_logger->debug('Returning Horde_Controller_Response');
+        return $response;
+    }
+}
diff --git a/framework/Controller/lib/Horde/Controller/Scanner.php b/framework/Controller/lib/Horde/Controller/Scanner.php
deleted file mode 100644 (file)
index 16f9292..0000000
+++ /dev/null
@@ -1,170 +0,0 @@
-<?php
-/**
- * Copyright 2007 Maintainable Software, LLC
- * Copyright 2008-2010 The Horde Project (http://www.horde.org/)
- *
- * @author     Mike Naberezny <mike@maintainable.com>
- * @author     Derek DeVries <derek@maintainable.com>
- * @author     Chuck Hagenbuch <chuck@horde.org>
- * @license    http://opensource.org/licenses/bsd-license.php
- * @category   Horde
- * @package    Horde_Controller
- */
-
-/**
- * Horde_Routes_Mapper requires a list of all possible controller names
- * in order to build the regular expressions it uses for matching routes.
- * It uses a callback, controllerScan, to get this list.
- *
- * Depending on the routes connected to the mapper, it may be possible to
- * determine all of the controller names from the routes themselves.  If
- * not, the filesystem must be scanned to determine the controller names.
- *
- * This class contains two controllerScan strategies, one that scans the
- * filesystem and one that doesn't, and can determine the most efficient
- * strategy to use for a given mapper.
- *
- * @author     Mike Naberezny <mike@maintainable.com>
- * @author     Derek DeVries <derek@maintainable.com>
- * @author     Chuck Hagenbuch <chuck@horde.org>
- * @license    http://opensource.org/licenses/bsd-license.php
- * @category   Horde
- * @package    Horde_Controller
- */
-class Horde_Controller_Scanner
-{
-    /**
-     * @var Horde_Routes_Mapper
-     */
-    protected $_mapper;
-
-    /**
-     * controllerScan strategy selected for this mapper.
-     * @var callback
-     */
-    protected $_callback;
-
-    /**
-     * Array of controller names collected from route hardcodes
-     * @var array
-     */
-    protected $_controllers;
-
-
-    /**
-     * Constructor.  Analyze the routes connected to this mapper to
-     * select a controllerScan strategy.
-     *
-     * @param  Horde_Routes_Mapper
-     */
-    public function __construct($mapper)
-    {
-        $this->_mapper = $mapper;
-        $this->analyze();
-    }
-
-    /**
-     * Analyze the routes connected to the mapper.  If all of the possible
-     * controller names can be determined from the routes themselves, select
-     * the scanHardcodes() strategy that returns them collected from the
-     * routes.  If the possible controller names cannot be determined this
-     * way, select the scanFilesystem() strategy.
-     */
-    public function analyze()
-    {
-        $needScan = false;
-        $controllers = array();
-        foreach ($this->_mapper->matchList as $route) {
-            if (in_array('controller', $route->hardCoded)) {
-                $controllers[ $route->defaults['controller'] ] = true;
-            } else {
-                $needScan = true;
-                break;
-            }
-        }
-        $this->_controllers = array_keys($controllers);
-
-        if ($needScan || empty($this->_controllers)) {
-            $this->_callback = array($this, 'scanFilesystem');
-        } else {
-            $this->_callback = array($this, 'scanHardcodes');
-        }
-    }
-
-    /**
-     * Get the controllerScan callback stategy selected for this mapper.
-     *
-     * @return callback
-     */
-    public function getCallback()
-    {
-        return $this->_callback;
-    }
-
-    /**
-     * Scan a directory and return an array of the controllers it contains.
-     * The array is used by Horde_Routes to build its matching regexps.
-     *
-     * @param  string  $dirname  Controller directory
-     * @param  string  $prefix   Prefix controllers found with string
-     * @return array             Controller names
-     */
-    public function scanFilesystem($dirname = null, $prefix = '')
-    {
-        $controllers = array();
-
-        if ($dirname === null) {
-            return $controllers;
-        }
-
-        $baseregexp = preg_quote($dirname, '/');
-
-        foreach (new RecursiveIteratorIterator(
-                 new RecursiveDirectoryIterator($dirname)) as $entry) {
-
-            if ($entry->isFile()) {
-                // match .php files that don't start with an underscore
-                if (preg_match('/^[^_]{1,1}.*\.php$/', basename($entry->getFilename())) != 0) {
-                    // strip off base path: dirname/admin/users.php -> admin/users.php
-                    $controller = preg_replace("/^$baseregexp(.*)\.php/", '\\1', $entry->getPathname());
-
-                    // PrepareController -> prepare_controller -> prepare
-                    $controller = strtolower(preg_replace('/([a-z])([A-Z])/', "\${1}_\${2}", $controller));
-                    $controller = substr($controller, 0, -(strlen('_controller')));
-
-                    // add to controller list
-                    $controllers[] = $prefix . $controller;
-                }
-            }
-        }
-
-        $callback = array('Horde_Routes_Utils', 'longestFirst');
-        usort($controllers, $callback);
-
-        return $controllers;
-    }
-
-    /**
-     * Return an array of controller names that were collected from the
-     * hardcodes of the routes connected to this mapper.
-     *
-     * @param  string  $dirname  For method signature compatibility only
-     * @param  string  $prefix   Prefix controllers found with string
-     * @return array             Controller names
-     */
-    public function scanHardcodes($dirname = null, $prefix = null)
-    {
-        if ($prefix === null) {
-            $controllers = $this->_controllers;
-        } else {
-            $controllers = array();
-            foreach ($this->_controllers as $controller) {
-                $controllers[] = $prefix . $controller;
-            }
-        }
-
-        usort($controllers, 'Horde_Routes_Utils::longestFirst');
-        return $controllers;
-    }
-
-}
\ No newline at end of file
diff --git a/framework/Controller/lib/Horde/Controller/SettingsExporter.php b/framework/Controller/lib/Horde/Controller/SettingsExporter.php
new file mode 100644 (file)
index 0000000..42da2f3
--- /dev/null
@@ -0,0 +1,20 @@
+<?php
+/**
+ * Interface for the object that builds a request chain around a controller.
+ *
+ * @category Horde
+ * @package  Horde_Controller
+ * @author   Bob McKee <bob@bluestatedigital.com>
+ * @author   James Pepin <james@bluestatedigital.com>
+ * @license  http://opensource.org/licenses/bsd-license.php BSD
+ */
+interface Horde_Controller_SettingsExporter
+{
+    /**
+     */
+    public function exportBindings(Horde_Injector $injector);
+
+    /**
+     */
+    public function exportFilters(Horde_Controller_FilterCollection $filters, Horde_Injector $injector);
+}
diff --git a/framework/Controller/lib/Horde/Controller/SettingsExporter/Default.php b/framework/Controller/lib/Horde/Controller/SettingsExporter/Default.php
new file mode 100644 (file)
index 0000000..5e93263
--- /dev/null
@@ -0,0 +1,24 @@
+<?php
+/**
+ * Default controller request builder
+ *
+ * @category Horde
+ * @package  Horde_Controller
+ * @author   Bob McKee <bob@bluestatedigital.com>
+ * @author   James Pepin <james@bluestatedigital.com>
+ * @license  http://opensource.org/licenses/bsd-license.php BSD
+ */
+class Horde_Controller_SettingsExporter_Default implements Horde_Controller_SettingsExporter
+{
+    /**
+     */
+    public function exportBindings(Horde_Injector $injector)
+    {
+    }
+
+    /**
+     */
+    public function exportFilters(Horde_Controller_FilterCollection $filters, Horde_Injector $injector)
+    {
+    }
+}
diff --git a/framework/Controller/lib/Horde/Controller/StatusCodes.php b/framework/Controller/lib/Horde/Controller/StatusCodes.php
deleted file mode 100644 (file)
index 64fdb18..0000000
+++ /dev/null
@@ -1,139 +0,0 @@
-<?php
-/**
- * Copyright 2007-2008 Maintainable Software, LLC
- * Copyright 2008-2010 The Horde Project (http://www.horde.org)
- *
- * @author     Mike Naberezny <mike@maintainable.com>
- * @license    http://opensource.org/licenses/bsd-license.php BSD
- * @category   Horde
- * @package    Horde_Controller
- */
-class Horde_Controller_StatusCodes
-{
-    /**
-     * All known status codes and their messages
-     * @var array
-     */
-    public static $statusCodes = array(
-      100 => "Continue",
-      101 => "Switching Protocols",
-      102 => "Processing",
-
-      200 => "OK",
-      201 => "Created",
-      202 => "Accepted",
-      203 => "Non-Authoritative Information",
-      204 => "No Content",
-      205 => "Reset Content",
-      206 => "Partial Content",
-      207 => "Multi-Status",
-      226 => "IM Used",
-
-      300 => "Multiple Choices",
-      301 => "Moved Permanently",
-      302 => "Found",
-      303 => "See Other",
-      304 => "Not Modified",
-      305 => "Use Proxy",
-      307 => "Temporary Redirect",
-
-      400 => "Bad Request",
-      401 => "Unauthorized",
-      402 => "Payment Required",
-      403 => "Forbidden",
-      404 => "Not Found",
-      405 => "Method Not Allowed",
-      406 => "Not Acceptable",
-      407 => "Proxy Authentication Required",
-      408 => "Request Timeout",
-      409 => "Conflict",
-      410 => "Gone",
-      411 => "Length Required",
-      412 => "Precondition Failed",
-      413 => "Request Entity Too Large",
-      414 => "Request-URI Too Long",
-      415 => "Unsupported Media Type",
-      416 => "Requested Range Not Satisfiable",
-      417 => "Expectation Failed",
-      422 => "Unprocessable Entity",
-      423 => "Locked",
-      424 => "Failed Dependency",
-      426 => "Upgrade Required",
-
-      500 => "Internal Server Error",
-      501 => "Not Implemented",
-      502 => "Bad Gateway",
-      503 => "Service Unavailable",
-      504 => "Gateway Timeout",
-      505 => "HTTP Version Not Supported",
-      507 => "Insufficient Storage",
-      510 => "Not Extended"
-    );
-
-    /**
-     * Given a status parameter, determine whether it needs to be converted
-     * to a string. If it is an integer, use the $statusCodes hash to lookup
-     * the default message. If it is a string, build $symbolToStatusCode
-     * and convert it.
-     *
-     *   interpret(404)         => "404 Not Found"
-     *   interpret("notFound")  => "404 Not Found"
-     *
-     * Differences from Rails:
-     *   - $status is camelized, not underscored.
-     *   - an unknown status raises an exception
-     *
-     * @param  string|integer  Status code or "symbol"
-     * @return string          Header
-     */
-    public static function interpret($status)
-    {
-        // Status from integer or numeric string
-        if (is_numeric($status)) {
-            if (isset(self::$statusCodes[$status])) {
-                return $status . ' ' . self::$statusCodes[$status];
-            } else {
-                $msg = 'Unknown status code: ' . $status;
-                throw new InvalidArgumentException($msg);
-            }
-
-        // Status from string
-        } elseif (is_string($status)) {
-            // Build a string-to-integer lookup for converting a symbol (like
-            // 'created' or 'notImplemented') into its corresponding HTTP status
-            // code (like 200 or 501).
-            static $symbolToStatusCode = array();
-            $inflector = new Horde_Support_Inflector();
-            if (empty($symbolToStatusCode)) {
-                foreach (self::$statusCodes as $code => $message) {
-                    $symbol = $inflector->camelize($message, $first='lower');
-                    $symbolToStatusCode[$symbol] = $code;
-                }
-            }
-
-            // Convert status symbol to integer code, return header
-            if (isset($symbolToStatusCode[$status])) {
-                return self::interpret($symbolToStatusCode[$status]);
-            }
-
-            // Error: Status symbol could not be converted to an integer code
-            // Try to help if the developer mixed up underscore/camel
-            $msg = 'Unknown status: \'' . $status . '\'';
-            if (strpos($status, '_')) {
-                $status = $inflector->camelize($status, $first='lower');
-                if (isset($symbolToStatusCode[$status])) {
-                    $msg .= ' (underscore), did you mean \'' . $status . '\' (camel)?';
-                }
-            }
-            throw new InvalidArgumentException($msg);
-
-        // Status is an unknown type
-        } else {
-            $msg = '$status must be numeric or string, got '
-                 . gettype($status);
-            throw new InvalidArgumentException($msg);
-        }
-
-    }
-
-}
diff --git a/framework/Controller/lib/Horde/Controller/UrlWriter.php b/framework/Controller/lib/Horde/Controller/UrlWriter.php
deleted file mode 100644 (file)
index 330e300..0000000
+++ /dev/null
@@ -1,84 +0,0 @@
-<?php
-/**
- * Copyright 2007-2008 Maintainable Software, LLC
- * Copyright 2008-2010 The Horde Project (http://www.horde.org)
- *
- * @author     Mike Naberezny <mike@maintainable.com>
- * @license    http://opensource.org/licenses/bsd-license.php BSD
- * @category   Horde
- * @package    Horde_Controller
- */
-class Horde_Controller_UrlWriter
-{
-    /**
-     * Defaults to merge into route parameters when not using named routes.
-     * @var array
-     */
-    protected $_defaults;
-
-    /**
-     * @var Horde_Routes_Util
-     */
-    protected $_utils;
-
-    /**
-     * Class constructor
-     *
-     * @param  array                   $defaults  Defaults to merge for urlFor()
-     * @param  null|Horde_Route_Utils  $utils     Route utilities
-     */
-    public function __construct($defaults = array(), $utils = null)
-    {
-        $this->_defaults = $defaults;
-        if ($utils === null) {
-            $utils = Horde_Controller_Dispatcher::singleton()->getRouteUtils();
-        }
-        $this->_utils = $utils;
-    }
-
-    /**
-     * Generate a URL.  Same signature as Horde_Routes_Utils->urlFor().
-     *
-     * @param  $first   mixed
-     * @param  $second  mixed
-     * @return string
-     */
-    public function urlFor($first, $second = array())
-    {
-        // anonymous route: serialize to params & merge defaults
-        //   urlFor(array('controller' => 'books'))
-        if (is_array($first)) {
-            $first = array_merge($this->_defaults,
-                                 $this->_serializeToParams($first));
-        }
-
-        // named route: serialize to params only (no merge)
-        //   urlFor('notes', array('action' => 'show', 'id' => 1))
-        if (is_array($second)) {
-            $second = $this->_serializeToParams($second);
-        }
-
-        // url generation "route memory" is not useful here
-        $this->_utils->mapperDict = array();
-
-        // generate url
-        return $this->_utils->urlFor($first, $second);
-    }
-
-    /**
-     * Serialize any objects in the collection supporting toParam() before
-     * passing the collection to Horde_Routes.
-     *
-     * @param  array  $collection
-     * @param  array
-     */
-    protected function _serializeToParams($collection)
-    {
-        foreach ($collection as &$value) {
-            if (is_object($value) && method_exists($value, 'toParam')) {
-                $value = $value->toParam();
-            }
-        }
-        return $collection;
-    }
-}
\ No newline at end of file
index 1294e57..c068860 100644 (file)
@@ -36,29 +36,35 @@ http://pear.php.net/dtd/package-2.0.xsd">
    <dir name="lib">
     <dir name="Horde">
      <dir name="Controller">
-      <dir name="Mime">
-       <file name="Type.php" role="php" />
-      </dir> <!-- /lib/Horde/Controller/Mime -->
+      <dir name="Filter">
+       <file name="Gzip.php" role="php" />
+      </dir> <!-- /lib/Horde/Controller/Filter -->
       <dir name="Request">
-       <file name="Base.php" role="php" />
-       <file name="Cli.php" role="php" />
        <file name="Http.php" role="php" />
-       <file name="Mock.php" role="php" />
+       <file name="Null.php" role="php" />
       </dir> <!-- /lib/Horde/Controller/Request -->
-      <dir name="Response">
-       <file name="Base.php" role="php" />
-       <file name="Cli.php" role="php" />
-       <file name="Http.php" role="php" />
-       <file name="Mock.php" role="php" />
-      </dir> <!-- /lib/Horde/Controller/Response -->
+      <dir name="ResponseWriter">
+       <file name="Web.php" role="php" />
+       <file name="WebDebug.php" role="php" />
+      </dir> <!-- /lib/Horde/Controller/ResponseWriter -->
+      <dir name="SettingsExporter">
+       <file name="Default.php" role="php" />
+      </dir> <!-- /lib/Horde/Controller/SettingsExporter -->
       <file name="Base.php" role="php" />
-      <file name="Dispatcher.php" role="php" />
       <file name="Exception.php" role="php" />
-      <file name="FileUpload.php" role="php" />
-      <file name="Scanner.php" role="php" />
-      <file name="StatusCodes.php" role="php" />
-      <file name="UrlWriter.php" role="php" />
+      <file name="FilterCollection.php" role="php" />
+      <file name="FilterRunner.php" role="php" />
+      <file name="Null.php" role="php" />
+      <file name="PostFilter.php" role="php" />
+      <file name="PreFilter.php" role="php" />
+      <file name="Request.php" role="php" />
+      <file name="RequestConfiguration.php" role="php" />
+      <file name="Response.php" role="php" />
+      <file name="ResponseWriter.php" role="php" />
+      <file name="Runner.php" role="php" />
+      <file name="SettingsExporter.php" role="php" />
      </dir> <!-- /lib/Horde/Controller -->
+     <file name="Controller.php" role="php" />
     </dir> <!-- /lib/Horde -->
    </dir> <!-- /lib -->
   </dir> <!-- / -->
@@ -80,21 +86,25 @@ http://pear.php.net/dtd/package-2.0.xsd">
  <phprelease>
   <filelist>
    <install name="lib/Horde/Controller/Base.php" as="Horde/Controller/Base.php" />
-   <install name="lib/Horde/Controller/Dispatcher.php" as="Horde/Controller/Dispatcher.php" />
    <install name="lib/Horde/Controller/Exception.php" as="Horde/Controller/Exception.php" />
-   <install name="lib/Horde/Controller/FileUpload.php" as="Horde/Controller/FileUpload.php" />
-   <install name="lib/Horde/Controller/Scanner.php" as="Horde/Controller/Scanner.php" />
-   <install name="lib/Horde/Controller/StatusCodes.php" as="Horde/Controller/StatusCodes.php" />
-   <install name="lib/Horde/Controller/UrlWriter.php" as="Horde/Controller/UrlWriter.php" />
-   <install name="lib/Horde/Controller/Mime/Type.php" as="Horde/Controller/Mime/Type.php" />
-   <install name="lib/Horde/Controller/Request/Base.php" as="Horde/Controller/Request/Base.php" />
-   <install name="lib/Horde/Controller/Request/Cli.php" as="Horde/Controller/Request/Cli.php" />
+   <install name="lib/Horde/Controller/Filter/Gzip.php" as="Horde/Controller/Filter/Gzip.php" />
+   <install name="lib/Horde/Controller/FilterCollection.php" as="Horde/Controller/FilterCollection.php" />
+   <install name="lib/Horde/Controller/FilterRunner.php" as="Horde/Controller/FilterRunner.php" />
+   <install name="lib/Horde/Controller/Null.php" as="Horde/Controller/Null.php" />
+   <install name="lib/Horde/Controller/PostFilter.php" as="Horde/Controller/PostFilter.php" />
+   <install name="lib/Horde/Controller/PreFilter.php" as="Horde/Controller/PreFilter.php" />
    <install name="lib/Horde/Controller/Request/Http.php" as="Horde/Controller/Request/Http.php" />
-   <install name="lib/Horde/Controller/Request/Mock.php" as="Horde/Controller/Request/Mock.php" />
-   <install name="lib/Horde/Controller/Response/Base.php" as="Horde/Controller/Response/Base.php" />
-   <install name="lib/Horde/Controller/Response/Cli.php" as="Horde/Controller/Response/Cli.php" />
-   <install name="lib/Horde/Controller/Response/Http.php" as="Horde/Controller/Response/Http.php" />
-   <install name="lib/Horde/Controller/Response/Mock.php" as="Horde/Controller/Response/Mock.php" />
+   <install name="lib/Horde/Controller/Request/Null.php" as="Horde/Controller/Request/Null.php" />
+   <install name="lib/Horde/Controller/Request.php" as="Horde/Controller/Request.php" />
+   <install name="lib/Horde/Controller/RequestConfiguration.php" as="Horde/Controller/RequestConfiguration.php" />
+   <install name="lib/Horde/Controller/Response.php" as="Horde/Controller/Response.php" />
+   <install name="lib/Horde/Controller/ResponseWriter/Web.php" as="Horde/Controller/ResponseWriter/Web.php" />
+   <install name="lib/Horde/Controller/ResponseWriter/WebDebug.php" as="Horde/Controller/ResponseWriter/WebDebug.php" />
+   <install name="lib/Horde/Controller/ResponseWriter.php" as="Horde/Controller/ResponseWriter.php" />
+   <install name="lib/Horde/Controller/Runner.php" as="Horde/Controller/Runner.php" />
+   <install name="lib/Horde/Controller/SettingsExporter/Default.php" as="Horde/Controller/SettingsExporter/Default.php" />
+   <install name="lib/Horde/Controller/SettingsExporter.php" as="Horde/Controller/SettingsExporter.php" />
+   <install name="lib/Horde/Controller.php" as="Horde/Controller.php" />
   </filelist>
  </phprelease>
 </package>
diff --git a/framework/Controller/test/Horde/Controller/FilterRunnerTest.php b/framework/Controller/test/Horde/Controller/FilterRunnerTest.php
new file mode 100644 (file)
index 0000000..81bee64
--- /dev/null
@@ -0,0 +1,73 @@
+<?php
+class Horde_Controller_FilterRunnerTest extends Horde_Test_Case
+{
+    public function testFilterRunnerDoesNotCallControllerWhenAPreFilterHandlesTheRequest()
+    {
+        $filter = $this->getMock('Horde_Controller_PreFilter', array('processRequest'));
+        $filter->expects($this->once())
+            ->method('processRequest')
+            ->will($this->returnValue(Horde_Controller_PreFilter::REQUEST_HANDLED));
+
+        $runner = new Horde_Controller_FilterRunner($this->_getControllerMockNeverCalled());
+        $runner->addPreFilter($filter);
+        $runner->processRequest($this->getMock('Horde_Controller_Request'), new Horde_Controller_Response());
+    }
+
+    public function testShouldUsePreFiltersInFirstInFirstOutOrder()
+    {
+        // The second filter should never be called because first filter returns
+        // REQUEST_HANDLED, meaning it can handle the request.
+        $preFilter1 = $this->getMock('Horde_Controller_PreFilter', array('processRequest'));
+        $preFilter1->expects($this->once())
+            ->method('processRequest')
+            ->will($this->returnValue(Horde_Controller_PreFilter::REQUEST_HANDLED));
+
+        $preFilter2 = $this->getMock('Horde_Controller_PreFilter', array('processRequest'));
+        $preFilter2->expects($this->never())
+            ->method('processRequest');
+
+        $runner = new Horde_Controller_FilterRunner($this->_getControllerMockNeverCalled());
+        $runner->addPreFilter($preFilter1);
+        $runner->addPreFilter($preFilter2);
+        $this->_runFilterRunner($runner);
+    }
+
+    public function testShouldUsePostFiltersInFirstInLastOutOrder()
+    {
+        // Both filters should be called because the first filter returns
+        // REQUEST_HANDLED, meaning it can handle the request
+        $postFilter1 = $this->getMock('Horde_Controller_PostFilter', array('processResponse'));
+        $postFilter1->expects($this->once())
+            ->method('processResponse')
+            ->will($this->returnValue(Horde_Controller_PreFilter::REQUEST_HANDLED));
+
+        $postFilter2 = $this->getMock('Horde_Controller_PostFilter', array('processResponse'));
+        $postFilter2->expects($this->once())
+            ->method('processResponse');
+
+
+        $controller = $this->getMock('Horde_Controller', array('processRequest'));
+        $controller->expects($this->once())
+            ->method('processRequest');
+
+        $runner = new Horde_Controller_FilterRunner($controller);
+        $runner->addPostFilter($postFilter1);
+        $runner->addPostFilter($postFilter2);
+        $this->_runFilterRunner($runner);
+    }
+
+    private function _getControllerMockNeverCalled()
+    {
+        $controller = $this->getMock('Horde_Controller', array('processRequest'));
+        $controller->expects($this->never())
+            ->method('processRequest');
+        return $controller;
+    }
+
+    private function _runFilterRunner(Horde_Controller_FilterRunner $runner)
+    {
+        $response = $this->getMock('Horde_Controller_Response', array('processRequest'));
+        $response->expects($this->never())->method('processRequest');
+        $runner->processRequest(new Horde_Controller_Request_Null(), $response);
+    }
+}
diff --git a/framework/Core/lib/Horde/Core/Binder/Mapper.php b/framework/Core/lib/Horde/Core/Binder/Mapper.php
new file mode 100644 (file)
index 0000000..1cae6fb
--- /dev/null
@@ -0,0 +1,16 @@
+<?php
+class Horde_Core_Binder_Mapper implements Horde_Injector_Binder
+{
+    public function create(Horde_Injector $injector)
+    {
+        $mapper = new Horde_Routes_Mapper();
+
+
+        return $mapper;
+    }
+
+    public function equals(Horde_Injector_Binder $binder)
+    {
+        return false;
+    }
+}
diff --git a/framework/Core/lib/Horde/Core/Controller/NotFound.php b/framework/Core/lib/Horde/Core/Controller/NotFound.php
new file mode 100644 (file)
index 0000000..d53f9c8
--- /dev/null
@@ -0,0 +1,11 @@
+<?php
+class Horde_Core_Controller_NotFound implements Horde_Controller
+{
+    /**
+     */
+    public function processRequest(Horde_Controller_Request $request, Horde_Controller_Response $response)
+    {
+        $response->setHeader('HTTP/1.0 404 ', 'Not Found');
+        $response->setBody('<h1>404 File Not Found</h1>');
+    }
+}
diff --git a/framework/Core/lib/Horde/Core/Controller/RequestConfiguration.php b/framework/Core/lib/Horde/Core/Controller/RequestConfiguration.php
new file mode 100644 (file)
index 0000000..96adfc0
--- /dev/null
@@ -0,0 +1,71 @@
+<?php
+/**
+ * Object to contain request information as it relates to which controller to
+ * create.
+ *
+ * @category Horde
+ * @package  Horde_Core
+ */
+class Horde_Core_Controller_RequestConfiguration implements Horde_Controller_RequestConfiguration
+{
+    /**
+     */
+    protected $_classNames = array();
+
+    /**
+     */
+    protected $_application;
+
+    /**
+     * Constructor
+     */
+    public function __construct()
+    {
+        $this->_classNames = array(
+            'controller' => 'Horde_Core_Controller_NotFound',
+            'settings'   => 'Horde_Controller_SettingsExporter_Default',
+        );
+    }
+
+    /**
+     */
+    public function setApplication($application)
+    {
+        $this->_application = $application;
+    }
+
+    /**
+     */
+    public function getApplication()
+    {
+        return $this->_application;
+    }
+
+    /**
+     */
+    public function setControllerName($controllerName)
+    {
+        $this->_classNames['controller'] = $controllerName;
+    }
+
+    /**
+     */
+    public function getControllerName()
+    {
+        return $this->_classNames['controller'];
+    }
+
+    /**
+     */
+    public function setSettingsExporterName($settingsName)
+    {
+        $this->_classNames['settings'] = $settingsName;
+    }
+
+    /**
+     */
+    public function getSettingsExporterName()
+    {
+        return $this->_classNames['settings'];
+    }
+}
diff --git a/framework/Core/lib/Horde/Core/Controller/RequestMapper.php b/framework/Core/lib/Horde/Core/Controller/RequestMapper.php
new file mode 100644 (file)
index 0000000..ce93c7a
--- /dev/null
@@ -0,0 +1,65 @@
+<?php
+class Horde_Core_Controller_RequestMapper
+{
+    /**
+     * @var Horde_Routes_Mapper $mapper
+     */
+    protected $_mapper;
+
+    public function __construct(Horde_Routes_Mapper $mapper)
+    {
+        $this->_mapper = $mapper;
+    }
+
+    public function getRequestConfiguration(Horde_Injector $injector)
+    {
+        $request = $injector->getInstance('Horde_Controller_Request');
+        $registry = $injector->getInstance('Horde_Registry');
+        $settingsFinder = $injector->getInstance('Horde_Core_Controller_SettingsFinder');
+
+        $config = $injector->createInstance('Horde_Core_Controller_RequestConfiguration');
+
+        $uri = substr($request->getPath(), strlen($registry->get('webroot', 'horde')));
+        $uri = trim($uri, '/');
+        if (strpos($uri, '/') === false) {
+            $app = $uri;
+        } else {
+            list($app,) = explode('/', $uri, 2);
+        }
+
+        // Check for route definitions.
+        $fileroot = $registry->get('fileroot', $app);
+        $routeFile = $fileroot . '/config/routes.php';
+        if (!file_exists($routeFile)) {
+            throw new Horde_Routes_Exception('Not routable: ' . $uri . '. ' . $routeFile . ' does not exist.');
+        }
+
+        // Push $app onto the registry
+        $registry->pushApp($app);
+        $config->setApplication($app);
+
+        // Application routes are relative only to the application. Let the
+        // mapper know where they start.
+        $this->_mapper->prefix = $registry->get('webroot', $app);
+
+        // Set the application controller directory
+        $this->_mapper->directory = $registry->get('fileroot', $app) . '/app/controllers';
+
+        // Load application routes.
+        $mapper = $this->_mapper;
+        include $routeFile;
+
+        // Match
+        // @TODO Cache routes
+        $match = $this->_mapper->match($request->getPath());
+
+        if (isset($match['controller'])) {
+            $config->setControllerName(ucfirst($app) . '_' . ucfirst($match['controller']) . '_Controller');
+            $config->setSettingsExporterName($settingsFinder->getSettingsExporterName($config->getControllerName()));
+        } else {
+            $config->setControllerName('Horde_Core_Controller_NotFound');
+        }
+
+        return $config;
+    }
+}
diff --git a/framework/Core/lib/Horde/Core/Controller/SettingsFinder.php b/framework/Core/lib/Horde/Core/Controller/SettingsFinder.php
new file mode 100644 (file)
index 0000000..3b46e9a
--- /dev/null
@@ -0,0 +1,31 @@
+<?php
+class Horde_Core_Controller_SettingsFinder
+{
+    public function getSettingsExporterName($controllerName)
+    {
+        $settingsName = $this->_mapName($controllerName);
+        $current = $controllerName;
+        while (class_exists($current)) {
+            $settingsName = $this->_mapName($current);
+            if (class_exists($settingsName)) {
+                return $settingsName;
+            }
+
+            $current = $this->_getParentName($current);
+        }
+
+        return 'Horde_Controller_SettingsExporter_Default';
+    }
+
+    private function _mapName($controllerName)
+    {
+        return str_replace('_Controller', '_SettingsExporter', $controllerName);
+    }
+
+    private function _getParentName($controllerName)
+    {
+        $klass = new ReflectionClass($controllerName);
+        $parent = $klass->getParentClass();
+        return $parent->name;
+    }
+}
diff --git a/framework/Core/lib/Horde/Core/Factory/Request.php b/framework/Core/lib/Horde/Core/Factory/Request.php
new file mode 100644 (file)
index 0000000..9856c14
--- /dev/null
@@ -0,0 +1,10 @@
+<?php
+/**
+ */
+class Horde_Core_Factory_Request
+{
+    public function create(Horde_Injector $injector)
+    {
+        return new Horde_Controller_Request_Http($_SERVER['REQUEST_URI']);
+    }
+}
index c753bd7..b1fd524 100644 (file)
@@ -293,25 +293,39 @@ class Horde_Registry
             'Horde_Tree' => new Horde_Core_Binder_Tree(),
             'Horde_Token' => new Horde_Core_Binder_Token(),
             'Horde_Vfs' => new Horde_Core_Binder_Vfs(),
-            'Net_DNS_Resolver' => new Horde_Core_Binder_Dns()
+            'Net_DNS_Resolver' => new Horde_Core_Binder_Dns(),
         );
 
         /* Define factories. */
         $factories = array(
+            'Horde_Controller_Request' => array(
+                'Horde_Core_Factory_Request',
+                'create',
+            ),
+            'Horde_Controller_RequestConfiguration' => array(
+                'Horde_Core_Controller_RequestMapper',
+                'getRequestConfiguration',
+            ),
             'Horde_Kolab_Server_Composite' => array(
                 'Horde_Core_Factory_KolabServer',
-                'getComposite'
+                'getComposite',
             ),
             'Horde_Kolab_Session' => array(
                 'Horde_Core_Factory_KolabSession',
-                'getSession'
+                'getSession',
             ),
             'Horde_Kolab_Storage' => array(
                 'Horde_Core_Factory_KolabStorage',
-                'getStorage'
+                'getStorage',
             )
         );
 
+        /* Define implementations. */
+        $implementations = array(
+            'Horde_Controller_ResponseWriter' => 'Horde_Controller_ResponseWriter_Web',
+            'Horde_View_Base' => 'Horde_View',
+        );
+
         /* Setup injector. */
         $GLOBALS['injector'] = $injector = new Horde_Injector(new Horde_Injector_TopLevel());
 
@@ -321,6 +335,9 @@ class Horde_Registry
         foreach ($factories as $key => $val) {
             $injector->bindFactory($key, $val[0], $val[1]);
         }
+        foreach ($implementations as $key => $val) {
+            $injector->bindImplementation($key, $val);
+        }
 
         $GLOBALS['registry'] = $this;
         $injector->setInstance('Horde_Registry', $this);
@@ -1117,7 +1134,12 @@ class Horde_Registry
          * be done here because it is possible to try to load app-specific
          * libraries from other applications. */
         $app_lib = $this->get('fileroot', $app) . '/lib';
-        $GLOBALS['injector']->getInstance('Horde_Autoloader')->addClassPathMapper(new Horde_Autoloader_ClassPathMapper_Prefix('/^' . $app . '(?:$|_)/i', $app_lib));
+        $autoloader = $GLOBALS['injector']->getInstance('Horde_Autoloader');
+        $autoloader->addClassPathMapper(new Horde_Autoloader_ClassPathMapper_Prefix('/^' . $app . '(?:$|_)/i', $app_lib));
+        $applicationMapper = new Horde_Autoloader_ClassPathMapper_Application($this->get('fileroot', $app) . '/app');
+        $applicationMapper->addMapping('Controller', 'controllers');
+        $applicationMapper->addMapping('SettingsExporter', 'settings');
+        $autoloader->addClassPathMapper($applicationMapper);
 
         $checkPerms = !isset($options['check_perms']) || !empty($options['check_perms']);
 
index 4d72958..5f8d0de 100644 (file)
@@ -125,6 +125,12 @@ Application Framework.</description>
        <file name="Twitter.php" role="php" />
        <file name="Vfs.php" role="php" />
       </dir> <!-- /lib/Horde/Core/Binder -->
+      <dir name="Controller">
+       <file name="NotFound.php" role="php" />
+       <file name="RequestConfiguration.php" role="php" />
+       <file name="RequestMapper.php" role="php" />
+       <file name="SettingsFinder.php" role="php" />
+      </dir> <!-- /lib/Horde/Core/Controller -->
       <dir name="Factory">
        <file name="Ajax.php" role="php" />
        <file name="Auth.php" role="php" />
@@ -141,6 +147,7 @@ Application Framework.</description>
        <file name="KolabStorage.php" role="php" />
        <file name="Ldap.php" role="php" />
        <file name="LoginTasks.php" role="php" />
+       <file name="Request.php" role="php" />
        <file name="Share.php" role="php" />
        <file name="Tree.php" role="php" />
        <file name="Vfs.php" role="php" />
@@ -342,6 +349,7 @@ Application Framework.</description>
    <install as="Horde/Core/Auth/Signup/Null.php" name="lib/Horde/Core/Auth/Signup/Null.php" />
    <install as="Horde/Core/Auth/Signup/Sql.php" name="lib/Horde/Core/Auth/Signup/Sql.php" />
    <install as="Horde/Core/Auth/Signup/SqlObject.php" name="lib/Horde/Core/Auth/Signup/SqlObject.php" />
+   <install as="Horde/Core/Autoloader.php" name="lib/Horde/Core/Autoloader.php" />
    <install as="Horde/Core/Autoloader/Callback/Mime.php" name="lib/Horde/Core/Autoloader/Callback/Mime.php" />
    <install as="Horde/Core/Autoloader/Callback/Nls.php" name="lib/Horde/Core/Autoloader/Callback/Nls.php" />
    <install as="Horde/Core/Binder/Ajax.php" name="lib/Horde/Core/Binder/Ajax.php" />
@@ -377,6 +385,10 @@ Application Framework.</description>
    <install as="Horde/Core/Binder/Token.php" name="lib/Horde/Core/Binder/Token.php" />
    <install as="Horde/Core/Binder/Twitter.php" name="lib/Horde/Core/Binder/Twitter.php" />
    <install as="Horde/Core/Binder/Vfs.php" name="lib/Horde/Core/Binder/Vfs.php" />
+   <install as="Horde/Core/Controller/NotFound.php" name="lib/Horde/Core/Controller/NotFound.php" />
+   <install as="Horde/Core/Controller/RequestConfiguration.php" name="lib/Horde/Core/Controller/RequestConfiguration.php" />
+   <install as="Horde/Core/Controller/RequestMapper.php" name="lib/Horde/Core/Controller/RequestMapper.php" />
+   <install as="Horde/Core/Controller/SettingsFinder.php" name="lib/Horde/Core/Controller/SettingsFinder.php" />
    <install as="Horde/Core/Factory/Ajax.php" name="lib/Horde/Core/Factory/Ajax.php" />
    <install as="Horde/Core/Factory/Auth.php" name="lib/Horde/Core/Factory/Auth.php" />
    <install as="Horde/Core/Factory/Crypt.php" name="lib/Horde/Core/Factory/Crypt.php" />
@@ -392,6 +404,7 @@ Application Framework.</description>
    <install as="Horde/Core/Factory/KolabStorage.php" name="lib/Horde/Core/Factory/KolabStorage.php" />
    <install as="Horde/Core/Factory/Ldap.php" name="lib/Horde/Core/Factory/Ldap.php" />
    <install as="Horde/Core/Factory/LoginTasks.php" name="lib/Horde/Core/Factory/LoginTasks.php" />
+   <install as="Horde/Core/Factory/Request.php" name="lib/Horde/Core/Factory/Request.php" />
    <install as="Horde/Core/Factory/Share.php" name="lib/Horde/Core/Factory/Share.php" />
    <install as="Horde/Core/Factory/Tree.php" name="lib/Horde/Core/Factory/Tree.php" />
    <install as="Horde/Core/Factory/Vfs.php" name="lib/Horde/Core/Factory/Vfs.php" />
@@ -414,13 +427,6 @@ Application Framework.</description>
    <install as="Horde/Themes/Element.php" name="lib/Horde/Themes/Element.php" />
    <install as="Horde/Themes/Image.php" name="lib/Horde/Themes/Image.php" />
    <install as="Horde/Themes/Sound.php" name="lib/Horde/Themes/Sound.php" />
-   <install as="Horde/Core/AllTests.php" name="test/Horde/Core/AllTests.php" />
-   <install as="Horde/Core/Autoload.php" name="test/Horde/Core/Autoload.php" />
-   <install as="Horde/Core/phpunit.xml" name="test/Horde/Core/phpunit.xml" />
-   <install as="Horde/Core/url.phpt" name="test/Horde/Core/url.phpt" />
-   <install as="Horde/Core/Binder/CacheTest.php" name="test/Horde/Core/Binder/CacheTest.php" />
-   <install as="Horde/Core/Factory/KolabServerTest.php" name="test/Horde/Core/Factory/KolabServerTest.php" />
-   <install as="Horde/Core/Factory/KolabSessionTest.php" name="test/Horde/Core/Factory/KolabSessionTest.php" />
   </filelist>
  </phprelease>
  <changelog>
index 4c90b5a..5ad2fe1 100644 (file)
  *   Controllers/
  *     FooController.php -> class App_FooController
  * no nested components?
+
+request processing steps:
+
+- bootstrap
+- injector bindings
+* customization point
+- create request
+- create request mapper
+- $config = $mapper->getRquestConfiguration($request)
+- create runner
+- execute runner
+  - get settings exporter
+  - export bindings
+  - get controller name
+  - create response
+  - create controller builder
+  - create filter runner
+  - export filters
+  - handle internal redirects
+  - return response
+- write response
+
+add filtered requests/blue_filter port to Horde?
  */
 
 require_once dirname(__FILE__) . '/lib/Application.php';
 Horde_Registry::appInit('horde');
 
-// Set up our request and routing objects
-$request = new Horde_Controller_Request_Http();
-$mapper = new Horde_Routes_Mapper();
-
-$uri = substr($request->getUri(), strlen($registry->get('webroot', 'horde')));
-if (strpos($uri, '/') === false) {
-    $app = $uri;
-    $path = '';
-} else {
-    list($app, $path) = explode('/', $uri, 2);
-}
-
-// Check for route definitions.
-$fileroot = $registry->get('fileroot', $app);
-$routeFile = $fileroot . '/config/routes.php';
-if (!file_exists($routeFile)) {
-    throw new Horde_Controller_Exception('Not routable: ' . $uri);
-}
-
-// @TODO Use the registry to check app permissions, etc.
-// $registry->pushApp($app);
-
-// Application routes are relative only to the application. Let the mapper know
-// where they start.
-$mapper->prefix = $registry->get('webroot', 'horde') . '/' . $app;
-
-// @TODO ? $mapper->createRegs(array('blogs', 'comments', 'posts')) would avoid
-// the directory scan entirely. The argument is the name of every controller in
-// the system. Should also cache the controller scan.
-
-// Load application routes.
-include $routeFile;
-
-// Set up application class and controller loading
-// @TODO separate $app from class names so that there can be multiple instances
-// of an app in the registry?
-$injector->getInstance('Horde_Autoloader')->addClassPathMapper(new Horde_Autoloader_ClassPathMapper_Prefix('/^' . $app . '(?:$|_)/i', $fileroot . '/lib/'));
+$request = $injector->getInstance('Horde_Controller_Request');
 
-// Create our controller context.
-$context = array(
-    'mapper' => $mapper,
-    'controllerDir' => $fileroot . '/app/controllers',
-    'viewsDir' => $fileroot . '/app/views',
-    // 'logger' => '',
-);
+$runner = $injector->getInstance('Horde_Controller_Runner');
+$config = $injector->getInstance('Horde_Controller_RequestConfiguration');
+$response = $runner->execute($injector, $request, $config);
 
-// Dispatch.
-$dispatcher = Horde_Controller_Dispatcher::singleton($context);
-$dispatcher->dispatch($request);
+$responseWriter = $injector->getInstance('Horde_Controller_ResponseWriter');
+$responseWriter->writeResponse($response);