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