]> git.decadent.org.uk Git - maypole.git/commitdiff
Introduction to Flox, beginnings of request chapter.
authorSimon Cozens <simon@simon-cozens.org>
Mon, 5 Apr 2004 22:16:18 +0000 (22:16 +0000)
committerSimon Cozens <simon@simon-cozens.org>
Mon, 5 Apr 2004 22:16:18 +0000 (22:16 +0000)
git-svn-id: http://svn.maypole.perl.org/Maypole/trunk@115 48953598-375a-da11-a14b-00016c27c3ee

doc/Flox.pod
doc/Request.pod

index ee0a96ac0d3097995c330422a57c6f5920591705..b8d3d7a5f02108f7c18c32a13cacf073dbd306b6 100644 (file)
@@ -1 +1,21 @@
-=head1 FlOx: A Social Networking Site
+=head1 Flox: A Free Social Networking Site
+
+Friendster, Tribe, and now Google's Orkut - it seems like in early 2004,
+everyone wanted to be a social networking site. At the time, I was too
+busy to be a social networking site, as I was working on my own project
+at the time, a web application server called Maypole. However, I
+realised that if I could implement a social networking system using
+Maypole, then Maypole could probably do anything.
+
+I'd already decided there was room for a free, open-source networking
+site, and then Peter Sergeant came up with the hook - localizing it to
+universities and societies, and tying in meet-ups with restaurant
+bookings. I called it Flox, partially because it flocks people together
+and partially because it's localised for my home town of Oxford and its
+university.
+
+Flox is still in, uh, flux, but it does the essentials. In this chapter,
+we're going to see how it was put together, and how the techniques shown
+in the L<Request.pod> chapter can help to create a sophisticated web
+application. Of course, I didn't have this manual available at the time,
+so it took a bit longer than it should have done...
index 815ea1d439d71a535b33edf68eff5a1e81a43405..6af82784ba5263dc2e78a07a681251bf63e1bbd2 100644 (file)
@@ -1,9 +1,127 @@
 =head1 Maypole Request Hacking Cookbook
 
-=head2 Changing how params are parsed
+=head2 Frontend hacks
 
-=head2 Authentication
+=head3 Changing how params are parsed
 
-=head2 REST
+=head3 REST
 
-=head2 Filling Content Yourself
+=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<Solution>: Munge the contents of C< $r-E<gt>params > before jumping
+to the original C<do_edit> routine. For instance, in this method,
+we use a C<Net::Amazon> 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<do_edit> 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<Solution>: 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:
+
+    <song>
+        <header>
+            <title>Layla</title>
+            <artist>Derek and the Dominos</artist>
+            <key>Dm</key>
+        </header>
+        <lyrics>
+          <verse>...</verse>
+          <chorus>
+            <line> <sup>A</sup>Lay<sup>Dm</sup>la <sup>Bb</sup> </line> 
+            <line> <sup>C</sup>Got me on my <sup>Dm</sup>knees </line> 
+            ...
+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<use> 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<XML::TreeBuilder> 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<Template::Plugin::XSLT> module, this is as simple as putting the
+following into F<templates/song/view>:
+
+    [%
+        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<text/html>, out of your database.
+
+B<Solution>: Fill the content and content-type yourself.
+
+Here's a subroutine which displays the C<photo> for either a specified
+user or the currently logged in user. We set the C<output> 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<content_type> 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;
+        }
+    }