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