=head1 Maypole Request Hacking Cookbook =head2 Frontend hacks =head3 Changing how params are parsed =head3 REST =head2 Authentication hacks =head2 Creating and editing hacks =head3 Getting data from external sources You want to supplement the data received from a form with additional data from another source. B: Munge the contents of C< $r-Eparams > before jumping to the original C routine. For instance, in this method, we use a C object to fill in some fields of a database row based on an ISBN: sub create_from_isbn :Exported { my ($self, $r) = @_; my $response = $ua->search(asin => $r->{params}{isbn}); my ($prop) = $response->properties; # Rewrite the CGI parameters with the ones from Amazon @{$r->{params}{qw(title publisher author year)} = ($prop->title, $prop->publisher, (join "/", $prop->authors()), $prop->year()); # And jump to the usual edit/create routine $self->do_edit($r); } The request will carry on as though it were a normal C POST, but with the additional fields we have provided. =head2 Content display hacks =head3 XSLT Here's a hack I've used a number of times. You want to store structured data in a database and to abstract out its display. B: You have your data as XML, because handling big chunks of XML is a solved problem. Build your database schema as usual around the important elements that you want to be able to search and browse on. For instance, I have an XML format for songs which has a header section of the key, title and so on, plus another section for the lyrics and chords:
Layla Derek and the Dominos Dm
... ALayDmla Bb CGot me on my Dmknees ... I store the title, artist and key in the database, as well as an "xml" field which contains the whole song as XML. To load the songs into the database, I can C the driver class for my application, since that's a handy way of setting up the database classes we're going to need to use. Then the handy C will handle the XML parsing for us: use Songbook; use XML::TreeBuilder; my $t = XML::TreeBuilder->new; $t->parse_file("songs.xml"); for my $song ($t->find("song")) { my ($key) = $song->find("key"); $key &&= $key->as_text; my ($title) = $song->find("title"); $title = $title->as_text; my ($artist) = $song->find("artist"); $artist = $artist->as_text; my ($first_line) = $song->find("line"); $first_line = join "", grep { !ref } $first_line->content_list; $first_line =~ s/[,\.\?!]\s*$//; Songbook::Song->find_or_create({ title => $title, first_line => $first_line, song_key => Songbook::SongKey->find_or_create({name => $key}), artist => Songbook::Artist->find_or_create({name => $artist}), xml => $song->as_XML }); } Now we need to set up the custom display for each song; thankfully, with the C module, this is as simple as putting the following into F: [% USE transform = XSLT("song.xsl"); song.xml | $transform %] We essentially pipe the XML for the selected song through to an XSL transformation, and this will fill out all the HTML we need. Job done. =head3 Displaying pictures You want to serve a picture, or something else which doesn't have a content type of C, out of your database. B: Fill the content and content-type yourself. Here's a subroutine which displays the C for either a specified user or the currently logged in user. We set the C slot of the Maypole request object: if this is done then the view class is not called upon to process a template, since we already have some output to display. We also set the C using one from the database. sub view_picture :Exported { my ($self, $r) = @_; my $user = $r->{objects}->[0] || $r->{user}; if ($r->{content_type} = $user->photo_type) { $r->{output} = $user->photo; } }