]> git.decadent.org.uk Git - maypole.git/blob - lib/Maypole/View/TT.pm
pod and pod-coverage tests now pass
[maypole.git] / lib / Maypole / View / TT.pm
1 package Maypole::View::TT;
2 use base 'Maypole::View::Base';
3 use Maypole::Constants;
4 use Template;
5 use File::Spec::Functions qw(catdir tmpdir);
6
7 our $error_template; 
8 { local $/; $error_template = <DATA>; }
9
10 use strict;
11 our $VERSION = 2.11;
12
13 sub template {
14     my ( $self, $r ) = @_;
15
16     unless ($self->{tt}) {
17         my $view_options = $r->config->view_options || {};
18         $self->{provider} = Template::Provider->new($view_options);
19         $self->{tt}       = Template->new({
20             %$view_options,
21             LOAD_TEMPLATES => [ $self->{provider} ],
22         });
23     }
24
25     $self->{provider}->include_path([ $self->paths($r) ]);
26
27     my $template_file = $r->template;
28     my $ext = $r->config->template_extension;
29     $template_file .= $ext if defined $ext;
30
31     my $output;
32     if ($self->{tt}->process($template_file, { $self->vars($r) }, \$output )) {
33         $r->{output} = $output;
34         return OK;
35     } else {
36         $r->{error} = "TT error for template '$template_file'\n" . $self->{tt}->error;
37         return ERROR;
38     }
39 }
40
41
42 sub report_error {
43     my ($self, $r, $error, $type) = @_;
44     my $output;
45     # Need to be very careful here.
46     my $tt = Template->new;
47     if ($tt->process(\$error_template,
48         { err_type => $type, error => $error, 
49          config => { %{$r->{config}}},
50           request => $r, # We have that at least
51         eval{$self->vars($r)} }, \$output )) {
52         $r->{output} = $output;
53         if ($tt->error) { $r->{output} = "<html><body>Even the error template
54         errored - ".$tt->error."</body></html>"; }
55         $r->{content_type}      ||= "text/html";
56         $r->{document_encoding} ||= "utf-8";
57         return OK;
58     }
59     return ERROR;
60 }
61
62
63 =head1 NAME
64
65 Maypole::View::TT - A Template Toolkit view class for Maypole
66
67 =head1 SYNOPSIS
68
69     BeerDB->config->view("Maypole::View::TT"); # The default anyway
70
71     # Set some Template Toolkit options
72     BeerDB->config->view_options( {
73         TRIM        => 1,
74         COMPILE_DIR => '/var/tmp/mysite/templates',
75     } );
76
77     .....
78
79     [% PROCESS macros %]
80
81     [% pager %]
82
83     [% link %]
84
85     [% maybe_link_view %]
86
87 =head1 DESCRIPTION
88
89 This is the default view class for Maypole; it uses the Template Toolkit to fill
90 in templates with the objects produced by Maypole's model classes. Please see
91 the L<Maypole manual|Maypole::Manual>, and in particular, the
92 L<view|Maypole::Manual::View> chapter for the template variables available and
93 for a refresher on how template components are resolved.
94
95 The underlying Template toolkit object is configured through
96 C<$r-E<gt>config-E<gt>view_options>. See L<Template|Template> for available
97 options.
98
99 =over 4
100
101 =item template
102
103 Processes the template and sets the output. See L<Maypole::View::Base>
104
105 =item report_error
106
107 Reports the details of an error, current state and parameters
108
109 =back
110
111 =head1 TEMPLATE TOOLKIT INTRODUCTION
112
113 The Template Toolkit uses it's own mini language described in
114 L<Template::Manual::Directives>.
115
116 A simple example would be :
117
118 =over 4
119
120 re:[% subject %]
121
122 Dear [% title %] [% surname %],
123 Thank you for your letter dated [% your.date %]. This is to
124 confirm that we have received it and will respond with a more
125 detailed response as soon as possible. In the mean time, we
126 enclose more details of ...
127
128 =back
129
130 TT uses '[%' and '%]' (by default) to delimit directives within a template, and
131 the simple directives above just display the value of variable named within
132 those delimiters -- [% title %] will be replaced inline with the value of the
133 'title' variable passed in the 'stash' to the template when it is processed.
134
135 You can access nested data through the dot ('.') operator, which will
136 dereference array or hash elements, but can also be used to call methods on
137 objects, i.e. '[% name.salutation("Dear %s,") %]'. The other main operator is
138 underscore ('_'), which will concatonate strings or variables.
139
140 The value returned by a directive replaces the directive inline when the
141 template is processes, you can also SET a value which will not return anything,
142 or CALL a method or operation which will also not return anything.
143
144 You can specify expressions using the logical (and, or, not, ?:) and mathematic
145 operators (+ - * / % mod div).
146
147 Results of TT commands are interpolated in the place of the template tags, unless
148 using SET or CALL, i.e. [% SET foo = 1 %], [% GET foo.bar('quz'); %]
149
150 =over 4
151
152 [% template.title or default.title %]
153
154 [% score * 100 %]
155
156 [% order.nitems ? checkout(order.total) : 'no items' %]
157
158 =back
159
160 TT allows you to include or re-use templates through it's INCLUDE, PROCESS and
161 INSERT directives, which are fairly self explainatory. You can also re-use parts
162 of template with the BLOCK or MACRO directives.
163
164 Conditional and Looping constructs are simple and powerful, and TT provides an
165 inbuilt iterator and helper functions and classes that make life sweet.
166
167 Conditional directives are IF, UNLESS, ELSIF, ELSE and behave as they would in
168 perl :
169
170 =over 4
171
172 [% IF age < 10 %]
173   Hello [% name %], does your mother know you're  using her AOL account?
174 [% ELSIF age < 18 %]
175   Sorry, you're not old enough to enter (and too dumb to lie about your age)
176 [% ELSE %]
177   Welcome [% name %].
178 [% END %]
179
180 [% UNLESS text_mode %] [% INCLUDE biglogo %] [% END %]
181
182 =back
183
184 Looping directives are FOREACH, LAST and BREAK.
185
186 FOREACH loops through a HASH or ARRAY processing the enclosed block for each
187 element.
188
189 Looping through an array
190
191  [% FOREACH i = items %]
192  [% i %]
193  [% END %]
194
195 Looping through a hash
196
197  [% FOREACH u IN users %]
198  * [% u.key %] : [% u.value %]
199  [% END %]
200
201 Looping through an array of hashes
202
203  [% FOREACH user IN userlist %]
204  * [% user.id %] [% user.name %]
205  [% END %]
206
207 The LAST and BREAK directive can be used to exit the loop.
208
209 The FOREACH directive is implemented using the Template::Iterator module. A
210 reference to the iterator object for a FOREACH directive is implicitly available
211 in the 'loop' variable. The loop iterator object provides a selection of methods
212 including size(), max(), first(), last(), count(), etc
213
214 =over 4
215
216   [% FOREACH item IN [ 'foo', 'bar', 'baz' ] -%]
217     [%- "<ul>\n" IF loop.first %]
218       <li>[% loop.count %]/[% loop.size %]: [% item %]
219     [%- "</ul>\n" IF loop.last %]
220   [% END %]
221
222 =back
223
224 See Template::Iterator for further details on looping and the Iterator.
225
226 You might notice the minus ('-') operator in the example above, it is used to
227 remove a newline before or after a directive so that you can layout the Template
228 logic as above but the resulting output will look exactly how you require it.
229
230 You will also frequently see comments and multi-line directives, # at the start
231 of a directive marks it as a comment, i.e. '[%# this is a comment %]'. A
232 multiline directive looks like :
233
234  [% do.this;
235     do.that;
236     do.the_other %]
237
238 You can see that lines are terminated with a semi-colon (';') unless the
239 delimter ('%]') closes the directive.
240
241 For full details of the Template Toolkit see Template::Manual and
242 Template::Manual::Directives, you can also check the website, mailing list or
243 the Template Toolkit book published by O Reilly.
244
245 =head1 TEMPLATE PLUGINS, FILTERS AND MACROS
246
247 The Template Toolkit has a popular and powerful selection of Plugins and
248 Filters.
249
250 TT Plugins provide additional functionality within Templates, from accessing CGI
251 and databases directly, handling paging or simple integration with Class::DBI
252 (for those rare occasions where you don't actually need Maypole). See
253 L<Template::Manual::Plugins>.
254
255 One plugin that is indispensible when using Maypole and the Template View is
256 C<Template::Plugin::Class> -- This allows you to import and use any class
257 installed within a template. For example :
258
259 =over 4
260
261 [% USE foo = Class('Foo') %]
262 [% foo.bar %]
263
264 =back
265
266 Would do the equivilent of 'use Foo; Foo->bar;' in perl. See
267 L<Template::Plugin::Class> for details.
268
269 TT Filters process strings or blocks within a template, allowing you to
270 truncate, format, escape or encode trivially. A useful selection is included
271 with Template Toolkit and they can also be found on CPAN or can be written
272 easily. See L<Template::Manual::Filters>.
273
274 TT Macros allow you to reuse small blocks of content, directives, etc. The MACRO
275 directive allows you to define a directive or directive block which is then
276 evaluated each time the macro is called. Macros can be passed named parameters
277 when called.
278
279 Once a MACRO is defined within a template or 'include'd template it can be used
280 as if it were a native TT directive. Maypole provides a selection of powerful
281 and useful macros in the templates/ directory of the package and these are used
282 in the beerdb and default templates. See the MACRO section of the
283 L<Template::Manual::Directives> documentation.
284
285 =head1 ACCESSING MAYPOLE VALUES
286
287 =head2 request
288
289 You can access the request in your templates in order to see the action, table, etc as well
290 as parameters passed through forms :
291
292 for example
293
294 Hello [% request.params.forename %] [% request.params.surname %] !
295
296 or 
297
298 Are you want to [% request.action %] in the [% request.table %] ?
299
300 =head2 config
301
302 You can access your maypole application configuration through the config variable :
303
304 <link base="[% config.uri_base %]"/>
305
306 =head2 object and objects
307
308 Objects are passed to the request using r->objects($arrayref) and are accessed in the templates
309 as an array called objects.
310
311 [% FOR objects %] <a href="[% config.uri_base %]/[% request.table %]/view/[% object.id %]"> [% object %] </a> [% END %]
312
313 =head1 MAYPOLE MACROS AND FILTERS
314
315 Maypole provides a collection of useful and powerful macros in the templates/factory/macros
316  and other templates. These can be used in any template with [% PROCESS templatename %].
317
318 =head2 link
319
320 This creates an <A HREF="..."> to a command in the Apache::MVC system by
321 catenating the base URL, table, command, and any arguments.
322
323 =head2 maybe_link_view
324
325 C<maybe_link_view> takes something returned from the database - either
326 some ordinary data, or an object in a related class expanded by a
327 has-a relationship. If it is an object, it constructs a link to the view
328 command for that object. Otherwise, it just displays the data.
329
330 =head2 pager
331
332 This is an include template rather than a macro, and it controls the pager
333 display at the bottom (by default) of the factory list and search views/template.
334 It expects a C<pager> template argument which responds to the L<Data::Page> interface.
335
336 This macro is in the pager template and used as :
337
338 [% PROCESS pager %]
339
340 Maypole provides a pager for list and search actions, otherwise you can
341 provide a pager in the template using Template::Plugin::Pagination.
342
343 [% USE pager = Pagination(objects, page.current, page.rows) %]
344 ...
345 [% PROCESS pager %]
346
347 The pager will use a the request action  as the action in the url unless the
348 pager_action variable is set, which it will use instead if available.
349
350 =head2 other macros
351
352 =head1 AUTHOR
353
354 Simon Cozens
355
356 =cut
357
358 1;
359
360 __DATA__
361 <html><head><title>Maypole error page</title>
362 <style type="text/css">
363 body { background-color:#7d95b5; font-family: sans-serif}
364 p { background-color: #fff; padding: 5px; }
365 pre { background-color: #fff; padding: 5px; border: 1px dotted black }
366 h1 { color: #fff }
367 h2 { color: #fff }
368 .lhs {background-color: #ffd; }
369 .rhs {background-color: #dff; }
370 </style>
371 </head> <body>
372 <h1> Maypole application error </h1>
373
374 <p> This application living at <code>[%request.config.uri_base%]</code>, 
375 [%request.config.application_name || "which is unnamed" %], has
376 produced an error. The adminstrator should be able to understand
377 this error message and fix the problem.</p>
378
379 <h2> Some basic facts </h2>
380
381 <p> The error was found in the [% err_type %] stage of processing
382 the path "[% request.path %]". The error text returned was:
383 </p>
384 <pre>
385     [% error %]
386 </pre>
387
388 <h2> Request details </h2>
389
390 <table> 
391     [% FOR thing = ["model_class", "table", "template", "path",
392     "content_type", "document_encoding", "action", "args", "objects"] %]
393     <tr> <td class="lhs"> [%thing %] </td> <td class="rhs"> [%
394     request.$thing.list.join(" , ") %] </td></tr>
395     [% END %]
396 </table>
397
398 <h2> Application configuration </h2>
399 <table> 
400     [% FOR thing = config.keys %]
401     <tr> <td class="lhs"> [%thing %] </td> <td class="rhs"> [% 
402     config.$thing.list.join(" , ") %] </td></tr>
403     [% END %]
404 </table>
405
406 </body>
407 </html>
408
409