Bric::AdvTemplates - Template Producing: Advanced Topics $LastChangedRevision$ $LastChangedDate: 2004-05-24 18:17:44 -0700 (Mon, 24 May 2004) $ This document discusses some templating techniques that require existing knowledge of the Bricolage publishing system. The text and examples will assume the reader has already read and understood the first template document, 'Producing Templates on the Bricolage System'. It is possible not to associate a template with an Element. Doing so creates a template that will be used for every type of Story in a particular category and output channel. These are called "Autohandler Templates" or just "Autohandlers". Autohandlers usually provide wrapper HTML for normal Templates, such as a standard site header and footer. Unlike normal templates that display particular fields of a story, autohandlers will usually just have some HTML along with a line of code that says "put all remaining output here". This line of code is the 'chain_next' method of the $burner object passed into all templates. It is called like this: % $burner->chain_next; Autohandlers do have access to the $story and $element objects as well. You might use these if you want to add some customization based on the story. Autohandlers run before any normal template is run, and if there is more than one autohandler in a directory tree, they will each run in order from the top of the directory tree down. For example, let's say we publish a Column story in the News/Politics category, and there is a Root category autohandler and a News category autohandler: /News/Politics/column.mc /autohandler /News/autohandler When this Story is published, the system searches for any autohandlers, starting at '/' working its way down to the publish category '/News/Politics'. In this example, the templates will run in the following order: /autohandler /News/autohandler /News/Politics/column.mc Here is some very simple code to illustrate the above publish. Here is the code for the root level autohandler: <!-- Code for /autohandler --> <html> <head><title>The Site</title></head> <body bgcolor='white'> % $burner->chain_next; </body> </html> Here is some code for the 'News' category autohandler: <!-- Code for /News/autohandler --> <h1>Weekly News</h1> <table><tr><td width=570> % $burner->chain_next; </td></tr></table> Here is some code for the 'Column' template: <!-- Code for /News/Politics/column.mc --> <b><% $story->get_title %></b> % my $n = 1; % while (my $p = $element->get_data('paragraph', $n++)) { <p><% $p %></p> % } Now, if we publish a Column story that has a title 'Man Bites Dog' and contains two paragraphs of text, the resulting published story would look like: <!-- Code for /autohandler --> <html> <head><title>The Site</title></head> <body bgcolor='white'> <!-- Code for /News/autohandler --> <h1>Weekly News</h1> <table><tr><td width=570> <!-- Code for /News/Politics/column.mc --> <b>Man Bites Dog</b> <p>Earlier today a man was reported biting a dog</p> <p>The dog was unavailable for comment</p> </td></tr></table> </body> </html> Elements in the Bricolage system can be marked as "paginated". Such Elements can be used to re-execute templates over successive pages, automatically outputting a new file each time. All templates output to a file named for the "File Name" and "File Extension" properties of the current Output Channel. Templates based on paginated Elements will simply append a number to the output file for each additional time they run. So a story containing 4 paginated elements will (assuming that the "File Name" and "File Extension" properties are set to "index" and "html", respectively), upon publication, produce the files: There are two ways to display templates based on paginated elements. The first is the normal 'display_element' method that is used to display all elements. The second is by using the 'display_pages' method.

Using 'display_element'
When displaying a paginated element using 'display_element', the output of the page elements will not trigger output of the data to a new file. Thus display_element() handles paginated Elements in the same way it handled all other Elements. Use this approach to output all pages on a single page -- in print versions of a story, for example. For example, say we have a 'Column' story that contains 'Page' elements. These 'Page' elements are paginated elements. Here is some template code for 'Page' (assume page contains just paragraphs): <!-- Code for 'page.mc' --> % my $n = 1; % while (my $p = $element->get_data('paragraph', $n++)) { <p><% $p %></p> % } Here is some template code for 'column': <!-- Code for 'column' --> <html> <head><title><% $story->get_title %></title></head> <body> % may $n = 1; % while (my $pg = $element->get_container('page', $n++)) { % $burner->display_element($pg); % } </body> </html> These templates will work exactly as you would expect: each page is output as any other element is output, and the results of all this output are included in a single page: index.html : <!-- Code for 'column' --> <html> <head><title>Our Column Title</title></head> <body> <!-- Code for 'page.mc' --> <p>page 1 para 1 content</p> <p>page 1 para 2 content</p> <!-- Code for 'page.mc' --> <p>page 2 para 1 content</p> <p>page 2 para 2 content</p> <!-- Code for 'page.mc' --> <p>page 3 para 1 content</p> <p>page 3 para 2 content</p> </body> </html>

Using 'display_pages'
Use the "display_pages()" method to create new files for each page element. The "display_pages()" method takes one or more names of paginated elements: % $burner->display_pages('page', 'another_page'); When a story is published, the entire template containing the call to "display_pages()" is run once for every instance of the named element passed to "display_pages()". So if we have a 'Column' containing 3 'Page' elements and no "Another Page" elements, the 'Column' template will be run 3 times. Each time it runs, the "display_pages()" method will run the 'Page' template with each succeeding page. So we can use our earlier example with just a minor change to produce the output we want: <!-- Code for 'column' --> <html> <head><title><% $story->get_title %></title></head> <body> % $burner->display_pages('page'); </body> </html> The code for the 'page' template: <!-- Code for 'page.mc' --> % my $n = 1; % while (my $p = $element->get_data('paragraph', $n++)) { <p><% $p %></p> % } This approach will output the following files: index.html: <!-- Code for 'column' --> <html> <head><title>Our Column Title</title></head> <body> <!-- Code for 'page.mc' --> <p>page 1 para 1 content</p> <p>page 1 para 2 content</p> </body> </html> index1.html: <!-- Code for 'column' --> <html> <head><title>Our Column Title</title></head> <body> <!-- Code for 'page.mc' --> <p>page 2 para 1 content</p> <p>page 2 para 2 content</p> </body> </html> index2.html: <!-- Code for 'column' --> <html> <head><title>Our Column Title</title></head> <body> <!-- Code for 'page.mc' --> <p>page 3 para 1 content</p> <p>page 3 para 2 content</p> </body> </html> Autohandlers will work with multi-page templates using display_pages. Certain types of elements can have other stories or media objects related to them. If an element has related stories or media you can get those objects using the methods: % my $rel_story = $element->get_related_story; % my $rel_media = $element->get_related_media; The $rel_story object will have all the methods that the exported $story object has since they are the same object. To get the top element of the story you can use the method 'get_tile': % my $rel_element = $rel_story->get_tile; The $rel_element object is the same object as the exported $element object and has all the same methods. The $rel_media object has the same methods as $story does but adds a few more specific to media objects. They are: % my $uri = $rel_media->get_uri; % my $type = $rel_media->get_media_type; % my $size = $rel_media->get_size; The method 'get_uri' returns the URI of the published media object. This can be used like: <img src="<% $rel_media->get_uri %>"> in your templates. The 'get_media_type' method returns the mime type of the media object. These are text values of the form 'image/jpeg'. The 'get_size' method will return the file size of the media file. Bricolage extends HTML::Mason tags so that template developers can write blocks of code that only run in certain contexts. There are four contexts in which a template is run and in each context, code can be executed: A template developer would like that all code will be syntax checked, have some special debug messages to appear to the story editor previewing the article; have some special code that only runs at publish time; have some cookie handling code that only runs in realtime, when an actual user is requesting the page. With HTML::Mason a developer could use "<%text>" tags, but they would not get syntax checked by Bricolage, and could only run in the frontend servers. With Bricolage Custom Tags, tags are processed according to the context. Bricolage supports the following Mason Custom tags: Only runs in syntax checking. Useful to comment out codes that you still want to check as valid Perl code, but not to be used in any other context. Code inside a "<%preview>" tag gets removed when published, but gets executed when a preview is done. This is equivalent to the default Bricolage behaviour, since the code is run in preview and publish mode. Can be quite useful in complex templates where a developer wants to explicitly tell the context. Code kept during the publish process, runs in preview mode. In order to simplify the template developer's life to some degree, we've created configuration directives that, when turned on, will add a global variable to all templates called $writer. This variable contains an XML::Writer object that can be used to output XML from your templates. Of course one can simply use Mason for outputting XML, but if one needs to output a lot of XML, one may wish to take advantage of the convenience interface provided by XML::Writer. To use $writer, simply set the INCLUDE_XML_WRITER configuration to "Yes" in bricolage.conf. Another directive, XML_WRITER_ARGS, can be used to pass extra arguments to the XML::Writer instantiator (see XML::Writer for details of the arguments it accepts -- just don't use the OUTPUT argument, as Bricolage needs to set this argument in order to ensure that all XML is properly output). Then all you need do is use $writer inside a %perl section. Here's an example: <%perl> $writer->startTag("greeting", "class" => "simple"); $writer->characters("Hello, world!"); $writer->endTag("greeting"); $writer->end(); </%perl> For more details on how to use XML::Writer, see XML::Writer. Since Bricolage's templates are all Perl-based, it's of course possible to load Perl modules and use them in your templates. You might want to use DBI to pull data in from another database, or use XML::RSS to burn in headlines from another site. This is one of the great strengths of Bricolage's templating architecture, and we strongly urge you to exploit this strength. However, it's not efficient to load modules directly in your templates, since every time they're run, they'll load the template into a separate Apache process, and therefore use more system resources (memory). It would be better to load all of the modules you'll need at Bricolage startup time, so that they get shared across processes and therefore use less memory. With the PERL_LOADER configuration directive in bricolage.conf, you can do just that. The PERL_LOADER directive takes, on a single line, an arbitrary line of Perl code, and executes it in the namespace reserved for Mason templates (sorry, this functionality isn't available for HTML::Template templates at this time). So you can execute a whole bunch of Perl "use" statements here, and all the modules will be available to you in your templates without needing to reload them there. Here's an example: PERL_LOADER = use XML::RSS; use CGI qw(:standard); use Apache::DBI; Garth Webb <garth@perijove.com> Bric, Bric::AdvTemplates, Bric::Biz::AssetType, Bric::Biz::Asset::Formatting, Bric::Biz::Asset::Business::Story, Bric::Biz::Asset::Business::Parts::Tile, Bric::Biz::Asset::Business::Parts::Tile::Data, Bric::Biz::Asset::Business::Parts::Tile::Container, Bric::Util::Burner