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