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