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