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