Mir CMS 1.1: developer's guide

Revision History
Revision 0.0.0August 2005
First steps

Table of Contents

1. Introduction
Architecture overview
Concepts used by Mir
2. The producer framework
How to use the producers.xml file
Introduction
Node arguments
Expressions
Node types
How the producer framework is implemented

List of Figures

1.1. mir architecture (single host)
1.2. mir architecture (mirrored)
1.3. An example of article generation with the producers framework
2.1. A producer in the producer.xml file is parsed into a tree of ProducerNodes

List of Tables

2.1.
2.2.

Abstract

This document is meant for helping mir developers. It is assumed that you have already installed mir and have some basic knowledge on how to use it. However, reading the introduction chapter might also be worthwhile for sysadmins and template designers.

Chapter 1. Introduction

Architecture overview

Mir is a fairly large piece of software (over 50.000 lines) written in java. It relies on the tomcat servlet framework for administration and open publishing tasks. However, unlike many other CMS's that generate html on the fly (usually using a cache), Mir writes html pages into a static site. Normally, users that visit the mir site, will only see the static site. Users that want to publish content (open publishing) will contact the dynamic site. Administrators will also contact the dynamic site (see figure).

Figure 1.1. mir architecture (single host)

mir architecture (single host)

This architecture is interesting because most page requests will be done on the static site. Static pages are efficiently handled by the web server. It is also interesting because it permits easy mirroring of the static site. Mirrors are simple to set up, since only static pages are involved (see figure).

Figure 1.2. mir architecture (mirrored)

mir architecture (mirrored)

Concepts used by Mir

Here's a list of important terms and concepts used by Mir. Many of these concepts will be further detailed later on in this guide.

producer

As mentioned in the architecture overview, mir generates a set of static html pages called the static site. Generating these pages involves using a template engine to replace variables (representing data) in template files. Template files are written by web designers. In many CMS systems, the available variables (data) are hard-coded into the software. In practice, this is often not flexible enough for web designers. Mir allows designers to request the data they need by using the producer framework, configured in the producers.xml file.

Originally producers were mostly used to generate pages, but they are now used for a lot of other tasks. In fact, the producers.xml file provides a simple, xml-based, programing (scripting) language, so that site administrators can script their own custom tasks without changing the java code. For example, on indymedia.org, producers are used to pull rss feeds for the global wire.

Figure 1.3. An example of article generation with the producers framework

An example of article generation with the producers framework

In the admin web interface, producers appear, for example, on the admin->"Generate manually"->"advanced page": (as Tasks)

Further information on producers may be found at XXX and in the javadoc (classes Producer, NodedProducer, roducerNode...).

database

Mir uses a postgres database to store it's data. Currently, the mir.storage, mircoders.storage, mir.entity, and mir.entity.adapter packages are used to access the database in Mir. However, these packages are obsolete and will be replaced by the hibernate object/relational persistence system.

localizer

Localizers provide a customization framework that allows different mir sites to behave differently. This is effectively used by various indymedia sites to customize things such open posting mechanisms, open posting validation, data model enhancements, etc.

The MirLocalizer interface describes the centralized localizer, that may be accessed via the global MirGlobal.localizer() function. The central localizer provides accessors to domain specific localizers (like MirBasicOpenPostingLocalizer).

Default behavior is provided by the MirBasicLocalizer and it's associated classes. These classes can be extended to override default behavior.

generator

A generator is an abstraction for a template engine. Different template engines are supported right now: freemarker (used most often), tal (used by some sites) and velocity (not used at all but supported anyway).

logger

Logging is understood here as "system logs" : information written out to files that may be used to track what is going on in Mir, especially errors and warnings. Mir uses the log4j logging system. This allows us to split logs into different files, making them easier to understand.

mircoders packages

Originally, the mircoders packages were meant for local developments, as opposed to the core Mir code. This distinction is now obsolete and these packages will probably be reorganized in future releases.

bundles

bundles are key/value pairs in a text file, used for configuration and internationalization

Chapter 2. The producer framework

How to use the producers.xml file

[FIXME: this should be in the user's guide, not here]

Introduction

With Mir, it is now possible to set up arbitrary producers using an xml file.

Producers consist of "nodes". Every node has a specific function. For example, it is possible to use a node to generate a file out of a template. Or it is possible to use a node to enumerate over a collection of articles.

A producer is defined using a Producer tag:

    
    <producer name="content"/>
  

would define a producer named content.

In a producer, verbs must be defined. Verbs are sub-tasks of a producer.


    <producer name="content">
      <verbs>
        <verb name="new">
        </verb>
        <verb name="all">
        <verb>
      </verbs>
    <producer name="content"/>  
  

would define a producer with verbs named all and new.

And also the specific nodes and their relationship should be specified:


    <producer name="content">
      <verbs>
        <verb name="new">
        </verb>
        <verb name="all">
        <verb>
      </verbs>
      <body>           
        <Generate 
            generator="/producer/startpage.template" 
            destination="${config.storageRoot}/index.shtml"/>
      </body>  
    </producer>  
  

we will later learn that this producer generates a single file.

Producers can be made to do different things for different verbs:


    <producer name="content">
      <verbs>
        <verb name="new">
          <Set key="count" value="3"/>
        </verb>
        <verb name="all">
          <Set key="count" value="5"/>
        <verb>
      </verbs>
      <body>           
        <Generate 
            generator="/producer/startpage.template" 
            destination="${config.storageRoot}/index.shtml"/>
      </body>  
    </producer>  
  

if a producer is called with a specific verb, first the nodes of that verb are processed, and only thereafter the body: in our case, if the producer content is called with verb new, first the variable count is set to 3, and after that, a file is generated.

Node arguments

Nodes can have arguments. Arguments that amount to integer values can be direct expressions. Arguments that amount to text values can be enriched with expressions between ${}.

Some examples:

    <Log message="This article has the following title: ${content.title}"/>

  

Log has 1 mandatory argument, message. This argument should be text, but can be enriched with expressions enclosed by ${ and }. In this example, the title of an article is logged.

    <Set key="age" value="34+22*(3+2)"/>
  

Set has 2 mandatory argument: key and value. The key arugment is a fixed text, the value argument is a direct expression, in this case of arithmetic nature.

Expressions

Expressions, either direct expressions, or expressions between ${}, can contain the following constructions:

a string literal 'hello'
a numeric literal 2138
a variable reference content.title
arithmetic operators 3 + 4 *(2+5-2)
string operators 'hello' ++ ' ' ++ 'Mir'
boolean operators (content.id==3) or (content.id in (5,7,2,8) and (content.title!='hello')

Node types

There are a number of different node types that can be used inside producer definitions. Here an overview:

Name Set
Purpose Alter a variable's using a free expression
Arguments
key The variable
value The expression to set the variable to
Example <Set key="data.result" value="3 + 5 * (5-2)"/>
Name Define
Purpose Alter a variable's using a string
Arguments
key The variable
value The string to set the variable to
Example <Define key="filename" value="/var/www/${content.id}.shtml"/>
Name If
Purpose Create a conditional part of a producer
Arguments
condition The expression to test
Sub tags
then The part to process if the expression evaluates to true
false The part to process if the expression evaluates to false
Name nodedefinition
Purpose Acts as a "funtion" (or "macro") that can be "called" elsewhere in the producer
Arguments
name The name of the newly created node ("function")
Sub tags
parameters a list describing the arguments the "function" must be given
definition the actual body of the function, containing the code that should be executed. Note that this may contain a <sub/> tag that is replaced by the child nodes of the calling node (FIXME is this true???)
Name Log
Purpose Log a message in the producer log
Arguments
message The message to log
Name Enumerate
Purpose Enumerate over the results of a query
Arguments
key The variable name that receives the enumerated record
table The table that is used to enumerate over
selection (optional) The condition (where clause) of the query.
order (optional) The order in which the results of the query are enumerated.
skip (optional) The number of records to skip
limit (optional) The maximum number of records to enumerate
Sub tags
This node can have subnodes that will be processed for every enumerated record
Name List
Purpose Store the results of a query into a variable
Arguments
key The variable name that receives the result list
table The table that is used to select from
selection (optional) The condition (where clause) of the query.
order (optional) The order in which the results of the query are put into the list.
skip (optional) The number of records to skip
limit (optional) The maximum size of the list
Name Batch
Purpose Divide the results of a query into batches
Arguments
key The variable name that receives the batch
infokey The variable name that receives meta information on the batches
table The table that is used to select from
batchsize The size of a batch (the first batch however varies in size)
selection (optional) The condition (where clause) of the query.
order (optional) The order in which the results of the query are put into the list.
skip (optional) The number of records to skip
process (optional) The maximum number of batches to process
minbatchsize (optional) The minimal size of the first batch
Sub tags
batches The part to process for every batch
batchlist The part to process once with the meta info
Name Generate
Purpose Generate a page using a generator (i.e. an abstraction of a template)
Arguments
generator the generator to use
destination the specification of the destination.
parameters Additional configuration info for the generator (for freemarker this now only contains the wanted encoding, empty for the default).
Remarks This node is used to have an actual page generated. The generator parameter usually is the name of a template. The destination is the file to be generated. Variable references are possible in all arguments, and, especially for the destination attribute, widely used.
Example <Generate generator="/producer/content.template" destination="/var/www/${content.date.formatted.yyyy}/${content.date.formatted.MM}/${content.id}.shtml"/>
Name DeleteFile
Purpose Delete a file
Arguments
filename The filename of the file to delete
Name SetFileDate
Purpose Set a file's date
Arguments
filename The filename
date The date to use
Name Resource
Purpose Make a message resource bundle available
Arguments
key The variable in which the bundle will be stored
bundle The bundle to use
language (optional) The specific language to use
Name Execute
Purpose Execute a script
Arguments
command The command to execute
Name ModifyContent
Purpose Modify a field of an article
Arguments
key The variable containing the article
field The field to modify
value The value to set the field to
Name MarkContent
Purpose Mark an article as produced
Arguments
key The variable containing the article

How the producer framework is implemented

A Producer is a set of tasks, scripted in xml. Producers allow mir installations to have their own actions that can be called for instance when a new article is posted. Originally producers were mostly used to generate pages, but they are used for a lot of other tasks such as pulling rss feeds for the global wire on indymedia.org. Producers are added and configured through the producers.xml file.

The xml nodes contained within a <producer> tag in the producers.xml file define a small program. This program (or script) may contain constructs such as if clauses, loops and variables... The program is parsed into a tree of ProducerNodes (figure). The root of this tree is defined in a NodedProducer (which is the only class that currently implements the Producer interface). When the Producer is executed, the produce methods of each node are recursively called, effectively executing the program as it was scripted.

Figure 2.1. A producer in the producer.xml file is parsed into a tree of ProducerNodes

A producer in the producer.xml file is parsed into a tree of ProducerNodes