]> git.decadent.org.uk Git - maypole.git/blob - lib/Maypole/Manual/Request.pod
added more documentation on redirect_request and get_protocol
[maypole.git] / lib / Maypole / Manual / Request.pod
1 =head1 NAME
2
3 Maypole::Manual::Request - Maypole Request Hacking Cookbook
4
5 =head1 DESCRIPTION
6
7 Hacks; design patterns; recipes: call it what you like, this chapter is a
8 developing collection of techniques which can be slotted in to Maypole
9 applications to solve common problems or make the development process easier.
10
11 As Maypole developers, we don't necessarily know the "best practice" for
12 developing Maypole applications ourselves, in the same way that Larry Wall
13 didn't know all about the best Perl programming style as soon as he wrote
14 Perl. These techniques are what we're using at the moment, but they may
15 be refined, modularized, or rendered irrelevant over time. But they've
16 certainly saved us a bunch of hours work.
17
18 =head2 Frontend hacks
19
20 These hacks deal with changing the way Maypole relates to the outside world;
21 alternate front-ends to the Apache and CGI interfaces, or subclassing chunks
22 of the front-end modules to alter Maypole's behaviour in particular ways.
23
24 =head3 Separate model class modules
25
26 You want to put all the C<BeerDB::Beer> routines in a separate module,
27 so you say:
28
29     package BeerDB::Beer;
30     BeerDB::Beer->has_a(brewery => "BeerDB::Brewery");
31     sub foo :Exported {}
32
33 And in F<BeerDB.pm>, you put:
34
35     use BeerDB::Beer;
36
37 It doesn't work.
38
39 B<Solution>: It doesn't work because of the timing of the module loading.
40 C<use BeerDB::Beer> will try to set up the C<has_a> relationships
41 at compile time, when the database tables haven't even been set up,
42 since they're set up by
43
44     BeerDB->setup("...")
45
46 which does its stuff at runtime. There are two ways around this; you can
47 either move the C<setup> call to compile time, like so:
48
49     BEGIN { BeerDB->setup("...") }
50
51 or move the module loading to run-time (my preferred solution):
52
53     BeerDB->setup("...");
54     BeerDB::Beer->require;
55
56 =head3 Redirecting to SSL for sensitive information
57
58 You have a website with forms that people will be entering sensitive information into,
59 such as credit cards or login details. You want to make sure that they aren't sent
60 in plain text but over SSL instead.
61
62 B<Solution>
63
64 The solution is a bit tricky for 2 reasons :
65
66 Firstly -- Many browsers and web clients will change a redirected 
67 POST request into a GET request (which displays all that sensitive information in the
68 browser, or access logs and possibly elsewhere) and/or drops the values on the floor.
69
70 Secondly -- If somebody has sent that sensitive information in plain text already, then
71 sending it again over SSL won't solve the problem.
72
73 Redirecting a request is actually rather simple :
74
75 $r->redirect_request('https://www.example.com/path'); # perldoc Maypole for API
76
77 .. as is checking the protocol :
78
79 $r->get_protocol(); # returns 'http' or 'https'
80  
81 You should check that the action that generates the form that people will enter
82 the sensitive information into is https and redirect if not.
83
84 You should also check that no information is lost when redirecting, possibly by 
85 storing it in a session and retrieving it later - see Maypole::Plugin::Session
86
87 =head3 Debugging with the command line
88
89 You're seeing bizarre problems with Maypole output, and you want to test it in
90 some place outside of the whole Apache/mod_perl/HTTP/Internet/browser circus.
91
92 B<Solution>: Use the L<Maypole::CLI> module to go directly from a URL to
93 standard output, bypassing Apache and the network altogether.
94
95 L<Maypole::CLI> is not a standalone front-end, but to allow you to debug your
96 applications without having to change the front-end they use, it temporarily
97 "borgs" an application. If you run it from the command line, you're expected
98 to use it like so:
99
100     perl -MMaypole::CLI=Application -e1 'http://your.server/path/table/action'
101
102 For example:
103
104     perl -MMaypole::CLI=BeerDB -e1 'http://localhost/beerdb/beer/view/1?o2=desc'
105
106 You can also use the C<Maypole::CLI> module programatically to create
107 test suites for your application. See the Maypole tests themselves or
108 the documentation to C<Maypole::CLI> for examples of this.
109
110 Don't forget also to turn on debugging output in your application:
111
112     package BeerDB;
113     use strict;
114     use warnings;
115     use Maypole::Application qw(-Debug);
116
117 =head3 Changing how URLs are parsed
118
119 You don't like the way Maypole URLs look, and want something that either
120 fits in with the rest of your site or hides the internal workings of the
121 system.
122
123 B<Solution>: So far we've been using the C</table/action/id/args> form
124 of a URL as though it was "the Maypole way"; well, there is no Maypole
125 way. Maypole is just a framework and absolutely everything about it is 
126 overridable. 
127
128 If we want to provide our own URL handling, the method to override in
129 the driver class is C<parse_path>. This is responsible for taking
130 C<$r-E<gt>path> and filling the C<table>, C<action> and C<args> slots
131 of the request object. Normally it does this just by splitting the path
132 on 'C</>' characters, but you can do it any way you want, including
133 getting the information from C<POST> form parameters or session variables. 
134
135 For instance, suppose we want our URLs to be of the form
136 C<ProductDisplay.html?id=123>, we could provide a C<parse_path> method
137 like so:
138
139     sub parse_path {
140         my $r = shift;
141         $r->path("ProductList.html") unless $r->path;
142         ($r->path =~ /^(.*?)([A-Z]\w+)\.html/);
143         $r->table(lc $1);
144         $r->action(lc $2);
145         my %query = $r->ar->args;
146         $self->args([ $query{id} ]);
147     }
148
149 This takes the path, which already has the query parameters stripped off
150 and parsed, and finds the table and action portions of the filename,
151 lower-cases them, and then grabs the C<id> from the query. Later methods
152 will confirm whether or not these tables and actions exist.
153
154 See the L<iBuySpy Portal|Maypole::Manual::BuySpy> for another
155 example of custom URL processing.
156
157 =head3 Maypole for mobile devices
158
159 You want Maypole to use different templates to display on particular
160 browsers.
161
162 B<Solution>: There are several ways to do this, but here's the neatest
163 we've found. Maypole chooses where to get its templates either by
164 looking at the C<template_root> config parameter or, if this is not
165 given, calling the C<get_template_root> method to ask the front-end to
166 try to work it out. We can give the front-end a little bit of help, by
167 putting this method in our driver class:
168
169     sub get_template_root {
170         my $r = shift;
171         my $browser = $r->headers_in->get('User-Agent');
172         if ($browser =~ /mobile|palm|nokia/i) {
173             "/home/myapp/templates/mobile";
174         } else {
175             "/home/myapp/templates/desktop";
176         }
177     }
178
179 (Maybe there's a better way to detect a mobile browser, but you get the
180 idea.)
181
182 =head2 Content display hacks
183
184 These hacks deal primarily with the presentation of data to the user,
185 modifying the F<view> template or changing the way that the results of
186 particular actions are displayed.
187
188 =head3 Null Action
189
190 You need an "action" which doesn't really do anything, but just formats
191 up a template.
192
193 B<Solution>: There are two ways to do this, depending on what precisely
194 you need. If you just need to display a template, C<Apache::Template>
195 style, with no Maypole objects in it, then you don't need to write any
196 code; just create your template, and it will be available in the usual
197 way.
198
199 If, on the other hand, you want to display some data, and what you're
200 essentially doing is a variant of the C<view> action, then you need to
201 ensure that you have an exported action, as described in the
202 L<templates and actions|Maypole::Manual::StandardTemplates/"C<view> and C<edit>">
203 chapter:
204
205     sub my_view :Exported { }
206
207 =head3 Template Switcheroo
208
209 An action doesn't have any data of its own to display, but needs to display
210 B<something>.
211
212 B<Solution>: This is an B<extremely> common hack. You've just issued an
213 action like C<beer/do_edit>, which updates the database. You don't want
214 to display a page that says "Record updated" or similar. Lesser
215 application servers would issue a redirect to have the browser request
216 C</beer/view/I<id>> instead, but we can actually modify the Maypole
217 request on the fly and, after doing the update, pretend that we were
218 going to C</beer/view/I<id>> all along. We do this by setting the
219 objects in the C<objects> slot and changing the C<template> to the
220 one we wanted to go to.
221
222 In this example from L<Flox|Maypole::Manual::Flox>, we've just
223 performed an C<accept> method on a C<Flox::Invitation> object and we
224 want to go back to viewing a user's page.
225
226     sub accept :Exported {
227         my ($self, $r) = @_;
228         my $invitation = $r->objects->[0];
229         # [... do stuff to $invitation ...]
230         $r->objects([$r->user]);
231         $r->model_class("Flox::User");
232         $r->template("view");
233     }
234
235 This hack is so common that it's expected that there'll be a neater
236 way of doing this in the future.
237
238 =head3 XSLT
239
240 Here's a hack I've used a number of times. You want to store structured
241 data in a database and to abstract out its display.
242
243 B<Solution>: You have your data as XML, because handling big chunks of
244 XML is a solved problem. Build your database schema as usual around the
245 important elements that you want to be able to search and browse on. For
246 instance, I have an XML format for songs which has a header section of
247 the key, title and so on, plus another section for the lyrics and
248 chords:
249
250     <song>
251         <header>
252             <title>Layla</title>
253             <artist>Derek and the Dominos</artist>
254             <key>Dm</key>
255         </header>
256         <lyrics>
257           <verse>...</verse>
258           <chorus>
259             <line> <sup>A</sup>Lay<sup>Dm</sup>la <sup>Bb</sup> </line> 
260             <line> <sup>C</sup>Got me on my <sup>Dm</sup>knees </line> 
261             ...
262
263 I store the title, artist and key in the database, as well as an "xml"
264 field which contains the whole song as XML.
265
266 To load the songs into the database, I can C<use> the driver class for
267 my application, since that's a handy way of setting up the database classes
268 we're going to need to use. Then the handy L<XML::TreeBuilder> will handle
269 the XML parsing for us:
270
271     use Songbook;
272     use XML::TreeBuilder;
273     my $t = XML::TreeBuilder->new;
274     $t->parse_file("songs.xml");
275
276     for my $song ($t->find("song")) {
277         my ($key) = $song->find("key"); $key &&= $key->as_text;
278         my ($title) = $song->find("title"); $title = $title->as_text;
279         my ($artist) = $song->find("artist"); $artist = $artist->as_text;
280         my ($first_line) = $song->find("line");
281         $first_line = join "", grep { !ref } $first_line->content_list;
282         $first_line =~ s/[,\.\?!]\s*$//;
283         Songbook::Song->find_or_create({
284             title => $title,
285             first_line => $first_line,
286             song_key => Songbook::SongKey->find_or_create({name => $key}),
287             artist => Songbook::Artist->find_or_create({name => $artist}),
288             xml => $song->as_XML
289         });
290     }
291
292 Now we need to set up the custom display for each song; thankfully, with
293 the L<Template::Plugin::XSLT> module, this is as simple as putting the
294 following into F<templates/song/view>:
295
296     [%
297         USE transform = XSLT("song.xsl");
298         song.xml | $transform
299     %]
300
301 We essentially pipe the XML for the selected song through to an XSL
302 transformation, and this will fill out all the HTML we need. Job done.
303
304 =head3 Displaying pictures
305
306 You want to serve a picture, a Word document, or something else which
307 doesn't have a content type of C<text/html>, out of your database.
308
309 B<Solution>: Fill the content and content-type yourself.
310
311 Here's a subroutine which displays the C<photo> for either a specified
312 user or the currently logged in user. We set the C<output> slot of the
313 Maypole request object: if this is done then the view class is not called
314 upon to process a template, since we already have some output to display.
315 We also set the C<content_type> using one from the database.
316
317     sub view_picture :Exported {
318         my ($self, $r) = @_;
319         my $user = $r->objects->[0];
320         $r->content_type($user->photo_type);
321         $r->output($user->photo);
322     }
323
324 Of course, the file doesn't necessarily need to be in the database
325 itself; if your file is stored in the filesystem, but you have a file
326 name or some other pointer in the database, you can still arrange for
327 the data to be fetched and inserted into C<$r-E<gt>output>.
328
329 =head3 REST
330
331 You want to provide a programmatic interface to your Maypole site.
332
333 B<Solution>: The best way to do this is with C<REST>, which uses a
334 descriptive URL to encode the request. For instance, in
335 L<Flox|Maypole::Manual::Flox> we
336 describe a social networking system. One neat thing you can do with
337 social networks is to use them for reputation tracking, and we can use
338 that information for spam detection. So if a message arrives from
339 C<person@someco.com>, we want to know if they're in our network of
340 friends or not and mark the message appropriately. We'll do this by
341 having a web agent (say, L<WWW::Mechanize> or L<LWP::UserAgent>) request
342 a URL of the form
343 C<http://flox.simon-cozens.org/user/relationship_by_email/person%40someco.com>.
344 Naturally, they'll need to present the appropriate cookie just like a
345 normal browser, but that's a solved problem. We're just interested in
346 the REST request.
347
348 The request will return a single integer status code: 0 if they're not
349 in the system at all, 1 if they're in the system, and 2 if they're our
350 friend.
351
352 All we need to do to implement this is provide the C<relationship_by_email>
353 action, and use it to fill in the output in the same way as we did when
354 displaying a picture. Since C<person%40someco.com> is not the ID of a
355 row in the user table, it will appear in the C<args> array:
356
357     use URI::Escape;
358     sub relationship_by_email :Exported {
359         my ($self, $r) = @_;
360         my $email = uri_unescape($r->args->[0]);
361         $r->content_type("text/plain");
362         my $user;
363         unless (($user) = Flox::User->search(email => $email)) {
364             $r->content("0\n"); return;
365         }
366
367         if ($r->user->is_friend($user)) { $r->contenti("2\n"); return; };
368         $r->content("1\n"); return;
369     }
370
371 =head3 Component-based Pages
372
373 You're designing something like a portal site which has a number of
374 components, all displaying different bits of information about different
375 objects. You want to include the output of one Maypole request call while
376 building up another. 
377
378 B<Solution>: Use L<Maypole::Plugin::Component>. By inheriting like this:
379
380     package BeerDB;
381     use Maypole::Application qw(Component);
382
383 you can call the C<component> method on the Maypole request object to
384 make a "sub-request". For instance, if you have a template
385
386     <DIV class="latestnews">
387     [% request.component("/news/latest_comp") %]
388     </DIV>
389
390     <DIV class="links">
391     [% request.component("/links/list_comp") %]
392     </DIV>
393
394 then the results of calling the C</news/latest_comp> action and template
395 will be inserted in the C<latestnews> DIV, and the results of calling
396 C</links/list_comp> will be placed in the C<links> DIV. Naturally, you're
397 responsible for exporting actions and creating templates which return 
398 fragments of HTML suitable for inserting into the appropriate locations.
399
400 Alternatively, if you've already got all the objects you need, you can
401 probably just C<[% PROCESS %]> the templates directly.
402
403 =head3 Bailing out with an error
404
405 Maypole's error handling sucks. Something really bad has happened to the
406 current request, and you want to stop processing now and tell the user about
407 it.
408
409 B<Solution>: Maypole's error handling sucks because you haven't written it
410 yet. Maypole doesn't know what you want to do with an error, so it doesn't
411 guess. One common thing to do is to display a template with an error message
412 in it somewhere.
413
414 Put this in your driver class:
415
416     sub error { 
417         my ($r, $message) = @_;
418         $r->template("error");
419         $r->template_args->{error} = $message;
420         return OK;
421     }
422
423 And then have a F<custom/error> template like so:
424
425     [% PROCESS header %]
426     <H2> There was some kind of error... </H2>
427     <P>
428     I'm sorry, something went so badly wrong, we couldn't recover. This
429     may help:
430     </P>
431     <DIV CLASS="messages"> [% error %] </DIV>
432
433 Now in your actions you can say things like this:
434
435     if (1 == 0) { return $r->error("Sky fell!") }
436
437 This essentially uses the template switcheroo hack to always display the
438 error template, while populating the template with an C<error> parameter.
439 Since you C<return $r-E<gt>error>, this will terminate the processing
440 of the current action.
441
442 The really, really neat thing about this hack is that since C<error>
443 returns C<OK>, you can even use it in your C<authenticate> routine:
444
445     sub authenticate {
446         my ($self, $r) = @_;
447         $r->get_user;
448         return $r->error("You do not exist. Go away.")
449             if $r->user and $r->user->status ne "real";
450         ...
451     }
452
453 This will bail out processing the authentication, the model class, and
454 everything, and just skip to displaying the error message. 
455
456 Non-showstopper errors or other notifications are best handled by tacking a
457 C<messages> template variable onto the request:
458
459     if ((localtime)[6] == 1) {
460         push @{$r->template_args->{messages}}, "Warning: Today is Monday";
461     }
462
463 Now F<custom/messages> can contain:
464
465     [% IF messages %]
466     <DIV class="messages">
467     <UL>
468         [% FOR message = messages %]
469            <LI> [% message %] </LI>
470         [% END %]
471     </UL>
472     </DIV>
473     [% END %]
474
475 And you can display messages to your user by adding C<PROCESS messages> at an
476 appropriate point in your template; you may also want to use a template
477 switcheroo to ensure that you're displaying a page that has the messages box in
478 it.
479
480 =head2 Authentication and Authorization hacks
481
482 The next series of hacks deals with providing the concept of a "user" for
483 a site, and what you do with one when you've got one.
484
485 =head3 Logging In
486
487 You need the concept of a "current user".
488
489 B<Solution>: Use something like
490 L<Maypole::Plugin::Authentication::UserSessionCookie> to authenticate
491 a user against a user class and store a current user object in the
492 request object.
493
494 C<UserSessionCookie> provides the C<get_user> method which tries to get
495 a user object, either based on the cookie for an already authenticated
496 session, or by comparing C<user> and C<password> form parameters
497 against a C<user> table in the database. Its behaviour is highly
498 customizable and described in its documentation.
499
500 =head3 Pass-through login
501
502 You want to intercept a request from a non-logged-in user and have
503 them log in before sending them on their way to wherever they were
504 originally going. Override C<Maypole::authenticate> in your driver
505 class, something like this:
506
507 B<Solution>:
508
509     use Maypole::Constants; # Otherwise it will silently fail!
510
511     sub authenticate {
512         my ($self, $r) = @_;
513         $r->get_user;
514         return OK if $r->user;
515         # Force them to the login page.
516         $r->template("login");
517         return OK;
518     }
519
520 This will display the C<login> template, which should look something
521 like this:
522
523     [% INCLUDE header %]
524
525       <h2> You need to log in </h2>
526
527     <DIV class="login">
528     [% IF login_error %]
529        <FONT COLOR="#FF0000"> [% login_error %] </FONT>
530     [% END %]
531       <FORM ACTION="[% base ; '/' ; request.path %]" METHOD="post">
532     Username: 
533         <INPUT TYPE="text" NAME="[% config.auth.user_field || "user" %]"><BR>
534     Password: <INPUT TYPE="password" NAME="password"> <BR>
535     <INPUT TYPE="submit">
536     </FORM>
537     </DIV>
538     [% INCLUDE footer %]
539
540 Notice that this request gets C<POST>ed back to wherever it came from, using
541 C<request.path>. This is because if the user submits correct credentials,
542 C<get_user> will now return a valid user object, and the request will pass
543 through unhindered to the original URL.
544
545 =head3 Logging Out
546
547 Now your users are logged in, you want a way of having them log out
548 again and taking the authentication cookie away from them, sending
549 them back to the front page as an unprivileged user.
550
551 B<Solution>: Just call the C<logout> method of
552 C<Maypole::Plugin::Authentication::UserSessionCookie>. You may also want
553 to use the template switcheroo hack to send them back to the frontpage.
554
555 =head3 Multi-level Authorization
556
557 You have both a global site access policy (for instance, requiring a
558 user to be logged in except for certain pages) and a policy for
559 particular tables. (Only allowing an admin to delete records in some
560 tables, say, or not wanting people to get at the default set of methods
561 provided by the model class.) 
562
563 You don't know whether to override the global C<authenticate> method or
564 provide one for each class.
565
566 B<Solution>: Do both.
567 Maypole checks whether there is an C<authenticate> method for the model
568 class (e.g. BeerDB::Beer) and if so calls that. If there's no such
569 method, it calls the default global C<authenticate> method in C<Maypole>,
570 which always succeeds. You can override the global method as we saw
571 above, and you can provide methods in the model classes.
572
573 To use per-table access control you can just add methods to your model
574 subclasses that specify individual policies, perhaps like this:
575
576     sub authenticate { # Ensure we can only create, reject or accept
577         my ($self, $r) = @_;
578         return OK if $r->action =~ /^(issue|accept|reject|do_edit)$/;
579         return; # fail if any other action
580     }
581
582 If you define a method like this, the global C<authenticate> method will
583 not be called, so if you want it to be called you need to do so
584 explicitly:
585
586     sub authenticate { # Ensure we can only create, reject or accept
587         my ($self, $r) = @_;
588         return unless $r->authenticate($r) == OK; # fail if not logged in
589         # now it's safe to use $r->user
590         return OK if $r->action =~ /^(accept|reject)$/
591             or ($r->user eq 'fred' and $r->action =~ /^(issue|do_edit)$/);
592         return; # fail if any other action
593     }
594
595 =head2 Creating and editing hacks
596
597 These hacks particularly deal with issues related to the C<do_edit>
598 built-in action.
599
600 =head3 Limiting data for display
601
602 You want the user to be able to type in some text that you're later
603 going to display on the site, but you don't want them to stick images in
604 it, launch cross-site scripting attacks or otherwise insert messy HTML.
605
606 B<Solution>: Use the L<CGI::Untaint::html> module to sanitize the HTML
607 on input. C<CGI::Untaint::html> uses L<HTML::Sanitizer> to ensure that
608 tags are properly closed and can restrict the use of certain tags and
609 attributes to a pre-defined list.
610
611 Simply replace:
612
613     App::Table->untaint_columns(
614         text      => [qw/name description/]
615     );
616
617 with:
618
619     App::Table->untaint_columns(
620         html      => [qw/name description/]
621     );
622
623 And incoming HTML will be checked and cleaned before it is written to
624 the database.
625
626 =head3 Getting data from external sources
627
628 You want to supplement the data received from a form with additional
629 data from another source.
630
631 B<Solution>: Munge the contents of C< $r-E<gt>params > before jumping
632 to the original C<do_edit> routine. For instance, in this method,
633 we use a L<Net::Amazon> object to fill in some fields of a database row
634 based on an ISBN:
635
636     use Net::Amazon;
637     my $amazon = Net::Amazon->new(token => 'YOUR_AMZN_TOKEN');
638
639     ...
640
641     sub create_from_isbn :Exported {
642        my ($self, $r) = @_;
643        my $book_info = $amazon->search(asin => $r->params->{isbn})->properties;
644
645        # Rewrite the CGI parameters with the ones from Amazon
646        $r->params->{title} = $book_info->title;
647        $r->params->{publisher} = $book_info->publisher;
648        $r->params->{year} = $book_info->year;
649        $r->params->{author} = join('and', $book_info->authors());
650  
651        # And jump to the usual edit/create routine
652        $self->do_edit($r);
653     }
654
655 The request will carry on as though it were a normal C<do_edit> POST, but
656 with the additional fields we have provided.
657 You might also want to add a template switcheroo so the user can verify
658 the details you imported.
659
660 =head3 Catching errors in a form
661
662 A user has submitted erroneous input to an edit/create form. You want to
663 send him back to the form with errors displayed against the erroneous
664 fields, but have the other fields maintain the values that the user
665 submitted.
666
667 B<Solution>: This is basically what the default C<edit> template and
668 C<do_edit> method conspire to do, but it's worth highlighting again how
669 they work. 
670
671 If there are any errors, these are placed in a hash, with each error
672 keyed to the erroneous field. The hash is put into the template as
673 C<errors>, and we process the same F<edit> template again:
674
675         $r->template_args->{errors} = \%errors;
676         $r->template("edit");
677
678 This throws us back to the form, and so the form's template should take
679 note of the errors, like so:
680
681      FOR col = classmetadata.columns;
682         NEXT IF col == "id";
683         "<P>";
684         "<B>"; classmetadata.colnames.$col; "</B>";
685         ": ";
686             item.to_field(col).as_HTML;
687         "</P>";
688         IF errors.$col;
689             "<FONT COLOR=\"#ff0000\">"; errors.$col; "</FONT>";
690         END;
691     END;
692
693 If we're designing our own templates, instead of using generic ones, we
694 can make this process a lot simpler. For instance:
695
696     <TR><TD>
697     First name: <INPUT TYPE="text" NAME="forename">
698     </TD>
699     <TD>
700     Last name: <INPUT TYPE="text" NAME="surname">
701     </TD></TR>
702
703     [% IF errors.forename OR errors.surname %]
704         <TR>
705         <TD><SPAN class="error">[% errors.forename %]</SPAN> </TD>
706         <TD><SPAN class="error">[% errors.surname %]</SPAN> </TD>
707         </TR>
708     [% END %]
709
710 The next thing we want to do is to put the originally-submitted values
711 back into the form. We can do this relatively easily because Maypole
712 passes the Maypole request object to the form, and the POST parameters
713 are going to be stored in a hash as C<request.params>. Hence:
714
715     <TR><TD>
716     First name: <INPUT TYPE="text" NAME="forename"
717     VALUE="[%request.params.forename%]">
718     </TD>
719     <TD>
720     Last name: <INPUT TYPE="text" NAME="surname"
721     VALUE="[%request.params.surname%]"> 
722     </TD></TR>
723
724 Finally, we might want to only re-fill a field if it is not erroneous, so
725 that we don't get the same bad input resubmitted. This is easy enough:
726
727     <TR><TD>
728     First name: <INPUT TYPE="text" NAME="forename"
729     VALUE="[%request.params.forename UNLESS errors.forename%]">
730     </TD>
731     <TD>
732     Last name: <INPUT TYPE="text" NAME="surname"
733     VALUE="[%request.params.surname UNLESS errors.surname%]"> 
734     </TD></TR>
735
736 =head3 Uploading files and other data
737
738 You want the user to be able to upload files to store in the database.
739
740 B<Solution>: It's messy.
741
742 First, we set up an upload form, in an ordinary dummy action. Here's
743 the action:
744
745     sub upload_picture : Exported {}
746
747 And here's the F<custom/upload_picture> template:
748
749     <FORM action="/user/do_upload" enctype="multipart/form-data" method="POST">
750
751     <P> Please provide a picture in JPEG, PNG or GIF format:
752     </P>
753     <INPUT TYPE="file" NAME="picture">
754     <BR>
755     <INPUT TYPE="submit">
756     </FORM>
757
758 (Although you'll probably want a bit more HTML around it than that.)
759
760 Now we need to write the C<do_upload> action. At this point we have to get a
761 little friendly with the front-end system. If we're using L<Apache::Request>,
762 then the C<upload> method of the C<Apache::Request> object (which
763 L<Apache::MVC> helpfully stores in C<$r-E<gt>{ar}>) will work for us:
764
765     sub do_upload :Exported {
766         my ($class, $r) = @_;
767         my $user = $r->user;
768         my $upload = $r->ar->upload("picture");
769
770 This returns a L<Apache::Upload> object, which we can query for its
771 content type and a file handle from which we can read the data. It's
772 also worth checking the image isn't going to be too massive before we
773 try reading it and running out of memory, and that the content type is
774 something we're prepared to deal with. 
775
776     if ($upload) {
777         my $ct = $upload->info("Content-type");
778         return $r->error("Unknown image file type $ct")
779             if $ct !~ m{image/(jpeg|gif|png)};
780         return $r->error("File too big! Maximum size is ".MAX_IMAGE_SIZE)
781             if $upload->size > MAX_IMAGE_SIZE;
782
783         my $fh = $upload->fh;
784         my $image = do { local $/; <$fh> };
785
786 Don't forget C<binmode()> in there if you're on a platform that needs it.
787 Now we can store the content type and data into our database, store it
788 into a file, or whatever:
789
790         $r->user->photo_type($ct);
791         $r->user->photo($image);
792     }
793
794 And finally, we use our familiar template switcheroo hack to get back to
795 a useful page:
796
797         $r->objects([ $user ]);
798         $r->template("view");
799     }
800
801 Now, as we've mentioned, this only works because we're getting familiar with
802 C<Apache::Request> and its C<Apache::Upload> objects. If we're using
803 L<CGI::Maypole> instead, we can write the action in a similar style:
804
805     sub do_upload :Exported {
806         my ($class, $r) = @_;
807         my $user = $r->user;
808         my $cgi = $r->cgi;
809         if ($cgi->upload == 1) { # if there was one file uploaded
810             my $filename = $cgi->param('picture');
811             my $ct = $cgi->upload_info($filename, 'mime');
812             return $r->error("Unknown image file type $ct")
813                 if $ct !~ m{image/(jpeg|gif|png)};
814             return $r->error("File too big! Maximum size is ".MAX_IMAGE_SIZE)
815                 if $cgi->upload_info($filename, 'size') > MAX_IMAGE_SIZE;
816             my $fh = $cgi->upload($filename);
817             my $image = do { local $/; <$fh> };
818             $r->user->photo_type($ct);
819             $r->user->photo($image);
820         }
821
822         $r->objects([ $user ]);
823         $r->template("view");
824     }
825
826 It's easy to adapt this to upload multiple files if desired.
827 You will also need to enable uploads in your driver initialization,
828 with the slightly confusing statement:
829
830     $CGI::Simple::DISABLE_UPLOADS = 0; # enable uploads
831
832 Combine with the "Displaying pictures" hack above for a happy time.
833
834 =head2 Links
835
836 L<Contents|Maypole::Manual>,
837 Next L<Flox|Maypole::Manual::Flox>,
838 Previous L<The Beer Database, Twice|Maypole::Manual::Beer>
839
840