\documentclass{article}
\usepackage{ulem}
\usepackage{graphicx}
\usepackage{hyperref}
\pagestyle{headings}
\begin{document}
\part{Horde\_Controller}
Documentation on using Horde\_Controller Framework

\section{General Info}
Horde uses a lot of behind-the-scenes magic to glue together controller based request processing. In normal cases, the application developer only needs to create a routes configuration file entry and a controller class, horde will handle the rest.<br />
To add custom pre and post filters to a controller, just add the corresponding \$app\_\$controller\_SettingsExporter class to the app/settings dir.

On this page, developers of specialised endpoints (RPC, REST, etc) can read up on how the pieces work together.

\section{General flow of processing}
A request object (Interface Horde\_Request) is generated from server variables.<br />
Some helper generates a Horde\_Controller\_RequestConfiguration instance (a config). The config defines which controller to execute and which <a href="https://wiki.horde.org/SettingsExporter">SettingsExporter</a> to use. The <a href="https://wiki.horde.org/SettingsExporter">SettingsExporter</a> affects which pre and post filters are run by the <a href="https://wiki.horde.org/FilterRunner">FilterRunner</a>, among other things.<br />
The request and the config are fed into a runner. The runner will return a response object. The runner may first execute prefilters on the request. Depending on the prefilter outcome, the originally defined controller may not be executed at all.<br />
After the controller is run, post filters may change the response (for example, tidying output, applying compression)

\section{Horde App specific flow of processing}
Web Server configuration redirects any unmatched URLs to horde/rampage.php

rampage.php sets up a horde environment with auth and the horde base app

Get a Horde\_Controller\_Request\_Http request object from running Horde\_Core\_Factory\_Request<br />
Get a Horde\_Controller\_Runner instance<br />
Get a Horde\_Core\_Controller\_RequestConfiguration from Horde\_Core\_Controller\_RequestMapper::getRequestConfiguration<br />
The first path component after the horde base app's webroot will be interpreted as the app name.<br />
The remaining parts of the request uri are interpreted relative to the identified app's web root.<br />
The requestMapper in turn will get a list of routes to match from app fileroot/config/routes.php<br />
If no defined route matches, a default Horde\_Core\_Controller\_NotFound is returned

Otherwise, a controller class name is found and the identified app's context is loaded (\$registry->pushApp())<br />
The controller class found is added to the <a href="https://wiki.horde.org/RequestConfiguration">RequestConfiguration</a>.<br />
The <a href="https://wiki.horde.org/SettingsFinder">SettingsFinder</a> looks for an \$app\_\$controller\_SettingsExporter class, otherwise it adds the Horde\_Controller\_SettingsExporter\_Default to the config - which exports no filters or bindings to the runner

Let the runner create a \$response = \$runner->execute(\$injector, \$request, \$config);

Finally, get a responseWriter (\_Web is the default) and render the response object to the output

\section{How to use Horde\_Controllers in Horde Core Apps}
Horde Controllers are independent from the Horde Ajax Framework. Controllers need Horde\_Routes and need rewrite rules.

Ajax application controllers do not live in \texttt{lib/} but in \texttt{\$app/app/controllers/}

Example:

Class \texttt{Nag\_CompleteTask\_Controller} in \texttt{nag/app/controllers/CompleteTask.php}

<pre><code class="language-php">
<?php
class Nag\_CompleteTask\_Controller extends Horde\_Controller\_Base
\{
    public function processRequest(Horde\_Controller\_Request \$request, Horde\_Controller\_Response \$response)
    \{
        /* Toggle the task's completion status if we're provided with a
         * valid task ID. */
        \$requestVars = \$request->getRequestVars();
        if (isset(\$requestVars['task']) \&\& isset(\$requestVars['tasklist'])) \{
            \$nag\_task = new Nag\_CompleteTask();
            \$result = \$nag\_task->result(\$requestVars['task'], \$requestVars['tasklist']);
        \} else \{
            \$result = array('error' => 'missing parameters');
        \}

        \$requestVars = \$request->getGetVars();
        if (!empty(\$requestVars['format']) \&\&
            \$requestVars['format'] == 'json') \{
            \$response->setContentType('application/json');
            \$response->setBody(json\_encode(\$result));
        \} elseif (\$requestVars['url']) \{
            \$response->setRedirectUrl(\$requestVars['url']);
        \}
    \}
\}
?>
</code></pre>
A Horde Controller based app needs a \texttt{config/routes.php} file.

For example

<pre><code class="language-php">
<?php
/**
 * Setup default routes
 */
\$mapper->connect('/t/complete',
    array(
        'controller' => 'CompleteTask',
    ));
?>
</code></pre>
defines a route for a call like <a href="http://www.example.com/nag/t/complete">www.example.com/nag/t/complete</a> to be handled by the CompleteTask controller seen above.

The endpoint script is horde/rampage.php  - At the moment, only authenticated calls are supported<br />
The controller is passed the request (in this case, a json request) and handles it (with a json answer in this case)

\section{Usage for UI}
\subsection{Example: Output buffering for Horde\_PageOutput}
Render a complete page with Horde\_PageOutput by capturing any html in an output buffer and handing it to the response object.

The Route in config/routes.php

<pre><code>
<?php
/**
 * Setup default routes
 */
\$user = \$registry->getAuth();
\$dt = new Horde\_Date(mktime());

\$mapper->connect('EnterTimes', '/enter/:year/:month/:user/',
    array(
        'controller' => 'EnterTimes',
        'action' => 'view',
        'user' => \$user,
        'year' => \$dt->year,
        'month' => sprintf('\%02d', \$dt->month),
        'conditions' => array('method' => array('GET')),
        'requirements' => array('month' => '\textbackslash\{\}d\{1,2\}')
    ));
</code></pre>
NOTE: The method conditions are currently ignored by production Horde.<br />
You need a patched Horde\_Core for this to work

The Controller in \$app/app/controller/<a href="https://wiki.horde.org/EnterTimes">EnterTimes</a>.php

<pre><code>
<?php
class Timetool\_EnterTimes\_Controller extends Horde\_Controller\_Base
\{
    /**
     * Get variables from routes matching
     */
    protected \$\_matchDict;

    public function processRequest(Horde\_Controller\_Request \$request, Horde\_Controller\_Response \$response)
    \{
        /* obtain a Routes mapper to fit the Horde\_Routes defined in config/routes.php */
        \$this->\_mapper = \$this->getInjector()->getInstance('Horde\_Routes\_Mapper');
        /* get the dictionary */
        \$this->\_matchDict = new Horde\_Support\_Array(\$this->\_mapper->match(\$request->getPath()));

        switch (\$this->\_matchDict->action) \{
        case "view":
            return \$this->view(\$request, \$response);
            break;
        \}
    \}

    public function view(\$request, \$response)
    \{
        \$vars = Horde\_Variables::getDefaultVariables();
        \$injector = \$this->getInjector();
        // Start a buffer to prevent PageOutput from
        // engaging the output buffer, circumventing the response
        // object and any potential post filters
        ob\_start();
        \$page\_output = \$injector->getInstance("Horde\_PageOutput");
        \$page\_output->header(array("title" => \_("Enter Timesheet")));
        // Bug: The sidebar is not correctly rendered if the main screen does not contain anything
        echo "something";
        \$page\_output->footer();
        // Feed any output to the response body and close the buffer
        \$response->setBody(ob\_get\_contents());
        ob\_end\_clean();
    \}
\}
</code></pre>
\subsection{Example: Generating URLs from Routes inside the controller}
\end{document}
