Introduction

replace me

[ top ]

Putting it together - an overview of a XIMS installation and how the modules work together

The internal XIMS module dependencies

Check out xims.*.dia in the doc directory to get an overview.

The logic of a XIMS request

Typical URLs of XIMS-objects have the form http://host/goxims/interface/parameters. Because xims is using an event-based CGI-Application-Framework (CGI::XMLApplication), those URLs can have event-names attached in the querystring or POST-content, like http://host/goxims/content/path/to/object?edit=edit for example. The "interface"-part of the URL describes what should be managed, for now that can be "content" only. Other possible values include "users", or "tasks", etc.. In the case the interface is "content", the parameters can also be a virtual path to a content-object included in the path_info-part of the URL.

The following diagram of the simplified dataflow of a XIMS-request shows the actors involved.

For example, we shall look at what happens, when we request a folder called 'subtest' contained in a folder called 'test' which itself happens to be at the top of the virtual hierarchy. The request URL for this object would be http://host//goxims/content/test/subtest.

After authentification, goxims uses the first part of the path_info-information to resolve the interface. After stripping the interface-part, in this case it is "content", goxims users the rest of the path_info to locate the requested content-object and with it, its object-type. In our example, the object-type of the content-object with the location 'subtest' is 'Folder'. After knowing that, goxims finishes with loading bin/folder.pm. This application-class loads the corresponding content-object-class, XIMS::Folder, and they together are responsible for the object-specific application logic.

Based on the requested event, in this case it is the event 'default' because of the missing event-parameters, the content-object-class (or its SUPER-classes) load the object-info and pass them back to the application-class, which uses its SUPER-class XIMS::CGI to put the content in a XML-DOM via XIMS::SAX. In our example the content is a list of children of the 'test'-folder.

Based on the requested event, the application-class selects a XSL-stylesheet to be rendered with the XML-DOM to the client. In our example case the stylesheet is called 'folder_default.xsl'. The actual XSL-Transformation is done via CGI::XMLApplication.

[ top ]

Writing a XIMS module

XIMS modules are handlers for specific sub-types of the basic xims object. The handlers have to exist on several layers: One in the XIMS-Data-Repository - SQL layer, one handler for defining the object class, one handling the application-class, and another one handling the object-type-specific user-interface, in form of XSL-Stylesheets, for example.

Writing a xims module takes therefore the following four steps. We will use an "Anonymous Discussion Forum" as a sample XIMS module to have explicit examples.

Defining the Object-Type

Defining the object-type means letting xims know that a new module shall exist. Our discussion-forum needs two object-types to work, the forum itself and contributions posted to it. The implicit naming convention for object-type names uses capitalized nouns including capitalization in noun groups. The two DDL-statements for our discussion-forum object-types look like the following then (assuming we use the Oracle-Data-Provider):

              INSERT INTO CI_OBJECT_TYPES ( id, name ) VALUES ( OBT_SEQ.NEXTVAL, 'AnonDiscussionForum' );
              INSERT INTO CI_OBJECT_TYPES ( id, name ) VALUES ( OBT_SEQ.NEXTVAL, 'AnonDiscussionForumContrib' );
              

Implementing the Object-Class

The object classes define the different respectively additional behaviour of the module classes compared to the standard XIMS object. XIMS::AnonDiscussionForum shall be a subclass of XIMS::Folder. Because all forum class specifics can be handled using the default fields, it is enough to merely copy Folder.pm to AnonDiscussionForum.pm and adjust the naming of the package and the variables and values in the constructor.

For the implementation of XIMS::AnonDiscussionForumContrib slightly more adjustments are needed. Being a subclass of XIMS::Document we can start out with a copy of Document.pm and make the according naming adjustments, similar to the one we did with XIMS::AnonDiscussionForum. Whereas XIMS::Folder and XIMS::AnonDiscussionForum are more or less clones, XIMS::AnonDiscussionForumContrib needs to store some extra information that XIMS::Document does not. Our anonymous discussion-forum contribution shall store the name of the (anonymous) author, his or her email-address, and the IP-Address the posting originated from; three fields that cannot be handled using the default fields. Because possible values for those fields will be short strings, storing them in form of key-value pairs in the attributes fields will do the trick. Using attributes() and attribute_by_key() methods the task gets even more easier, as the following example accessor-method for the author field, shows.

sub author {
    my $self= shift;
    my $author = shift;
    my $retval;

    if ( length $author ) {
        $self->attribute( author => $author );
        $retval = 1;
    }
    else {
        return $self->attribute_by_key('author');
    }

    return $retval;
}
     

Implementing the Application Class

Currently, the application-classes are implemented using CGI::XMLApplication. Similar to the creation of the object-classes we can start out with a copy of a similar class and adjust the naming. anondiscussionforum.pm is a subclass of folder.pm; only event_default and the publishing events need to be overwritten, since our discussionforums shall not be exported. anondiscussionforumcontrib.pm and document.pm look more different from each other. For example, anondiscussionforumcontrib::event_store() has to handle "author", "email", and "senderip", whereas document::event_store() does not. On the other hand, there is no handling of keywords or abstract needed in the anonymous discussionforum contribution application class.

Creating the XSL-Stylesheets

The last step to get a XIMS module working are XSL-Stylesheets for the events "create", "default", and "edit". For example, for our anonymous discussion forum we need the stylesheets: anondiscussionforum_create.xsl, anondiscussionforum_default.xsl, and anondiscussionforum_edit.xsl. To create those stylesheets we have to follow the known, copy-take-as-template-and-adapt-philosophy for now. In the case of anondiscussionforum_create.xsl this means replacing "Document" with "Contribution" and adapting the needed HTML-form fields.

Communication between application and serialization classes

Most application classes inherit XIMS::CGI, which itself inherits CGI::XMLApplication. For communication from the application classes to the serialization classes a XIMS::ApplicationContext object is used.

XIMS::ApplicationContext

'properties', 'apache', 'user', # used for user-management 'userlist', # used for privilege managment 'object', # used for content-object-management 'objectlist', # used for content-object-listings like search results or site maps 'parent', # needed during object creation 'session', 'cookie', 'sax_filter', # a reference to a list of SAX filter classes 'sax_generator', # a reference to a SAX generator class

Application Context Properties

Application properties are used to change the behaviour of the runtime system. All flags and information that are not used in the content or by the SAX pipeline should go here.

Filter properties are used to control the behaviour of some filter in the SAX pipeline. Such flags are only useful for filters that are application specific, since XIMS filter do not define any overall flags in this section.

Content properties are use mostly. These filter allow to control what data will appear in the content delivered to the client. The content properties are only used for the basic XML data, but not for the stylesheet.

The following lists describes all items that are recognized by the core system.

(1) Application Properties

  • style

    This tells which style for the current object shall be used. This defines basicly the appearance of the user interface. Actually this is only a part of the effective stylesheet name. XIMS assumes the object type with the style name stored in this property as the stylesheet name.

    If no style is defined in the preferences, style is automaticly set to 'default'. (see example below)

  • styleprefix

    Per default, XIMS assumes the object type's name as the stylesheet prefix. In some cases this is not what a programer wants. Through the styleprefix one can alter this behaviour.

  • preservelocation

    Useful for handling object types, where the location should not be checked for unsafe characters like URLLinks for example.

  • keepsuffix

    Object types like File or Image should have kept their suffix untouched during object creation.

(2) Filter Properties

none defined yet

(3) Content Properties

  • getchildren

    This itself can contain subclasses holding properties regarding the serialization for the context object's children.

    • objectid

      Set this to to an object id to get its children for contentbrowsing.

    • objecttypes

      Set this to a reference to an array of object type names to filter specific object types. Useful for browsing for special object types.

  • escapebody

    For objects that contain XML data in their body, it some times it is usefull not to process the XML. If this data should be stored as it is (as CDATA) to the resulting DOM, one has to set this flag to TRUE (1).

  • childrenbybodyfilter

  • getformatsandtypes

    If set, information on all available object types and data formats is added to the DOM. Whereas per default, only the information for the used object types and data formats respectively being used from current request's objects is added. getformatsandtypes is useful to get a list of available object types for object creation for example.

  • siblingscount

    Used for getting sibling information during repositionin objects.

[ top ]

A Matter of Style

We follow the style-guidelines laid out in perldoc perlstyle. The following list is - with a few exceptions - an excerpt of some of the main principles there, please read and follow the rest of them.

  • use strict

  • 4-spaces indentation

  • Opening curly on same line as keyword, if possible, otherwise line up.

  • Line up corresponding items vertically.

  • Space before the opening curly of a multi-line BLOCK.

  • One-line BLOCK may be put on one line, including curlies.

  • No space before the semicolon.

  • Semicolon INCLUDED in "short" one-line BLOCK.

  • Trailing "," in hash and array definitions.

  • Space around most operators.

  • Space around a "complex" subscript (inside brackets).

  • Blank lines between chunks that do different things.

  • Uncuddled elses.

  • No space between function name and its opening parenthesis.

  • Space after each comma.

  • Long lines broken after an operator (except "and" and "or").

[ top ]

Commenting style

Global fuzz

We chose not to use POD for commenting our code. We want comments where things actually happen, POD-markup gets pretty unreadable if used that way. We therefore had to find our own convention doing the job. (Relax, its quite simple and readable both for humans and machines :)

comment2dbk.pl is a simple script that parses sourcefiles and aims to generate DocBook-XML out of it. The parser isn't all too smart, so you better strictly follow the conventions. To put it other way 'round: If the output makes sense, you probably got it right.

To nail it down in a few sentences: We have a block of comments before method-declarations, each divided by the four sections SYNOPSIS, PARAMETER, RETURNS and DESCRIPTION. Additionally we allow (encourage, actually) blocks of comments on arbitrary places in the method-body. These are simply appended to the DESCRIPTION. If used before the first method it gives a block that can drop some wisdom about the whole package or file - we agree that there is at maximum one package per file, don't we? We also have a convention for grouping method to logical blocks but decided not to use it for now. But read on and stick your toes into...

The gory details

Starting with an (well, kind of made up) example is probably not the worst idea:

package Circus::FleaCircus;

@ISA = qw/Foo::Circus/;

#
# Circus::FleaCircus is a class representing a fleacircus. (you guessed that, right?)
#
# Beeing derived from Foo::Circus, it is composed of flea, trainer, audience und showroom
# instances, and implements methods to steer these.
#

use Foo::Circus;
use Circus::Fleas;
use Circus::Trainer;
use Circus::Audience;
use Circus::Showroom;

# ###########################################################################
# some quite clever methods
# ###########################################################################


##
#
# SYNOPSIS
#
#     Circus::FleaCircus->new($numOfFleas, $context)
#
# PARAMETER
#
#     $numOfFleas: the number of fleaobjects instantiated
#     $context:    the application context object
#
# RETURNS
#
#     $fleaCircus: FleaCircus object
#
# DESCRIPTION
#
#     Constructor
#
sub new {
    my $self       = shift;
    my $numOfFleas = shift;
    my $context    = shift;

    my $fleaCircus = undef; #return value
    #
    # The SuperClass Foo::Circus still
    # takes care of most things related to Showroom and Audience.
    #
    $self->SUPER::new();

    if ( $context ) {
        #warn "Buhu!";
        my $dp = $context->data_provider();
 ....
      

The Example may look a stuffed, but shows almost anything we can do right now. A rendered output of this sample can be seen here

comm2dbk.pl generates the filetitles from it's idea of the relative path to the parsed file. Call it with root_of_our_sourcetree as CWD.

#
# Circus::FleaCircus is a class representing a fleacircus. (you guessed that, right?)
#
# Beeing derived from Foo::Circus, it is composed of flea, trainer, audience und showroom
# instances, and implements methods to steer these.
#
      

Now the first comment block: It starts with a line with a single hash. When the parser finds a line consisting of zero to any whitespaces, a single hash, and zero to any whitespaces till newline. Without that starting hash the whole block would have been ommitted. The next single-hash line closes this and opens up the next paragraph. The block is closed with another single-hash-line. That beeing mere for optical reasons, the parser actually recognises a line without any(!) hash as end of block.

...
# ###########################################################################
# some quite clever methods
# ###########################################################################
...
        

A hash on the first column, a single space and one to any number of hashes marks a grouping. The idea is that you should group functionally similar methods together i.E internal helper functions at the end of the file. Right now we dont render it to the docs, but we most likely will later. The Pattern is reserved and you are encouraged to use it.

...
##
#
# SYNOPSIS
#
#     Circus::FleaCircus->new($numOfFleas, $context)
#
# PARAMETER
#
#     $numOfFleas: the number of fleaobjects instantiated
#     $context:    the application context object
#
# RETURNS
#
#     $fleaCircus: FleaCircus object
#
# DESCRIPTION
#
#     Constructor
#
sub new {
...
      

Two hashes at the beginning of a line mark the start a method-block. One Hash at the beginning of a line, one space and at least four capital letters are recognised as heading. The headings 'SYNOPSIS' and 'DESCRIPTION' are threated specially:

SYNOPSIS makes the parser chose the text of the following line as title for the Method-Section.

...
    my $fleaCircus = undef; #return value
    #
    # The SuperClass Foo::Circus still
    # takes care of most things related to Showroom and Audience.
    #
    $self->SUPER::new();
...
      

DESCRIPTION lets the parser pull all following comment blocks as paragraphs under the 'DESCRIPTION' heading. (at least until the next heading.) So be aware that the comments you markup in the code should give a somewhat continous reading in the docs.

...
    if ( $context ) {
        #warn "Buhu!";
        my $dp = $context->data_provider();
...
      

Comments not using one of these patterns are ignored, you still can comment out code, or make single or multiline comments that dont go into the docs.

[ top ]

Debuglevel Reference

replace me

Level Description Comment
1 Fatals unrecoverable errors
2 Errors serious error conditions
3 Warnings non-serious error-conditions
4 Infos "interesting" events
5 Infos, verbose ie: method START and EXIT
6 SQL-strings and values of important variables

[ top ]