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