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