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