Open Source Content Management System

mRFC 0022: Unifying Midgard's style handling

  1. Revision history
  2. Background
  3. Unifying the style engine
  4. Midgard style model
  5. Style rendering
  6. Style engine interface
  7. Style engine implementation
  8. Using the style engine
  9. The Midgard root file

This proposal deals with how to unify the style handling in Midgard and MidCOM for better flexibility and performance.

This mRFC been submitted to the Midgard Community for discussion and approval under the Creative Commons Attribution-ShareAlike license.

Revision history

2005-11-02 Created by Tarjei Huse and Henri Bergius

2005-12-06 Notes from Torben Nehmer:

  • Representation of the current MidCOM Style system is inaccurate (MidCOM style element can also come from the db)
  • It will never be possible to cache all MidCOM style operations using mod_midgard due to the dynamic operation of MidCOM.
  • See also my Mail "Style Queue/MRFC 22 comments" to midgard-dev on 2005-12-06, 13:21 UTC. It contains a more complete explainaiton of the system required to really deprecated the original MidCOM style system.

2005-12-13 Revised proposal by Jukka Zitting

Background

Midgard CMS has a very powerful style engine that allows site layouts to be split into small pieces named elements. The elements can contain any data including HTML and PHP code.

The elements can be overridden on multiple levels on the Midgard styles. On bottom of the style queue is the host's style. Individual pages or hosts can use a child style, where elements of the host style can be overridden. And for individual pages or page trees, the elements can be overridden using page elements.

For performance reasons the combined page and style element set is cached into a PHP file in Midgard's cache directory.

These features in Midgard's page delivery mechanism are considered one of the most powerful in any CMS systems (see several CMS Watch kudos lists). However, what breaks it is the way pages are composed in the Midgard Component Framework.

MidCOM uses topics instead of pages as its site directories, and so elements of the normal site style can't be overridden on per-directory level. To cure this problem to some degree, MidCOM has introduced its own style engine that allows styling of component output.

Style queues in Midgard 1.7

The combination of Midgard's own style engine, and the MidCOM style engine allows some flexibility in styling Midgard sites. However, having two separate styling systems is both confusing and inefficient.

Unifying the style engine

This document proposes a unified style engine API and a possible implementation for both the traditional Midgard style model and the MidCOM style engine. The proposed unified style engine is called Midgard Style Engine v2.0.

The main design principles for the unified style engine are:

  • The engine must be backwards compatible with existing Midgard styles
  • The engine must cover the needs of both midgard-php and MidCOM
  • The engine must be simple, easy to use, and well documented

Additional design considerations are:

  • It should be easy to implement a PHP-only version of the engine
  • It should be easy to optimize the engine for performance
  • It should be easy to extend the engine with caching, etc.

Midgard style model

The Midgard Style Engine is built on the concept of styles as collections of style elements.

Style may contain elements

Each style element is identified by a name that is unique within the style that the element belongs to. Each element also contains the element value, a snippet of HTML or PHP code with Midgard template codes. Both the element name and value are character strings.

Elements have name and value

It is possible for a style to extend (or inherit) another style. In this case the child style appears to contain all the style elements of the parent style in addition to it's own style elements. The child style element takes precedence in case a similarly named element is found in the parent style. There is no limit to the depth or width of the style inheritance tree as long as no inheritance cycles exist.

Styles can extend styles

Style rendering

Midgard styles are rendered into HTML (or any other character-based format) by the Midgard Style Engine. The style engine is first given a style and the name of the element to render. The style engine then looks up the value of the named element in the given style (or the first parent style that contains such an element), and parses and executes the element value using the PHP language and the Midgard template codes described below. Depending on the style engine configuration the result is either buffered in memory or sent to a client.

The Midgard Style Engine relies on normal PHP for flow control and other custom processing when rendering the style elements. In addition to normal PHP, the Midgard template codes <(element)> and &(variable); can be used to direct the rendering process. The <(element)> code is replaced with the rendered content of the referenced style element and the &(variable); code is replaced with the contents of the named variable. See the existing Midgard documentation for details of the Midgard template codes. Note that there are also some other less used template formats like <[element]> and ....

The variable context in which the style elements are executed is managed by the style engine. The traditional Midgard styles are always executed in the global variable context while the MidCOM style engine has used separate variable contexts for each style element. It is up to the application that uses the style engine to consistently specify the variable context type to be used when executing the style elements.

Style engine interface

The Midgard Style Engine v2.0 is accessed mainly through a single interface class called midgard_style_engine. This interface hides all the logic of locating, parsing, and rendering the styles from the application.

/**
 * The Midgard Style Engine interface.
 *
 * This is the main class of the Midgard Style Engine. Applications
 * use the style engine by acquiring a midgard_style_engine instance
 * and invoking the render() method to render style elements.
 *
 * Each style engine instance is associated with a specific style
 * context. The style context contains the style (or styles) from
 * which referenced style elements are found.
 */
class midgard_style_engine {

    /**
     * Renders the given style element within the context of this
     * style engine. The output of the rendering process is printed
     * to the normal PHP output stream.
     *
     * Any style element references are resolved using the style
     * context associated with this style engine.
     *
     * @param string $element style element value
     */
    function render($element);

}

Style engine implementation

The midgard_style_engine class is an empty interface class (not a true interface due to PHP 4 limitations) to allow for extensibility by various subclasses. The traditional Midgard style engine is implemented as the traditional_midgard_style_engine class.

/**
 * The traditional Midgard Style Engine implementation.
 *
 * This class implements the traditional Midgard style parsing and
 * rendering rules. The style context used to locate the style
 * elements is given as a constructor option.
 */
class traditional_midgard_style_engine extends midgard_style_engine {

    /**
     * Creates a traditional style engine instance. The given
     * midgard_style_context instance is used for locating
     * style elements for rendering.
     *
     * @param object $context style context
     */
    function traditional_midgard_style_engine($context);

}

The style context is passed to the style engine implementation as an external dependency for greatest extensibility. Like midgard_style_engine, also the midgard_style_context class is an empty interface class.

/**
 * The Midgard style context interface.
 *
 * This interface class represensts a Midgard style context used
 * by the standard Midgard Style Engine implementation. A style
 * context is used to locate the unparsed values of referenced
 * style elements.
 */
class midgard_style_context {

    /**
     * Returns the value of named style element. Returns NULL
     * if the named element is not found.
     *
     * @param string $name style element name
     * @return string style element value, or NULL
     */
    function get_element($name);

}

The standard style engine implementation contains a number of midgard_style_context subclasses that cover all the needs of normal Midgard applications. The standard style context implementations all support context chaining as a generic form of style inheritance. If a style element is not found within a chained style context, then the next style context in the chain is consulted.

Applications are free to create their own style context implementations to extend the capabilities of the Midgard Style Engine.

Using the style engine

Normal style editors are not directly affected by the new style engine. All the existing styles and the Midgard template codes function just as before.

The differences are that the new style engine will allow more options in where and how to store the styles (database, file system, etc.) and make it possible for applications to dynamically switch the style contexts in use.

A standard code template for instantiating a new style engine and using it to render a style element is:

<?php
$context = ...;
$style = new standard_midgard_style_engine($context);
$style->render("...");
?>

When a style element is being rendered, a reference to the current style engine will be available as $_MIDGARD["midgard_style_engine"] in the $_MIDGARD superglobal. If the style engine uses a standard style context, then a reference to that context is available as $_MIDGARD["midgard_style_context"].

The Midgard root file

The Midgard root file will be modified to use the new style engine implementation just like any other Midgard application. The code snippet below implements the standard Midgard style bootstrap sequence using the new style engine.

<?php
$context = new traditional_midgard_style_context($page_id);
$engine = new midgard_style_engine($context);
$template = "<(code-compat)><(code-global)><(code-init)>";
if ($context->get_element("ROOT") === NULL) {
    $template .= "<(content)>";
} else {
    $template .= "<(ROOT)>";
}
$template .= "<(code-finish)>";
$engine->render($template);
?>

Back

Designed by Nemein, hosted by Anykey