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