=pod =head1 Name Bric::AdvTemplates - Template Producing: Advanced Topics =head1 Version $LastChangedRevision$ =head1 Date $LastChangedDate: 2005-12-19 21:39:08 +0100 (Mon, 19 Dec 2005) $ =head1 Introduction This document discusses some templating techniques that require existing knowledge of the Bricolage publishing system. The text and examples assume that you have already read and understood the L. =head1 Utility Templates Bricolage supports three different types of templates. We've already discussed category templates and element templates in L. Here we introduce the third: utility templates. Utility templates are associated with no element types, unlike element templates. They I associated with categories, but only to manage where they're stored on the file system; they of course do not execute as part of a category template chain. The purpose of utility templates is to create libraries of commonly reused code, so that you can maintain that code in one place, rather than copy and paste it into many different element and/or category templates. Most Bricolage templaters create a category structure just for utility templates. For example, you might have a category called F, with a F subcategory of templates specific to XHTML output. Utility templates are never executed by Bricolage. Rather, you execute them using the standard calling syntax of your templating architecture. For example, say that you want to have a simple include to pull in at the end of a story, and you support many different types of stories. The simple thing to do is to create a utility template. In Mason, I might create a utility template named F that looks like this: <%args> $license
Licensed under the <% $license %> license
\ To call it, I just use native Mason syntax at the bottom of a story template: <& /util/xhtml/license.mc, license => 'Creative Commons' &> In Template Toolkit, the utility template would be named F and looks like this:
Licensed under the [% license %] license
To call it I just use native TT syntax at the bottom of a story template: [% INCLUDE util/xhtml/license.tt license = 'BSD' -%] In PHP, the utility template would be named F and looks like this: Licensed under the $license license\n"; } ?> As you can see, it's a bit different in PHP, as the file doesn't actually output anything itself, but declares a function that does. We just need to include the template and then call the function: In HTML::Template templates, it's not possible to pass arguments to an included F<.tmpl> template; use a F<.pl> template to define functions, if necessary. But you can of course include another template. So if the utility template, named F, looked something like this:
Licensed under the Creative Commons license
You can include it like so: Of course these are contrived examples. But hopefully the utility (no pun intended) of utility templates is apparent. =head1 Multipage Stories As discussed in L, element types in Bricolage can be marked as "paginated." Such element types can be used to re-execute their element 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 element types will simply append a number to the output file for each additional time they run. For example, a story containing four paginated elements will (assuming that the "File Name" and "File Extension" properties are set to "index" and "html", respectively), upon publication, produce the files: =over =item F =item F =item F =item F =back Sometimes you'd rather not use the numbers, but some other identifier for successive page file names. In such a case, you can use the burner object's C method to set alternatives. If there are more pages than extensions, the burner will start using numbers again. For example, this statement: $burner->set_page_extensions('', '_two', '_three'); Would cause a four-page story to output the following file names; =over =item F =item F =item F =item F =back Now, regardless of the file names, there are two ways to execute templates based on paginated elements. The first is the normal C method, and the second is the C method. =head2 Using C When formatting a paginated element using C, the page elements will B trigger the output of data to a new file. Thus C handles paginated elements in the same way that it handles all other elements. Use this approach to output all pages on a single page, such as for the print version of a story. For example, say we have a "Column" story that contains paginated "Page" subelements. Here is some Mason template code for "Page" (assume that it contains only paragraphs): % foreach my $p ($element->get_elements('paragraph')) {

<% $p->get_value %>

% } In Template Toolkit, that's: [% FOREACH e = element.get_elements('paragraph') -%]

[% e.get_value %]

[% END -%] And in PHP, it's: get_elements('paragraph') as $e) { echo '

', $e->get_value(), "

\n"; } ?> And now, some simple template code for "Column." Mason first: <% $story->get_title %> % $burner->display_element($_) for $element->get_elements('page'); Next, Template Toolkit: [% story.get_title() %] [% FOREACH e = element.get_elements('page') -%] [% burner.display_element(e) %] [% END -%] And finally, of course, PHP: <?= $story->get_title() ?> get_elements('page') as $e) { $burner->display_element($e); } ?> Now, 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. For a column story with three "Page" subelements, the output would look something like this: Our Column Title

page 1 para 1 content

page 1 para 2 content

page 2 para 1 content

page 2 para 2 content

page 3 para 1 content

page 3 para 2 content

=head2 Using C The C method, on the other hand, creates new files for each page element. The C method takes a single key name or an array reference of key names of paginated elements: % my @page_key_names = qw/page another_page/; % $burner->display_pages(\@page_key_names); Here, pages with key names "page" and "another_page" are published. The template with the call to C, as well as any surrounding category templates, is run multiple times, once for every instance of a paginated element identified by the key names argument. So if we have a "Column" story containing three "Page" subelements and no "Another Page" elements, the "Column" template will be executed three times. Each time it runs, the C method will execute the "Page" template with each succeeding page. So we can use our earlier example with just a minor change to the Column template to produce the output we want. The Mason template is now: <% $story->get_title %> % $burner->display_pages([ qw(page another_page) ]); Template Toolkit now looks like this: [% story.get_title() %] [% burner.display_pages([ 'page', 'another_page' ]) %] And PHP looks like this: <?= $story->get_title() ?> display_pages(array( 'page', 'another_page' )); ?> Note that in each case, we've removed the C block with its call to C and substituted a call to C. The output is now in three files that look like this: index.html: Our Column Title

page 1 para 1 content

page 1 para 2 content

index1.html: Our Column Title

page 2 para 1 content

page 2 para 2 content

index2.html: Our Column Title

page 3 para 1 content

page 3 para 2 content

=head1 Including Related Stories and Media Certain types of container elements can have other stories or media objects related to them. If an element has a related story or media document (or both!), you can get at it using methods on the container element. In Mason: % my $rel_story = $element->get_related_story; % my $rel_media = $element->get_related_media; In Template Toolkit: [% rel_story = element.get_related_story %] [% rel_media = element.get_related_media %] In PHP: $rel_story = $element->get_related_story(); $rel_media = $element->get_related_media(); The related story object is of course in the same class as the global $story object. To get the top element of the related story, call the C method. The returned element is in the same class as the global $element object: It's the top-level container element for the related story. Related media are L objects. They inherit from the same class as the story class, namely L, but has a slightly different interface. These methods are the most notable: =over =item C Media objects are associated with only a single category, and so there is no distinction between a primary URI and secondary URIs. But thanks to their ability to be associated multiple output channels, they might have different output channel formats. You can therefore pass in an output channel object or ID to get the media document's URI for that output channel? As of Bricolage 1.10, there is also a C method in the media class to create parity with the interface of the story class. But, really, for creating URIs in Templates, you're best off using the C method of the burner class: my $uri = $burner->best_uri($rel_media)->as_string; =item C Returns a object describing the media type (a.k.a. the "MIME Type") of the media document. =back =head1 Bricolage Mason Custom Tags The Bricolage Mason burner 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 executed, and each context has a corresponding Mason tag: =over =item C<< <%chk_syntax> >> Only runs in syntax checking--that is, when saving a template in the Templating UI. This tag is useful to comment out code that you want to check as valid Perl code, but not to be used in any other context. <%chk_syntax>; print "This is a no-op.\n"; =item C<< <%preview> >> Code inside a C<< <%preview> >> tag executes only during previews, and is ignored by publishes. It can be useful for sending messages to the previewer or carrying out some action that should only be carried out during previews: <%preview>; $burner->throw_error('What are you thinking??') if $element->get_value('is_porn'); =item C<< <%publish> >> Code inside a C<< <%publish> >> block will only execute during a publish, and not during a preview. For example, you might want to create a record in a database only when a story is published: <%publish>; my $dbh = DBI->connect('DBI:mysql:database=mydb;host=db.example.com'); $dbh->do( 'INSERT INTO blog VALUES (?, ?)', undef, $story->get_title, '

' . join( '

', map { $_->get_value } $element->get_elements('paragraph') ) . '

' ); =item C<< <%realtime> >> This block is equivalent to Mason's C<< <%text> >> block. Any of its contents will be ignored by the burner and simply output to the file. Once distributed to a Mason-powered destination server, any Mason code inside the block will execute: <%realtime> <& /path/to/delivery_server/include.mhtml &> =back =head1 Formatting XML in the Mason burner In order to simplify the Mason template developer's life to some degree, we've created F configuration directives that, when enabled, will add a global variable to the Mason name space called $writer. This variable contains an L object that can be used to output XML from your templates. Of course you can simply use Mason for outputting XML, but if you need to output a B of XML, you might want to take advantage of the XML::Writer object. To use $writer, simply set the C F directive to "Yes". Another directive, C, can be used to pass extra parameters to the XML::Writer constructor (see L for details of the arguments it accepts -- just don't use the C parameter, 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 C<< <%perl> >> block. Here's an example: <%perl>; $writer->startTag('greeting', 'class' => 'simple'); $writer->characters('Hello, world!'); $writer->endTag('greeting'); $writer->end; See the L documentation for more details on how to use it. =head1 Using Other Perl Modules Since Bricolage's templates are all Perl-based (even the PHP templates run inside a PHP interpreter inside a Perl interpreter), it is of course possible to load Perl modules and use them in your templates. You might want to use the L to pull data in from another database, or use L 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 it. 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 C F configuration directive, you can do just that. The C directive takes, on a single line, an arbitrary line of Perl code, and executes it in the name space reserved for Mason, Template Toolkit, and PHP templates (any exports won't be available to HTML::Template templates, although any imported modules of course will be). So you can execute a whole bunch of Perl C 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; =head1 Author Garth Webb David Wheeler =head1 See Also =over =item L =item L =item L =item L =item L =item L =item L =item L =item L =item L =item L =item L =back =cut