HTML::Processor - HTML Template Processor

- NAME
- SYNOPSIS
- DESCRIPTION

- Module Defaults
- Object Construction
- Syntax
- Precedence
- Including external files
- Debugging
- Option blocks
- If / else blocks
- Loops

- Loop Options
- Loop If/Else
- Nested Loops

- Sorting
- Processing Variables

- variable concatenation
- variable math

- Processing the Template

- AUTHOR
- COPYRIGHT
- SEE ALSO

# NAME

HTML::Processor - HTML Template Processor

# SYNOPSIS

-perl

            use HTML::Processor;

            $tpl = new HTML::Processor;

            -or with config options-

            $tpl = new HTML::Processor ({
               debug     => "Normal",
               footprint => 1,
               clean     => 0
            });

            # data
            %animals = (
                mammals    => {
                   types => [qw(monkey lion zebra elephant)],
                   count => 120
                },
                fish       => {
                   types => [qw(swordfish shark guppy tuna marlin tunny)],
                   count => 85
                },
                reptiles   => {
                   types => [qw(monitor python crocodile tortoise)],
                   count => 25
                },
                birds      => {
                   types => [qw(eagle pigeon kite crow owl sparrow)],
                   count => 57
               }

            );

            # create parent loop object
            my $animals = $tpl->new_loop("animals");
            foreach my $animal_type( keys %animals){
               # add data to the parent loop
               $animals->array("animal_type", $animal_type);
               $animals->array("count", $animals{$animal_type}{ count });

               # create new nested loop object 'keyed' on
               # the parent via $animal_type
               my $types = $tpl->new_loop("types", $animal_type);
               foreach my $type ( @{ $animals{$animal_type}{types} }){
                  # populate each 'child' loop
                  $types->array("type", $type);
               }
            }
            # set variables
            $tpl->variable("what", "ANIMALS");
            $tpl->variable("count", 2);

            # process and print parsed template
            print $tpl->process("templates/animals.html");

-html

            <html>
            <head>
                    <title>Sample</title>
            </head>
            <body>
            [TPL variable='what']:<br>
            <table width="200">
            [TPL LOOP name='animals']
               <tr>
                  <td>[TPL array='animal_type'] [[TPL array='count']]</td>
               </tr>
               <tr>
                  <td align="right">
                  [TPL LOOP name='types']
                     [TPL array='type']<br>
                  [TPL LOOP END]
                  </td>
               </tr>
            [TPL LOOP END]
            </table>
            <br><br>
               [TPL IF count == '2']
                  count is  2
               [TPL ELSE]
                  count is not 2
               [TPL ENDIF]
            <br><br>

            [TPL include='footer.inc']

-output

            <!--- TEMPLATE: templates/animals.html --->
            <html>
            <head>
            <title>Sample</title>
            </head>
            <body>
            ANIMALS:<br>
            <table width="200">
            <tr>
            <td>mammals [120]</td>
            </tr>
            <tr>
            <td align="right">
                                    monkey<br>
                                    lion<br>
                                    zebra<br>
                                    elephant<br>
            </td>
            </tr>
            <tr>
            <td>fish [85]</td>
            </tr>
            <tr>
            <td align="right">
                                    swordfish<br>
                                    shark<br>
                                    guppy<br>
                                    tuna<br>
                                    marlin<br>
                                    tunny<br>
            </td>
            </tr>
            <tr>
            <td>birds [57]</td>
            </tr>
            <tr>
            <td align="right">
                                    eagle<br>
                                    pigeon<br>
                                    kite<br>
                                    crow<br>
                                    owl<br>
                                    sparrow<br>
            </td>
            </tr>
            <tr>
            <td>reptiles [25]</td>
            </tr>
            <tr>
            <td align="right">
                                    monitor<br>
                                    python<br>
                                    crocodile<br>
                                    tortoise<br>
            </td>
            </tr>
            </table>
            <br><br>
                            count is  2
            <br><br>
            <!--- INCLUDED: templates/footer.inc --->
            <br>
            COMMON FOOTER
            </body>
            </html>

# DESCRIPTION

The Processor.pm module is designed to remove html from perl scripts without putting too much Perl into the html. The syntax (configurable) is somewhat verbose in order to not scare off html coders, while retaining some Perl logic and functionality. It has a fairly basic set of methods and is not as heavy duty as some of the other Template parsers out there but manages to cover most of the essential html processing operations.

Documentation Syntax

As the module deals with PERL CODE, HTML CODE and OUTPUT the documentation will indicate these as separate blocks:

-perl

-html

-output

## Module Defaults

## Object Construction

        use HTML::Processor;

            $tpl = new HTML::Processor; # with defaults
            -or-
            $tpl = new HTML::Processor({
               debuglevel  => 'Verbose',
               footprint   => 0,
               clean       => 0,
               syntax_pre  => '\[% ',
               syntax_post => ' %\]'
            });

When creating a new object some of the module defaults may be overridden by passing a hash of options to the constructor, these include:

- debuglevel

           values: [Off|Fatal|Normal|Verbose]  # case sensitive
              Default: Off
          Actions: Off => no debug info is displayed
                   Fatal => only fatal errors are displayed
                   Normal => basic processing info is displayed
                   Verbose => verbose info about the processing stage
                   ** Debug info is appended to the output

- footprint

               Values: [1|0]
              Default: 0
              Actions: 1 => leave an html comment describing the
                            location and name of the primary template
                            at the start of the output:
                            <!-- TEMPLATE: templates/animals.html --->
                            and for each included file:
                            <!-- TEMPLATE BEGIN INCLUDE templates/footer.html -->
                          -- included file data
                        <!-- TEMPLATE END INCLUDE: templates/footer.html -->
                       2 => don't

- clean

               Values: [1|0]
              Default: 1
              Actions: 1 => remove blank lines and leading whitespace
                            from template output - reduces html
                            file size for efficiency but soure is
                            no longer 'pretty'
                       2 => don't

- syntax_pre

               Values: pretty much anything BUT escape special chars
              Default: '\[TPL ' # note the space
              Actions: Sets the opening syntax for template tags, may
                       conform to a variety of existing styles eg:
                       '<\% ' asp style
                       '<\? ' php style
                       '\[% ' Template-Toolkit style
                       Watch out for spaces and charaters which have
                       meaning within regular expressions

- syntax_post

               Values: as above
              Default: '\]'
              Actions: As above

Default Template Syntax examples

            [TPL variable='varname']
            [TPL LOOP name='loop_name']
               [TPL array='loop_item']
            [TPL LOOP END]

Alternative syntax examples

            [% variable='varname' %]
            [% LOOP BEGIN name='loop_name' %]
               [% array='loop_item' %]
            [% LOOP END %]

            <? variable='varname' ?>
            <? LOOP BEGIN name='loop_name' ?>
               <? array='loop_item' ?>
            <? LOOP END ?>

## Syntax

When passing variables to object methods and when reflecting the variables in the html, the names must be matchable by: [a-zA-Z0-9_-]

Names must be quoted in single quotes in the HTML/text but double quotes are fine within Perl code.

Bad examples:

-perl

            $tpl->variable("my~name", $val);

-html

            [TPL variable='my~name']
            [TPL variable="myname"]

## Precedence

The template data is processed in the following order

       (0. SORT - if called)
        1. INCLUDE
        2. OPTION
        3. IF/ELSE
        4. VARIABLES
        5. LOOPS
               5a. LOOP OPTION
               5b. LOOP IF/ELSE

    Thus, if a LOOP is nested within an OPTION, the OPTION is
    evaluated & if true, the LOOP is processed.

## Including external files

File fragments or complete html files may be included into any template file. The included files may also contain any Template code blocks or tags. The name and path of the included file may be stored within a template object or interpreted from the include statement in the html.

The root path is relative to the primary template opened in by the

            $tpl->process("templates/main_template.html")

method.

Syntax:

-perl

        # specifying includes from Perl
            $tpl->include("footer", "templates/footer.html");

-html

            [TPL include='footer']
            # $tpl->include("footer") will be accessed for filename

            -or-

        # specifying includes from the template

            [TPL include='fragments/footer.inc']
            # footer.inc is included by means of the direct
            # reference to its location in the html
            # the method call: $tpl->include("footer", "templates/footer.html");
            # IS NOT REQUIRED IN PERL
            # valid included file extensions are: (.inc|.htm|.html)

To expand on this lets take a script in /cgi-bin which calls

            $tpl->process("templates/main_template.html")

Within 'main_template.html' is the include:

            [TPL include='fragments/footer.inc']

    Within footer.inc is the include:

        [TPL include='../../other_files/foo.htm']

When processed we have:

        /cgi-bin/templates/main_template.html
        # which then includes
        /cgi-bin/templates/fragments/footer.inc
        # which then includes
        /cgi-bin/other_files/foo.htm

The include process keeps track of the locations of all included files relative to the template root.

## Debugging

There are several methods available for debugging the Template object.

        1. Debugging Perl
           HTML::Processor can be used to display program info during runtime
           to assist in Perl debugging. There are 2 methods for this:
           A: $tpl->print_die("data_to_print");
              "data_to_print" can be anything printable. This method
              will print an HTML header and then the data and exit
              the program.

           B: $tpl->print("data", 'line terminator')
              this will print an HTML header(once) then data
              inline as the script is executed. Sometimes usefull
              for viewing a loop's content eg:

              while ( @loop_data ) {
                $tpl->print($_, "<br>");
              }

              This outputs the data with HTML linebreaks for viewing
              in the browser.

           Output from both methods will be printed before any template
           content data.

        2. Debugging the Template Object
           The HTML::Processor object can be debugged in 2 ways:
           A: Using the Config Option at object creation set 'debuglevel'
              as describe in 'Object Creation' above. Debug data is appended
              the the end of object content.
              eg: $tpl = new HTML::Processor({ debuglevel => 'Verbose' });

           B: Viewing the entire content of the object via Data::Dumper
              $tpl->process("templates/template.html", 1);
              Pass an additional parameter to the process method and the
              object internal data is passed to Data::Dumper and appended
              to the end of object output content.

## Option blocks

Option blocks are chunks of html/text which are displayed only if a condition is true. The internal 'option' hash is tested first and may be set explicitly, if the variable name is not found there, the 'variables' hash is tested.

If both tests return false the block is excluded from temlate output. True is returned for anything but 0 or '' (empty string).

Syntax:

-perl

            # explicit
            $hour = (localtime)[2];
            $morning = ($hour < 12) ? 1 : 0;
            $tpl->option("morning", $morning);
            $tpl->variable("time", scalar localtime)
            -or-
            # implicit, re-using an object variable
            $tpl->variable("morning", scalar localtime) if ((localtime)[2] < 12);

-html

            # explicit
            [TPL OPTION name='morning']
               Good morning<br>
               It is now: [TPL variable='time']
            [TPL OPTION END]

            # implicit
            [TPL OPTION name='morning']
               Good morning<br>
               It is now: [TPL variable='morning']
            [TPL OPTION END]

-output

            # implicit and explicit
            Good morning
            It is now: Sat Jun 2 09:23:29 2001

The logic may be inverted in an option block eg:

-perl

        # A
        $tpl->option("optA", 1);
        # B
        $tpl->option("optB", 0);

    I<-html>

        [TPL OPTION name='optA']
               option A data
            [TPL OPTION END]

        [TPL OPTION name!='optB']
               option B data
            [TPL OPTION END]

Both of the above evaluate as true and the content will be output in both cases.

## If / else blocks

If/Else blocks function in a similar way to Perl if(){} elsif(){} else{} constructs and display the contents of the first block for which the expression returns true. Regular object variables are used for the expression. Evaluation operators include:

            ( == != < <= > >= )

Equality and Inequality, for both strings and numerics, is via `==` and `!=` respectively. HTML::Processor will determine whether the values for comparison are strings or numerics and apply the appropriate operators. Numbers handled are floating points, integers or comma delimited floating points ( eg. 2,999,999.34 ). All other number formats will be treated as strings.

Syntax:

-perl

        # Assume the 'hour' is 11:00
            $tpl->variable("hour", (localtime)[2]);

-html

            [TPL IF hour < '12']
               Good Morning
            [TPL ELSIF hour >= '18']
               Good Evening
            [TPL ELSE]
               Good Afternoon
            [TPL ENDIF]

-output

            Good morning

## Loops

Loops handle multiple records of a given format. Each loop must be named and can be thought of as similar to a database table with rows and columns. Each 'row' represents a data record and the 'columns' are data fields. Loops may be nested within other loops by means of associating the nested loop to its parent via a 'key' value (one of the data fields). Loops may also contain conditional arguments, If/Else and Option blocks, which evaluate a condition for each loop of a loop. Loop syntax is best explained by example:

Syntax:

-perl

        # consider this basic data set:
        my @pets_data = (
            "Boots,5,cat",
            "Rover,3,dog",
            "Tweety,1,budgie"
        );

        # create a new loop object
        # and populate the object via the loop
        # 'array' method
        $pets = $tpl->new_loop("pets");
        foreach my $pet (@pets_data){
            my ( $name, $age, $type ) = split (/,/, $pet);
            $pets->array("name", $name);
            $pets->array("age",  $age);
            $pets->array("type", $type);
        }

-html

        My Pets:<br>
        NAME, AGE, TYPE<br>
        [TPL LOOP name='pets']
            [TPL array='name'], [TPL array='age'], [TPL array='type']<br>
        [TPL LOOP END]

    I<-output>

        My Pets:
        NAME, AGE, TYPE
        Boots, 5, cat
        Rover, 3, dog
        Tweety, 1, budgie

### Loop Options

Loop options evaluate a data field within the loop, if true the optinal content is displayed. They are the same as normal options but derive their scope from the current loop of the loop which they are in.

Using our above data set:

-perl

        # consider, again, this basic data set:
        my @pets_data = (
            "Boots,5,cat",
            "Rover,3,dog",
            "Tweety,1,budgie"
        );

        # create a new loop object
        # and populate the object via the loop
        # 'array' method
        $pets = $tpl->new_loop("pets");
        foreach my $pet (@pets_data){
            my ( $name, $age, $type ) = split (/,/, $pet);
            $pets->array("name", $name);
            $pets->array("age",  $age);
            $pets->array("type", $type);
            # set the variable 'cat' as true for testing
            # optional content
            $pets->array("cat", 1) if $type eq "cat";
        }

-html

        My Pets:<br>
        NAME, AGE, TYPE<br>
        [TPL LOOP name='pets']
            [TPL array='name'], [TPL array='age'], [TPL array='type']
             [TPL LOOP OPTION name = 'cat']
                'meeow'
             [TPL LOOP OPTION END]
            <br>
        [TPL LOOP END]

-output

        My Pets:
        NAME, AGE, TYPE
        Boots, 5, cat 'meeow'
        Rover, 3, dog
        Tweety, 1, budgie

Internally, each time the loop is evaluated if variable 'cat' is true the data is displayed.

### Loop If/Else

Same as normal If/Else but, like the Loop Option, tests values within current loop of loop.

-html

        My Pets:<br>
        NAME, AGE, TYPE<br>
        [TPL LOOP name='pets']
            [TPL array='name'], [TPL array='age'], [TPL array='type']
             [TPL LOOP IF type = 'cat']
                'meeow'
             [TPL ELSIF type = 'dog']
                'woof'
             [TPL ELSIF type = 'bird']
                'tweet'
             [TPL LOOP ENDIF]
            <br>
        [TPL LOOP END]

-output

        My Pets:
        NAME, AGE, TYPE
        Boots, 5, cat 'meeow'
        Rover, 3, dog 'woof'
        Tweety, 1, budgie 'tweet'

### Nested Loops

The concept of nested loops is similar to a database 'join' where multi-record content for a field is derived from elsewhere based on a 'key'. Nests may be several levels deep provided the syntax is maintained. Consider the following:

-perl

        %animals = (
                mammals    => {
                   types => [qw(monkey lion zebra elephant)],
                   count => 120
                },
                fish       => {
                   types => [qw(swordfish shark guppy tuna marlin tunny ray)],
                   count => 85
                },
                reptiles   => {
                   types => [qw(monitor python crocodile tortoise)],
                   count => 25
                },
                birds      => {
                   types => [qw(eagle pigeon owl)],
                   count => 57
               }
            );

Here each type of animal has a variable-length record for the names of its types.

        # create parent loop object
            my $animals = $tpl->new_loop("animals");
            foreach my $animal_type( keys %animals){
               # add data to the parent loop
               $animals->array("animal_type", $animal_type);
               $animals->array("count", $animals{$animal_type}{ count });

               # create new nested loop object 'keyed' on
               # the parent via $animal_type
               my $types = $tpl->new_loop("types", $animal_type);
               foreach my $type ( @{ $animals{$animal_type}{types} }){
                  # populate each 'child' loop
                  $types->array("type", $type);
               }
            }

        # IMPORATNAT SYNTAX
        # NEW PARENT LOOP OBJECT is created outside the block

        my $parent = $tpl->new_loop("parent");

        foreach ( @parent_data ) {
            my ( $val1,$val2,$val3,$parent_key ) = @_;
            $parent->array("val1", $val1);

            # create a NEW CHILD LOOP OBJECT for each loop
            # of the parent loop, use a unique value in
            # the parent data as the 'key' for each
            # child loop

            my $child = $tpl->new_loop("child", $parent_key);

            foreach ( @child_data ) {
                $child->array("name", $_);
            }
        }

-html

            <table>
            [TPL LOOP name='animals']
               <tr>
                  <td>[TPL array='animal_type'] [[TPL array='count']]</td>
               </tr>
               <tr>
                  <td align="right">
                  [TPL LOOP name='types']
                     [TPL array='type']<br>
                  [TPL LOOP END]
                  </td>
               </tr>
            [TPL LOOP END]
            </table>

## Sorting

Sort is a handy method to sort output data by columns. Often output is an HTML table with rows and columns. The data may be derived from several database calls, or other methods, and it becomes difficult to sort the output internally in Perl. This is where the Sort method comes in. Sort is applied to a named LOOP on one of its columns. This is again best illustrated by example (see the supplied perl example file for the full data example - the example makes use of CGI.pm to grab incoming sort data)

-perl

        $tpl->sort($cgi->param('sortby'));
        print $tpl->process("templates/countries.html");

-html

        <table>
        <tr>
            <td>name [<a href="scriptname.cgi?sort=countries-name">sort</a>]</td>
            <td>
                population
                [<a href="scriptname.cgi?sort=population-ASC">ASC</a>
                | <a href="scriptname.cgi?sort=population-DESC">DESC</a>]
            </td>
            <td>currency [<a href="scriptname.cgi?sort=currency">sort</a>]</td>
            <td>capital [<a href="scriptname.cgi?sort=capital">sort</a>]</td>
            <td>area [<a href="scriptname.cgi?sort=area">sort</a>]</td>
        </tr>
        [TPL LOOP name='countries']
        <tr>
            <td>[TPL array='name']</td>
            <td>[TPL array='population']</td>
            <td>[TPL array='currency']</td>
            <td>[TPL array='capital']</td>
            <td>[TPL array='area'] Km<sup>2</sup></td>
        </tr>
        [TPL LOOP END]
        </table>

The method $tpl->`sortby('name_of_column_to_sort')` is called just prior the $tpl->`process()` The value of the column to sort on 'name_of_column_to_sort' is derived from the query string in the HTML link. The HTML link must be of the form:

        sort=name_of_array

'name_of_array' is the named array used when populating the loop object.

The direction of the sort will change alternately for each array sorted beginning with Ascending. To specifically start with a descinding sort use:

        sort=name_of_array-desc

If the direction is specified IN UPPERCASE as:

        sort=name_of_array-ASC
        -or-
        sort=name_of_array-DESC

The direction will not alternate for that array during a sort, it will always be in the direction specified.

## Processing Variables

simple variable get and set

After a new template object has been created, variables can be added to or returned from the object via:

            $tpl->variable("foo", $bar);
            # set foo = $bar

            $tpl->variable("foo");
            # return the value of foo

Variables may be re-set within the script:

        $tpl->variable("value", $value);
        $tpl->variable("value", sprintf("%.2f", $tpl->variable("value")));
        # this is clearly just an illustration - it could have been
        # done in one go.

        # another example which comes in handy
        $tpl->variable("checkbox_a", ($checkbox_val) ? "checked" : ""));

-perl

            $varval = "hello world";
            $tpl->variable("greet", $varval);
            -or-
            $tpl->variable("greet", "hello world");

-html

            [TPL variable='greet']

-output

            hello world

### variable concatenation

Once a variable is in an object you may want to alter it in some way or add strings to it.

Syntax:

            $tpl->concat("varname", value, invert);

If 'invert' is true ie. `not(0|'')` value will be pre-pended to 'varname'

-perl

            $tpl->variable("greet", "hello world");
            $tpl->concat("greet", " its only me");

You can also invert the concatenation and pre-pend a string

            $tpl->variable("message", "rain is wet");
            $tpl->concat("message", "roses are red, ", 1);

-html

            [TPL variable='greet']<br>
            [TPL variable='message']

-output

            hello world its only me
            roses are red, rain is wet

### variable math

Basic mathematical operators may be applied to variables in the form:

- '+' addition
- '-' subtraction
- '*' multiplication
- '/' division

Syntax:

            $tpl->math("varname", value, operation);
            # varname = varname operation value

            -invert the operation

            $tpl->math("varname", value, operation, invert);
            # varname = value operation varname

addition and subtraction

            $tpl->variable("one", 1);                        # one = 1
            $tpl->variable("two", 2);                        # two = 2
            $tpl->variable("three", 3);                      # three = 3

            $tpl->math("two", 2, "+");                       # two = 4
        # NOTE - this is the same as:
        $tpl->variable("two", $tpl->variable("two") + 2);

            $tpl->math("two", $tpl->variable("one"), "+");   # two = 5

            # using originally declared values
            $tpl->math("two", 2, "-");                       # two = 0

            # invert the operation
            $tpl->math("three", 1, "-", 1);                  # three = -2
            # tranlates as:
            # 1 - $tpl->variable("three")
            # 1 - 3
            # -2

multiplication and division

            $tpl->variable("one", 1);                        # one = 1
            $tpl->variable("two", 2);                        # two = 2
            $tpl->variable("three", 3);                      # three = 3

            $tpl->math("two", 3, "*");                       # two = 6
            $tpl->math("two", 12, "/", 1);                                   # two = 2

## Processing the Template

$tpl->process(``template_path/template_name.html'');

This method returns the parsed template data, substituting all template syntax for object data. Typical usage:

-perl

        sub foo {
            $tpl->variable("varname", 'varval')
            ... populate template object with data
            ...

            print "Content-type: text/html";
            print $tpl->process("template_path/template.html");

            # print template and object data
            print $tpl->process("template_path/template.html", 1);
        }

# AUTHOR

Paul Schnell pschnell@touchpowder.com

Thanks to Alexis Orssich and Tom Robinson for ideas and putting the code through its paces.

# COPYRIGHT

Copyright (c) 2001 Paul Schnell. All rights reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.

# SEE ALSO

Html::Template Html::Mason Template