+ my $user = $r->{objects}->[0];
+ $r->{content_type} = $user->photo_type;
+ $r->{output} = $user->photo;
+ }
+
+Of course, the file doesn't necessarily need to be in the database
+itself; if your file is stored in the filesystem, but you have a file
+name or some other pointer in the database, you can still arrange for
+the data to be fetched and inserted into C<$r-E<gt>{output}>.
+
+=head3 Component-based Pages
+
+You're designing something like a portal site which has a number of
+components, all displaying different bits of information about different
+objects. You want to include the output of one Maypole request call while
+building up another.
+
+B<Solution>: Use C<Maypole::Component>. By inheriting from this, you can
+call the C<component> method on the Maypole request object to make a
+"sub-request". For instance, if you have a template
+
+ <DIV class="latestnews">
+ [% request.component("/news/latest_comp") %]
+ </DIV>
+
+ <DIV class="links">
+ [% request.component("/links/list_comp") %]
+ </DIV>
+
+then the results of calling the C</news/latest_comp> action and template
+will be inserted in the C<latestnews> DIV, and the results of calling
+C</links/list_comp> will be placed in the C<links> DIV. Naturally, you're
+responsible for exporting actions and creating templates which return
+fragments of HTML suitable for inserting into the appropriate locations.
+
+=head3 Bailing out with an error
+
+Maypole's error handling sucks. Something really bad has happened to the
+current request, and you want to stop processing now and tell the user about
+it.
+
+B<Solution>: Maypole's error handling sucks because you haven't written it
+yet. Maypole doesn't know what you want to do with an error, so it doesn't
+guess. One common thing to do is to display a template with an error message
+in it somewhere.
+
+Put this in your driver class:
+
+ sub error {
+ my ($r, $message) = @_;
+ $r->{template} = "error";
+ $r->{template_args}{error} = $message;
+ return OK;
+ }
+
+And then have a F<custom/error> template like so:
+
+ [% PROCESS header %]
+ <H2> There was some kind of error... </H2>
+ <P>
+ I'm sorry, something went so badly wrong, we couldn't recover. This
+ may help:
+ </P>
+ <DIV CLASS="messages"> [% error %] </DIV>
+
+Now in your actions you can say things like this:
+
+ if (1 == 0) { return $r->error("Sky fell!") }
+
+This essentially uses the template switcheroo hack to always display the
+error template, while populating the template with an C<error> parameter.
+Since you C<return $r-E<gt>error>, this will terminate the processing
+of the current action.
+
+The really, really neat thing about this hack is that since C<error>
+returns C<OK>, you can even use it in your C<authenticate> routine:
+
+ sub authenticate {
+ my ($self, $r) = @_;
+ $r->get_user;
+ return $r->error("You do not exist. Go away.")
+ if $r->{user} and $r->{user}->status ne "real";
+ ...
+ }
+
+This will bail out processing the authentication, the model class, and
+everything, and just skip to displaying the error message.
+
+Non-showstopper errors or other notifications are best handled by tacking a
+C<messages> template variable onto the request:
+
+ if ((localtime)[6] == 1) {
+ push @{$r->{template_args}{messages}}, "Warning: Today is Monday";
+ }
+
+Now F<custom/messages> can contain:
+
+ [% IF messages %]
+ <DIV class="messages">
+ <UL>
+ [% FOR message = messages %]
+ <LI> [% message %] </LI>
+ [% END %]
+ </UL>
+ </DIV>
+ [% END %]
+
+And you can display messages to your user by adding C<PROCESS messages> at an
+appropriate point in your template; you may also want to use a template
+switcheroo to ensure that you're displaying a page that has the messages box in
+it.
+
+=head2 Authentication hacks
+
+The next series of hacks deals with providing the concept of a "user" for
+a site, and what you do with one when you've got one.
+
+=head3 Logging In
+
+You need the concept of a "current user".
+
+B<Solution>: Use something like
+C<Maypole::Authentication::UserSessionCookie> to authenticate a user
+against a user class and store a current user object in the request
+object.
+
+C<UserSessionCookie> provides the C<get_user> method which tries to get
+a user object, either based on the cookie for an already authenticated
+session, or by comparing C<username> and C<password> form parameters
+against a C<user> table in the database. Its behaviour is highly
+customizable, so see the documentation, or the authentication paper at
+C<http://maypole.simon-cozens.org/docs/authentication.html> for examples.
+
+=head3 Pass-through login
+
+You want to intercept a request from a non-logged-in user and have
+them log in before sending them on their way to wherever they were
+originally going.
+
+B<Solution>:
+
+ sub authenticate {
+ my ($self, $r) = @_;
+ $r->get_user;
+ return OK if $r->{user};
+ # Force them to the login page.
+ $r->{template} = "login";
+ return OK;
+ }
+
+This will display the C<login> template, which should look something
+like this:
+
+ [% INCLUDE header %]
+
+ <h2> You need to log in </h2>
+
+ <DIV class="login">
+ [% IF login_error %]
+ <FONT COLOR="#FF0000"> [% login_error %] </FONT>
+ [% END %]
+ <FORM ACTION="/[% request.path%]" METHOD="post">
+ Username:
+ <INPUT TYPE="text" NAME="[% config.auth.user_field || "user" %]"> <BR>
+ Password: <INPUT TYPE="password" NAME="password"> <BR>
+ <INPUT TYPE="submit">
+ </FORM>
+ </DIV>
+
+Notice that this request gets C<POST>ed back to wherever it came from, using
+C<request.path>. This is because if the user submits correct credentials,
+C<get_user> will now return a valid user object, and the request will pass
+through unhindered to the original URL.
+
+=head3 Logging Out
+
+Now your users are logged in, you want a way of having them log out
+again and taking the authentication cookie away from them, sending
+them back to the front page as an unprivileged user.
+
+B<Solution>: This action, on the user class, is probably overkill, but
+it does the job:
+
+ sub logout :Exported {
+ my ($class, $r) = @_;
+ # Remove the user from the request object
+ my $user = delete $r->{user};
+ # Destroy the session
+ tied(%{$r->{session}})->delete;
+ # Send a new cookie which expires the previous one
+ my $cookie = Apache::Cookie->new($r->{ar},
+ -name => $r->config->{auth}{cookie_name},
+ -value => undef,
+ -path => "/"
+ -expires => "-10m"
+ );
+ $cookie->bake();
+ # Template switcheroo
+ $r->template("frontpage");
+ }
+
+=head3 Multi-level Authentication
+
+You have both a global site access policy (for instance, requiring a
+user to be logged in except for certain pages) and a policy for
+particular tables. (Only allowing an admin to delete records in some
+tables, say, or not wanting people to get at the default set of methods
+provided by the model class.)
+
+You don't know whether to override the global C<authenticate> method or
+provide one for each class.
+
+B<Solution>: Do both. Have a global C<authenticate> method which calls
+a C<sub_authenticate> method based on the class:
+
+ sub authenticate {
+ ...
+ if ($r->{user}) {
+ return $r->model_class->sub_authenticate($r)
+ if $r->model_class->can("sub_authenticate");
+ return OK;