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