
=head1 NAME

OpenInteract2::Manual::Templates - Using the Template Toolkit with OpenInteract2

=head1 SYNOPSIS

This document reviews how the templating system works in
OpenInteract. Template processing is at the heart of OpenInteract, and
it is important to understand it well.

This document focuses on web applications and the Template Toolkit. If
other templating systems get implemented in OpenInteract we'll stick
pointers in here to those docs.

=head1 WHAT IS A TEMPLATE?

A template is simply HTML combined with directives meant for the
template processing engine. Here's an example:

  1: <p>Welcome back 
  2:   <font color="red">[% OI.login.full_name %]</font>!</p>

When run through the template processing engine with a normal user
object in the 'OI.login' key, this will result in:

  1: <p>Welcome back 
  2:   <font color="red">Charlie Brown</font>!</p>

So the information between the '[%' and ' %]' symbols
('OI.login.full_name') was replaced by other text depending on the
user who was viewing the page. If another user viewed the page, she
might have seen:

  1: <p>Welcome back 
  2:   <font color="red">Peppermint Patty</font>!</p>

OpenInteract provides a number of tools for you in every template you
write (see
L<OpenInteract2::ContentGenerator::TT2Plugin|OpenInteract2::ContentGenerator::TT2Plugin>). However,
you can also provide your templates access to query results from the
various data stores that SPOPS provides.

=head1 CREATING YOUR OWN TEMPLATE

The general strategy behind OpenInteract applications is a well-known
one: separate the display of data from how the data are retrieved or
operated on.

To this end the code behind an OpenInteract application normally just
retrieves some data using parameters supplied by the user and then
hands it off to the template. The template doesn't care how the data
were retrieved -- it just knows what is supposed to be there. The
template and code enter into a sort of contract -- the template
expects certain data which both the code and the system provide.

So, let's do an example. Let's say you want to display a list of users
who have accessed the system in the last n minutes. Your code might
have a subroutine like this:

  1: sub list_time_limit {
  2:     my ( $self ) = @_;
  3:     my $request = CTX->request;
  4:     my $time_limit = $self->param( 'time_limit' )
  5:                      || $request->param( 'time_limit' );
  6: 
  7:     # This SQL is Sybase-specific, but should be clear
  8:     my $where = 'datediff( minute, last_access, getdate() ) <= 30';
  9: 
 10:     # Note: 'fetch_group' returns an arrayref of objects.
 11:     my $user_list = eval {
 12:         CTX->lookup_object( 'user' )
 13:            ->$user_class->fetch_group({ where => $where,
 14:                                         order => 'last_access' })
 15:     };
 16:     return $self->generate_content( { user_list  => $user_list,
 17:                                       time_limit => $time_limit };
 18:                                     { name => 'mypkg::user_list' } );
 19: }

(The actual code would have lots of good things like error checking,
but this is just an example.)

Note that we simply passed a hashref of variables to the method
C<generate_content()>, which decides what template engine to use and
passes along some basic information about our action to the
template. What we did not say was how the variables we passed were to
be displayed.

And your template might look like:

  1: [%- DEFAULT theme = OI.theme_properties -%]
  2: 
  3: <h2>User Listing</h2>
  4: 
  5: <p>Users with accesses in the last <b>[% time_limit %]</b> minutes.
  6: 
  7: <table border="0" cellpadding="4">
  8: 
  9: [% PROCESS header_row( [ 'Username', 'Full Name', 'Last Access' ] ) %]
 10: 
 11: [% FOREACH user_object = user_list %]
 12:  <tr align="center" valign="middle">
 13:    <td>[% user_object.login_name %]</td>
 14:    <td>[% user_object.full_name %]</td>
 15:    <td>[% user_object.last_access %]</td>
 16:  </tr>
 17: [% END %]
 18:  
 19: </table>

There are a few things at work here:

=over 4

=item 1.

We're using the scalar variable 'time_limit'. Since this is a simple
scalar, we can just refer to it by name in the template as a variable
(line 5) and the contents of the variable will replace this directive.

=item 2.

We loop through the variable 'user_list' which we passed to the
template. The FOREACH directive used in the template (line 11) is very
similar to the C<foreach> loop in perl -- for every thing in the list
'user_list', we assign that thing to the variable 'user_object' which
we can then use within the loop.

Within the loop we use both properties of the user object
('login_name' and 'last_access', lines 12 and 14) and call a method on
the object ('full_name', line 13).

One of the nice features of the Template Toolkit is that it treats
objects and hashrefs in much the same way, using the dot notation. So
'user_object.full_name' could transparently translate to either:

  $user_object->full_name()
  $user_object->{full_name}
  


Here we're using the 'user_object' variable (obviously) as an
object. But we could modify the perl code to instead get all the
information about the user and combine it with other information into
a hashref and feed it to the same template. If we were to do this, we
would not have to modify B<a single line> of our template.

=item 3.

We access the OpenInteract plugin ('OI') and find the theme properties
from it ('OI.theme_properties', line 1). These get assigned to a
variable so we can use it multiple times throughout the template
rather than calling the plugin every time.

Note that we did not explicitly pass the plugin into the template via
the variable hashref, as we did in this example with the variables
'time_limit' and 'user_list'. Instead, think of the 'OI' plugin as
part of the template environment. You can use it to access information
about the current user, the theme being used, various text
manipulation routines, and more. We talk about it more below, but the
plugin is well-documented in
L<OpenInteract2::ContentGeneration::TT2Plugin|OpenInteract2::ContentGeneration::TT2Plugin>.

=back

Now, what if we wanted to change the display of the data? We could
replace the 'user_list' template with the following:

  1: <h2>User Listing</h2>
  2: 
  3: <p>Users with accesses in the last <b>[% time_limit %]</b> minutes.
  4: 
  5: <ul>
  6: [% FOREACH user_object = user_list %]
  7:  <li>[% user_object.full_name %] ([% user_object.login_name %])
  8:       accessed the system at [% user_object.last_access %]</li>
  9: [% END %]
 10: </ul>

If we did this, we would not have to change B<a single line> of our
back-end code, since the "contract" between the action task and
template hasn't changed. This contract specifies that the task will
provide a list of user objects and a time limit to the template. Even
though the template uses these data somewhat differently now, the code
is isolated from this change and indeed never cares about it.

Similarly, our content output could be a PDF instead of an HTML
page. Instead of calling the template processing engine, we pass the
data off to a separate process which formats it according to various
rules and creates a PDF to send to the user. Again, the backend code
does not need to be modified at all. We just need to change the action
configuration to specify this new method of generating content and
create whatever PDF-specific methods are to be used.

=head1 HOW DOES IT WORK

If you're curious about this, then you first need to understand how
OpenInteract actions work. See
L<OpenInteract2::Action|OpenInteract2::Action> for more information.

OpenInteract2 can support multiple types of templating engines. Each
action specifies (in its metadata) the templating engine that will
process the data generated by the action into usable content. It can
also be modifies programmatically by the action code itself.

The templating engines are setup in the server configuration using the
'content_generator' key. OpenInteract comes with at least one content
generator: 'TT'. Each content generator can have multiple processors
so that each processor can be configured for a particular task.

In practice, many sites will only have one content generator and one
template processor for the web content. But you might want to use a
different content generator when you're creating customized emails or
something similar.

=head1 SEE ALSO

L<OpenInteract2::ContentGenerator::TT2Plugin|OpenInteract2::ContentGenerator::TT2Plugin>
 
L<Template|Template>

L<Template::Manual|Template::Manual>

=head1 COPYRIGHT

Copyright (c) 2001-2003 Chris Winters. All rights reserved.

=head1 AUTHORS

Chris Winters E<lt>Chris@cwinters.comE<gt>
