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