Last Updated:

Generate site content using the Template Toolkit - perl

Template Toolkit - perl

DESCRIPTION

This tutorial is an introduction to the Template Toolkit and shows some typical ways to use this library to generate site content. The tutorial describes how to generate static pages using the tpage and ttree utilities and dynamic pages using CGI scripts and Apache/mod_perl handlers.

An introduction, brief description, and explanation using examples of the various features of the Template Toolkit. For more information, see template, template::Manual, and subsections such as

     perldoc Template                  # using the Template.pm module
   perldoc Template::Manual # documentation content
   perldoc Template::Manual::Config # configuration options

The documentation also comes in HTML format (or more precisely, in the form of HTML templates). See the 'docs' directory of the package for more information on building html documentation.

If you are reading this guide as part of the HTML documentation, then you do not need to worry about it. You can sit down, relax and enjoy reading the rest of the manual...

INTRODUCTION

The Template Toolkit is a set of Perl modules that collectively implement a template processing system. In this context, a template is a text document containing special markup tags called 'directives'. A directive is an instruction to the template processor to perform some action and replace the directive in the document with the result of that action. Directives are used to define or output the value of a variable, perform circular operations on arrays (FOREACH), perform conditional operations (IF/UNLESS/ELSE), include and execute other patterns (INCLUDE), and so on.

Otherwise, the document is a plain text file and can have any content (e.g. HTML, XML, RTF, LaTeX, etc.). Directives are included in the document inside special tagging tags. By default, [% and %] are used as such tags, but they can be replaced with others through the module configuration options. An example of an HTML document with additional Template Toolkit directives.

   [% INCLUDE header
      title = 'This is an HTML example'
   %]
   <h1>Some Interesting Links</h1>
   [% webpages = [
         { url => 'http://foo.org', title => 'The Foo Organisation' }
         { url => 'http://bar.org', title => 'The Bar Organisation' }
      ]
   %]
   Links:
   <ul>
   [% FOREACH link = webpages %]
      <li><a href="[% link.url %]">[% link.title
%]</a>
   [% END %]

   </ul>
   [% INCLUDE footer %]

This example shows how the INCLUDE directive is used to load, process, and include separate 'header' and 'footer' templates in the current document. These files might look like this:

header:

    <html>
    <head>

    <title>[% title %]</title>
    </head>

    <body bgcolor="#ffffff">

footer:

    <hr>
    <center>
    &copy; Copyright 2000 Me, Myself, I
    </center>
    </body>
    </html>

The example also shows how to use the FOREACH directive to construct a table of references using a loop through the 'webpages' array. We define an array inside the template that contains several hash references consisting of two elements 'url' and 'title'. The FOREACH directive traverses the array using 'link' as a pointer to each element in the array (hash reference). The directives [%link.url %] and [%link.title %] then retrieve the appropriate hash values and insert them into the document.

The following sections show other ways to define the data that is used in the templates.

STATIC PAGE GENERATION

After creating the template, we can now process it and get some output. The fastest and easiest way to do this is to use the tpage utility. It comes as part of the Template Toolkit library and must be installed in the perl executable directory.

If you saved your template as a 'mypage.html' file, you need to run the command:

    tpage mypage.html

This command will start processing the template, and the output will be sent to STDOUT (i.e. fly across your screen). You may redirect the output to a file, but be careful not to specify the same file name as the template name, otherwise you will overwrite it. You can use various extensions for templates and regular files. For example, for templates, use the extension '.atml' (apparently 'Another Template Markup Language'?) and the usual '.html' for output files (assumed that you are creating HTML). Or, you can redirect the output to another directory, for example

    tpage mypage.atml > mypage.html
    tpage templates/mypage.html > html/mypage.html

The tpage utility is quite simple and is designed to allow you to simply process the template without having to write perl code. A more flexible ttree utility is described below, but for now we only need the output generated by the command from the example above (for brevity, the empty lines have been removed):

    <html>
    <head>
    <title>This is an HTML example</title>
    </head>

    <body bgcolor="#ffffff">

    <h1>Some Interesting Links</h1>

    Links:
    <ul>
       <li><a href="http://foo.org">The Foo Organisation</a>

       <li><a href="http://bar.org">The Bar Organisation</a>
    </ul>

    <hr>

    <center>

    &copy; Copyright 2000 Me, Myself, I
    </center>

    </body>
    </html>

The header and footer templates are included (assuming you created them and are in the current directory) and the link data is converted to a list in the HTML file.

The ttree utility, also included as part of the Template Toolkit, provides a more flexible way to handle templates. When you first run the utility, you will be prompted to create a configuration file, in most cases called '.ttreerc' in your home directory. Answer 'y' to create the file.

The ttree documentation describes how you can change the location of this file, and also describes the syntax and purpose of the various options in this file. The sample configuration file contains comments that can also help.

    perldoc ttree
    ttree -h

In short, the configuration file describes the directories in which the templates are stored (src) and where the corresponding files (dest) will be stored, and some other directories (lib), which may contain template files that you plan to include using the INCLUDE directive in your original templates. Also in the configuration file, you can specify parser options (such as 'verbose' and 'recurse') and use regular expressions to limit the list of files that need to be processed by the parser (ignore, accept) or specify the files that need to be copied (copy) instead of processing.

Example of a .ttreerc file:

$HOME/.ttreerc:

    verbose
    recurse
    # directory where other ttree config files are stored
    cfg = ~/.ttree
    src  = ~/websrc/src
    lib  = ~/websrc/lib
    dest = ~/public_html/test
    ignore = \b(CVS|RCS)\b
    ignore = ^#

You can create many other configuration files and save them in the directory specified in the 'cfg' option as shown above. You can then point the ttree command to the '-f filename' option to use files from this directory.

When you run the script, it compares all files in the 'src' directory (including files in subdirectories if the 'recurse' option is set) with files in the 'dest' directory. If the output file does not exist or it has an earlier modification time than the corresponding source file (template), the source file will be processed by the parser and the output will be written to the output file. The '-a' option forces the script to process all files regardless of the modification time.

The script does not process files from the 'lib' directory, but it does include the directory in a variable INCLUDE_PATH passed to the template engine so that it can locate the files specified in the INCLUDE or PROCESS directives. Thus, the 'lib' directory is a good place to store elements used in templates such as header, footer, etc., which are not documents in the full sense of the word.

You can also specify various Template Toolkit options in the configuration file. For more information, refer to the ttree documentation and quick help ('ttree -h'). For example

$HOME/.ttreerc:

    pre_process = config
    interpolate
    post_chomp

The 'pre_process' option allows you to specify the template that will be processed before each file. Not surprisingly, there is also a 'post_process' option to add a template to be processed after the file has been processed. In the snippet above, we specify that the 'config' template will be used as a prefix template. We can create this file in the 'lib' directory and use it to define some common variables, including previously defined links to web pages and which we may want to use in other templates. Also in this file we can include the head and title of the HTML document, or menus, which will then be included at the beginning of any template, but for now we will use a separate 'header' file for this purpose.

$lib/config:

    [% root     = '~/abw'
       home     = "$root/index.html"
       images   = "$root/images"

       email    = 'email@domain.com'
       graphics = 1
       webpages = [
         { url => 'http://foo.org', title => 'The Foo Organsiation' }
         { url => 'http://bar.org', title => 'The Bar Organsiation' }
       ]
    %]

Once you have prepared the 'header' and 'footer' templates from the example above and placed them in the 'lib' directory, you can start creating web pages like the one below in your 'src' directory and process them with ttree.

$src/newpage.html:

    [% INCLUDE header
       title = 'Another Template Toolkit Test Page'
    %]
    <a href="[% home %]">Home</a>
    <a href="mailto:[% email %]">Email</a>
    [% IF graphics %]
    <img src="[% images %]/logo.gif" align=right width=60 height=40>
    [% END %]
    [% INCLUDE footer %]

Here we have shown how to use predefined variables as flags to enable various features (e.g. 'graphics') and define common variables such as email address, home page URLs, picture directory, etc. This approach allows you to define these variables once, and thus maintain their integrity on all pages and ensure the ability to easily change their values to new ones.

After running ttree, you should see output similar to the following (assuming the verbose flag is set).

  ttree 1.14 (Template Toolkit version 1.02a)
        Source: /home/abw/websrc/src
   Destination: /home/abw/public_html/test
  Include Path: [ /home/abw/websrc/lib ]
        Ignore: [ \b(CVS|RCS)\b, ^# ]
          Copy: [  ]
        Accept: [ * ]
    + newpage.html

'+' before 'newpage.html' indicates that the file has been processed by the processor and the output is stored in the destination directory. If you run this command again, you will see that in this output line, instead of '+', '-' will be displayed and the reason why the file was not processed by the processor will be given.

    - newpage.html                     (not modified)

The processor detected a 'newpage.html' page in the destination directory with a modification date greater than that of the original file, and did not waste time reprocessing it. To force all source files to be processed, use the '-a' option. Optionally, you can use one or more source files as command-line arguments for the ttree utility:

    ttree newpage.html

The file received after processing by the processor will look like this.

$dest/newpage.html:

    <html>
    <head>
    <title>Another Template Toolkit Test Page</title>
    </head>

    <body bgcolor="#ffffff">

    <a href="~/abw/index.html">Home</a>
    <a href="mailto:email@domain.com">Email</a>
    <img src="~/abw/images/logo.gif" align=right width=60 height=40>

    <hr>

    <center>
    &copy; Copyright 2000 Me, Myself, I
    </center>

    </body>
    </html>

You can add as many documents as you want to the 'src' directory and ttree will process them similarly. In this way, you can completely build static site content with a single command. An additional benefit is that you can be sure of the integrity of the links, the uniform header style, and all other components implemented through common templates and variables.

DYNAMIC PAGE GENERATION USING CGI SCRIPTS

The Template module provides a simple interface to the Template Toolkit library for use in CGI scripts and Apache/mod_perl handlers. Simply include the Template module in your scripts using the 'use' command, create an instance of the object using the new() method, and then call the object's method process(), passing the name of the template file as a parameter. The second parameter to pass is a reference to the hash with the variables we want to make available in the template:

    #!/usr/bin/perl -w
    use strict;
    use Template;
    my $file = 'src/greeting.html';
    my $vars = {
       message  => "Hello World\n"
    };
    my $template = Template->new();
    $template->process($file, $vars)
        || die "Template process failed: ", $template->error(),
"\n";

In order for our scripts to work with the templates from the previous examples, we can add some configuration options in the constructor call to inform the parser about our environment:

   my $template->new({
         # where to look for template files
         INCLUDE_PATH => '/home/abw/websrc/src:/home/abw/websrc/lib',
         # preprocess lib/config to define additional variables
         PRE_PROCESS => 'config',
     });

Note that here we define the 'config' file in the PRE_PROCESS option. This means that the templates we'll be processing can use the same global variables that we defined earlier for use in static pages. We don't need to re-define them in the script. However, we can provide additional script-specific data and functionality through a hash of variables passed to the process() method.

Hash elements can contain plain text or other variables, array references, other hashes, functions, or objects. The Template Toolkit will automatically apply the correct procedure to access different types of data when you use variables in the template.

Here's a more detailed example for further consideration. Among the various template variables defined in the '$vars', we created a reference to a CGI object and a reference to the 'get_user_projects' function.

    #!/usr/bin/perl -w
    use strict;
    use Template;
    use CGI;
    $| = 1;
    print "Content-type: text/html\n\n";
    my $file = 'userinfo.html';
    my $vars = {
        'version'  => 3.14,
        'days'     => [ qw( mon tue wed thu fri sat sun ) ],
        'worklist' => \&get_user_projects,
        'cgi'      => CGI->new(),
        'me'       => {
            'id'     => 'abw',
            'name'   => 'Andy Wardley',
        },
    };
    sub get_user_projects {
        my $user = shift;
        my @projects = ...   # fill with data
        return \@projects; }
    my $template = Template->new({
        INCLUDE_PATH => '/home/abw/websrc/src:/home/abw/websrc/lib',
        PRE_PROCESS  => 'config',
    });
    $template->process($file, $vars)
        || die $template->error();

Below is an example of a template that we can use in a script.

$src/userinfo.html:

    [% INCLUDE header
       title = 'Template Toolkit CGI Test'
    %]
    <a href="mailto:[% email %]">Email [% me.name
%]</a>
    <p>This is version [% version %]</p>
    <h3>Projects</h3>
    <ul>
    [% FOREACH project = worklist(me.id) %]
       <li> <a href="[% project.url %]">[% project.name
%]</a>

    [% END %]
    </ul>
    [% INCLUDE footer %]

This example shows how you can separate the perl implementation (code) and the view (HTML), which not only makes it easier to maintain isolation, but also allows you to reuse existing template elements such as headers, footers, etc. By using templates to organize the output of CGI scripts, you can ensure the same integrity that we achieved when building static pages using ttree and other means.

Moreover, we can modify our script in such a way that it will process any file from a set of templates that use a particular model. A CGI script to support a database of users can, for example, process one template for a form for introducing new users, the same form with some set variables to update existing user records, and a third template to list all users of the system, etc. You can implement in perl code the logic of your application, and then choose one or another template for that To provide the necessary output for the current state of the application.

DYNAMIC PAGE GENERATION VIA THE APACHE/MOD_PERL ENGINE

NOTE: You can download the Apache::Template module from the CPAN network, which provides a simple and easy-to-use interface between Apache/mod_perl and the Template Toolkit. At the time of writing this document, the first release (0.01) has been available, which offers only the most basic features, but it implements most, if not all, of what is described below. You should avoid having to write your own handler. However, in many cases, you may need to write your own handler for your own needs, and this section will show you where to start.

The Template module can be used in the usual way from the Apache/mod_perl handler. The following is a typical snippet of the Apache httpd.conf configuration file:

    PerlModule CGI;
    PerlModule Template
    PerlModule MyOrg::Apache::User
    PerlSetVar websrc_root   /home/abw/websrc
    <Location /user/bin>

        SetHandler     perl-script
        PerlHandler    MyOrg::Apache::User
    </Location>

This fragment defines the URL '/user/bin', all requests to which will be passed to the handler() method of the MyOrg::Apache::User module. This module might look something like this:

    package MyOrg::Apache::User;

    use strict;
    use vars qw( $VERSION );
    use Apache::Constants qw( :common );
    use Template qw( :template );
    use CGI;

    $VERSION = 1.59;

    sub handler {
        my $r = shift;
           my $websrc = $r->dir_config('websrc_root')
            or return fail($r, SERVER_ERROR,
                           "'websrc_root' not specified");
          my $template = Template->new({
             INCLUDE_PATH => "$websrc/src/user:$websrc/lib",
             PRE_PROCESS => 'config',
             OUTPUT => $r, # direct output to Apache request object
         });

         my $params = {
             uri => $r->uri,
             cgi => CGI->new,
         };

         # use path_info to determine the pattern to be processed
         my $file = $r->path_info;
         $file =~ s[^/][];

         $r->content_type('text/html');
         $r->send_http_header;

         $template->process($file, $params)
             || return fail($r, SERVER_ERROR, $template->error());

         return OK;
     }

     sub-fail {
         my ($r, $status, $message) = @_;
         $r->log_reason($message, $r->filename);
         return $status;
     }

The handler takes the request as a parameter and uses it to determine the 'websrc_root' value from the configuration file. This value is then used to determine the INCLUDE_PATH when the Template object is created. Next, we get the URI from the request and create a CGI object. Both objects are defined as template variables.

The template name itself is retrieved from the query PATH_INFO. In our example, it will be part of the URL coming after '/user/bin', that is, for '/user/bin/edit', the template will be the 'edit' file located in the "$websrc/src/user" Next, we display the headers and process the template. All output is sent directly to the print() method of the Apache request object.

USE OF EXTENSION MODULES (PLUGINS)

As we have already shown, when creating dynamic content using CGI scripts or Apache/mod_perl handlers, you can bind data and functions from Perl to template variables. The Template Toolkit also provides an interface for creating extension modules (plugins) that allows you to define this additional data and/or functionality in a separate module and then load it and use it for its intended purpose using the USE directive.

The main advantage of this approach is that you can use such an extension in any templates, even those used when building static content using tpage or ttree. You don't have to specifically write a wrapper in Perl just to load the module and make it available through a template's hash of variables.

Let's demonstrate this principle with the example of a DBI plugin written by Simon Matthews <sam@knowledgepool.com>. You can create this template in your 'src' directory, process it with ttree and see the result. Of course, this example depends on having a suitable SQL database, but you can adapt it to your capabilities, or simply consider it as an example of the capabilities of the Template Toolkit.

    [% INCLUDE header
       title = 'User Info'
    %]

    [% USE DBI('dbi:mSQL:mydbname') %]

    <table border=0 width="100%">
    <tr>
      <th>User ID</th>
      <th>Name</th>

      <th>Email</th>
    </tr>

    [% FOREACH user = DBI.query('SELECT * FROM user ORDER BY id') %]
    <tr>
      <td>[% user.id %]</td>

      <td>[% user.name %]</td>
      <td>[% user.email %]</td>
    </tr>
    [% END %]

    </table>

    [% INCLUDE footer %]

A plugin is a regular Perl module located in a specific location and consistent with known standards so that the Template Toolkit can find it and automatically load it. You can create your own plugin by inheriting from the Template::P lugin module.

The following is an example that defines some data ('foo' and 'people') and also an object method ('bar'). For lack of a better word, we'll call the plugin 'FooBar' and create it in the 'MyOrg::Template::P lugin::FooBar' package. We added 'MyOrg' to the package's common name 'Template::P lugin::*' to avoid naming conflicts with existing plugins.

A blank of the module can be made using the Perl utility h2xs:

    h2xs -A -X -n MyOrg::Template::Plugin::FooBar

This command will create a directory structure that reflects the name of the package with a set of files that make up your new module. You can edit the FooBar.pm to make it look something like this:

    package MyOrg::Template::Plugin::FooBar;
    use Template::Plugin;
    use vars qw( $VERSION );
    use base qw( Template::Plugin );
    $VERSION = 1.23;
    sub new {
        my ($class, $context, @params) = @_;
   bless {
            _CONTEXT => $context,
            foo      => 25,
            people   => [ 'tom', 'dick', 'harry' ],
        }, $class;
    }
    sub bar {
        my ($self, @params) = @_;        
# ...does something...        
return $some_value; }

As is customary in Perl, the plugin constructor new() gets the class name as the first parameter. The next parameter is a reference to an instance of the Template::Context object. Right now, you don't have to worry about this object. This is the main object of the Template Toolkit parser. It provides access to the functionality of the processor and some plugins may need to interact with it. We don't need it yet, but we'll still keep this reference in the '_CONTEXT' object variable. An initial underline is a variable naming convention that indicates that the variable is private and the Template Toolkit will not attempt to access that variable. The other definable variables 'foo' and 'people' are regular data that will be accessible from templates using this plugin. Following the reference to the Template::Context object are additional parameters that are specified with the USE directive, such as the data source, 'dbi:mSQL:mydbname', which we used in the previous example with the DBI plug-in.

If you used h2xs to create a preset of a module, you already have a Makefile.PL file and can follow the familiar procedure of building and installing the module. Don't forget to add tests to test.pl!

    perl Makefile.PL
    make
    make test
    make install

If you did not or could not install the plugin in the usual location for storing Perl modules (for example, if you do not have enough privileges), you can set the PERL5LIB environment variable to specify another location where the module is installed. If you're using ttree, you can add the following line to your configuration file instead. This would be equivalent to adding '/path/to/modules' to the @INC array.

$HOME/.ttreerc:

    perl5lib = /path/to/modules

Another additional line needs to be added to tell the parser the new package name we created for our plugins:

$HOME/.ttreerc:

    plugin_base = 'MyOrg::Template::Plugin'

If you are writing a script that uses the Template module directly, this variable can be passed as a configuration parameter when the object is created.

    use Template;
    my $template = Template->new({
        PLUGIN_BASE => 'MyOrg::Template::Plugin'
    });

Now we can create a template using this plugin:

    [% INCLUDE header
       title = 'FooBar Plugin Test'
    %]
    [% USE FooBar %]
    Some values available from this plugin:
      [% FooBar.foo %] [% FooBar.bar %]
    The users defined in the 'people' list:
    [% FOREACH uid = FooBar.people %]

      * [% uid %]
    [% END %]
    [% INCLUDE footer %]

The 'foo', 'bar' and 'people' members from the FooBar plugin are automatically converted to the corresponding variables or method calls to the underlying object.

Using this approach, you can create a single module that contains all the functionality of the application, which can then be used on demand in any template. The simple interface between template directives and module objects allows you to create complex, dynamic content from a few simple templates without knowing anything about the underlying implementation.