Bric::Templates(3)

Name
Bric::Templates - Producing Templates on the Bricolage System

Version
$LastChangedRevision$

Date
$LastChangedDate: 2005-12-03 18:19:49 -0800 (Sat, 03 Dec 2005) $

Background
Before talking about templates, let's take a moment to discuss all the major players in the publish process and get the terminology down. The major players involved in publishing a story are:

* Element Types
* Elements
* Templates
* Fields

A quick word about each of these elements.

  Element Types
These are content container definitions. They describe what kinds of content that a story based on the element type will have. For instance, an "Editorial" element type might define that an Editorial contains "An author name, a title, and one or more paragraphs". Element types cannot affect the formatting or look of a story, just the structure of the content.

  Elements
Elements are instances of element types, and contain the content of stories. The structure of Elements adheres to that defined by their types.

  Templates
For a given element, a template formats its content for output. The formatted content will then be saved in one or more files by the Bricolage burner.

  Fields
Fields contain the content itself. Given an element type to constrain the types of fields, a content author then enters the content into fields. Upon preview or publication of the story, this content is then run through a template to produce one ore more output files.

Template Overview

  More About Elements
The first place one starts when making a new story is with an element. An element is based on an element type, which defines the structure of a story. For example, a "Column" element type might define a column story as having:

* An author field
* A column topic field
* One or more paragraph fields

Given this definition, a content author creating a new column element will have these three types of fields available to her.

Elements may also contain other elements. For example, the "Column" element type above might be used for a column reviewing music. A music column might want to have a short blurb about the albums that it reviews. So we create another element type called "Music Blurb" with these fields:

* Artist name
* Album name
* Label name
* Summary

Now in our "Column" element type we can add the "Music Blurb" element type as a Subelement. So now the structure looks like this:

* An author field
* A column topic field
* One or more paragraph fields
* Any number of Music Blurb subelements

Now a content author editing a Column story has the ability to add an author field, a topic for the column, a few paragraphs, and a blurb about the album she is reviewing. Element types can defined elements to be nested as deeply as needed. So an element can contain an subelement, that contains another subelement, etc.

The most important thing to understand about element types is that they simply define the structure of the content that the author is allowed to use when creating or editing a story. The element type has no bearing on the actual content itself, or the formatting of that data!

  Template Basics
Templates are the objects that actually format content. There are three basic types of templates: element templates, category templates, and utility templates. We'll start with the most common, element templates.

An element template is associated with a single element type and knows how to format content for elements of that type. For example, for our hypothetical "Column" element type, we might create a template to output HTML like this:

  <html>
    <head><title>$column_topic</title></head>
    <body>
      <h1>$column_topic</h1>
      <h2>By $author</h2>

      <p>$para1</p>
      <p>$para2</p>
      ...
      display_element(music_blurb)
      ...
    </body>
  </html>

This example doesn't represent the actual syntax for inserting values, but is meant to convey a template's relationship to an element. We'll get into the nuts and bolts later.

Note that the Music Blurb element element is referenced by its "key name", which is defined by the element type, and is simply passed to a display function. Remember, element templates represent the content for only a single element; another element template formats a the Music Blurb element.

  Templates And Categories
While a template formats the contents of a single element, there may be many templates that all format the same element. Why? Well, templates also belong to a single category. Imagine that the site for which we've created the current element and template examples is an arts and media site. There might be different sections of the site: one for music, one for theater, and one for gallery art. This site might therefore define the following categories:

* music
* theater
* gallery

Categories may contain subcategories. This is just to provide greater categorization detail:

* music
* music/dj
* music/classical
* music/rock
* theater
* theater/broadway
* theater/community
* gallery
* gallery/painting
* gallery/sculpture

In addition to these categories, Bricolage always provides a "root" category for each site. If you don't want something to be specific to a particular category, you can assign it to the root category. Root is usually shown as "/" in Bricolage, and category URIs always end in "/":

* /
* /music/
* /music/dj/
* /music/clasical/
* /music/rock/
* ...

So, what does this all mean for templates? By allowing many templates, each associated with a different category, to format the same element, we can have a custom format for the content in each section of the site. Say we want to have a column run in all the major categories of our site. That is, we want a Music column, a Theater column, and a Gallery column. We want to collect the same data for each of these columns, but we want the data to look different when published to each of these categories. To do so, you can simply create a column element template in each of those categories.

But note that a new template for every single category is not necessary. The "DJ", "Classical" and "Rock" subcategories will all default to using the template associated with the "Music" category. If there is no need to change how the content is displayed between any category, you can simply create a template associated with the root category, and column stories in any category will use it. In fact, the vast majority of Bricolage installations have all element templates defined in the root category, with only the occasional element template defined in a subcategory on an as-needed basis.

  Templates And Output Channels
Another attribute of a template is its association with an "Output Channel." An output channel is a collection of templates designed to format stories in a common format. So it's not unusual to have several output channels, such as XHTML, RSS, or WML, each of which outputs the contents of stories in a specific format.

Furthermore, output channels can include templates from other output channels. For example, an "Email" output channel might include templates from the XHTML output channel. Thus, when a story is published to the Email output channel, if the burner cannot find its templates in the Email output channel, it will look for them in the XHTML output channel. This approach to template sharing can be extremely useful for maintaining the consistency of formatting across output channels, as well as to facilitate code reuse.

  Bringing It All Together
By now you might be asking, Where do all my templates go, how does the system know what categories have templates in them, and how are they used during a publish?

Now, while there should be no need to manipulate templates directly on the Bricolage Server's file system (and it could cause the Bricolage system considerable confusion, anyway), let's briefly examine how the files are laid out. Hopefully, this exercise will make clear a few things about templates and publishing.

Let's start with names. Element types are given key names when they are created, and the key names uniquely identify element types throughout Bricolage. In our examples above, we created a "Column" element type. When you create an element template and choose its associated element type, it automatically assumes the key name of that element type. So, if our element type is named "Column" and its key name is "column", our template is will also be named "Column" and its base file name will be "column".

The template code may be written in one of four templating languages: Mason, Template Toolkit, PHP, or HTML::Template. Each output channel is associated with a templating language, so all templates in a single output channel (or any output channel it includes) will be written in the same templating language. The template file suffix varies depending on the templating language: ".mc" for Mason, ".tt" for Template Toolkit, ".php" for PHP, or ".pl" or ".tmpl" for HTML::Mason. (The examples in this tutorial will be in Mason, Template Toolkit, and PHP; See Bric::HTMLTemplate for a tutorial on HTML::Template templates, which are a bit different from the others.) So the file name for our Column element template would be either column.mc, column.tt, column.php, column.pl or column.tmpl.

The category associated with a template determines the path of the template file. So for a template in the /music/rock/ category, the path to the template would be /music/rock/column.mc.

Finally, the output channel is also used as a part of the path on the file system. Rather than take the output channel's name, its ID is used. Let's just say that the ID for primary channel output containing our Column element template is 1. So, our column template would live in oc_1/music/rock/column.mc.

If we assume that our element root is $COMP_ROOT, the full path to our template is $COMP_ROOT/oc_1/music/rock/column.mc. In template code, you don't need to worry about the full path, however, just the relative path, /music/rock/column.mc or even just music/rock/column.mc. The individual templating architectures handle the searching for templates in the proper output channel directories using their own path searching algorithms.

  Publish Examples
So, let's assume that we have three Column templates in a single Mason output channel: one associated with the root category, one associated with the Music category, and one associated with the Sculpture Gallery category. Their full path names would be:

  /column.mc
  /music/column.mc
  /gallery/sculpture/column.mc

Now, say that somebody creates a new column. When creating a new story, the author has the opportunity to associate it with one or more categories. The table below illustrates which column.mc template would be used for a publish in a particular category.

    Category where Story Published       Template element Used
    -------------------------------      ----------------------------
    /music/rock/                         /music/column.mc
    /theater/broadway/                   /column.mc
    /gallery/                            /column.mc
    /gallery/art/                        /column.mc
    /gallery/sculpture/                  /gallery/sculpture/column.mc

As you can see, a story published in a particular category will look up the category URI to find an element template to format it if it can't find it in the the story's own category. This functionality works identically for all supported templating architectures.

Note that in the case of the stories published in "/music/rock/" and in "/gallery/sculpture/", templates were found before the root template at /column.mc. It's important to realize that element templates do not chain. That is, the story published to "/music/rock/" will not be formatted by both /music/column.mc and /column.mc. Once an element template is found, the search stops. Thus element templates in subcategories override element templates in parent categories. Mason

  One Final Trick
Although element templates do not chain, sometimes you might want formatting code to be inherited rather than overridden. If you're familiar with Mason you may have noticed the similarity between how element templates are handled and the Mason concept of "dhandlers". In fact in Mason output channels, story element templates are dhandlers. Subelement templates of course are not, but they do enjoy the same category URI lookup algorithm when the burner searches for them. The other category architectures use the same algorithm for all element templates.

But there's another type of template that's useful for inheriting formatting behavior from parent categories. These templates are known as "category templates." A category template resides in a category, and wraps the execution of story element templates. In fact, in Mason output channels, category templates are implemented as Mason "autohandlers", while in Template Toolkit output channels, they're implemented as "wrappers". The PHP and HTML::Template burners emulate autohandlers and wrappers, as well. Their file names are autohandler in Mason, wrapper.tt in Template Toolkit, cat_tmpl in PHP, and category.pl or category.tmpl in HTML::Template. In any case, in Bricolage, these templates are all known as "category templates," regardless of the templating language in which they're implemented.

Category templates enjoy a different lookup algorithm. Like Mason's autohandlers, category templates are executed from the root category "down" the category URI. Furthermore, unlike element templates, the search does not stop as soon as a category template is found. Rather, it continues down the category URI path, executing each category template it finds in a cascading execution pattern.

For example, say that we have the following category templates defined in a Mason output channel:

  /autohandler
  /music/dj/autohandler
  /gallery/autohandler

This table outlines how they would be executed for stories in a select number of categories:

    Category where Story Published       Category Templates Executed
    -------------------------------      ----------------------------
    /music/rock/                         /autohandler
    /music/dj/                           /autohandler, /music/dj/autohandler
    /theater/broadway/                   /autohandler
    /gallery/                            /autohandler, /gallery/autohandler
    /gallery/art/                        /autohandler, /gallery/autohandler

Given a category URI, Bricolage searches for the first possible category template in that path, and then works its way down the path, executing any other autohandlers until it finally reaches the story's category. Then it starts searching for the story element template. In this way, category templates wrap the execution of story element templates for any type of story published in the output channel. This makes them ideal for the headers and footers that are common to all pages on a site (for a root-level category template), or category-specific headers/navigation, etc. (for subcategory category templates).

  Category Template Example
What good are category templates? Let's continue with our running example of the arts and media site. Let's say that, regardless of what type of story is published, you want the same basic HTML header and footer. Whether the story is a "column" or a "review" or an "editorial," you want the same header and footer on every page. The easiest and most efficient way to do this is by creating a root-level category template.

If we create a new category template in the root category of a Mason output channel (for this example, we'll assume an output channel with ID 1), then we'll get the file:

  $COMP_ROOT/oc_1/autohandler

The code for this template might look like this:

  <html>
    <head><title>Arts n' Media</title></head>
    <body>

      <ul id="nav">
        <li><a href="/music">Music</a></li>
        <li><a href="/theater">Theater</a></li>
        <li><a href="/gallery">Gallery</a></li>
      </ul>

      <div id="content">
  % $burner->chain_next(@_);
      </div>

      <p id="foot">The contents of this site are licensed under a <a
      rel="license" href="http://creativecommons.org/licenses/by-nc/2.0/"
      title="Creative Commons Attribution-NonCommercial License">Creative
      Commons License</a>.</p>

    </body>
  </html>

If you're familiar with Mason, most of this should look pretty normal. The only difference from your standard Mason autohandler is the call to "$burner->chain_next(@_)" rather than "$m->call_next". This is where Bricolage will set things up and call "$m->call_next" internally to execute the proper element template and output the story document content. Given this category template, if we publish a column to the '/music/rock/' category, the following templates will be used in this order:

    $COMP_ROOT/oc_1/autohandler
    $COMP_ROOT/oc_1/music/rock/column.mc

The content that column.mc outputs after being run will wrapped by the HTML in the category template. Furthermore, because the story element template is implemented as a dhandler, and the category template is implemented as an autohandler, Mason's inheritance methodology will work perfectly. If you declare a "<%method>" or "<%attr>" block in column.mc, then the category template can call it.

If we create a new category template in the root category of a Template Toolkit output channel (for this example, we'll assume an output channel with ID 2), then we'll get the file:

  $COMP_ROOT/oc_2/wrapper.tt

The code for this template might look like this:

  <html>
    <head><title>Arts n' Media</title></head>
    <body>

      <ul id="nav">
        <li><a href="/music">Music</a></li>
        <li><a href="/theater">Theater</a></li>
        <li><a href="/gallery">Gallery</a></li>
      </ul>

      <div id="content">
  [% CONTENT %]
      </div>

      <p id="foot">The contents of this site are licensed under a <a
      rel="license" href="http://creativecommons.org/licenses/by-nc/2.0/"
      title="Creative Commons Attribution-NonCommercial License">Creative
      Commons License</a>.</p>
    </body>
  </html>

Here the behavior is identical to your standard Template Toolkit wrapper. Unlike the Mason autohandler, however, the execution order is reversed:

    $COMP_ROOT/oc_2/music/rock/column.tt
    $COMP_ROOT/oc_2/wrapper.tt

The formatted content output by column.tt will be stored in the "CONTENT" variable in the Template Toolkit stash, and then the wrapper template will be executed.

Any values stored in the stash by the element template are available to either wrapper template, and those stored by the "/music/" wrapper template would be available to the root wrapper template. This functionality roughly corresponds with Mason inheritance.

As for PHP, its category template is, in fact, almost identical to the Template Toolkit category template:

  <html>
    <head><title>Arts n' Media</title></head>
    <body>

      <ul id="nav">
        <li><a href="/music">Music</a></li>
        <li><a href="/theater">Theater</a></li>
        <li><a href="/gallery">Gallery</a></li>
      </ul>

      <div id="content">
  <?= $content ?>
      </div>

      <p id="foot">The contents of this site are licensed under a <a
      rel="license" href="http://creativecommons.org/licenses/by-nc/2.0/"
      title="Creative Commons Attribution-NonCommercial License">Creative
      Commons License</a>.</p>
    </body>
  </html>

As with Template Toolkit wrappers, the story element template executes first, and the formatted output of the element template is stored in the $content global variable for access by the category template. Furthermore, because the execution order is the same:

    $COMP_ROOT/oc_3/music/rock/column.php
    $COMP_ROOT/oc_3/cat_tmpl

You can set up global variables (or better, a global associative array) in your element templates and have them available to the category template. This equates to Template Toolkit's stash and likewise corresponds to Mason's inheritance features.

Subcategory Category Template Example

As a last example of the chaining of category templates, let's say that we wanted some special HTML to appear just in the music section, regardless of what type of story we were publishing. Again, the best approach is to create a category template in the music category. The Mason version would live in the Music category directory:

    $COMP_ROOT/oc_1/music/autohandler

The Mason code for this category template might look like this:

        <h1>Music</h1>
        <div class="music">
          <img src="/ui/img/music_icon.png" alt="Air Guitar />
        % $burner->chain_next(@_);
        </div>

So, given this category template, in combination with the original root-level category template, if we again publish a column to the '/music/rock/' category, the following templates will be executed in this order:

    $COMP_ROOT/oc_1/autohandler
    $COMP_ROOT/oc_1/music/autohandler
    $COMP_ROOT/oc_1/music/rock/column.mc

After publishing this column, the final page output will look like this (for now, just assume that column.mc outputs the story content in "<p>" tags):

  <html>
    <head><title>Arts n' Media</title></head>
    <body>

      <ul id="nav">
        <li><a href="/music">Music</a></li>
        <li><a href="/theater">Theater</a></li>
        <li><a href="/gallery">Gallery</a></li>
      </ul>

      <div id="content">
        <h1>Music</h1>
        <div class="music">
          <img src="/ui/img/music_icon.png" alt="Air Guitar />
          <p>This is a test column.</p>
        </div>
      </div>

      <p id="foot">The contents of this site are licensed under a <a
      rel="license" href="http://creativecommons.org/licenses/by-nc/2.0/"
      title="Creative Commons Attribution-NonCommercial License">Creative
      Commons License</a>.</p>
    </body>
  </html>

Writing Templates
The above sections should give you an idea of how to create a template -- what it means to associate a template with an element, a category, and an output channel. Now let's talk a little about actually writing template code.

Writing template code is no different than writing Mason code. If you've written Mason code, you should have no problem writing templates. If you have not written Mason code before, I suggest you get familiar with it by reading HTML::Mason::Devel. A tutorial on Mason is beyond the scope of this document.

  Terminology
Given that template code is just Mason, Template Toolkit, PHP, or HTML::Template code, the only thing you need to know is how to access the story data. So let's take a small break to discuss terminology for story objects.

Stories are based on element types. Before you can create a new story, you must select an element type upon which your story will be based. Based on your selection, you will then be given a list of available fields for which you can enter content. If we use the "column" element type that we defined earlier, our fields will be:

•   An author field

•   A column topic field

•   1 or more paragraph fields

•   1 or more Music Blurbs

Now, if you remember, the author field, column topic field, and paragraph fields are all just text fields, while the music blurb is a subelement of the column element. This gives us two varieties of elements that can be in a story: "Container Elements" and "Field Elements", also generally referred to as "Elements" and "Fields", respectively.

In the interest of keeping things simple for the story authors, the Bricolage user interface uses a single term for both types of elements, "Element." Early user testing showed that authors didn't know or care about the difference between field elements and container elements. But this should have little bearing on your job as a template developer. You can simply think of them as "Container Elements" and "Field Elements," or, collectively, as "Elements".

So our new story based on the column element type may contain the following elements:

•   An author field element

•   A topic field element

•   A paragraph field element

•   A music blurb container element

Now that we know what a story can have and how to refer to them, let's look at the methods to access them.

  Available Objects and Methods
First, there are three variables exported into the global space of each templating architecture for use in templates. These are:

$story
    The story object

$element
    The current element object

$burner
    The burner object

Story Object Methods

The story object represents the story document being published. It is used to access all the metadata of a story. It can be used to indirectly access the actual content of the story, but there is a simpler way to do so, which we'll get to shortly. You can read the full list of methods callable on the story object by reading Bric::Biz::Asset::Business::Story, but let's highlight some of the most important and useful ones here:

"$story->get_title"
    Retrieve the title of this story.

"$story->get_description"
    Retrieve the description for this story.

"$story->get_cover_date"
    Returns the story's cover date, which is an arbitrary date associated with the story.

"$story->get_publish_date"
    The date when the story was most recently published; always available when publishing a story, but will be "undef" when previewing if the story has not been published.

"$story->get_first_publish_date"
    The date when the story was first published; always available when publishing a story, but will be "undef" when previewing if the story has not been published.

$story->get_primary_uri
    Retrieve the primary URI for the story, which is determined by its primary category and the URI format of its primary output channel.

Element Object Methods

The element object contains the content of a story. Since a story is based upon an element type, the field and container elements in the $element object are defined by its element type. so if the element type defined "author," "topic," and "paragraph," fields, you know that you can access those field elements via $element. Where $element really becomes interesting, however, is in its container elements.

In our example, we had defined a "music_blurb" element element type as a subelement of the "column" element type. Because it is not a story type element, there can be no "music_blurb" story; it can only be part of a larger "column" story. However, "music_blurb", just like any other element type, needs to have a template that knows how to format it. So while burning our "column" story, we will at some point have to call out to the "music_blurb" template to format our "music_blurb" element. When the "music_blurb" template executes, $story will still contain our "column" story object, but $element will contain an instance of a "music_blurb" element, and will allow access to the field and container subelements defined by the "music_blurb" element type.

Sound confusing? Well, you can read the full documentation for the interface of an element object in Bric::Biz::Element::Container, as well as its parent class in Bric::Biz::Element, but let's just go over some of the more significant methods callable on $element, and we'll get to an example soon enough.

"$element->get_field($key_name, $num)"
    This method returns the field object with the key name given by the $key_name argument. For example, if the element object was a "column" element, you could use "author", "topic", or "paragraph" for the key name to get the corresponding field.

    The $num argument is optional. It is meant for fields that can occur more than once, such as "paragraph"s in our "column" example. If $num is not given then it defaults to "1", meaning that the the first instance of the field identified by $key_name will be returned. The ordering is determined by the content author (you want all your paragraphs to be in the correct order, right?).

"$element->get_value($key_name, $num, $format)"
    This method is a convenient shortcut for

      $element->get_field($key_name, $num)->get_value($format);

    In other words, it returns the content associated with the field element given by $key_name. For example, if the element object was a "column" element, you could use "author", "topic", or "paragraph" for the key name to get the value stored for the corresponding field.

    The $format argument is optional, and may contain a "strftime" format string. Naturally, this is useful only for date fields, and is ignored by all others. If not specified, it defaults to the value stored in the "Date/Time Format" global preference. See DateTime for full documentation of the "strftime" format.

"$element->get_fields(@key_names)"
    Returns a list or anonymous array of the field subelements of the element. If called with no arguments, it returns all of the field subelements, in the order specified by the content editor. They can and will be mixed. For example, for our "column" example, it might return two paragraphs, then a topic, then three more paragraphs, then an author, and then two more paragraphs.

    If you only want specific fields but to still get them in order, simply pass in a list of the key names of the fields you're interested in. For example, called like this:

      my @elems = $element->get_fields(qw(paragraph topic));

    only paragraph and topic fields will be returned, but still in the order specified by the content author.

"$element->get_container($key_name, $num)"
    This method returns a container element object with the key name specified by the $key_name argument. This object is of the same type as $element. Usually you will just pass it to a method that will execute the correct template for you.

    As with "get_value()", the $num argument is optional, and is used to retrieve specific container elements that occur more than once in a given story. If $num is not given then it defaults to "1", meaning that the value for the first instance of the container element identified by $key_name will be returned. The ordering is determined by the content author.

"$element->get_containers(@key_names)"
    This method corresponds to "get_fields()" but returns a list or anonymous array of the container subelements of the element. If called with no arguments, it returns all of the container subelements, in the order specified by the content editor. They can and will be mixed. If you only want specific fields but to still get them in order, simply pass in a list of the key names of the fields you're interested in.

"$element->get_elements(@key_names"
    This method is perhaps the most used method on the $element object. It returns a list or anonymous array of both container and field element objects. Again, pass in a list of field and/or container element type key names to get back only elements based on those element types. For example, called like this:

      my @elems = $element->get_fields(qw(paragraph topic music_blurb));

    only paragraph, topic, and music_blurb elements will be returned, but still in the order specified by the content author.

"$element->get_place"
    Content authors have the opportunity to arrange the elements of their story in a particular order. This method returns a number giving this container element's place among all other subelements of $element (both data and container).

    If $element is the element object for the story itself (i.e. the "column" element of a "column" story) "get_place()" will return 1.

"$element->get_object_order"
    For a container element that can occur more than once in a story, this method returns its place among container elements of the same type. For example a "column" story might contain several "music_blurb" container elements. If $element happens to be the second of three "music_blurb" container elements, "get_object_order()" will return 2.

    If $element is the element object for the story itself (i.e. the "column" element of a "column" story) "get_object_order()" will return 1.

"$element->get_key_name"
    Return the key name of the container element.

"$element->get_name"
    Return the name of the container element.

Field Object Methods

The "get_field()", "get_fields()", and "get_elements()" methods each can return field elements. These are represented by the Bric::Biz::Element::Field class, which like the container element class, inherits from Bric::Biz::Element. But we'll cover their most important methods here:

"$field->get_value($format)"
    Returns the field's value; that is, the content. The "strftime" $format argument is optional, and only used for date fields. If not provided, it will default to the value stored in the "Date/Time Format" global preference. See DateTime for full documentation of the "strftime" format.

"$field->get_place"
    Content authors have the opportunity to arrange the elements of their story in a particular order. This method returns a number giving the field's place relative to all of the other subelements of the field's parent element.

"$field->get_object_order"
    For a field that can occur more than once in a story, this method returns its place among fields of the same type. For example a "column" story might contain several "paragraph" container elements. If a field happens to be the second of three "paragraph" field elements, "get_object_order()" will return 2.

"$field->get_key_name"
    Returns the key name of this field element.

"$field->get_name"
    Returns the name of this field element.

And finally, both the field and container element classes have a single method to help you easily disambiguate containers from fields in your template code:

"$element->is_container"
    Returns 1 if $element is a container element object and 0 if it is a field element.

Burner Object Methods

The burner object handles the burning of a story; that is, pushing it through templates and writing the resulting formatted content to disk. Each templating architecture has its own burner subclass, so there is some variation in its interface depending on what templating architecture you're using. You can read the documentation of the complete burner interface and the interfaces of its subclasses in:

Bric::Util::Burner
    The burner base class, where most methods are defined.

Bric::Util::Burner::Mason
    The Mason burner class.

Bric::Util::Burner::TemplateToolkit
    The Template Toolkit burner class.

Bric::Util::Burner::PHP
    The PHP burner class.

Bric::Util::Burner::Template
    The HTML::Template burner class.

But let's cover the most important methods here (HTML::Template users are encouraged to read Bric::HTMLTemplate, since its interface is pretty different from the other templating architectures).

"$burner->display_element($element)"
    Outputs the content of the field or container element passed as its argument. For field elements, this is synonymous with calling "$field->get_value". For container elements, "display_element()" searches up the URI path of the category to which the story is currently being published for the relevant element template. If it finds it, it executes it, adds its output to the output buffer, and returns control to the calling template.

"$burner->sdisplay_element($element)"
    This method is identical to "display_element()" except that it returns the output rather than adding it to the output buffer.

"$burner->display_pages(\@key_names)"
    This method works like "display_element()", only it has some magic to format only one element at a time, output its contents, and write a file, then start the burn of the next page, until all pages specified by the array reference of key names have been burned to separate files to disk. All template code above and below the call to "display_pages()" will be executed for each page in the story.

"$burner->chain_next"
    This is a Mason-specific method. From a category template, it executes the next category template or the specific story template. It should not be called from any other template.

"$burner->get_mode"
    Returns a value indicating in what mode the burner is running. The value returned can be compared to three constants imported into the template name space: "PUBLISH_MODE", "PREVIEW_MODE", and (for completeness) "SYNTAX_MODE". Useful for when your template needs to have different behaviors between previews and publishes.

"$burner->set_encoding($encoding)"
    Sets the character encoding output by the template. Defaults to "utf-8" in the Mason, Template Toolkit, and HTML::Template burners, and "raw" in the PHP burner. With the exception of the PHP burner, you should change the encoding if your template is converting its output from UTF-8 (which is how all content is stored in the database) to some other encoding so that the content gets properly written to disk.

"$burner->get_oc"
    Returns the Bric::Biz::OutputChannel object through which the story is being burned. Useful when using output channel includes, where the template executing may be in an output channel included by the output channel through which the story is being burned.

"$burner->get_cat"
    Returns the Bric::Biz::Category object to which the story is currently being burned. Useful for stories associated with multiple categories, as each category association triggers a separate burn.

"$burner->best_uri($doc)"
    Returns a "URI|URI" object representing the best URI for the document passed. Useful for multisite Bricolage installations, where one story might which to create a link to another story in a different site. In that case, it will provide the best URI to use, either to an alias in the same site as the current site, or to a fully qualified URL to the other site.

"$burner->get_page"
    Returns the zero-indexed page number currently being burned. For multipage stories, each page may be output to a separate file. The first page will have the value "0", the second the value "1", and so on.

"$burner->publish_another($doc, $pub_time)"
    If you want the publication of a story to trigger the publication of some other story or of an unrelated media document, you can look up the document and pass it to "publish_another()". By default, the story passed will be scheduled for publish at the same time as the current story is being published. Pass in an ISO-8601-formatted date string as the second argument to schedule the document for publication at a later date. During previews, this method simply returns without doing anything.

"$burner->preview_another($doc, $oc_id)"
    Like "publish_another()", but triggers the preview of another document, rather than its publication. Useful in preview mode when you want to trigger another document for preview. The second argument, an output channel object ID, is optional. If not present, the document will be previewed in either the same output channel as is currently being previewed, or in its primary output channel. In publish mode, this method method simply retrains without doing anything.

"$burner->blaze_another($doc)"
    This method can be called instead of "preview_another()" or "publish_another()", and will pass through to the proper method depending on the mode. Furthermore, in preview mode, it will burn the document to all associated output channels, rather than just the current or primary output channel as in "preview_another()".

  A Template Example
Now let's take a look at some template code. For this example, we'll use the "column" element type included in a default Bricolage install for our story. This "Column" is a little different than the one we've been discussing; here's the definition for this element type:

Column
    Fields:

    deck

    Subelements:

    page
Page
    Fields:

    paragraph
    pull_quote
    next
    previous

    Subelements:

    inset
Inset
    Fields:

    copy

Note that this story structure is three levels deep rather than the two levels we were discussing before. Furthermore, the "page" element type was created with a flag marking it as a paginated element type. We'll rely on its marking as a page to pass the key name "page" to "display_pages()" so as to output all of the "page" elements as separate files during the burn.

Here is the Mason code for the "column" template:

  <html>
    <head>
      <title><% $story->get_title %></title>
    </head>
    <body>

  % $burner->display_pages('page');

    </body>
  </html>

Here is the Template Toolkit version:

  <html>
    <head>
      <title>[% story.get_title %]</title>
    </head>
    <body>

  [%- burner.display_pages('page') -%]

    </body>
  </html>

and the PHP:

  <html>
    <head>
      <title><?= $story->get_title() ?></title>
    </head>
    <body>

  <?php $burner->display_pages('page'); ?>

    </body>
  </html>

Note that the code is essentially identical in all three templating architectures; only the templating syntax varies.

The call to "display_pages()" simply iterates through each "page" container element in the "column" story and displays it. Remember that the page element was created with the 'paginated' flag and will start a new output file for each page. Also, note that we haven't output the contents of the "deck" field. This field is meant for use as a teaser when another story creates a link to this story. (Formatting of related stories is covered in Bric::AdvTemplates.)

Next let's look at some example code for the "page" element template. Here's the Mason version:

  % unless ($burner->get_page) {
  %   # Only output the title on the first page.
      <h1><% $story->get_title %></h1>
  % }

  <%perl>;
  foreach my $e ($element->get_elements(qw(paragraph pull_quote inset))) {
      my $key_name = $e->get_key_name;

      if ($key_name eq 'paragraph') {
          $m->print('<p>', $e->get_value, "<p>\n");
      }

      elsif ($key_name eq 'pull_quote') {
          $m->print('<blockquote>', $e->get_value, "<blockquote>\n");
      }

      else {
          # It's an inset, which is a container element.
          $burner->display_element($e);
      }
  }
  </%perl>

  <div id="pagenav">
  % if (my $prev_page = $burner->prev_page_file) {
      <a href="<% $prev_page %>">&lt; <%
          $element->get_value('previous') || 'Back'
      %></a>
  % }
  % if (my $next_page = $burner->next_page_file) {
      <a href="<% $next_page %>"><%
          $element->get_value('next') || 'Next'
      %> &gt;</a>
  % }
  </div>

Here's the Template Toolkit version:

  [%- UNLESS $burner.get_page %]
      [% # Only output the title on the first page. -%]
      <h1>[% story.get_title %]</h1>
  [% END -%]

  [% FOREACH e = element.get_elements('paragraph', 'pull_quote', 'inset') -%]
      [% SWITCH e.get_key_name -%]

      [%- CASE 'paragraph' -%]
          <p>[% e.get_value %]</p>

      [%- CASE 'pull_quote' -%]
          <blockquote>[% e.get_value %]</blockquote>

      [%- CASE -%]
          [%- burner.display_element(e) -%]
      [%- END -%]
  [% END -%]

  <div id="pagenav">
  [%- IF (prev_page = burner.prev_page_file) %]
      <a href="[% prev_page %]">&lt; [%
          element.get_value('previous') || 'Back'
      %]</a>
  [% END -%]
  [%- IF (next_page = burner.next_page_file) %]
      <a href="[% next_page %]">[%
          element.get_value('next') || 'Next'
      %] &gt;</a>
  [% END -%]
  </div>

And finally, the PHP version:

  <?php
  if (!$burner->get_page()) {
      # Only output the title on the first page.
      echo '<h1>', $story->get_title(), "</h1>\n";
  }

  foreach ($element->get_elements('paragraph', 'pull_quote', 'inset') as $e) {
      switch ($e->get_key_name()) {

          case 'paragraph':
              echo '<p>', $e->get_value(), "</p>\n";
              break;

          case 'pull_quote':
              echo '<blockquote>', $e->get_value(), "</blockquote>\n";
              break;

          default:
              # It's an inset, which is a container element.
              $burner->display_element($e);
      }
  }

  echo '<div id="pagenav">', "\n";

  if ($prev_page = $burner->prev_page_file()) {
      $label = $element->get_value('previous') or 'Back';
      echo '<a href="', $prev_page, '">&lt; ', $label, "</a>\n";
  }

  if ($next_page = $burner->next_page_file()) {
      $label = $element->get_value('next') or 'Next';
      echo '<a href="', $next_page, '">', $label, " &gt;</a>\n";
  }
  echo "</div>\n";
  ?>

Again, the various architectures use the same programming logic and output the same content (modulo white space issues). In each case, the code outputs a header for the first page of the column only, and then the story page content. Note how we've passed element key names to "get_elements()" to retrieve only the elements we want to be ordered. Next, links to a previous page and a next page are output, but only if those pages exist. The "next_page_file()" method returns a file name only if there will be a next page, and the "prev_page_file()" method returns a file name for all pages but the first page.

It is important to realize that there are no restrictions against having a container and a field element with the same name. While this is unlikely to happen (you're doing the element type design and not doing that, right?), if it were true you'd have to check the "is_container()" method to tell them apart.

And finally, We have the code for the "inset" container element template. Here's the Mason version:

  <div class="inset">
    <p><% $element->get_value('copy') %></p>
  </div>

Here's the Template Toolkit version:

  <div class="inset">
    <p>[% element.get_value('copy') %]</p>
  </div>

And the PHP version:

  <div class="inset">
    <p><?= $element->get_value('copy') ?></p>
  </div>

That's about it. If you create and deploy new templates for these three element types, and paste in the above code examples, you should then be able to create and preview a new "Column" story. Make sure that you create your templates in the root category to ensure you can publish a story to any category.

Appendix

  Class Names
    Object Type             Associated Perl Class
    ---------------------   ---------------------------------
    Element Type            Bric::Biz::ElementType
    Container Element Type  Bric::Biz::ElementType::Container
    Field Element Type      Bric::Biz::ElementType::Field
    Template                Bric::Biz::Asset::Template
    Story                   Bric::Biz::Asset::Business::Story
    Element                 Bric::Biz::Element
    Field Element           Bric::Biz::Element::Field
    Container Element       Bric::Biz::Element::Container
    Burner                  Bric::Util::Burner

  Glossary

Category Template
    A template associated with a category that wraps the execution of all story element type templates in that category and its subcategories. Corresponds to Mason's "autohandlers" and Template Toolkits "wrappers".

Burner
    The Bricolage object responsible for pushing a story through templates and writing the resulting output to files.

Category
    Categories are hierarchical organizing objects used to categorize Bricolage documents. The URI path of a category is used to form part of each document's URI and is used to organize templates.

Element Type
    The definition of the structure of a document or the subelement of a document.

Element
    A part of a document, either a container element or a field element.

Container Element
    An element containing structured content based on an Element Type.

Field element
    An element that represents textual content.

dhandler
    A Mason concept. In Mason output channels, story element type templates are implemented as dhandlers.

Mason
    A popular Perl-based templating architecture.

Template Toolkit
    Another popular Perl-based templating architecture.

HTML::Template
    Yet another popular Perl-based templating architecture.

PHP A popular server-based Web programming language, ideal for templating.

Output Channel
    A collection of templates based on a single templating architecture, designed to output content in a single format (such as XHTML, RSS, PDF, etc.).

Story
    A content document.

Template
    A file containing template code to format part of a story for output.

Authors
David Wheeler <david@kineticode.com>

Garth Webb <garth@perijove.com>

See Also

Bric::AdvTemplates
Bric::Biz::ElementType
Bric::Biz::Asset::Template
Bric::Biz::Asset::Business::Story
Bric::Biz::Element
Bric::Biz::Element::Field
Bric::Biz::Element::Container
Bric::Util::Burner
Bric::Util::Burner::Mason
Bric::Util::Burner::TemplateToolkit
Bric::Util::Burner::Template
Bric::Util::Burner::PHP