Bric::AdvTemplates(3p)User Contributed Perl DocumentatioBric::AdvTemplates(3p)



Name
       Bric::AdvTemplates - Template Producing: Advanced Topics

Version
       $LastChangedRevision$

Date
       $LastChangedDate: 2005-12-19 21:39:08 +0100 (Mon, 19 Dec 2005) $

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
       Bric::Templates.

Utility Templates
       Bricolage supports three different types of templates. We've already
       discussed category templates and element templates in Bric::Templates.
       Here we introduce the third: utility templates.

       Utility templates are associated with no element types, unlike element
       templates. They are 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
       /util, with a /util/xhtml 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 /util/xhtml/license.mc that looks
       like this:

         <%args>
         $license
         </%args>
         <div>Licensed under the <% $license %> license</div>\

       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
       /util/xhtml/license.tt and looks like this:

         <div>Licensed under the [% license %] license</div>

       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 /util/xhtml/license.php and
       looks like this:

         <?php
         function license($license = "GPL") {
             echo "<div>Licensed under the $license license</div>\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:

         <?php
            include_once("util/xhtml/license.php");
            license('BSD');
         ?>

       In HTML::Template templates, it's not possible to pass arguments to an
       included .tmpl template; use a .pl template to define functions, if
       necessary. But you can of course include another template. So if the
       utility template, named /util/xhtml/license.tmpl, looked something like
       this:

         <tmpl_if include_license>
           <div>Licensed under the Creative Commons license</div>
         </tmpl_if>

       You can include it like so:

         <tmpl_include name="util/xhtml/license.tmpl">

       Of course these are contrived examples. But hopefully the utility (no
       pun intended) of utility templates is apparent.

Multipage Stories
       As discussed in Bric::Templates, 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:

       index.html
       index1.html
       index2.html
       index3.html

       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 "set_page_extensions()" 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;

       index.html
       index_two.html
       index_three.html
       index1.html

       Now, regardless of the file names, there are two ways to execute
       templates based on paginated elements. The first is the normal
       "display_element()" method, and the second is the "display_pages()"
       method.

   Using "display_element()"
       When formatting a paginated element using "display_element()", the page
       elements will not trigger the output of data to a new file. Thus
       "display_element()" 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):

             <!-- page -->
         % foreach my $p ($element->get_elements('paragraph')) {
             <p><% $p->get_value %></p>
         % }

       In Template Toolkit, that's:

             <!-- page -->
         [% FOREACH e = element.get_elements('paragraph') -%]
             <p>[% e.get_value %]</p>
         [% END -%]

       And in PHP, it's:

             <!-- page -->
         <?php
         foreach ($element->get_elements('paragraph') as $e) {
             echo '    <p>', $e->get_value(), "</p>\n";
         }
         ?>

       And now, some simple template code for "Column." Mason first:

           <html>
             <head><title><% $story->get_title %></title></head>
             <body>
           % $burner->display_element($_) for $element->get_elements('page');
             </body>
           </html>

       Next, Template Toolkit:

           <html>
             <head><title>[% story.get_title() %]</title></head>
             <body>
         [% FOREACH e = element.get_elements('page') -%]
             [% burner.display_element(e) %]
         [% END -%]
             </body>
           </html>

       And finally, of course, PHP:

           <html>
             <head><title><?= $story->get_title() ?></title></head>
             <body>
               <?php
               foreach ($element->get_elements('page') as $e) {
                   $burner->display_element($e);
               }
               ?>
             </body>
           </html>

       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:

         <html>
           <head><title>Our Column Title</title></head>
           <body>

             <!-- page -->
             <p>page 1 para 1 content</p>
             <p>page 1 para 2 content</p>

             <!-- page -->
             <p>page 2 para 1 content</p>
             <p>page 2 para 2 content</p>

             <!-- page -->
             <p>page 3 para 1 content</p>
             <p>page 3 para 2 content</p>

           </body>
         </html>

   Using "display_pages()"
       The "display_pages()" method, on the other hand, creates new files for
       each page element. The "display_pages()" 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 "display_pages()", 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 "display_pages()" 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:

           <html>
             <head><title><% $story->get_title %></title></head>
             <body>
           % $burner->display_pages([ qw(page another_page) ]);
             </body>
           </html>

       Template Toolkit now looks like this:

           <html>
             <head><title>[% story.get_title() %]</title></head>
             <body>
         [% burner.display_pages([ 'page', 'another_page' ]) %]
             </body>
           </html>

       And PHP looks like this:

           <html>
             <head><title><?= $story->get_title() ?></title></head>
             <body>
               <?php
           $burner->display_pages(array( 'page', 'another_page' ));
               ?>
             </body>
           </html>

       Note that in each case, we've removed the "foreach" block with its call
       to "display_element()" and substituted a call to "display_pages()". The
       output is now in three files that look like this:

       index.html:

         <html>
           <head><title>Our Column Title</title></head>
           <body>

             <!-- page -->
             <p>page 1 para 1 content</p>
             <p>page 1 para 2 content</p>

           </body>
         </html>

       index1.html:

         <html>
           <head><title>Our Column Title</title></head>
           <body>

             <!-- page -->
             <p>page 2 para 1 content</p>
             <p>page 2 para 2 content</p>

           </body>
         </html>

       index2.html:

         <html>
           <head><title>Our Column Title</title></head>
           <body>

             <!-- page -->
             <p>page 3 para 1 content</p>
             <p>page 3 para 2 content</p>

           </body>
         </html>

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
       "get_element()" 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 Bric::Biz::Asset::Business::Media objects.  They
       inherit from the same class as the story class, namely
       Bric::Biz::Asset::Busines, but has a slightly different interface.
       These methods are the most notable:

       "get_uri($oc)"
           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 "primary_uri()" 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 "best_uri()" method of the burner class:

             my $uri = $burner->best_uri($rel_media)->as_string;

       "get_media_type()"
           Returns a <LBric::Util::MediaType|Bric::Util::MediaType> object
           describing the media type (a.k.a. the "MIME Type") of the media
           document.

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:

       "<%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";
             </%chk_syntax>

       "<%preview>"
           Code inside a "<%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');
             </%preview>

       "<%publish>"
           Code inside a "<%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,
                 '<p>' . join(
                     '</p><p>',
                      map { $_->get_value } $element->get_elements('paragraph')
                 ) . '</p>'
             );
             </%publish>

       "<%realtime>"
           This block is equivalent to Mason's "<%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 &>
             </%realtime>

Formatting XML in the Mason burner
       In order to simplify the Mason template developer's life to some
       degree, we've created bricolage.conf configuration directives that,
       when enabled, will add a global variable to the Mason name space called
       $writer. This variable contains an XML::Writer 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 lot of XML, you might
       want to take advantage of the XML::Writer object.

       To use $writer, simply set the "INCLUDE_XML_WRITER" bricolage.conf
       directive to "Yes". Another directive, "XML_WRITER_ARGS", can be used
       to pass extra parameters to the XML::Writer constructor (see
       XML::Writer for details of the arguments it accepts -- just don't use
       the "OUTPUT" 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 "<%perl>" block. Here's an example:

         <%perl>;
         $writer->startTag('greeting', 'class' => 'simple');
         $writer->characters('Hello, world!');
         $writer->endTag('greeting');
         $writer->end;
         </%perl>

       See the XML::Writer documentation for more details on how to use it.

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 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 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 "PERL_LOADER" bricolage.conf configuration directive, 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 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 "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;

Author
       Garth Webb <garth@perijove.com>

       David Wheeler <david@kineticode.com>

See Also
       Bric::Templates
       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



perl v5.12.2                      2005-12-19            Bric::AdvTemplates(3p)
