Copyright © 2012 Pierre Pronchery <khorben@defora.org>
Table of Contents
These notes are based on a development version of DaPortal 2.
First, make sure that you have downloaded the latest stable version of
DaPortal 2. It can be found there: http://www.defora.org/os/project/download/12/DaPortal.
This guide will assume the resulting archive to be called
DaPortal.tar.gz.
Alternatively, you may choose to track the development of DaPortal 2, or any given branch from the Source Code Management system. Some instructions to do so can be found here: http://www.defora.org/os/project/download/12/DaPortal.
In this document, the reader is expected to be familiar with the installation of DaPortal. This process is documented in Installing DaPortal, which contains important information about the organization of files and directories relevant to DaPortal.
DaPortal has a single entry-point for all of its applications, found
in src/daportal.php. Its contents are simple enough to
be reproduced here fully:
<?php
require_once('./system/config.php');
global $config;
$config = new Config();
$config->load('../daportal.conf');
require_once('./system/engine.php');
if(($engine = Engine::attachDefault()) !== FALSE
&& ($request = $engine->getRequest()) !== FALSE)
{
$page = $engine->process($request);
$engine->render($page);
}
unset($engine);
?>First, the code reads configuration data from the ../daportal.conf file.
This file is organized as an ini-style configuration file, with series of
variables (one per line) found in separate sections. This file is currently
used for most of the configureable aspects of DaPortal.
Next, the engine.php file is loaded from the
system sub-directory. This directory contains generic,
library code that can freely be used by the different modules. After
including this file:
The relevant implementation of the
Engine class is loaded,
The user's request is determined and obtained,
The output (or “page”) is rendered and delivered to the user.
Finally, the engine loaded is explicitely free'd; this was found to be
necessary in some cases (like with the GtkEngine) as
otherwise, some functionalities of PHP no longer work as expected while
executing the engine's destructor routines.
In the rest of this documentation, source files will be mentioned
relatively to the src directory.
The actual processing engine for DaPortal is determined at run-time, or
possibly enforced in the daportal.conf configuration
file (through the backend variable in the
[engine] section).
When auto-detecting the engine, every PHP file found in the
engines directory is loaded in sequence. The file is
expected to provide a predictable class name, such as
FileNameEngine for
filename.php. It should implement the abstract
Engine class.
Two methods are used when detecting the adequate engine:
match(): returns an integer value,
representing a score at the relevance of the engine in the context
detected (typically between 0 and 100)
attach(): returns a boolean value,
TRUE in case of success or
FALSE otherwise.
This auto-detection methodology is found in multiple sub-systems of DaPortal (file formats, templates...).
Requests represent messages, typically sent from the end user and
directed at a given part of the DaPortal installation. Requests can be
created directly within the code, or obtained from user input (typically
interpreted by the Engine instance).
The following parameters define a request:
module: the module to be selected to
handle this request (required)
action: the action to be triggered
within the given module (optional)
ID and title: both
can be supplied independently, and their actual signification is up to
the module (and action) selected;
optionally, an arbitrary number of additional parameters, defined through a name and value.
Requests implement the Request class defined in
system/request.php. They are always processed through a
specific Engine instance.
The storage database is accessed through the processing engine, with
the actual storage backend being loaded automatically when it is required.
This typically happens when invoking the getDatabase()
method of the Engine instance.
Much like the main engine, the backend is selected as configured in the
[database] section of the
daportal.conf configuration file (or can be
automatically detected in some cases). The different classes implementing
the Database abstract class (described in
system/database.php) are loaded from individual files,
expected in the database directory.
A limited implementation of the Database class,
called DatabaseDummy, is returned by the engine when
it was unable to attach a functional Database
instance. It will simply fail to obtain any data.
User authentication within DaPortal is also delegated to a distinct
backend, as its proper operation typically depends on the actual
Engine instance selected. These backends are expected
in the auth sub-directory, and should implement the
Auth class from system/auth.php.
Auto-detection of the authentication backend to be loaded can be
circumvented through the [auth] section of the
daportal.conf configuration file.
The credentials with DaPortal include the following information, much like found on a typical UNIX system:
a user ID: either the default value, 0 (meaning anonymous, least privileges) or a positive integer (actual users, with equal privileges by default)
a username, which must be unique through the system;
a group ID: defaulting to 0 (meaning nogroup), they can be used by modules to assign differing privileges, or otherwise distinguishing groups of users;
a group name: the name of the default group of the user;
an additional list of groups that the user may be part of;
an administrative flag: it grants complete privileges to the current user, with the actual signification depending on the current module.
The concept of idempotence is also explicitly implemented (and hopefully enforced) throughout the code. Its actual meaning in the context of DaPortal is to prevent actions to be performed without the explicit consent of the user; this is the default for newly-created requests. It is up to each individual module to determine whether the current operation is “safe” in this regard or not.
The main rationale behind this concept is to prevent CSRF attacks with session-based, disconnected authentication mechanisms (like when using cookies over HTTP). It is also worth mentioning that some requests may be set to expire over time for increased security.
Modules are the most essential part of the DaPortal engine: they
implement the visible functionality to the end user, and provide the
operation logic. Each module has a distinct folder inside the
modules directory, where it should at least provide a
module.php file; this file is automatically loaded by
the Engine instance when required. It should
implement the Module class, as defined in
system/module.php.
A few modules are provided by a stock installation of DaPortal, among which:
“user”: assists with user registration, authentication, password resets and profile management;
“admin”: provides a user interface dedicated to administration duties;
“article”, “news”,
“wiki”...: extend the abstract
ContentModule in
modules/content/module.php, declining some
content-management functionality in differing contexts;
“search”: provides a generic way to search through the site's contents.
In turn, these modules can be extended and altered individually.
The modules react to requests through calls, more precisely the
call() public method of the
Module class. The relevant
Engine instance along with the request are passed to
the module, which is then responsible for handling the request, given the
resources provided by the Engine.
These calls are expected to offer a significant value in return of an operation; this is however not mandatory. Content is typically formatted and delivered back to the user by building pages, as described below in Section 6.1, “Building pages”.
Common functionality can be provided on demand to the modules,
typically through the inclusion of specific files in the
system directory. Some helpers are provided by a stock
installation of DaPortal, among which:
“content”: provides some functionality around the handling of content (creation, modification, deletion...)
“locale”: assists in the translation of the user interface (typically using the gettext PHP extension)
“mail”: helps sending e-mail;
“user”: to lookup and manage user information.
Modules themselves may be organized so as to allow easier integration and alteration of functionality, particularly when inheriting them. This is typically performed through the introduction of helper functions, specific to the given module (which are therefore declared as protected). Their implementation details are specific to each and every module, although consistency is hopefully preserved among most.
Cooperation between modules is sometimes helpful, or even necessary: this should however be performed without exposing additional, undesired functionality to the end user. A special flag can therefore be applied to requests when sending them through an engine for processing: they are then considered internal, and can be adequately treated as such by the target module.
The content generated through processing the requests is expected to be
composed of a series of chained elements, called
PageElement, each of which can also embed properties
or other instances of PageElement. For clarity, an
additional class, simply called Page, is also
available and meant to represent a top-level element.
Most importantly, each element has a given type, representing an expected layout or behavior when rendered. These types are largely inspired by graphical toolkits, such as Gtk+, and typically represent label widgets, file choosers, toolbars, menus, and so on.
The PageElement and Page
classes are both defined in the system/page.php
file.
Single requests are expected to only output the data that is directly
relevant to them; the final content delivered to the user may however
require more information, additional cosmetics, or even some reformatting or
editing. This is exactly the task of templates; again, they are found in the
templates directory, and should implement the
Template class, as defined in
system/template.php.
The template to choose can be determined by the engine at run-time, or
can also be enforced within the daportal.conf
configuration file, through the template section.
Rendering of the current page is the last step performed by the engine
(if at all necessary) before delivering its contents to the end user.
Indeed, some module calls may return data of any nature, like resources
(such as file or stream descriptors). The Engine
instance is then responsible for their proper handling.
However, in the case of pages (or page elements) the contents may be
formatted, transformed or otherwise converted in differing formats, as
available through the Engine instance. This instance
may use DaPortal's own set of classes and routines to perform this, as
provided by the Format and
FormatElements classes in
system/format.php, and then implemented in the
formats directory.
As usual within DaPortal, the choice of formatting backends and their
respective preferences can be influenced through the
daportal.conf configuration file, through the
[format] set of sections. The difference here, is that
backends can be specified per MIME type, as set through the
Engine instance. This is illustrated here:
[format] #this defines the default backend if nothing else matched #backend=html #backend=html5 backend=plain [format::text/html] #this defines the default backend for content explicitly set to be HTML #backend=html backend=html5 [format::html] #preferences for the html backend favicon=/favicon.png
Again, the final decision on which rendering backend to choose can be
enforced by the Engine instance.