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