Look and Feel
[ TOC ]
[ TOC ]
This document explains how the output of ADT is rendered and how you can
adjust it for you particular needs.
[ TOC ]
Our ADT follows the concept of separation between content generation and
output rendering phases. Therefore we use Action Handlers to process the
request and generate the data which later on will be rendered by the view
code. This enables us to use the same data for different output formats.
Think about user submitting his personal information and the program
needing to send the same response by email and to the browser. We want the
same data to be rendered in a different ways and different actions to be
taken according to the need.
Our view module uses templates to generate the output. There are many Perl
templating solutions out there. We believe that Template Toolkit, the
solution that we have chosen suits our users needs the best, since it
allows to perform easy things easily and complex things possible. Another
benefit of using Template Toolkit is an extensive documentation it comes
with. The documentation includes manuals, tutorials and other reference
material. You can access this material from http://template-toolkit.org/. Note
that we supply a copy of Template toolkit distribution, so you don't have
to do anything additional to start using our ADT. However, if you want to
use the latest Template Toolkit version, get it from the URL, we have just
mentioned. The Template Toolkit package is also available from http://cpan.org/.
Since the templating system that we use is well documented, we will
document here only things specific for our ADT view model.
[ TOC ]
Templates are used for rendering data created in the Action Handlers.
[ TOC ]
A simple template might look like this:
|
|
Hello [% data.user.first_name %] [% data.user.last_name %].
Today is [% data.date.string %].
|
Which could be written in Perl as:
|
|
print "Hello $data{user}{first_name} $data{user}{last_name}.\n";
print "Today is $data{date}{string}.\n";
|
So the data is templates can be a nested hash. Or a list:
|
|
[% FOREACH index = 0..data.total_disks %]
Disc : #[% index %]
Title : [% data.disks.$index.title %]
Artist: [% data.disks.$index.artist %]
[% END %]
|
which in Perl can be represented as:
|
|
foreach my $index ( 0..@{ $data{total_disks} } ){
print << "__INPUT__";
Disc : #$index
Title : $data{disks}[$index]{title}
Artist: $data{disks}[$index]{artist}
__INPUT__
}
|
So you just use '.' to access the members of the nested hash or array by
using the key names and indexes.
Of course you have simple scalars:
which in Perl would be:
[ TOC ]
You can make any data structure visible to the template from within Action
Handlers, by using the
$app->setAdditionalViewDisplayParam method.
For example you have a hash data structure with records' data (%my_records). Let's say that you want it to be visible under the
'records' key. After you add the following snippet to your Action Handler:
|
|
$app->setAdditionalViewDisplayParam
(
-PARAM_NAME => "-RECORDS",
-PARAM_VALUE => \%my_records,
);
|
Templates can access the data in the records hash as:
|
|
[% FOREACH $key = data.records.keys %]
key [% $key %], value [% data.records.$key %]
[% END %]
|
Note that the leading '-' stripped and the key becomes lowercase. The leading '-' has to be removed since Template Toolkit doesn't allow keys to start with '-'. The keys become lowercase to make it easier to distinguish them from the
Template Toolkit's language constructs which uses upper case.
[ TOC ]
You can create your local data in the templates. For example:
|
|
[% who = "eXtropia" %]
[% who %] rules
|
will print:
data is special container which protects the data created outside the templates
from the local template variables.. For this reason you should never set
any values inside the data container from within the templates. The following practice is very
undesirable:
|
|
[% data.user = { fname => 'Gunther', lname => 'Birznieks'} %]
|
Since you don't know whether some Action Handler has set this key already
for some other template. In this case you may destroy some data that other
templates rely on. Hence if you need to create local variables you
shouldn't use the data container.
[ TOC ]
In additional to the data you set from within the Action Handlers, all the
configuration data from the .cgi file is available through the same data container. Just like the data from the Action Handler, in order to access
the configuration data (in
@ACTION_HANDLER_ACTION_PARAMS) you should take the configuration key, strip the leading - and lowercase it. For example to access the key -SORT_FIELD1 from the @ACTION_HANDLER_ACTION_PARAMS you can write:
|
|
The sort field is [% data.sort_field1 %]
|
[ TOC ]
The templates are highly reusable pieces of representation. You can build
many little components and stack them together to build the final look and
feel. This means that you can use the same component in many places. The
simplest example is the use of the header and the footer. Usually the same
header and footer are used for all pages of the site. Headers usually have
different titles, but the rest is usually the same. So let's look at the
header and footer templates. Let's call these components header and footer.
All the templates have the extension .ttml. And we don't supply this extension when we use the template. So the two
components will reside in the header.ttml and footer.ttml files respectively. Let's also create a template body.ttml, which will represent the real body and use the other two templates.
For example to insert the template header.ttml into the template
body.ttml you can write:
|
|
[% embed('header') %]
This is the body
[% embed('footer') %]
|
where a footer.ttml can be:
and header.ttml:
As you can see we use a special function supplied by ADT to embed one
template into another. We don't use the standard INCLUDE and
PROCESS Template Toolkit functions, since we do some work behind the scenes as we
will explain in the moment.
This will generate the following page:
|
|
title: hello world
This is the body
<HR>
Created by eXtropia
|
But as we said before, we want to be able to have different titles in
different pages. This is solved very easily with the same embed()
function.
|
|
[% embed('header',
{-TITLE => 'A new title'}
) %]
This is the body
[% embed('footer') %]
|
now we adjust the header.ttml to be:
and we get the output:
|
|
title: A new title
This is the body
<HR>
Created by eXtropia
|
embed() accepts the template name as a first argument (without
the
.ttml extension), and a reference to an array or hash as a second argument. This
second argument can pass as many key-value pairs as wanted. They all will
be available inside data. container in the nested templates. Just remember that the key should start
with '-'.
[ TOC ]
The following notes are specific to the HTML templates.
[ TOC ]
No HTTP headers get send until all templates get processed. Therefore you
can override the default HTTP headers with your own from any template. You
can use the set_headers() function, which accepts a ref to
hash with headers as a single argument. For example to redirect the
response to http://example.com/ you can say:
Note that if you set the same header twice the previous header setting will
be overridden with the new value.
[ TOC ]
Every .cgi defined the @TEMPLATES_SEARCH_PATH array. This array specifies the search path for the templates. For example
in
addressbook.cgi you will see:
|
|
my @TEMPLATES_SEARCH_PATH =
qw(HTMLTemplates/AddressBook
HTMLTemplates/Default);
|
By default the templates are searched: first in the
HTMLTemplates/AddressBook so we can provide customized templates for the application and if the
template is not found it'll be searched in HTMLTemplates/Default. If still not find the application will print an error and die.
Let's say that you want to set up a few identical application but for them
to have a different look and feel. One way to do that is to have two .cgi files where you specify in the first one:
|
|
my @TEMPLATES_SEARCH_PATH =
qw(HTMLTemplates/CustomOne
HTMLTemplates/AddressBook
HTMLTemplates/Default);
|
and in the second:
|
|
my @TEMPLATES_SEARCH_PATH =
qw(HTMLTemplates/CustomTwo
HTMLTemplates/AddressBook
HTMLTemplates/Default);
|
So you can have two different sets of template for those templates that you
want to override. Those templates that weren't overridden can be still
found in either HTMLTemplates/AddressBook or
HTMLTemplates/Default.
Remember that any template can be overridden. You don't have to copy all
the templates into your custom directory if you don't intend to customize
them all.
[ TOC ]
In order to make easy look-n-feel changes ADT now uses Cascading Style
Sheets (CSS). You specify the look and feel in the CSS file, which is then
used for all web-pages. For more information about CSS please refer to http://www.w3.org/Style/CSS/.
ADT comes with our generic CSS file which is places inside a template
HTMLTemplates/Default/CSSView.ttml. You are urged to customize this template to create a unique look-n-feel
for your own version of the eXtropia application that you run.
To improve the performance you probably want to convert this template into
a normal CSS file, so it can be cached in the browsers. For example if you
have moved the style definitions into a CSS file which can be accessed as /myproject.css, you have to adjust .cgi files to point to this new location:
|
|
my $CSS_VIEW_URL = "/myproject.css";
|
[ TOC ]
[GB: Would like to see some example usage sections....from simple to
complex for example, explain the snippet of code that webdb uses to
generate the CGI.pm widgets for the modify form as a complex example and
explain how to print a simple iterative data structure as a more simple
example]
[SB: Why duplicating the so well written template toolkit documentation? Of
course it makes nicer for users to have all the documentation in one place,
but if they are planning on actually messing with templates (other than
changing the HTML) they *have* to read the real template-toolkit tutorial
and/or manual. If we duplicate the already written documentation who is
going to make sure that it stays in sync?
I've covered here only the things specific to ADT which aren't covered by
the tt tutorial. ]
[GB: I have to disagree. Although it may seem like duplication, it is not.
I think that examples are needed that are specific to eXtropia. Actually
maybe the best place for it (and you can reference it) is to change
configbyexample.pod so that the changing of the views and the email views
is made up to date.
This was an invaluable section that was personally tested by Hsien so she
could even make the simplest changes she wanted on the scripts without
being too technical 1 1/2 years ago]
[ TOC ]
[ TOC ]
[ TOC ]
|
Master Copy URL: http://www.extropia.com/support/docs/adt/
Copyright © 2000-2001 Extropia. All rights reserved.
|
[ TOC ]
|
Written by eXtropia. Last
Modified at 09/06/2001 |
|