6.0.0-git
2024-04-26
Last Modified 2013-07-23 by Ralf Lang

Documentation of Horde Ajax Applications mostly from poking around existing examples.

Convention

$app (lowercase, for example "passwd")
$App (First Uppercase, for example "Passwd")

Javascript libraries used

JavaScript for "dynamic" and "traditional" mode uses PrototypeJS
JavaScript for "smartmobile" mode uses jQuery Mobile

Using jquery alongside prototype

This is generally not accepted upstream.
jQuery can be made coexist with dynamic/traditional mode's PrototypeJS

For non-upstream custom code:

var $j = jQuery.noConflict();
$j(document).ready(
/* Do jquery stuff here. $.* is prototype, $j.* is jQuery. Some plugins may not like this, but DataTables, JQuery UI and mainstream stuff work */
);

Files and Classes of a Horde Ajax Application

Horde_Ajax_Application class

Bare Minimum for a Horde Ajax Application (on top of a skeleton Horde_Registry_Application):
A File $app/lib/Ajax/Application.php with a class

<?php
// phpdoc omitted
class $App_Ajax_Application extends Horde_Core_Ajax_Application {}
?>

Horde_Core_Ajax_Application documentation

Application-specific javascript code

Most apps which sport completely distinct dynamic and traditional views (kronolith, hermes) also have $app/js/$app.js

var $AppCore = {
/* What is strictly required here? */
}
document.observe('dom:loaded', $AppCore.onDomLoad.bind(AppCore));
// more observers as needed

View Selection

Common, but not strictly required:

Logic in $app/index.php to decide if traditional, ajax or other modes should be loaded.

TODO: Example

Horde Ajax Request Service

The Horde Base App provides a common receiver for ajax requests by Ajax_Applications.
It only works for AUTHENTICATED user requests.

Fails silently for malformed requests and unauthenticated users
It loads the app, passes the call's variables to $App_Ajax_Application and runs the ->doAction() method on this class.
Finally it returns a Response.

The default response type is JSON

TODO: Does this only work for pretty URL rewriting mode?

TODO: pretty example

horde/services/ajax.php

**
 * Processes an AJAX request and returns a JSON encoded result.
 *
 * Path Info:
 * ----------
 * http://example.com/horde/services/ajax.php/APP/ACTION
 *
 * 'APP' - (string) The application name.
 * 'ACTION' - (string) The AJAX action identifier.
 *
 * Reserved 'ACTION' strings:
 * 'logOut' - Logs user out of Horde.
 *
 * Copyright 2010-2012 Horde LLC (http://www.horde.org/)
 *
 * See the enclosed file COPYING for license information (GPL). If you
 * did not receive this file, see http://www.horde.org/licenses/gpl.
 *
 * @author  Michael Slusarz <slusarz@horde.org>
 * @package Horde
 */

$App_Ajax_Application_Handler

A Handler inherits from Horde_Core_Ajax_Application_Handler and can manage external/public (unauthenticated??) calls
extends Horde_Core_Ajax_Application_Handler

Seems like the difference between Horde 5 and Horde 4 is that each action has its own Handler class rather than a methods in the $App_Ajax_Application class?

Horde_Controllers

Horde Controllers seem to be somewhat independent from the stuff described above
Controllers need Horde_Routes and need rewrite rules

Ajax application controllers do not live in lib but in
$app/app/controllers/

Example:

class Nag_CompleteTask_Controller in nag/app/controllers/CompleteTask.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']);
        }
    }
}
?>

A Horde Controller based app needs a config/routes.php file.

For example

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

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

The endpoint script is horde/rampage.php - rampage.php currently seems to handle only authenticated calls.

The controller is passed the request (in this case, a json request) and handles it (with a json answer in this case)

Client side js infrastructure

important hordecore.js HordeCore class methods:

  • doAction(action, params, opts) - client side method for ajax requests against Horde_Ajax_Application
  • submitForm(form, opts) - ajax request to submit a form

TODO

The big picture

  • When do I use a service/ajax.php handler as opposed to a rampage.php Controller ?
  • Imples seem to be used to return snippets? "Don't use imples; they are archaic and can generally be rewritten using the Ajax framework instead"