\documentclass{article}
\usepackage{ulem}
\pagestyle{headings}
\begin{document}
\tableofcontents

\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.\newline
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.\newline
Some helper generates a Horde\_Controller\_RequestConfiguration instance (a config). The config defines which controller to execute and which SettingsExporter\footnote{http://example.com/index.php?page=SettingsExporter} to use. The SettingsExporter\footnote{http://example.com/index.php?page=SettingsExporter} affects which pre and post filters are run by the FilterRunner\footnote{http://example.com/index.php?page=FilterRunner}, among other things.\newline
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.\newline
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\newline
Get a Horde\_Controller\_Runner instance\newline
Get a Horde\_Core\_Controller\_RequestConfiguration from Horde\_Core\_Controller\_RequestMapper::getRequestConfiguration\newline
The first path component after the horde base app's webroot will be interpreted as the app name.\newline
The remaining parts of the request uri are interpreted relative to the identified app's web root.\newline
The requestMapper in turn will get a list of routes to match from app fileroot/config/routes.php\newline
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())\newline
The controller class found is added to the RequestConfiguration\footnote{http://example.com/index.php?page=RequestConfiguration}. \newline
The SettingsFinder\footnote{http://example.com/index.php?page=SettingsFinder} 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}\newline
\begin{verbatim}

<?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']);
        }
    }
}
?>
\end{verbatim}

A Horde Controller based app needs a \texttt{config/routes.php} file.

For example\newline
\begin{verbatim}

<?php
/**
 * Setup default routes
 */
$mapper->connect('/t/complete',
    array(
        'controller' => 'CompleteTask',
    ));
?>
\end{verbatim}

defines a route for a call like www.example.com/nag/t/complete to be handled by the CompleteTask controller seen above.

The endpoint script is horde/rampage.php  - At the moment, only authenticated calls are supported\newline
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\newline
\begin{verbatim}

<?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' => '\d{1,2}')
    ));

\end{verbatim}

NOTE: The method conditions are currently ignored by production Horde.\newline
You need a patched Horde\_Core for this to work

The Controller in \$app/app/controller/EnterTimes\footnote{http://example.com/index.php?page=EnterTimes}.php\newline
\begin{verbatim}

<?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();
    }
}

\end{verbatim}

\subsection{ Example: Generating URLs from Routes inside the controller}
\end{document}
