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