Revision history for Perl extension Maypole
2.0 XXX XXX XX XX:XX:XX XXX XXXX
- - Added parse_args() (Simon Flack)
+ - added parse_args() (Simon Flack)
- call additional_data() and authenticate() for plain templates
- merged Apache2::MVC (mod_perl2 support) into Apache::MVC
- added Maypole::Application universal front-end
- $r->{query} is now deprecated, use $r->{params} for GET and POST
- fixed multiple value handling (Simon Flack)
- added exception handling (Simon Flack)
- - Fixed documentation bugs
- - Changed default documentencoding to utf8. Change with
+ - fixed documentation bugs
+ - changed default documentencoding to utf8. Change with
$r->config->{document_encoding}
- - Removed Maypole::View::Mason as it's distributed separately on CPAN.
- - Factory templates are now XHTML 1.1 compliant.
+ - removed Maypole::View::Mason as it's distributed separately on CPAN.
+ - factory templates are now XHTML 1.1 compliant.
- made the config hash into -> Maypole::Config
- accessors for everything
- - Win32 support
+ - win32 support
+ - new documentation for Maypole.pm (Simon Flack)
+ - moved doc/*.pod to lib/Maypole/Manual
1.7 Sat Jul 17 20:15:26 BST 2004
- Emergency release - we lost the "use Maypole::Constants" from
+++ /dev/null
-
-=head1 Introduction to the Maypole Request Model
-
-This chapter serves as a gentle introduction to Maypole and setting up
-Maypole applications. We look at Maypole is, how to get it up and
-running, and how to start thinking about building Maypole applications.
-
-=head2 What is Maypole?
-
-Presumably you have some idea of what Maypole is all about, or otherwise
-you wouldn't be reading this manual. But Maypole is good at many
-different things, and you may have accidentally focussed on one aspect
-of Maypole while missing the big picture.
-
-For instance, you may know that Maypole is extremely good at putting web
-front-ends onto databases. This is true, but it's only a part of what
-Maypole does. You may have heard that Maypole is a web application
-framework, which is true, but it doesn't mean very much. There are a
-huge number of things that Maypole can do, because it's very much a
-blank slate. You can make it do what you will. In this manual, we'll be
-making it act as a front-end to a database, as a social network site, as
-an intranet portal, and many other things besides. It is a framework.
-
-I like to think that Maypole is a way of going from a URL to a method
-call to some output. If you have a URL like C</product/order/12>,
-Maypole is a way of having it load up product number 12, call an
-C<order> method, and produce a page about what it's just done. The
-reason Maypole is such a big deal is because it does all this for you.
-You no longer have to care about your web server. You hardly have to
-care about your database. You don't have to care about templating
-modules, parsing CGI parameters, or anything else. You only need to care
-about business logic, and the business logic in this instance is how you
-C<order> a product, and what you need to display about it once you've
-done so. This is what programming should be: only caring about the work
-that distinguishes one program from another.
-
-It does this using a technique called MVC for web applications.
-
-=head2 What is MVC for web applications?
-
-Maypole was originally called C<Apache::MVC>, reflecting its basis in
-the Model-View-Controller design pattern. (I had to change it firstly
-because Maypole isn't tied to Apache, and secondly because
-C<Apache::MVC> is a really dull name.) It's the same design pattern that
-forms the foundation of similar projects in other languages, such as
-Java's Struts framework.
-
-This design pattern is found primarily in graphical applications; the
-idea is that you have a Model class which represents and manipulates
-your data, a View class which is responsible for displaying that data to
-the user, and a Controller class which controls the other classes in
-response to events triggered by the user. This analogy doesn't
-correspond precisely to a web-based application, but we can take an
-important principle from it. As Andy Wardley explains:
-
- What the MVC-for-the-web crowd are really trying to achieve is a clear
- separation of concerns. Put your database code in one place, your
- application code in another, your presentation code in a third place.
- That way, you can chop and change different elements at will,
- hopefully without affecting the other parts (depending on how well your
- concerns are separated, of course). This is common sense and good practice.
- MVC achieves this separation of concerns as a by-product of clearly
- separating inputs (controls) and outputs (views).
-
-This is what Maypole does. It has a number of database drivers, a number
-of front-end drivers and a number of templating presentation drivers.
-In common cases, Maypole provides precisely what you need for all of
-these areas, and you get to concentrate on writing just the business
-logic of your application. This is one of the reasons why Maypole lets
-you develop so rapidly: because most of the time, you don't need to do
-any development at all.
-
-=head2 Installing Maypole
-
-The first thing you're going to need to do to get Maypole running is to
-install it. Maypole needs an absolute shedload of Perl modules from CPAN
-to do its job. I am unrepentant about this. Maypole does a lot of work,
-so that you don't have to. This is called code re-use, and if we're
-serious about code re-use, then Maypole should be re-using as much code
-as possible in terms of Perl modules. In another sense, this gives the
-impression that Maypole doesn't actually do all that much itself,
-because all it's doing is gluing together already-existing code. Well,
-welcome to code re-use.
-
-The downside of code re-use is, of course, that you then have to install
-a shedload of Perl modules from CPAN. If you're using OpenBSD or
-FreeBSD, the wonderful ports system will be your friend. There's a
-Maypole port in C<p5-Maypole>. Just type C<make install>.
-
-Debian users, hang in there. There's a package coming.
-
-For other Unices, the L<CPANPLUS> or C<CPAN> modules will help with
-this. If you don't have C<CPANPLUS> installed, my recommendation is to
-use C<perl -MCPAN -e install CPANPLUS> to install it and then throw
-C<CPAN.pm> away. In any case, one of these two should get all that
-Maypole needs:
-
- % perl -MCPANPLUS -e 'install Maypole'
- % perl -MCPAN -e 'install Maypole'
-
-I don't know if Maypole works on Windows. I'm not sure I care.
-
-You're also going to need a database server and a web server. For
-databases, I recommend SQLite (if you install the C<DBD::SQLite> module,
-you get the SQLite library for free) for prototyping and mysql for
-production; heavier duty users should use Postgresql or Oracle - Maypole
-should be happy with them all. Maypole is happiest when running under
-Apache C<mod_perl>, with the C<Apache::Request> module installed, but as
-I said, it is a blank slate, and everything is customizable. There is a
-C<CGI::Maypole> frontend available to run as a standalone CGI script.
-
-=head2 The Beer Database example
-
-Throughout this manual, we're going to be referring back to a particular
-application so that we can give concrete examples for the concepts we're
-talking about. We could say "C<related_accessors> returns a list of
-accessors which can be called to return a list of objects in a has-a
-relationship to the original", or we could say "if we call
-C<related_accessors> on while viewing C<brewery>, it returns C<beers>,
-because we can call C<beers> on a C<brewery> object to get a list of
-that berwery's beers."
-
-Because Maypole is all about beer. If you look carefully, you can
-probably see men playing cricket on the village green. The first
-ever Maypole application was written to help me keep track of the many
-different ales available in my area - their styles, their tastes, their
-breweries, prices and so on. Then the more I thought about it, the more
-I thought it was a particularly good data model for demonstrating
-different kinds of database relationships.
-
-We have a C<brewery> table, which has several C<beer>s. We'll call this
-a has-many relationship. The beers each have a C<style>; styles are
-stored in a separate table, so C<beer> has-a C<style>. Beers are in
-several pubs and a pub has several beers, so beers and pubs are in a
-many-to-many relationship. We use a link table called C<handpump> to
-relate pubs to beers.
-
-All in all, this gives us a schema like the following:
-
- create table brewery (
- id int not null auto_increment primary key,
- name varchar(30),
- url varchar(50),
- notes text
- );
-
- create table beer (
- id int not null auto_increment primary key,
- brewery integer,
- style integer,
- name varchar(30),
- url varchar(120),
- score integer(2),
- price varchar(12),
- abv varchar(10),
- notes text
- );
-
- create table handpump (
- id int not null auto_increment primary key,
- beer integer,
- pub integer
- );
-
- create table pub (
- id int not null auto_increment primary key,
- name varchar(60),
- url varchar(120),
- notes text
- );
-
- create table style (
- id int not null auto_increment primary key,
- name varchar(60),
- notes text
- );
-
-If you have C<DBD::SQLite> available, then a database like this will
-be created when Maypole was installed. Let's now see how to set it up
-with a web interface.
-
-=head2 Setting up Maypole
-
-The first thing we need for a Maypole interface to a database is to
-have a database. If you don't have one, now would be a good time to
-create one, using the schema above.
-
-The next thing we need is a module which is going to do all the work.
-Thankfully, it doesn't need to do B<all> the work itself. It's going to be a
-subclass of C<Maypole> or a Maypole front-end like C<Apache::MVC>.
-
-Here's the driver class for our beer database application. We're not
-going to go into much detail about it here; we'll do that in L<Beer.pod>.
-For now, simply admire its brevity, as you realise this is all the code
-you need to write for a simple database front-end:
-
- package BeerDB;
- use base 'Apache::MVC';
- BeerDB->setup("dbi:SQLite:t/beerdb.db");
- BeerDB->config->{uri_base} = "http://localhost/beerdb/";
- BeerDB->config->{rows_per_page} = 10;
- BeerDB->config->{display_tables} = [qw[beer brewery pub style]];
- BeerDB::Brewery->untaint_columns( printable => [qw/name notes url/] );
- BeerDB::Style->untaint_columns( printable => [qw/name notes/] );
- BeerDB::Beer->untaint_columns(
- printable => [qw/abv name price notes/],
- integer => [qw/style brewery score/],
- date => [ qw/date/],
- );
-
- use Class::DBI::Loader::Relationship;
- BeerDB->config->{loader}->relationship($_) for (
- "a brewery produces beers",
- "a style defines beers",
- "a pub has beers on handpumps");
- 1;
-
-This defines the C<BeerDB> application, which, as it inherits from
-C<Apache::MVC>, will be a mod_perl handler. This means we need to
-tell the Apache configuration about it:
-
- <Location /beerdb>
- SetHandler perl-script
- PerlHandler BeerDB
- </Location>
-
-And now we need some templates. As we'll see in the chapter on views,
-L<View.pod>, there are several types of template. We're going to copy
-the whole lot from the F<templates/> directory of the Maypole source
-package into the F</beerdb> directory under our web root.
-
-And that's it. We should now be able to go to C<http://localhost/beerdb/>
-and see a menu of things to browse; C<http://localhost/beerdb/beer/list>
-will give a list of beers. There might not be any yet. There's a box
-that lets you add them.
-
-If you have any problems getting to this point, you might want to look
-at L<http://wiki.simon-cozens.org/index.cgi?InstallationIssues>.
-
-Play about with the site. Add some beers. Maybe go out and buy some beers
-to review if you need some inspiration. Don't be ill on my carpet.
-
-=head2 Phases of a Maypole request
-
-Now you should have a feel for what Maypole can do. The important thing
-to know at this point is that this is by no means B<all> that Maypole
-can do. What you've seen in the beer database example is all that Maypole
-can do if you don't customize it at all.
-
-Remember that, for instance, we don't ever tell Maypole what tables our
-database has, or what columns each table has. We don't tell Maypole what
-those tables should be called or how to display them. We don't tell Maypole
-what to do - that we want to list, search, edit and delete beers and breweries.
-Maypole just works that out for itself. We can customize it and have Maypole
-do all sorts of interesting things with our database, and most of the rest
-of this manual will be about how to do that.
-
-In order to do that, we need to look at what Maypole's actually doing.
-
-As mentioned, Maypole is responsible for turning a URL into an object, a
-method call, and some templated output. Here's a handy diagram to
-explain how it does that:
-
-=for html
-<IMG SRC="maypole_process2.png">
-
-Maypole's process revolves around the concept of the Maypole request
-object. This is a little like Apache's request object, but at a much
-higher level - in fact, in C<mod_perl>-based Maypole front-ends, the
-Apache request object is incorporated in the Maypole request object. All
-that Maypole does is gradually flesh out this object until it contains
-something in the C<output> member, and then it is dispatched back to the
-front-end for output.
-
-So to start with, we take the Apache request (or CGI object, or other
-way of isolating what's going on) and break it down. For instance, we
-turn the URL C</beer/view/1> into
-
- {
- table => "beer",
- action => "view",
- args => [ 1 ]
- }
-
-Then Maypole will check that C<beer> is a real table, and find the class
-that models it. It also checks whether or not we're allowed to call the
-C<view> method over the network:
-
- {
- table => "beer",
- action => "view",
- args => [ 1 ],
- model_class => "BeerDB::Beer"
- }
-
-Then there's a user-defined authentication method, which by default just
-lets us do anything. Now we hand over to the model class, which loads up
-the object, and decides what template we want to use:
-
- {
- table => "beer",
- action => "view",
- args => [ ],
- objects => [ BeerDB::Beer->retrieve(1) ],
- model_class => "BeerDB::Beer",
- template => "view"
- }
-
-Then it calls C<BeerDB::Beer-E<gt>view>, passing in the request object
-as a parameter, and passes the whole lot to the view class for templating.
-In the next two chapters, we'll look at how Maypole's default model and
-view classes generally do what you want them to do.
+++ /dev/null
-=head1 The Beer Database, Twice
-
-We briefly introduced the "beer database" example in the L<About.pod>
-material, where we presented its driver class as a fait accompli. Where
-did all that code come from, and what does it actually mean?
-
-=head2 The big beer problem
-
-I have a seriously bad habit. This is not the beer problem; this is a
-programming problem. The bad habit is that when I approach a problem I
-want to solve, I get sidetracked deeper and deeper trying to solve more
-and more generic problems, and then, satisfied with solving the generic
-problem, I never get around to solving the specific problem. I always
-write libraries for people writing libraries, and never write
-applications.
-
-The thing with really good beer is that it commands you to drink more of
-it, and then by the morning you can't remember whether it was any good
-or not. After buying several bottles of some random central African
-lager on a dim recollection that it was really good and having it turn
-out to be abysmal, this really became a problem. If only I could have a
-database that I updated every time I buy a new beer, I'd be able to tell
-whether or not I should buy that Lithuanian porter again or whether it
-would be quicker just to flush my money down the toilet and cut out the
-middle-man.
-
-The only problem with databases on Unix is that there isn't really a
-nice way to get data into them. There isn't really a Microsoft Access
-equivalent which can put a simple forms-based front-end onto an
-arbitrary database, and if there is, I either didn't like it or couldn't
-find it, and after a few brews, you really don't want to be trying to type
-in your tasting notes in raw SQL.
-
-So you see a generic problem arising out of a specific problem here. I
-didn't want to solve the specific problem of the beer database, because
-I'd already had another idea for a database that needed a front-end. So
-for two years, I sat on this great idea of having a database of tasting
-notes for beer. I even bought that damned African beer again. Enough was
-enough. I wrote Maypole.
-
-=head2 The easy way
-
-The first Maypole application was the beer database. We've already met
-it; it looks like this.
-
- package BeerDB;
- use base 'Apache::MVC';
- BeerDB->set_database("dbi:SQLite:t/beerdb.db");
- BeerDB->config->{uri_base} = "http://localhost/beerdb/";
- BeerDB->config->{rows_per_page} = 10;
- BeerDB->config->{display_tables} = [qw[beer brewery pub style]];
- BeerDB::Brewery->untaint_columns( printable => [qw/name notes url/] );
- BeerDB::Style->untaint_columns( printable => [qw/name notes/] );
- BeerDB::Beer->untaint_columns(
- printable => [qw/abv name price notes/],
- integer => [qw/style brewery score/],
- date => [ qw/date/],
- );
-
- use Class::DBI::Loader::Relationship;
- BeerDB->config->{loader}->relationship($_) for (
- "a brewery produces beers",
- "a style defines beers",
- "a pub has beers on handpumps");
- 1;
-
-Now, we can split this into four sections. Let's look at them one
-at a time.
-
-=head3 Driver setup
-
-Here's the first section:
-
- package BeerDB;
- use base 'Apache::MVC';
- BeerDB->setup("dbi:SQLite:t/beerdb.db");
-
-This is actually all you need for a functional database front-end. Everything
-else is configuration details. This says three things: we're an application
-called C<BeerDB>. This package is called the B<driver class>, because
-it's a relatively small class which defines how the whole application is
-going to run.
-
-The second line says that our front-end is going to be C<Apache::MVC>,
-which is the Apache C<mod_perl> based version of Maypole; there's also
-a CGI version, C<CGI::Maypole>, and a command-line version for
-debugging, C<Maypole::CLI>, but C<Apache::MVC> is usually the one you
-want.
-
-Thirdly we're going to need to set up our database with the given DBI
-connection string. Now the core of Maypole itself doesn't know about
-DBI; as we explained in L<Model.pod>, this argument is passed to our
-model class wholesale. As we haven't said anything about a model
-class, we get the default one, C<Maypole::Model::CDBI>, which takes a
-DBI connect string. So this one line declares that we're using a C<CDBI>
-model class and it sets up the database for us. In the same way, we
-don't say that we want a particular view class, so we get the default
-C<Maypole::View::TT>.
-
-At this point, everything is in place; we have our driver class, it uses
-a front-end, we have a model class and a view class, and we have a data
-source.
-
-=head3 Application configuration
-
-The next of our four sections is the configuration for the application itself.
-
- BeerDB->config->{uri_base} = "http://localhost/beerdb/";
- BeerDB->config->{rows_per_page} = 10;
- BeerDB->config->{display_tables} = [qw[beer brewery pub style]];
-
-Maypole provides a method called C<config> which returns a hash reference
-of the application's whole configuration. We can use this to set some
-parameters; the C<uri_base> is used as the canonical URL of the base
-of this application, and Maypole uses it to construct links.
-
-By defining C<rows_per_page>, we say that any listings we do with the
-C<list> and C<search> templates should be arranged in sets of pages, with
-a maximum of 10 items on each page. If we didn't declare that, C<list>
-would try to put all the objects on one page, which could well be bad.
-
-Finally, we declare which tables we want our Maypole front-end to
-reference. If you remember from the schema, there's a table called
-C<handpump> which acts as a linking table in a many-to-many relationship
-between the C<pub> and C<beer> tables. As it's only a linking table, we
-don't want people poking with it directly, so we exclude it from the
-list of C<display_tables>.
-
-=head3 Editability
-
-The next section is the following set of lines:
-
- BeerDB::Brewery->untaint_columns( printable => [qw/name notes url/] );
- BeerDB::Style->untaint_columns( printable => [qw/name notes/] );
- BeerDB::Beer->untaint_columns(
- printable => [qw/abv name price notes/],
- integer => [qw/style brewery score/],
- date => [ qw/date/],
- );
-
-As explained in L<StandardTemplates.pod>, this is an set of instructions to
-C<Class::DBI::FromCGI> regarding how the given columns should be edited.
-If we didn't have this section, we'd be able to view and delete records,
-but adding and editing them wouldn't work. It took me ages to work that
-one out.
-
-=head3 Relationships
-
-Finally, we want to explain to Maypole how the various tables relate to
-each other. This is done so that, for instance, when displaying a beer,
-the brewery does not appear as an integer like "2" but as the name of
-the brewery from the C<brewery> table with an ID of 2.
-
-The usual C<Class::DBI> way to do this involves the C<has_a> and
-C<has_many> methods, but I can never remember how to use them, so I came
-up with the C<Class::DBI::Loader::Relationship> module; this was another
-yak that needed shaving on the way to the beer database:
-
- use Class::DBI::Loader::Relationship;
- BeerDB->config->{loader}->relationship($_) for (
- "a brewery produces beers",
- "a style defines beers",
- "a pub has beers on handpumps");
- 1;
-
-C<CDBIL::Relationship> acts on a C<Class::DBI::Loader> object and
-defines relationships between tables in a fairly free-form style.
-The equivalent in ordinary C<Class::DBI> would be:
-
- BeerDB::Brewery->has_many(beers => "BeerDB::Beer");
- BeerDB::Beer->has_a(brewery => "BeerDB::Brewery");
- BeerDB::Style->has_many(beers => "BeerDB::Beer");
- BeerDB::Beer->has_a(style => "BeerDB::Style");
-
- BeerDB::Handpump->has_a(beer => "BeerDB::Beer");
- BeerDB::Handpump->has_a(pub => "BeerDB::Pub");
- BeerDB::Pub->has_many(beers => [ BeerDB::Handpump => 'beer' ]);
- BeerDB::Beer->has_many(pubs => [ BeerDB::Handpump => 'pub' ]);
-
-Maypole's default templates will use this information to display, for
-instance, a list of a brewery's beers on the brewery view page.
-
-This is the complete beer database application; Maypole's default templates
-and the actions in the view class do the rest. But what if we want to do a
-little more. How would we begin to extend this application?
-
-=head2 The hard way
-
-Maypole was written because I don't like writing more Perl code than is
-necessary. I also don't like writing HTML. In fact, I don't really get
-on this whole computer thing, to be honest. But we'll start by ways that
-we can customize the beer application simply by adding methods or
-changing properties of the Perl driver code.
-
-The first thing we ought to look into is the names of the columns; most
-of them are fine, but that "Abv" column stands out. I'd rather that was
-"A.B.V.". Maypole uses the C<column_names> method to map between the
-names of the columns in the database to the names it displays in the
-default templates. This is provided by C<Maypole::Model::Base>, and
-normally, it does a pretty good job; it turns C<model_number> into
-"Model Number", for instance, but there was no way it could guess that
-C<abv> was an abbreviation. Since it returns a hash, the easiest way
-to correct it is to construct a hash consisting of the bits it got
-right, and then override the bits it got wrong:
-
- package BeerDB::Beer;
- sub column_names {
- (shift->SUPER::column_names(), abv => "A.B.V.")
- }
-
-Similarly, the order of columns is a bit wonky. We can fix this by
-overriding the C<display_columns> method; this is also a good way to
-hide away any columns we don't want to have displayed, in the same way
-as declaring the C<display_tables> configuration parameter let us hide
-away tables we weren't using:
-
- sub display_columns {
- ("name", "brewery", "style", "price", "score", "abv", "notes")
- }
-
-Hey, have you noticed that we haven't done anything with the
-beers/handpumps/pubs thing yet? Good, I was hoping that you hadn't.
-Ayway, this is because Maypole can't tell easily that a C<BeerDB::Beer>
-object can call C<pubs> to get a list of pubs. Not yet, at least; we're
-working on it. In the interim, we can explicitly tell Maypole which
-accessors are related to the C<BeerDB::Beer> class like so:
-
- sub related { "pubs" }
-
-Now when we view a beer, we'll have a list of the pubs that it's on at.
+++ /dev/null
-=head1 The Maypole iBuySpy Portal
-
-I think it's good fun to compare Maypole
-
-We begin with a length process of planning and investigating the
-sources. Of prime interest is the database schema and the initial data,
-which we convert to a Mysql database. Converting MS SQL to Mysql is not fun.
-I shall spare you the gore. Especially the bit where the default insert IDs
-didn't match up between the tables.
-
-The C<ibsportal> database has a number of tables which describe how the
-portal should look, and some tables which describe the data that should
-appear on it. The portal is defined in terms of a set of modules; each
-module takes some data from somewhere, and specifies a template to be
-used to format the data. This is quite different from how Maypole
-normally operates, so we have a choice as to whether we're going to
-completely copy this design, or use a more "natural" implementation in
-terms of having the portal display defined as a template itself, with
-all the modules specified right there in Template Toolkit code rather
-than picked up from the database. This would be much faster, since you
-get one shot of rendering instead of having to process each module's
-template independently. The thing is, I feel like showing off
-precisely how flexible Maypole is, so we'll do it the hard way.
-
-The first thing we need to do is get the database into some sort of
-useful shape, and work out the relationships between the tables. This of
-course requires half a day of playing with GraphViz, Omnigraffle and
-mysql, but ended up with something like this:
-
-=for html
-<img src="ibs-schema.png">
-
-This leads naturally to the following driver code:
-
- package Portal;
- use base 'Apache::MVC';
- Portal->setup("dbi:mysql:ibsportal");
- use Class::DBI::Loader::Relationship;
- Portal->config->{loader}->relationship($_) for (
- "A module has a definition", "A module has settings",
- "A tab has modules", "A portal has tabs",
- "A role has a portal", "A definition has a portal",
- "A module has announcements", "A module has contacts",
- "A module has discussions", "A module has events",
- "A module has htmltexts", "A module has links",
- "A module has documents",
- "A user has roles via userrole"
- );
- 1;
-
-As you can see, a portal is made up of a number of different tabs;
-the tabs contain modules, but they're separated into different panes,
-so a module knows whether it belongs on the left pane, the right pane
-or the center. A module also knows where it appears in the pane.
-
-We'll begin by mocking up the portal view in plain text, like so:
-
- use Portal;
- my $portal = Portal::Portal->retrieve(2);
- for my $tab ($portal->tabs) {
- print $tab,"\n";
- for my $pane (qw(LeftPane ContentPane RightPane)) {
- print "\t$pane:\n";
- for (sort { $a->module_order <=> $b->module_order }
- $tab->modules(pane => $pane)) {
- print "\t\t$_:\t", $_->definition,"\n";
- }
- }
- print "\n";
- }
-
-This dumps out the tabs of our portal, along with the modules in each
-tab and their types; this lets us check that we've got the database
-set up properly. If we have, it should produce something like this:
-
- Home
- LeftPane:
- Quick link: Quicklink
- ContentPane:
- Welcome to the IBuySpy Portal: Html Document
- News and Features: announcement
- Upcoming event: event
- RightPane:
- This Week's Special: Html Document
- Top Movers: XML/XSL
-
- ...
-
-Now we want to get the front page up; for the moment, we'll just have it
-display the module names and their definitions like our text mock-up,
-and we'll flesh out the actual modules later.
-
-But before we do that, we'll write a front-end URL handler method, to
-allow us to ape the ASP file names. Why do we want to make a Maypole
-site look like it's running C<.aspx> files? Because we can! - and
-because I want to show we don't necessarily B<have> to follow the
-Maypole tradition of having our URLs look like
-C</I<table>/I<action>/I<id>/I<arguments>>.
-
- our %pages = (
- "DesktopDefault.aspx" => { action => "view", table => "tab" },
- "MobileDefault.aspx" => { action => "view_mobile", table => "tab" },
- );
-
- sub parse_path {
- my $self = shift;
- $self->{path} ||= "DesktopDefault.aspx";
- return $self->SUPER::parse_path if not exists $pages{$self->{path}};
- my $page = $pages{$self->{path}} ;
- $self->{action} = $page->{action};
- $self->{table} = $page->{table};
- my %query = $self->{ar}->args;
- $self->{args} = [ $query{tabid} || $query{ItemID} || 1];
- }
-
- 1;
-
-Here we're overriding the C<parse_path> method which takes the C<path>
-slot from the request and populates the C<table>, C<action> and
-C<arguments> slots. If the user has asked for a page we don't know
-about, we ask the usual Maypole path handling method to give it a try;
-this will become important later on. We turn the default page,
-C<DesktopDefault.aspx>, into the equivalent of C</tab/view/1> unless
-another C<tabid> or C<ItemID> is given in the query parameters; this allows us
-to use the ASP.NET-style C<DesktopDefault.aspx?tabid=3> to select a tab.
-
-Now we have to create our C<tab/view> template; the majority of
-this is copied from the F<DesktopDefault.aspx> source, but our panes
-look like this:
-
- <td id="LeftPane" Width="170">
- [% pane("LeftPane") %]
- </td>
- <td width="1">
- </td>
- <td id="ContentPane" Width="*">
- [% pane("ContentPane") %]
- </td>
- <td id="RightPane" Width="230">
- [% pane("RightPane") %]
- </td>
- <td width="10">
-
- </td>
-
-The C<pane> macro has to be the Template Toolkit analogue of the Perl code
-we used for our mock-up:
-
- [% MACRO pane(panename) BLOCK;
- FOR module = tab.modules("pane", panename);
- "<P>"; module; " - "; module.definition; "</P>";
- END;
- END;
-
-Now, the way that the iBuySpy portal works is that each module has a
-definition, and each definition contains a path to a template:
-C<$module-E<gt>definition-E<gt>DesktopSrc> returns a path name for an C<ascx>
-component file. All we need to do is convert those files from ASP to the
-Template Toolkit, and have Maypole process each component for each module,
-right?
-
-=head2 Components and templates
-
-Dead right, but it was here that I got too clever. I guess it was the word
-"component" that set me off. I thought that since the page was made up of a
-large number of different modules, all requiring their own set of objects, I
-should use a seperate Maypole sub-request for each one, as shown in the
-"Component-based pages" recipe in L<Request.pod>.
-
-So this is what I did. I created a method in C<Portal::Module> that would
-set the template to the appropriate C<ascx> file:
-
- sub view_desktop :Exported {
- my ($self, $r) = @_;
- $r->{template} = $r->objects->[0]->definition->DesktopSrc;
- }
-
-and changed the C<pane> macro to fire off a sub-request for each module:
-
- [% MACRO pane(panename) BLOCK;
- FOR module = tab.modules("pane", panename);
- SET path = "/module/view_desktop/" _ module.id;
- request.component(path);
- END;
- END; %]
-
-This did the right thing, and a call to C</module/view_desktop/12> would
-look up the C<Html Document> module definition, find the C<DesktopSrc> to
-be F<DesktopModules/HtmlModule.ascx>, and process module 12 with that
-template. Once I had converted F<HtmlModule.ascx> to be a Template Toolkit
-file (and we'll look at the conversion of the templates in a second) it
-would display nicely on my portal.
-
-Except it was all very slow; we were firing off a large number of Maypole
-requests in series, so that each template could get at the objects it
-needed. Requests were taking 5 seconds.
-
-That's when it dawned on me that these templates don't actually need different
-objects at all. The only object of interest that C</module/view_desktop> is
-passing in is a C<module> object, and each template figures everything out by
-accessor calls on that. But we already have a C<module> object, in our C<FOR>
-loop - we're using it to make the component call, after all! Why not just
-C<PROCESS> each template inside the loop directly?
-
- [% MACRO pane(panename) BLOCK;
- FOR module = tab.modules("pane", panename);
- SET src = module.definition.DesktopSrc;
- TRY;
- PROCESS $src;
- CATCH DEFAULT;
- "Bah, template $src broke on me!";
- END;
- END;
- END; %]
-
-This worked somewhat better, and took request times from 5 seconds down
-to acceptable sub-second levels again. I could take the C<view_desktop>
-method out again; fewer lines of code to maintain is always good. Now
-all that remained to do for the view side of the portal was to convert
-our ASP templates over to something sensible.
-
-=head2 ASP to Template Toolkit
-
-They're all much of a muchness, these templating languages. Some of
-them, though, are just a wee bit more verbose than others. For instance,
-the banner template which appears in the header consists of 104 lines
-of ASP code; most of those are to create the navigation bar of tabs
-that we can view. Now I admit that we're slightly cheating at the moment
-since we don't have the concept of a logged-in user and so we don't
-distinguish between the tabs that anyone can see and those than only an
-admin can see, but we'll come back to it later. Still, 104 lines, eh?
-
-The actual tab list is presented here: (reformated slightly for sanity)
-
- <tr>
- <td>
- <asp:datalist id="tabs" cssclass="OtherTabsBg"
- repeatdirection="horizontal" ItemStyle-Height="25"
- SelectedItemStyle-CssClass="TabBg" ItemStyle-BorderWidth="1"
- EnableViewState="false" runat="server">
- <ItemTemplate>
- <a href='<%= Request.ApplicationPath %>/
- DesktopDefault.aspx?tabindex=<%# Container.ItemIndex %>&tabid=
- <%# ((TabStripDetails) Container.DataItem).TabId %>' class="OtherTabs">
- <%# ((TabStripDetails) Container.DataItem).TabName %></a>
- </ItemTemplate>
- <SelectedItemTemplate>
- <span class="SelectedTab">
- <%# ((TabStripDetails) Container.DataItem).TabName %></span>
- </SelectedItemTemplate>
- </asp:datalist>
- </td>
- </tr>
-
-But it has to be built up in some 22 lines of C# code which creates and
-populates an array and then binds it to a template parameter. See those
-C<E<lt>%#> and C<E<lt>%=> tags? They're the equivalent of our Template
-Toolkit C<[% %]> tags. C<Request.ApplicationPath>? That's our C<base>
-template argument.
-
-In our version we ask the portal what tabs it has, and display the
-list directly, displaying the currently selected tab differently:
-
- <table id="Banner_tabs" class="OtherTabsBg" cellspacing="0" border="0">
- <tr>
- [% FOR a_tab = portal.tabs %]
- [% IF a_tab.id == tab.id %]
- <td class="TabBg" height="25">
- <span class="SelectedTab">[%tab.name%]</span>
- [% ELSE %]
- <td height="25">
- <a href='[%base%]DesktopDefault.aspx?tabid=[%a_tab.id%]'
- class="OtherTabs">[%a_tab.name%]</a>
- [% END %]
- </td>
- [% END %]
- </tr>
- </table>
-
-This is the way the world should be. But wait, where have we pulled this
-C<portal> variable from? We need to tell the C<Portal> class to put the
-default portal into the template arguments:
-
- sub additional_data {
- shift->{template_args}{portal} = Portal::Portal->retrieve(2);
- }
-
-Translating all the other ASP.NET components is a similar exercise in drudgery;
-on the whole, there was precisely nothing interesting about them at all - we
-merely converted a particularly verbose templating language (and if I never see
-C<asp:BoundColumn> again, it'll be no loss) into a rather more sophisticated
-one.
-
-The simplest component, F<HtmlModule.ascx>, asks a module for its associated
-C<htmltexts>, and then displays the C<DesktopHtml> for each of them in a table.
-This was 40 lines of ASP.NET, including more odious C# to make the SQL calls
-and retrieve the C<htmltexts>. But we can do all that retrieval by magic, so
-our F<HtmlModule.ascx> looks like this:
-
- [% PROCESS module_title %]
- <portal:title EditText="Edit" EditUrl="~/DesktopModules/EditHtml.aspx" />
- <table id="t1" cellspacing="0" cellpadding="0">
- <tr valign="top">
- <td id="HtmlHolder">
- [% FOR html = module.htmltexts; html.DesktopHtml; END %]
- </td>
- </tr>
- </table>
-
-Now I admit that we've cheated here and kept that C<portal:title> tag
-until we know what to do with it - it's obvious that we should turn
-it into a link to edit the HTML of this module if we're allowed to.
-
-The next simplest one actually did provide a slight challenge;
-F<ImageModule.ascx> took the height, width and image source properties
-of an image from the module's C<settings> table, and displayed an C<IMG>
-tag with the appropriate values. This is only slightly difficult because
-we have to arrange the array of C<module.settings> into a hash of
-C<key_name> => C<setting> pairs. Frankly, I can't be bothered to do this
-in the template, so we'll add it into the C<template_args> again. This
-time C<addition_data> looks like:
-
- sub additional_data {
- my $r = shift;
- shift->{template_args}{portal} = Portal::Portal->retrieve(2);
- if ($r->{objects}->[0]->isa("Portal::Module")) {
- $r->{template_args}{module_settings} =
- { map { $_->key_name, $_->setting }
- $r->{objects}->[0]->settings };
- }
- }
-
-And the F<ImageModule.ascx> drops from the 30-odd lines of ASP into:
-
- [% PROCESS module_title; %]
- <img id="Image1" border="0" src="[% module_settings.src %]"
- width="[% module_settings.width %]"
- height="[% module_settings.height %]" />
- <br>
-
-Our portal is taking shape; after a few more templates have been translated,
-we now have a complete replica of the front page of the portal and all its
-tabs. It's fast, it's been developed rapidly, and it's less than 50 lines
-of Perl code so far. But it's not finished yet.
-
-=head2 Adding users
-
+++ /dev/null
-=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 - 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 student population.
-
-Flox is still in, uh, flux, but it does the essentials. 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...
-
-=head2 Mapping the concepts
-
-Any Maypole application should start with two things: a database schema,
-and some idea of what the pages involved are going to look like.
-Usually, these pages will be tying to displaying or editing some element
-of the database, so these two concepts should come hand in hand.
-
-When I started looking at social networking sites, I began by
-identifying the concepts which were going to make up the tables of the
-application. At its most basic, a site like Orkut or Flox has two
-distinct concepts: a user, and a connection between two users.
-Additionally, there's the idea of an invitation to a new user, which can
-be extended, accepted, declined or ignored. These three will make up the
-key tables; there are an extra two tables in Flox, but they're
-essentially enumerations that are a bit easier to edit: each user has an
-affiliation to a particular college or department, and a status in the
-university. (Undergraduate, graduate, and so on.)
-
-For this first run-through, we're going to ignore the ideas of societies
-and communities, and end up with a schema like so:
-
- CREATE TABLE user (
- id int not null auto_increment primary key,
- first_name varchar(50),
- last_name varchar(50),
- email varchar(255),
- profile text,
- password varchar(255),
- affiliation int,
- unistatus int,
- status ENUM("real", "invitee"),
- photo blob,
- photo_type varchar(30)
- );
-
- CREATE TABLE connection (
- id int not null auto_increment primary key,
- from_user int,
- to_user int,
- status ENUM("offered", "confirmed")
- );
-
- CREATE TABLE invitation (
- id char(32) not null primary key,
- issuer int,
- recipient int,
- expires date
- );
-
-Plus the definition of our two auxilliary tables:
-
- CREATE TABLE affiliation (
- id int not null auto_increment primary key,
- name varchar(255)
- );
-
- CREATE TABLE unistatus (
- id int not null auto_increment primary key,
- name varchar(255)
- );
-
-Notice that, for simplicity, invitations and friendship connections are
-quite similar: they are extended from one user to another. This means
-that people who haven't accepted an invite yet still have a place in the
-user table, with a different C<status>. Similarly, a connection between
-users can be offered, and when it is accepted, its status is changed to
-"confirmed" and a reciprocal relationship put in place.
-
-We also have some idea, based on what we want to happen, of what pages
-and actions we're going to define. Leaving the user aside for the
-moment, we want an action which extends an invitation from the current
-user to a new user. We want a page the new user can go to in order to
-accept that invitation. Similarly, we want an action which offers a
-friendship connection to an existing user, and a page the user can go to
-to accept or reject it. This gives us five pages so far:
-
- invitation/issue
- invitation/accept
-
- user/befriend
- connection/accept
- connection/reject
-
-Notice that the C<befriend> action is performed on a user, not a
-connection. This is distinct from C<invitation/issue> because when
-befriending, we have a real user on the system that we want to do
-something to. This makes sense if you think of it in terms of object
-oriented programming - we could say
-
- Flox::Connection->create(to => $user)
-
-but it's clearer to say
-
- $user->befriend
-
-Similarly, we could say
-
- Flox::User->create({ ... })->issue_invitation_to
-
-but it's clearer to say
-
- Flox::Invitation->issue( to => Flox::User->create({ ... }) )
-
-because it more accurately reflects the principal subject and object of
-these actions.
-
-Returning to look at the user class, we want to be able to view a user's
-profile, edit one's own profile, set up the profile for the first
-time, upload pictures and display pictures. We also need to handle the
-concepts of logging in and logging out.
-
-As usual, though, we'll start with a handler class which sets up the
-database:
-
- package Flox;
- use base qw(Apache::MVC);
- Flox->setup("dbi:mysql:flox");
- Flox->config->{display_tables} = [qw[user invitation connection]];
- 1;
-
-Very simple, as these things are meant to be. Now let's build on it.
-
-=head2 Authentication
-
-The concept of a current user is absolutely critical in a site like
-Flox; it represents "me", the viewer of the page, as the site explores
-the connections in my world. We've described the authentication hacks
-briefly in the L<Request.pod> chapter, but now it's time to go into a
-little more detail about how user handling is done.
-
-XXX
-
-We also want to be able to refer to the current user from the templates,
-so we use the overridable C<additional_data> method to give us a C<my>
-template variable:
-
- sub additional_data {
- my $r = shift; $r->{template_args}{my} = $r->{user};
- }
-
-I've called it C<my> rather than C<me> because we it lets us check
-C<[% my.name %]>, and so on.
-
-=head2 Viewing a user
-
-The first page that a user will see after logging in will be their own
-profile, so in order to speed development, we'll start by getting a
-C<user/view> page up.
-
-The only difference from a programming point of view between this action
-and the default C<view> action is that, if no user ID is given, then we
-want to view "me", the current user. Remembering that the default view
-action does nothing, our C<Flox::User::view> action only needs to do
-nothing plus ensure it has a user in the C<objects> slot, putting
-C<$r-E<gt>{user}> in there if not:
-
- sub view :Exported {
- my ($class, $r) = @_;
- $r->{objects} = [ $r->{user} ] unless @{$r->{objects}||[]};
- }
-
-Maypole, unfortunately, is very good at making programming boring. The
-downside of having to write very little code at all is that we now have
-to spend most of our time writing nice HTML for the templates.
-
-XXX
-
-The next stage is viewing the user's photo. Assuming we've got the photo
-stored in the database already (which is a reasonable assumption for the
-moment since we don't have a way to upload a photo quite yet) then we
-can use the a variation of the "Displaying pictures" hack from the
-Requests chapter:
-
- 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;
- } else {
- # Read no-photo photo
- $r->{content_type} = "image/png";
- $r->{output} = slurp_file("images/no-photo.png");
- }
- }
-
-We begin by getting a user object, just like in the C<view> action: either
-the user whose ID was passed in on the URL, or the current user. Then
-we check if a C<photo_type> has been set in this user's record. If so,
-then we'll use that as the content type for this request, and the data
-in the C<photo> attribute as the data to send out. The trick here is
-that setting C<$r-E<gt>{output}> overrides the whole view class processing
-and allows us to write the content out directly.
-
-In our template, we can now say
-
- <IMG SRC="/user/view_picture/[% user.id %]">
-
-and the appropriate user's mugshot will appear.
-
-However, if we're throwing big chunks of data around like C<photo>, it's
-now worth optimizing the C<User> class to ensure that only pertitent
-data is fetched by default, and C<photo> and friends are only fetched on
-demand. The "lazy population" section of C<Class::DBI>'s man page
-explains how to group the columns by usage so that we can optimize
-fetches:
-
- Flox::User->columns(Primary => qw/id/);
- Flox::User->columns(Essential => qw/status/);
- Flox::User->columns(Helpful => qw/ first_name last_name email password/)
- Flox::User->columns(Display => qw/ profile affiliation unistatus /);
- Flox::User->columns(Photo => qw/ photo photo_type /);
-
-This means that the status and ID columns will always be retrieved when
-we deal with a user; next, any one of the name, email or password
-columns will cause that group of data to be retrieved; if we go on to
-display more information about a user, we also load up the profile,
-affiliation and university status; finally, if we're throwing around
-photos, then we load in the photo type and photo data.
-
-These groupings are somewhat arbitrary, and there needs to be a lot of
-profiling to determine the most efficient groupings of columns to load,
-but they demonstrate one principle about working in Maypole: this is the
-first time in dealing with Maypole that we've had to explicitly list the
-columns of a table, but Maypole has so far Just Worked. There's a
-difference, though, between Maypole just working and Maypole working
-well, and if you want to optimize your application, then you need to
-start putting in the code to do that. The beauty of Maypole is that you
-can do as much or as little of such optimization as you want or need.
-
-So now we can view users and their photos. It's time to allow the users
-to edit their profiles and upload a new photo.
-
-=head2 Editing users
-
-XXX Editing a profile
-
-I introduced Flox to a bunch of friends and told them to be as ruthless
-as possible in finding bugs and trying to break it. And break it they
-did; within an hour the screens were thoroughly messed up as users had
-nasty HTML tags in their profiles, names, email addresses and so on.
-This spawned another hack in the request cookbook: "Limiting data for
-display". I changed the untaint columns to use C<html> untainting, and
-all was better:
-
- Flox::User->untaint_columns(
- html => [qw/first_name last_name profile/],
- printable => [qw/password/],
- integer => [qw/affiliation unistatus /],
- email => [qw/email/]
- );
-
-The next stage was the ability to upload a photo. We unleash the "Uploading
-files" recipe, with an additional check to make sure the photo is of a
-sensible size:
-
- use constant MAX_IMAGE_SIZE => 512 * 1024;
- sub do_upload :Exported {
- my ($class, $r) = @_;
- my $user = $r->{user};
- my $upload = $r->{ar}->upload("picture");
- if ($upload) {
- my $ct = $upload->info("Content-type");
- return $r->error("Unknown image file type $ct")
- if $ct !~ m{image/(jpeg|gif|png)};
- return $r->error("File too big! Maximum size is ".MAX_IMAGE_SIZE)
- if $upload->size > MAX_IMAGE_SIZE;
-
- my $fh = $upload->fh;
- my $image = do { local $/; <$fh> };
-
- use Image::Size;
- my ($x, $y) = imgsize(\$image);
- return $r->error("Image too big! ($x, $y) Maximum size is 350x350")
- if $y > 350 or $x > 350;
- $r->{user}->photo_type($ct);
- $r->{user}->photo($image);
- }
-
- $r->objects([ $user ]);
- $r->{template} = "view";
- }
-
-Now we've gone as far as we want to go about user editing at the moment.
-Let's have a look at the real meat of a social networking site: getting
-other people involved, and registering connections between users.
-
-=head2 Invitations
-
-We need to do two things to invitations working: first provide a way to
-issue an invitation, and then provide a way to accept it. Since what
-we're doing in issuing an invitation is essentially creating a new
-one, we'll use our usual practice of having a page to display the form
-to offer an invitation, and then use a C<do_edit> method to actually do
-the work. So our C<issue> method is just an empty action:
-
- sub issue :Exported {}
-
-and the template proceeds as normal:
-
- [% PROCESS header %]
- <h2> Invite a friend </h2>
-
- <FORM ACTION="[%base%]/invitation/do_edit/" METHOD="post">
- <TABLE>
-
-Now we use the "Catching errors in a form" recipe from L<Request.pod> and
-write our form template:
-
- <TR><TD>
- First name: <INPUT TYPE="text" NAME="forename"
- VALUE="[%request.params.forename%]">
- </TD>
- <TD>
- Last name: <INPUT TYPE="text" NAME="surname"
- VALUE="[%request.params.surname%]">
- </TD></TR>
- [% IF errors.forename OR errors.surname %]
- <TR>
- <TD><SPAN class="error">[% errors.forename %]</SPAN> </TD>
- <TD><SPAN class="error">[% errors.surname %]</SPAN> </TD>
- </TR>
- [% END %]
- <TR>
- ...
-
-Now we need to work on the C<do_edit> action. This has to validate the
-form parameters, create the invited user, create the row in the C<invitation>
-table, and send an email to the new user asking them to join.
-
-We'd normally use C<create_from_cgi> to do the first two stages, but this time
-we handle the untainting manually, because there are a surprising number of
-things we need to check before we actually do the create. So here's the
-untainting of the parameters:
-
- my ($self, $r) = @_;
- my $h = CGI::Untaint->new(%{$r->{params}});
- my (%errors, %ex);
- for (qw( email forename surname )) {
- $ex{$_} = $h->extract(
- "-as_".($_ eq "email" ? "email" : "printable") => $_
- ) or $errors{$_} = $h->error;
- }
-
-Next, we do the usual dance of throwing the user back at the form in case
-of errors:
-
- if (keys %errors) {
- $r->{template_args}{message} = "There was something wrong with that...";
- $r->{template_args}{errors} = \%errors;
- $r->{template} = "issue";
- return;
- }
-
-We've introduced a new template variable here, C<message>, which we'll use
-to display any important messages to the user.
-
-The first check we need to do is whether or not we already have a user
-with that email address. If we have, and they're a real user, then we
-abort the invite progress and instead redirect them to viewing that user's
-profile.
-
- my ($user) = Flox::User->search({ email => $ex{email} });
- if ($user) {
- if ($user->status eq "real") {
- $r->{template_args}{message} =
- "That user already seems to exist on Flox. ".
- "Is this the one you meant?";
-
- $self->redirect_to_user($r,$user);
- }
-
-Where C<redirect_to_user> looks like this:
-
- sub redirect_to_user {
- my ($self, $r, $user) = @_;
- $r->{objects} = [ $user ];
- $r->{template} = "view";
- $r->{model_class} = "Flox::User"; # Naughty.
- }
-
-This is, as the comment quite rightly points out, naughty. We're currently
-doing a C</invitation/do_edit/> and we want to turn this into a
-C</user/view/xxx>, changing the table, template and arguments all at once.
-To do this, we have to change the Maypole request object's idea of the model
-class, since this determines where to look for the template: if we didn't,
-we'd end up with C<invitation/view> instead of C<user/view>.
-
-Ideally, we'd do this with a Apache redirect, but we want to get that
-C<message> in there as well, so this will have to do. This isn't good practice;
-we put it into a subroutine so that we can fix it up if we find a better way
-to do it.
-
-Anyway, this is what we should do if a user already exists on the system
-and has accepted an invite already. What if we're trying to invite a user but
-someone else has invited them first and they haven't replied yet?
-
- } else {
- # Put it back to the form
- $r->{template_args}{message} =
- "That user has already been invited; ".
- "please wait for them to accept";
- $r->{template} = "issue";
- }
- return;
- }
-
-Race conditions suck.
-
-Okay. Now we know that the user doesn't exist, and so can create the new
-one:
-
- my $new_user = Flox::User->create({
- email => $ex{email},
- first_name => $ex{forename},
- last_name => $ex{surname},
- status => "invitee"
- });
-
-We want to give the invitee a URL that they can go to in order to
-accept the invite. Now we don't just want the IDs of our invites to
-be sequential, since someone could get one invite, and then guess the
-rest of the invite codes. We provide a relatively secure MD5 hash as
-the invite ID:
-
- my $random = md5_hex(time.(0+{}).$$.rand);
-
-For additional security, we're going to have the URL in the form
-C</invitation/accept/I<id>/I<from_id>/I<to_id>>, encoding the user ids
-of the two users. Now we can send email to the invitee to ask them to
-visit that URL:
-
- my $newid = $new_user->id;
- my $myid = $r->{user}->id;
- _send_mail(to => $ex{email}, url => "$random/$myid/$newid",
- user => $r->{user});
-
-I'm not going to show the C<_send_mail> routine, since it's boring.
-We haven't actually created the C<Invitation> object yet, so let's
-do that now.
-
- Flox::Invitation->create({
- id => $random,
- issuer => $r->{user},
- recipient => $new_user,
- expires => Time::Piece->new(time + LIFETIME)->datetime
- });
-
-You can also imagine a daily cron job that cleans up the C<Invitation>
-table looking for invitations that ever got replied to within their
-lifetime:
-
- ($_->expires > localtime && $_->delete)
- for Flox::Invitation->retrieve_all;
-
-Notice that we don't check whether the ID is already used. We could, but,
-you know, if MD5 sums start colliding, we have much bigger problems on
-our hands.
-
-Anyway, now we've got the invitation created, we can go back to whence we
-came: viewing the original user:
-
- $self->redirect_to_user($r, $r->{user});
-
-Now our invitee has an email, and goes B<click> on the URL. What happens?
-
-XXX
-
-=head2 Friendship Connections
-
-=head2
+++ /dev/null
-=head1 Maypole Model Classes
-
-=head2 Class::DBI
-
-=head2 Maypole::Model::CDBI
-
-Maypole model classes are pretty evil. The very first thing a Maypole
-model class will do in a Maypole application is to cause a load of
-table-based classes to come into being, and then assimilate them.
-
-What I mean by this is that when you set up a Maypole application, in
-your driver class, you'll say something like this:
-
- BeerDB->setup("dbi:mysql:beerdb");
-
-C<setup> is a Maypole method, and it hands its parameter to the model
-class. In our case, the model class is a DBI connect string, because
-that's what C<Maypole::Model::CDBI>, the C<Class::DBI>-based model
-expects. C<Maypole::Model::CDBI> has a method called C<setup_database>
-that creates all the C<Class::DBI> table classes after connecting to the
-database with that connect string. It does this by using
-C<Class::DBI::Loader>, a utility module which asks a database
-about its schema and sets up classes such as C<BeerDB::Beer> to inherit
-from C<Class::DBI>. This is just doing automatically what we did
-manually in our examples above.
-
-Now it gets interesting. The names of these classes are stashed away in
-the application's configuration, and then Maypole forcibly has these
-classes inherit from the model class. Our C<BeerDB::Beer> now inherits
-both from C<Class::DBI> and C<Maypole::Model::CDBI>.
-
-This is useful for two reasons. The first reason is that
-C<Maypole::Model::CDBI> is stuffed full of C<Class::DBI> goodies that
-make writing Maypole applications a lot easier:
-
- package Maypole::Model::CDBI;
- use base qw(Maypole::Model::Base Class::DBI);
- use Class::DBI::AsForm;
- use Class::DBI::FromCGI;
- use Class::DBI::Loader;
- use Class::DBI::AbstractSearch;
- use Class::DBI::Plugin::RetrieveAll;
- use Class::DBI::Pager;
-
-We'll meet most of these goodies in L<StandardTemplates.pod>, where we
-explain how C<Maypole::Model::CDBI> works.
-
-The second reason why we want our table classes to inherit from
-C<Maypole::Model::CDBI> is because C<CDBI> provides a useful set of
-default actions. So what's an action, and why are they useful?
-
-=head2 Extending a model class with actions
-
-Maypole operates primarily by turning URLs into method calls on a model
-class. All that the model stage of Maypole's operation does, when it
-comes down to it, is maintain a mapping of tables to classes, and
-despatch a HTTP request to a method call on the relevant table class. This
-means that if you request a URL such as
-
- http://localhost/beerdb/brewery/delete/20
-
-Maypole's first stage of operation is to turn that into
-C<BeerDB::Brewery-E<gt>delete(20)>. Now, it's slightly more complex than
-that. Firstly because it doesn't actually pass the parameter C<20>, but
-it passes an object representing row 20 in the database, but we can
-gloss over that for the second. No, the real issue is that Maypole does
-not allow you to call any method in the table class; that would be
-somewhat insecure.
-
-Instead, Maypole makes a distinction between the kind of methods that
-only the class itself and other Perl code can call, and the kind of
-methods that anyone can call from a URL. This latter set of methods are
-called B<exported> methods, and exporting is done with by means of Perl
-attributes. You define a method to be exported like so:
-
- sub drink :Exported {
-
-This will allow the user to access C</beerdb/beer/drink> over the web.
-An exported method accompanied with a template to render its output is
-sometimes called an B<action>.
-
-Maypole model classes like C<Maypole::Model::CDBI> come with a
-relatively handy set of actions which are all you need to set up a CRUD
-database front-end: viewing a row in a database, editing it, adding a
-new one, deleting, and so on. The most important thing about Maypole,
-though, is that it doesn't stop there. You can add your own.
-
-For instance, in our beer database application, we could create a
-C<BeerDB::Beer> package, and put some additional actions in there.
-
- package BeerDB::Beer;
- sub top_five :Exported {
- my ($class, $r) = @_;
- $r->{objects} = [ ($r->retrieve_all_sorted_by("score"))[-5..-1] ];
- }
-
-Our action is called as a class method with the Maypole request object.
-It uses the C<Class::DBI::Plugin::RetrieveAll> module's
-C<retrieve_all_sorted_by> mixin to get the top five scoring beers, and
-puts these in the C<objects> slot of the request of object. Next, the
-view class will come along and process the C<top_five> template with
-these five beers.
-
-We'll look more at how to put together actions in the
-L<StandardTemplates.pod> chapter and our case studies.
-
-=head2 What Maypole wants from a model
-
-=head2 Building your own model class
+++ /dev/null
-=head1 Overview of Maypole Documentation
-
-The Maypole documentation is arranged over several files; this is a good
-one to start with.
-
-Once you've read this, you should probably look at L<About.pod>, the
-guide to what Maypole is and how the Maypole request works. It also
-describes how to set up a simple CRUD web application in Maypole.
-
-The next two chapters are quite thorough, and you might want to skip
-over them if you already know how L<Class::DBI> and the L<Template>
-toolkit work. L<Model.pod> and L<View.pod> describe these technologies
-and their relationship to Maypole.
-
-Now we present the default actions and templates - the F<factory>
-templates - in L<StandardTemplates.pod>. When you have read these
-chapters, you are ready to start building more complex applications in
-Maypole than just a simple CRUD interface. L<Beer.pod> reintroduces the
-beer database application and shows how to move from the "magic" of the
-automatically supplied templates and actions to a customized application
-with user-specified actions.
-
-L<Request.pod> contains more information about the Maypole request
-object and provides some cookbook-style techniques: how to provide
-authentication, how to provide a REST interface, how to override various
-stages of the content generation process, and so on.
-
-The final chapters are examples of how to construct large web
-applications in Maypole: L<Flox.pod> describes a "social network" site
-similar to Friendster and Orkut; L<PetStore.pod> implements the Java/C#
-benchmark Pet Store application; and L<BuySpy.pod> implements the
-ASP.NET sample portal application.
+++ /dev/null
-=head1 Maypole Request Hacking Cookbook
-
-Hacks; design patterns; recipes: call it what you like, this chapter is a
-developing collection of techniques which can be slotted in to Maypole
-applications to solve common problems or make the development process easier.
-
-As Maypole developers, we don't necessarily know the "best practice" for
-developing Maypole applications ourselves, in the same way that Larry Wall
-didn't know all about the best Perl programming style as soon as he wrote
-Perl. These techniques are what we're using at the moment, but they may
-be refined, modularized, or rendered irrelevant over time. But they've
-certainly saved us a bunch of hours work.
-
-=head2 Frontend hacks
-
-These hacks deal with changing the way Maypole relates to the outside world;
-alternate front-ends to the Apache and CGI interfaces, or subclassing chunks
-of the front-end modules to alter Maypole's behaviour in particular ways.
-
-=head3 Separate model class modules
-
-You want to put all the C<BeerDB::Beer> routines in a separate module,
-so you say:
-
- package BeerDB::Beer;
- BeerDB::Beer->has_a(brewery => "BeerDB::Brewery");
- sub foo :Exported {}
-
-And in F<BeerDB.pm>, you put:
-
- use BeerDB::Beer;
-
-It doesn't work.
-
-B<Solution>: It doesn't work because of the timing of the module
-loading. C<use Beer::Beer> will try to set up the C<has_a> relationships
-at compile time, when the database tables haven't even been set up,
-since they're set up by
-
- BeerDB->setup("...")
-
-which does its stuff at runtime. There are two ways around this; you can
-either move the C<setup> call to compile time, like so:
-
- BEGIN { BeerDB->setup("...") }
-
-or move the module loading to run-time (my preferred solution):
-
- BeerDB->setup("...");
- BeerDB::Beer->require;
-
-=head3 Debugging with the command line
-
-You're seeing bizarre problems with Maypole output, and you want to test it in
-some place outside of the whole Apache/mod_perl/HTTP/Internet/browser circus.
-
-B<Solution>: Use the C<Maypole::CLI> module to go directly from a URL to
-standard output, bypassing Apache and the network altogether.
-
-C<Maypole::CLI> is not a standalone front-end, but to allow you to debug your
-applications without having to change the front-end they use, it temporarily
-"borgs" an application. If you run it from the command line, you're expected
-to use it like so:
-
- perl -MMaypole::CLI=Application -e1 'http://your.server/path/table/action'
-
-For example:
-
- perl -MMaypole::CLI=BeerDB -e1 'http://localhost/beerdb/beer/view/1?o2=desc'
-
-You can also use the C<Maypole::CLI> module programatically to create
-test suites for your application. See the Maypole tests themselves or
-the documentation to C<Maypole::CLI> for examples of this.
-
-=head3 Changing how URLs are parsed
-
-You don't like the way Maypole URLs look, and want something that either
-fits in with the rest of your site or hides the internal workings of the
-system.
-
-C<Solution>: So far we've been using the C</table/action/id/args> form
-of a URL as though it was "the Maypole way"; well, there is no Maypole
-way. Maypole is just a framework and absolutely everything about it is
-overridable.
-
-If we want to provide our own URL handling, the method to override in
-the driver class is C<parse_path>. This is responsible for taking
-C<$r-E<gt>{path}> and filling the C<table>, C<action> and C<args> slots
-of the request object. Normally it does this just by splitting the path
-on C</>s, but you can do it any way you want, including getting the
-information from C<POST> form parameters or session variables.
-
-For instance, suppose we want our URLs to be of the form
-C<ProductDisplay.html?id=123>, we could provide a C<parse_path> method
-like so:
-
- sub parse_path {
- my $r = shift;
- $r->{path} ||= "ProductList.html";
- ($r->{table}, $r->{action}) =
- ($r->{path} =~ /^(.*?)([A-Z]\w+)\.html/);
- $r->{table} = lc $r->{table};
- $r->{action} = lc $r->{action};
- my %query = $r->{ar}->args;
- $self->{args} = [ $query{id} ];
- }
-
-This takes the path, which already has the query parameters stripped off
-and parsed, and finds the table and action portions of the filename,
-lower-cases them, and then grabs the C<id> from the query. Later methods
-will confirm whether or not these tables and actions exist.
-
-See L<BuySpy.pod> for another example of custom URL processing.
-
-=head3 Maypole for mobile devices
-
-You want Maypole to use different templates to display on particular
-browsers.
-
-B<Solution>: There are several ways to do this, but here's the neatest
-we've found. Maypole chooses where to get its templates either by
-looking at the C<template_root> config parameter or, if this is not
-given, calling the C<get_template_root> method to ask the front-end to
-try to work it out. We can give the front-end a little bit of help, by
-putting this method in our driver class:
-
- sub get_template_root {
- my $r = shift;
- my $browser = $r->{ar}->headers_in->get('User-Agent');
- if ($browser =~ /mobile|palm|nokia/i) {
- "/home/myapp/templates/mobile";
- } else {
- "/home/myapp/templates/desktop";
- }
- }
-
-(Maybe there's a better way to detect a mobile browser, but you get the
-idea.)
-
-=head2 Content display hacks
-
-These hacks deal primarily with the presentation of data to the user,
-modifying the C<view> template or changing the way that the results of
-particular actions are displayed.
-
-=head3 Null Action
-
-You need an "action" which doesn't really do anything, but just formats
-up a template.
-
-B<Solution>: There are two ways to do this, depending on what precisely
-you need. If you just need to display a template, C<Apache::Template>
-style, with no Maypole objects in it, then you don't need to write any
-code; just create your template, and it will be available in the usual
-way.
-
-If, on the other hand, you want to display some data, and what you're
-essentially doing is a variant of the C<view> action, then you need to
-ensure that you have an exported action, as described in
-L<StandardTemplates.pod>:
-
- sub my_view :Exported { }
-
-=head3 Template Switcheroo
-
-An action doesn't have any data of its own to display, but needs to display
-B<something>.
-
-B<Solution>: This is an B<extremely> common hack. You've just issued an
-action like C<beer/do_edit>, which updates the database. You don't want
-to display a page that says "Record updated" or similar. Lesser
-application servers would issue a redirect to have the browser request
-C</beer/view/I<id>> instead, but we can actually modify the Maypole
-request on the fly and, after doing the update, pretend that we were
-going to C</beer/view/I<id>> all along. We do this by setting the
-objects in the C<objects> slot and changing the C<template> to the
-one we wanted to go to.
-
-In this example from L<Flox.pod>, we've just performed an C<accept>
-method on a C<Flox::Invitation> object and we want to go back to viewing
-a user's page.
-
- sub accept :Exported {
- my ($self, $r) = @_;
- my $invitation = $r->objects->[0];
- # [... do stuff to $invitation ...]
- $r->{objects} = [$r->{user}];
- $r->{model_class} = "Flox::User";
- $r->{template} = "view";
- }
-
-This hack is so common that it's expected that there'll be a neater
-way of doing this in the future.
-
-=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, a Word document, 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->{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 REST
-
-You want to provide a programmatic interface to your Maypole site.
-
-B<Solution>: The best way to do this is with C<REST>, which uses a
-descriptive URL to encode the request. For instance, in L<Flox.pod> we
-describe a social networking system. One neat thing you can do with
-social networks is to use them for reputation tracking, and we can use
-that information for spam detection. So if a message arrives from
-C<person@someco.com>, we want to know if they're in our network of
-friends or not and mark the message appropriately. We'll do this by
-having a web agent (say, L<WWW::Mechanize> or L<LWP::UserAgent>) request
-a URL of the form
-C<http://flox.simon-cozens.org/user/relationship_by_email/person%40someco.com>.
-Naturally, they'll need to present the appropriate cookie just like a
-normal browser, but that's a solved problem. We're just interested in
-the REST request.
-
-The request will return a single integer status code: 0 if they're not
-in the system at all, 1 if they're in the system, and 2 if they're our
-friend.
-
-All we need to do to implement this is provide the C<relationship_by_email>
-action, and use it to fill in the output in the same way as we did when
-displaying a picture. Since C<person%40someco.com> is not the ID of a
-row in the user table, it will appear in the C<args> array:
-
- use URI::Escape;
- sub relationship_by_email :Exported {
- my ($self, $r) = @_;
- my $email = uri_unescape($r->{args}[0]);
- $r->{content_type} = "text/plain";
- my $user;
- unless (($user) = Flox::User->search(email => $email)) {
- $r->{content} = "0\n"; return;
- }
-
- if ($r->{user}->is_friend($user)) { $r->{content} = "2\n"; return; };
- $r->{content} = "1\n"; return;
- }
-
-=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.
-
-Alternatively, if you've already got all the objects you need, you can
-probably just C<[% PROCESS %]> the templates directly.
-
-=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;
- }
- ...
- }
-
-And now your C<sub_authenticate> methods can specify the policy for
-each table:
-
- sub sub_authenticate { # Ensure we can only create, reject or accept
- my ($self, $r) = @_;
- return OK if $r->{action} =~ /^(issue|accept|reject|do_edit)$/;
- return;
- }
-
-=head2 Creating and editing hacks
-
-These hacks particularly deal with issues related to the C<do_edit>
-built-in action.
-
-=head3 Limiting data for display
-
-You want the user to be able to type in some text that you're later
-going to display on the site, but you don't want them to stick images in
-it, launch cross-site scripting attacks or otherwise insert messy HTML.
-
-B<Solution>: Use the C<CGI::Untaint::html> module to sanitize the HTML
-on input. C<CGI::Untaint::html> uses C<HTML::Sanitizer> to ensure that
-tags are properly closed and can restrict the use of certain tags and
-attributes to a pre-defined list.
-
-Simply replace:
-
- App::Table->untaint_columns(
- text => [qw/name description/]
- );
-
-with:
-
- App::Table->untaint_columns(
- html => [qw/name description/]
- );
-
-And incoming HTML will be checked and cleaned before it is written to
-the database.
-
-=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.
-
-=head3 Catching errors in a form
-
-A user has submitted erroneous input to an edit/create form. You want to
-send him back to the form with errors displayed against the erroneous
-fields, but have the other fields maintain the values that the user
-submitted.
-
-B<Solution>: This is basically what the default C<edit> template and
-C<do_edit> method conspire to do, but it's worth highlighting again how
-they work.
-
-If there are any errors, these are placed in a hash, with each error
-keyed to the erroneous field. The hash is put into the template as
-C<errors>, and we process the same F<edit> template again:
-
- $r->{template_args}{errors} = \%errors;
- $r->{template} = "edit";
-
-This throws us back to the form, and so the form's template should take
-note of the errors, like so:
-
- FOR col = classmetadata.columns;
- NEXT IF col == "id";
- "<P>";
- "<B>"; classmetadata.colnames.$col; "</B>";
- ": ";
- item.to_field(col).as_HTML;
- "</P>";
- IF errors.$col;
- "<FONT COLOR=\"#ff0000\">"; errors.$col; "</FONT>";
- END;
- END;
-
-If we're designing our own templates, instead of using generic ones, we
-can make this process a lot simpler. For instance:
-
- <TR><TD>
- First name: <INPUT TYPE="text" NAME="forename">
- </TD>
- <TD>
- Last name: <INPUT TYPE="text" NAME="surname">
- </TD></TR>
-
- [% IF errors.forename OR errors.surname %]
- <TR>
- <TD><SPAN class="error">[% errors.forename %]</SPAN> </TD>
- <TD><SPAN class="error">[% errors.surname %]</SPAN> </TD>
- </TR>
- [% END %]
-
-The next thing we want to do is to put the originally-submitted values
-back into the form. We can do this relatively easily because Maypole
-passes the Maypole request object to the form, and the POST parameters
-are going to be stored in a hash as C<request.params>. Hence:
-
- <TR><TD>
- First name: <INPUT TYPE="text" NAME="forename"
- VALUE="[%request.params.forename%]">
- </TD>
- <TD>
- Last name: <INPUT TYPE="text" NAME="surname"
- VALUE="[%request.params.surname%]">
- </TD></TR>
-
-Finally, we might want to only re-fill a field if it is not erroneous, so
-that we don't get the same bad input resubmitted. This is easy enough:
-
- <TR><TD>
- First name: <INPUT TYPE="text" NAME="forename"
- VALUE="[%request.params.forename UNLESS errors.forename%]">
- </TD>
- <TD>
- Last name: <INPUT TYPE="text" NAME="surname"
- VALUE="[%request.params.surname UNLESS errors.surname%]">
- </TD></TR>
-
-=head3 Uploading files and other data
-
-You want the user to be able to upload files to store in the database.
-
-B<Solution>: It's messy.
-
-First, we set up an upload form, in an ordinary dummy action. Here's
-the action:
-
- sub upload_picture : Exported {}
-
-And here's the template:
-
- <FORM action="/user/do_upload" enctype="multipart/form-data" method="POST">
-
- <P> Please provide a picture in JPEG, PNG or GIF format:
- </P>
- <INPUT TYPE="file" NAME="picture">
- <BR>
- <INPUT TYPE="submit">
- </FORM>
-
-(Although you'll probably want a bit more HTML around it than that.)
-
-Now we need to write the C<do_upload> action. At this point we have to get a
-little friendly with the front-end system. If we're using C<Apache::Request>,
-then the C<upload> method of the C<Apache::Request> object (which
-C<Apache::MVC> helpfully stores in C<$r-E<gt>{ar}>) will work for us:
-
- sub do_upload :Exported {
- my ($class, $r) = @_;
- my $user = $r->{user};
- my $upload = $r->{ar}->upload("picture");
-
-This returns a C<Apache::Upload> object, which we can query for its
-content type and a file handle from which we can read the data. It's
-also worth checking the image isn't going to be too massive before we
-try reading it and running out of memory, and that the content type is
-something we're prepared to deal with.
-
- if ($upload) {
- my $ct = $upload->info("Content-type");
- return $r->error("Unknown image file type $ct")
- if $ct !~ m{image/(jpeg|gif|png)};
- return $r->error("File too big! Maximum size is ".MAX_IMAGE_SIZE)
- if $upload->size > MAX_IMAGE_SIZE;
-
- my $fh = $upload->fh;
- my $image = do { local $/; <$fh> };
-
-Now we can store the content type and data into our database, store it
-into a file, or whatever:
-
- $r->{user}->photo_type($ct);
- $r->{user}->photo($image);
- }
-
-And finally, we use our familiar template switcheroo hack to get back to
-a useful page:
-
- $r->objects([ $user ]);
- $r->{template} = "view";
- }
-
-Now, as we've mentioned, this only works because we're getting familiar with
-C<Apache::Request> and its C<Apache::Upload> objects. If we're planning to use
-C<CGI::Maypole> instead, or want to write our application in a generic way so
-that it'll work regardless of front-end, then we need to replace the C<upload>
-call with an equivalent which uses the C<CGI> module to get the upload data.
-This is convoluted and horrific and we're not going to show it here, but it's
-possible.
-
-Combine with the "Displaying pictures" hack above for a happy time.
+++ /dev/null
-
-=head1 Maypole's Standard Templates and Actions
-
-As we saw in our CRUD example, Maypole does all it can to make your life
-easier; this inclues providing a set of default actions and
-factory-supplied templates. These are written in such a generic way,
-making extensive use of class metadata, that they are more or less
-applicable to any table or application. However, in order to progress
-from automatically generated CRUD applications to real customized
-applications, we need to begin by understanding how these default
-actions do their stuff, and how the default templates are put together.
-Once we have an understanding of what Maypole does for us automatically,
-we can begin to customize and create our own templates and actions.
-
-=head2 The standard actions
-
-A simple, uncustomized Maypole model class, such as one of the classes
-in the beer database application, provides the following default actions
-- that is, provides access to the following URLs:
-
-=over 3
-
-=item C</[table]/view/[id]>
-
-This takes the ID of an object in a table, retrieves the object, and
-presents it to the F<view> template.
-
-=item C</[table]/edit/[id]>
-
-This is the same as C<view>, but uses the F<edit> template to provide a
-web form to edit the object; it submits to C<do_edit>.
-
-=item C</[table]/do_edit/[id]>
-
-=item C</[table]/do_edit/>
-
-This provides both editing and row creation facilities.
-
-=item C</[table]/delete/id>
-
-This deletes a row, returning to the C<list> page.
-
-=item C</[table]/list/>
-
-This provides a paged list of the table suitable for browsing.
-
-=item C</[table]/search/>
-
-This handles a search query and presents the search results back to the
-F<list> template.
-
-=back
-
-We'll now look at how these actions are implemented, before moving on to
-take a detailed look at the templates they drive.
-
-=head3 C<view> and C<edit>
-
-These actions are very simple; their job is to take a row ID, turn it
-into an object, and hand it to the template to be displayed. However, as
-taking the first argument and turning it into an object is such a common
-action, it is handled directly by the model class's C<process> method.
-Similarly, the default template name provided by the C<process> method
-is the name of the acction, and so will be C<view> or C<edit>
-accordingly.
-
-So the code required to make these two actions work turns out to be:
-
- sub view :Exported { }
- sub edit :Exported { }
-
-That's right - no code at all. This shows the power of the templating
-side of the system. If you think about it for a moment, it is natural
-that these actions should not have any code - after all, we have
-separated out the concerns of "acting" and displaying. Both of these
-"actions" are purely concerned with displaying a record, and don't need
-to do any "acting". Remember that the "edit" method doesn't actually do
-any editing - this is provided by C<do_edit>; it is just another view of
-the data, albeit once which allows the data to be modified later. These
-two methods don't need to modify the row in any way, they don't need to
-do anything clever. They just are.
-
-So why do we need the subroutines at all? If the subroutines did not exist,
-we would be sent to the C<view> and C<edit> templates as would be
-expected, but these templates would not be provided with the right
-arguments; we need to go through the C<process> method in order to turn
-the URL argument into a row and thence into an object to be fed to the
-template. By exporting these methods, even though they contain no code
-themselves, we force Maypole to call C<process> and provide the class
-and object to the templates.
-
-The moral of this story is that if you need to have an action which is
-purely concerned with display, not acting, but needs to receive an ID
-and turn it into an object, then create an empty method. For instance,
-if we want to make an alternate view of a row which only showed the
-important columns, we might create a method
-
- sub short_view :Exported {}
-
-This will cause the row to be turned into an object and fed to the
-C<short_view> template, and that template would be responsible for
-selecting the particular columns to be displayed.
-
-=head3 C<do_edit>
-
-This action, on the other hand, actually has to do something. If it's
-provided with an ID, this is turned into an object and we're in edit
-mode, acting upon that object. If not, we're in create mode.
-
- sub do_edit :Exported {
- my ($self, $r) = @_;
- my $h = CGI::Untaint->new(%{$r->{params}});
- my ($obj) = @{$r->objects || []};
- if ($obj) {
- # We have something to edit
- $obj->update_from_cgi($h);
- } else {
- $obj = $self->create_from_cgi($h);
- }
-
-The C<CDBI> model uses L<Class::DBI::FromCGI> to turn C<POST> parameters
-into database table data. This in turn uses C<CGI::Untaint> to ensure
-that the data coming in is suitable for the table. If you're using the
-default C<CDBI> model, then, you're going to need to set up your tables
-in a way that makes C<FromCGI> happy.
-
-=over
-
-=item Digression on C<Class::DBI::FromCGI>
-
-C<CGI::Untaint> is a mechanism for testing that incoming form data
-conforms to various properties. For instance, given a C<CGI::Untaint>
-object that encapsulates some C<POST> parameters, we can extract an
-integer like so:
-
- $h->extract(-as_integer => "score");
-
-This checks that the C<score> parameter is an integer, and returns it if
-it is; if not, C<< $h->error >> will be set to an appropriate error
-message. Other tests by which you can extract your data are C<as_hex>
-and C<as_printable>, which tests for a valid hex number and an ordinary
-printable string respectively; there are other handlers available on
-CPAN, and you can make your own, as documented in L<CGI::Untaint>.
-
-To tell the C<FromCGI> handler what handler to use for each of your
-columns, you need to use the C<untaint_columns> methods in the classes
-representing your tables. For instance:
-
- BeerDB::Beer->untaint_columns(
- integer => ["score", ... ],
- );
-
-This must be done after the call to C<setup> in your handler, because
-otherwise the model classes won't have been set up to inherit from
-C<Class::DBI::FromCGI>.
-
-Remember that if you want to use drop-downs to set the value of related
-fields, such as the brewery for a beer, you need to untaint these as
-something acceptable for the primary key of that table:
-
- BeerDB::Beer->untaint_columns(
- integer => ["score", "brewery", "style" ],
- ...
- );
-
-This is usually integer, if you're using numeric IDs for your primary
-key. If not, you probably want C<printable>, but you probably know what
-you're doing anyway.
-
-=back
-
-The data is untainted, and any errors are collected into a hash which is
-passed to the template. We also pass back in the parameters, so that the
-template can re-fill the form fields with the original values. The user
-is then sent back to the C<edit> template.
-
- if (my %errors = $obj->cgi_update_errors) {
- # Set it up as it was:
- $r->{template_args}{cgi_params} = $r->{params};
- $r->{template_args}{errors} = \%errors;
- $r->{template} = "edit";
- }
-
-Otherwise, the user is taken back to viewing the new object:
-
- } else {
- $r->{template} = "view";
- }
- $r->objects([ $obj ]);
-
-Notice that this does use hard-coded names for the templates to go to next.
-Feel free to override this in your subclasses:
-
- sub do_edit :Exported {
- my ($class, $r) = @_;
- $class->SUPER::do_edit($r);
- $r->template("my_edit");
- }
-
-=head3 delete
-
-The delete method takes a number of arguments and deletes those rows from the
-database; it then loads up all rows and heads to the F<list> template.
-You almost certainly want to override this to provide some kind of
-authentication.
-
-=head3 list
-
-Listing, like viewing, is a matter of selecting objects for
-presentation. This time, instead of a single object specified in the
-URL, we want, by default, all the records in the table:
-
- sub list :Exported {
- my ($class, $r) = @_;
- $r->objects([ $self->retrieve_all ])
- }
-
-However, things are slightly complicated by paging and ordering by
-column; the default implementation also provides a C<Class::DBI::Pager>
-object to the templates and uses that to retrieve the appropriate bit of
-the data, as specified by the C<page> URL query parameter. See the F<pager>
-template below.
-
-=head3 search
-
-Searching also uses paging, and creates a query from the C<POST>
-parameters. It uses the F<list> template to display the objects once
-they've been selected from the database.
-
-=head2 The templates and macros
-
-Once these actions have done their work, they hand a set of objects to
-the templates; if you haven't specified your own custom template
-globally or for a given class, you'll be using the factory specified
-template. Let's take a look now at each of these and how they're put
-together.
-
-The beauty of the factory specified templates is that they make use of
-the classes' metadata as supplied by the view class. Although you're
-strongly encouraged to write your own templates, in which you don't need
-to necessarily be as generic, the factory templates will always do the
-right thing for any class without further modification, and as such are
-useful examples of how to build Maypole templates.
-
-=head3 Commonalities
-
-There are certain common elements to a template, and these are extracted
-out. For instance, all the templates call the F<header> template to
-output a HTML header, and nearly all include the F<macros> template to
-load up some common template functions. We'll look at these common
-macros as we come across them.
-
-=head3 F<view>
-
-=template view
-
-=head3 F<edit>
-
-The F<edit> template is pretty much the same as F<view>, but it uses the
-C<to_field> method on each column of an object to return a C<HTML::Element>
-object representing a form element to edit that property. These elements
-are then rendered to HTML with C<as_HTML>. It expects to see a list of
-editing errors, if any, in the C<errors> template variable:
-
- FOR col = classmetadata.columns;
- NEXT IF col == "id";
- "<P>";
- "<B>"; classmetadata.colnames.$col; "</B>";
- ": ";
- item.to_field(col).as_HTML;
- "</P>";
- IF errors.$col;
- "<FONT COLOR=\"#ff0000\">"; errors.$col; "</FONT>";
- END;
- END;
-
-=head3 F<list>
-
-Browsing records and search results are both handled by the F<list> template.
-The C<search> template argument is used to distinguish between the two cases:
-
- [% IF search %]
- <h2> Search results </h2>
- [% ELSE %]
- <h2> Listing of all [% classmetadata.plural %]</h2>
- [% END %]
-
-=head1 Customizing Generic CRUD Applications
+++ /dev/null
-=head1 Maypole View Classes
-
-In a large application, you will almost certainly want to customize the
-layout and design of the output pages. This task may even be the purview
-of a separate team of HTML designers rather than the programmers. Since
-a typical programmer will try to avoid touching HTML as much as possible
-and a typical designer will try to avoid touching Perl code, programmers
-have evolved a system of templating to separate the concerns of
-programming and designing.
-
-One of the core concepts in Maypole is the I<view class>, and this is
-responsible for routing the data produced in the model class into the
-templates produced by the designers. Of course, there are a great many
-possible templating systems and styles, and so there can be a great many
-possible Maypole view classes. Each view class will take the data from
-the controller, locate a template to be processed, and hand the whole
-lot to its preferred templating module, which will then do the hard work
-of filling in the template and coming up with the output.
-
-You can choose whatever Maypole view class you want, but the default
-view class is C<Maypole::View::TT>, and it feeds its data and templates
-to a module called the Template Toolkit.
-
-=head2 The Template Toolkit
-
-The Template Toolkit, written by Andy Wardley, is a very powerful and
-generic templating system. It provides its own little formatting language
-which supports loops, conditionals, hash and array dereferences and
-method calls, macro processing and a plug-in system to connect it to
-external Perl modules. There are several good introductions to the
-Template Toolkit available: you should have one installed as
-L<Template::Tutorial::Datafile>; there's one at
-L<http://www.perl.com/pub/a/2003/07/15/nocode.html>, and of course
-there's the "Badger Book" - I<The Perl Template Toolkit>, by Andy et al.
-
-We'll present a brief introduction here by deconstructing some of the
-templates used by Maypole applications. For more deconstruction, see
-L<StandardTemplates.pod>, which is an entire chapter dealing with the
-factory supplied templates.
-
-Here's the template which is called for the front page of the standard
-beer database application, C<custom/frontpage>.
-
- [% INCLUDE header %]
-
- <h2> The beer database </h2>
-
- <TABLE BORDER="0" ALIGN="center" WIDTH="70%">
- [% FOR table = config.display_tables %]
- <TR>
- <TD>
- <A HREF="[%table%]/list">List by [%table %]</A>
- </TD>
- </TR>
- [% END %]
- </TABLE>
-
-The first thing to note about this is that everything outside of the
-Template Toolkit tags (C<[%> and C<%]>) is output verbatim. That is,
-you're guaranteed to see
-
- <h2> The beer database </h2>
-
- <TABLE BORDER="0" ALIGN="center" WIDTH="70%">
-
-in the output somewhere. Inside the tags, magic happens. The first piece
-of magic is the C<[% INCLUDE header %]> directive. This goes away and
-finds a file called F<header> - don't worry about how it finds that yet,
-we'll come to that later on - and processes the file's contents as
-though they were right there in the template. Our F<header> file happens
-not to contain any C<[% %]> tags, but if it did, they would be processed
-in the same way as the ones in F<frontpage>.
-
-The next piece of magic is this line:
-
- [% FOR table = config.display_tables %]
-
-We're seeing a lot of things here at once. C<config> is where we should
-start looking. This is a template variable, which is what templates are
-all about - templating means getting data from somewhere outside and
-presenting it to the user in a useful way, and the C<config> hash is a
-prime example of data that we want to use. It's actually the hash of
-configuration parameters for this Maypole application, and one of the
-keys in that hash is C<display_tables>, the database tables that we're
-allowed to play with. In the application, we probably said something
-like
-
- BeerDB->config->{display_tables} = [qw[beer brewery pub style]];
-
-This stores the four values - C<beer>, C<brewery>, C<pub> and C<style> -
-in an array, which is placed in the config hash under the key
-C<display_tables>. Now we're getting them back again.
-
-The Template Toolkit's dot operator is a sort of do-the-right-thing
-operator; we can say C<array.0> to get the first element of an array,
-C<hash.key> to look up the C<key> key in a hash, and C<object.method> to
-call C<method> on an object. So, for instance, if we said
-C<config.display_tables.2>, we'd look up the C<display_tables> key in
-the configuration hash and get our array back, then look up the 2nd
-element and get C<pub>.
-
-The C<FOR> loop will repeat the code four times, setting our new
-variable C<table> to the appropriate array element. This code:
-
- [% FOR table = config.display_tables %]
- Hello [% table %]!
- [% END %]
-
-will produce something like
-
- Hello beer!
- Hello brewery!
- Hello pub!
- Hello style!
-
-In our case, though, we're printing out a table element linking to each
-database table in turn.
-
-Here's a slightly more complicated example, adapted from F<factory/pager>.
-This template is responsible for printing the little page menu at the
-bottom of a listing if there are more rows in the listing than we want
-on a single page.
-
- [% PROCESS macros %]
- <P ALIGN="center">Pages:
- [%
- FOREACH num = [pager.first_page .. pager.last_page];
- IF num == pager.current_page;
- "["; num; "] ";
- ELSE;
- SET args = "?page=" _ num;
- SET label = "[" _ num _ "]";
- link(classmetadata.moniker, "list", args, label);
- END;
- END;
- %]
- </P>
-
-Maypole will be providing a whole bunch of variables to this template,
-and we'll look at them all in a moment, but the only ones we need to care
-about are C<pager> and C<classmetadata>.
-
-We start by loading in a bunch of macros. Macros are Template Toolkit's
-functions - you can provide them some parameters and they'll run a little
-sub-template based on them. The C<macros> file contains some handy macros
-that I've found useful for constructing Maypole templates; again, these
-will be covered in full detail in L<StandardTemplates.pod>.
-
-We're going to be displaying something like this:
-
- Pages: [1] [2] [3] [4]
-
-with most of those numbers being a link to the appropriate page. This
-mean we're going to have to have a list of numbers, and the C<FOREACH> loop
-provides this: (C<FOREACH> and C<FOR> are identical, just like in Perl.)
-
- FOREACH num = [pager.first_page .. pager.last_page];
-
-Here we're manually constructing an array of numbers, using the range
-operator (C<..>) to fill in all the numbers from the C<first_page> (1)
-to the C<last_page> (4). The same dot operator is used to ask the C<pager>
-what its C<first_page> and C<last_page> is. Remember when we said
-C<config.display_tables>, we were looking up the C<display_tables> key
-in the C<config> hash? Well, this time we're not looking anything up in
-a hash. C<pager> is an object, and C<first_page> is a method. Thing is,
-you don't have to care. You can pretend it's a hash if you want. The
-syntax is the same, and Template Toolkit knows the right thing to do.
-
-Now we're going to be executing this loop four times, once each for C<num>
-being set to 1, 2, 3, and 4. At some point, we'll come across the page
-that we're actually on right now:
-
- IF num == pager.current_page;
-
-and in that case, we don't want to produce a link to it. We just want
-to output it as text, surrounded by square brackets:
-
- "["; num; "] ";
-
-We're using string literals to output the brackets. We don't have to
-do that. We could say it this way:
-
- [% ...
- IF num == pager.current_page;
- %]
- [ [% num %] ]
- [% ELSE %]
- ...
- [% END %]
-
-But you know, I quite like it my way.
-
-Now if the number we're printing isn't the number of the current page,
-we want to make a link. Here's how we do it:
-
- SET args = "?page=" _ num;
- SET label = "[" _ num _ "]";
- link(classmetadata.table, "list", args, label);
-
-C<SET> declares a new variable of our own. If there was anything called
-C<args> before, there isn't now. It's going to be the result of our
-statement C<"?page=" _ num>. C<_> is the concatenation operator, and
-glues C<?page=> onto the front of our number. So if we want to link to
-page 4, then the C<args> variable will contain C<?page=4>. Similarly,
-the C<label> variable will be C<[4]>.
-
-Now we call a macro, C<link> with these two variables and the value of
-C<classmetadata.table>. This macro takes four arguments, C<table>,
-C<action>, C<args> and C<label>, and constructs a link of the form
-
- <A HREF="[% config.base_url %]/[% table %]/[% action %][% args %]">
- [% label %]
- </A>
-
-In our case, it'll be filled in like so:
-
- <A HREF="[% config.base_url %]/[% classmetadata.table %]/list?page=4">
- [ 4 ]
- </A>
-
-Where C<classmetadata.table> will actually be the name of the current
-table, and C<config.base_url> will be replaced by the appropriate URL for
-this application.
-
-=head2 Locating Templates
-
-Another feature of C<Maypole::View::TT> which may not be present in
-alternate view class implementations - although they are strongly
-encouraged to provide it - is the way that templates are located.
-(Remember, I B<did> say I'd tell you about that later.) Template Toolkit
-allows whatever uses it to provide a path for template files to be
-located in. C<Maypole::View::TT> feeds it up to three possible
-directories to look things up in, and it will try to find a template in
-each of these in turn.
-
-When you configure a Maypole application, you can tell it the base
-directory of your templates like so:
-
- BeerDB->config->{template_root} = "/var/www/beerdb/templates";
-
-If you don't do this, most Maypole front-ends will use the current
-directory, which is generally what you want anyway. Off this directory,
-Maypole will look for a set of subdirectories.
-
-For instance, I said we were in the middle of processing the front page
-and looking up a template file called F<header>. Maypole will first look
-for this file in the F<custom> subdirectory. (say,
-F</var/www/beerdb/templates/custom>) If it doesn't find one, then it
-looks in the F<factory> subdirectory. If it doesn't find one there, then
-it gives up and dies with an error. But that's your fault, since you've
-called for a template which doesn't exist. Don't do that.
-
-This behaviour means that you can provide your own site-specific
-templates, but if you don't do so, then you get to use a generic one
-provided by Maypole. Maypole's "factory setting" templates are written
-in such a way as to try and do the right thing no matter what your
-application does. They are occasionally successful at this.
-
-Now the front page was a pretty simple example, since Maypole only looks
-up two directories. In most cases, it checks an additional directory,
-and this directory depends entirely on what Maypole is doing.
-
-If you're writing an e-commerce application, for example, you may well
-have a table which represents the product catalogue and all the products
-you can buy. Let's call this the C<product> table. You'll also have a
-data source which is specific to the user which contains all the
-products that they're buying on this particular visit to the site. In
-time-honoured tradition, we'll call this the C<basket> table.
-
-Now it ought to be reasonably apparent that you don't want the basket
-to be displayed in exactly the same way as the product catalogue. The
-templates for C<product/list> and C<basket/list> need to be different.
-This is where the third directory comes in. The other directory, which
-Maypole checks very first of all, is specific to the table that you're
-viewing. So if you go to C<http://your.shop.com/basket/list>, Maypole
-will look in the F<basket> directory first for a file called F<list>,
-and second in the F<custom> directory for a site-wide list template,
-and then fall-back to the F<factory> directory for a generic list
-template. It should be obvious that you probably want to provide all
-of F<basket/list>, F<basket/view>, F<product/list>, F<product/view>
-and any other combination of classes and actions that you can think of.
-
-=head2 What Maypole provides to a template
-
-C<Maypole::View::TT> provides quite a variety of template variables to
-the template. As these are the building blocks of your pages, it's worth
-looking at precisely what variables are available.
-
-The most important variable is called C<objects>, and is a list of all
-the objects that this page is going to deal with. For instance,
-in the template F</beer/view>, C<objects> will contain the C<BeerDB::Beer>
-object for the 23rd item in the database, while F</brewery/list> will
-fill C<objects> will all the breweries; or at least, all the breweries
-on the current page.
-
-This variable is so important that to help design templates with it,
-C<Maypole::View::TT> provides a helpful alias to it depending on
-context. For instance, if you're writing your own F</brewery/list>
-template, the data in C<objects> is also available in a template
-variable called C<breweries>. If you're working on F</brewery/view>,
-though, it's available in C<brewery>, since there's only one brewery to
-be displayed.
-
-Additionally, you can get the base URL for the application from the
-C<base> template variable; this allows you to construct links, as we
-saw earlier:
-
- <A HREF="[% base %]/brewery/edit/[% brewery.id %]">Edit this brewery</A>
-
-You can also get at the rest of the configuration for the site with the
-C<config> variable as we saw above, and the entire request object in
-C<request>, should you really need to poke at it. (I've only found this
-useful when working with authentication modules which stash a current user
-object in C<request.user>.)
-
-To allow the construction of the "generic" templates which live in
-F<factory>, Maypole also passes in a hash called C<classmetadata>,
-which contains all sorts of useful information about the class under
-examination:
-
-=over 3
-
-=item C<table>
-
-This is the name of the table that is represented by the class.
-
-=item C<class>
-
-This is the Perl's idea of the class; you don't need this unless you're
-doing really tricky things.
-
-=item C<moniker>
-
-This is a more human-readable version of the table name, that can be
-used for display.
-
-=item C<plural>
-
-The same, but a correctly-formed plural. For instance, "breweries".
-
-=item C<columns>
-
-The list of columns for display; see the section "Customizing Generic
-CRUD Applications" in L<StandardTemplates.pod>.
-
-=item C<colnames>
-
-This is a hash mapping the database's name for a column to a more
-human-readable name. Again, see "Customizing Generic CRUD Applications>.
-
-=item C<cgi>
-
-This is a slightly trickier one. It is a hash mapping column names to
-a C<HTML::Element> suitable for entering data into a new instance of
-that class. That is, for the C<beer> table, C<classmetadata.cgi.style>
-should be a C<HTML::Element> object containing a drop-down list of
-beer styles. This is explained in L<StandardTemplates.pod>.
-
-=item C<description>
-
-This is the human-readable description provided by a class.
-
-=item C<related_accessors>
-
-This is a list of accessors which can be called on an object to get
-lists of other things that this object "has". For instance, on a
-brewery, it would return C<beers>, since calling C<brewery.beers> would
-give you a list of beers produced by the brewery. Note that this only
-caters for accessors defining one-to-many relationships, not the
-ordinary one-to-one relationships, such as C<style>.
-
-=back
-
-=head2 Other view classes
-
-Please note that these template variables, C<config>, C<classmetadata>,
-C<objects> and its user-friendly alias, as well as the rest of them are
-a function of one particular view class, the default
-C<Maypole::View::TT> class. Other view classes may need to present an
-entirely different set of template variables, since the default ones
-might not make sense. The templates may look wildly different in other
-view class implementations. But that's OK, because you couldn't
-necessarily use the same templates with a different templating system
-anyway.
-
-For instance, in really dumb templating languages which can't handle
-dereferencing hashes or arrays - no wait, that's most of them - passing
-in a hash reference like C<classmetadata> won't help you since you can't
-get at any of its elements. So you'll need to take a look at the
-documentation for the appropriate view class to see what template
-variables it provides.
-
-So if, for some perverse reason, the Template Toolkit just isn't good
-enough for you, then you can set your own view class while configuring
-your application:
-
- package BeerDB;
- use base 'Apache::MVC';
- ...
- BeerDB->setup("dbi:SQLite:t/beerdb.db");
- BeerDB->config->{uri_base} = "http://localhost/beerdb/";
- BeerDB->config->{rows_per_page} = 10;
- BeerDB->config->{view} = "Maypole::View::Mason";
-
-Where do these alternate view classes come from? Gentle reader, they
-come from B<you>.
-
-=head2 Building your own view class
-
-I<You should probably skip this section for the first few readings of this manual. It's only intended for people extending Maypole.>
-
-Imagine you've found a brand new templating system that's B<much better>
-than the Template Toolkit. I know I'm stretching your imagination a bit
-here, but try. You'd like to use it with Maypole, which means writing your
-own view class. How is it done?
-
-We'll demonstrate by implementing a view class for C<HTML::Mason>,
-although no value judgement is implied. C<HTML::Mason> is a templating
-system which embeds pure Perl code inside its magic tags. The good side
-of this is that it can get into hash references and objects, and so
-providing C<classmetadata>, C<config> and the Maypole request object
-will work out just fine. The down side is that C<HTML::Mason> is used to
-running more or less standalone, and having all the template variables
-it wants already at its disposal through CGI parameters and the like, so
-we have to fiddle a bit to get these variables into our template.
-
-The key to building view classes is C<Maypole::View::Base>. This is the
-base class that you're going to inherit from and, to be honest, it does
-pretty much everything you need. It provides a method called C<vars>
-which returns a hash of all the template variables described above, so
-it would be good to feed those into C<HTML::Mason>. It also provides a
-C<paths> method which turns returns the full filesystem path of the
-three possible template paths as shown above. Again, it would be good to
-use this as our component paths if we can. It also has some methods we
-can override if we want to, but they're not massively important, so you
-can see L<Maypole::View::Base> for more about them.
-
-The module will do the right thing for us if we agree to provide a
-method called C<template>. This is responsible for taking the Maypole
-request object (of which more later) and putting the appropriate output
-either into C<$r-E<gt>{output}> or C<$r-E<gt>{error}>, depending, of
-course, whether things are OK or whether we got an error.
-
-Thankfully, C<HTML::Mason> makes things really easy for us. We B<can>
-use multiple template roots, so we can use the C<paths> method; we
-B<can> pass in a hash full of interesting data structures, so we can use
-the C<vars> method too. In fact, we have to do very little to make
-C<Maypole::View::Mason> work. Which is somewhat annoying, because it
-makes a boring example. But it means I can leave the fun ones to you!
-
-The doing-the-templating, in Mason and in any templating system, depends on
-three things: the paths that we're going to use to find our templates, the
-template name that we've been asked to fill out, and the set of variables that
-are going to be fed to the template. We'll assemble these for reference:
-
- sub template {
- my ($self, $r) = @_;
- my @paths = $self->paths($r);
- my $template = $r->template;
- my %vars = $self->args($r);
-
-We'll also declare somewhere to temporarily store the output:
-
- my $output;
-
-Now comes the part where we have to actually do something
-templating-language specific, so we open up our copy of "Embedding Perl
-in HTML with Mason" and find the bit where it talks about running Mason
-standalone. We find that the first thing we need to do is create a
-C<HTML::Mason::Interp> object which knows about the component roots.
-There's a slight subtlety in that the component roots have to be
-specified as an array of arrays, with each array being a two-element
-list of label and path, like so:
-
- comproot => [
- [ class => "/var/www/beerdb/templates/brewery" ],
- [ custom => "/var/www/beerdb/templates/custom" ],
- [ factory => "/var/www/beerdb/templates/factory" ],
- ]
-
-We also find that we can set the output method here to capture Mason's
-output into a scalar, and also that we can tell Mason to generate
-sensible error messages itself, which saves us from having to worry
-about catching errors. At the end of all this, we come up with a
-constructor for our C<HTML::Mason::Interp> object which looks like this:
-
- my $label = "path0";
- my $mason = HTML::Mason::Interp->new(
- comproot => [ map { [ $label++ => $_ ] } @paths ],
- output_method => \$output,
- error_mode => "output"
- );
-
-The next thing we need to do is run the template with the appropriate
-template variables. This turns out to be really easy:
-
- $mason->exec($template, %vars);
-
-Now we've got the data in C<$output>, we can put it into the request object,
-and return a true value to indicate that we processed everything OK. (If there
-was an error, then Mason will have produced some suitable output, so we can
-pretend that everything's OK anyway.)
-
- $r->{output} = $output;
- return 1;
-
-And that's all we need to do. Barely twenty lines of code for the finished
-product. Wasn't that easy? Don't you feel inspired to write Maypole view
-classes for your favourite templating language? Well, don't let me stop you!
-Patches are always welcome!
+++ /dev/null
-my @templates = <../templates/factory/*>;
-
-my %doc;
-
-for my $template (@templates) {
- open TEMP, $template or die $!;
- $template =~ s/.*factory\///g;
- while (<TEMP>) {
- next unless /^#?=for doc/... /^#?=cut/
- and not /(%#?\]|\[%#?)$/
- and not /=cut|=for doc/; # Much magic.
- s/^\s*#//g;
- $doc{$template} .= $_;
- }
-}
-
-while (<>) {
- if (!/^=template (\w+)/) { print; next; }
- die "Can't find doc for template $1" unless $doc{$1};
- print $doc{$1};
-}
$self->{args} = \@pi;
}
+sub get_template_root { "." }
+sub get_request { }
+
+sub parse_location {
+ die "Do not use Maypole directly; use Apache::MVC or similar";
+}
+
+sub send_output {
+ die "Do not use Maypole directly; use Apache::MVC or similar";
+}
+
=head1 NAME
Maypole - MVC web application framework
=head1 SYNOPSIS
-See L<Maypole>.
+See L<Maypole::Application>.
=head1 DESCRIPTION
-A large number of web programming tasks follow the same sort of pattern:
-we have some data in a datasource, typically a relational database. We
-have a bunch of templates provided by web designers. We have a number of
-things we want to be able to do with the database - create, add, edit,
-delete records, view records, run searches, and so on. We have a web
-server which provides input from the user about what to do. Something in
-the middle takes the input, grabs the relevant rows from the database,
-performs the action, constructs a page, and spits it out.
-
-Maypole aims to be the most generic and extensible "something in the
-middle" - an MVC-based web application framework.
-
-An example would help explain this best. You need to add a product
-catalogue to a company's web site. Users need to list the products in
-various categories, view a page on each product with its photo and
-pricing information and so on, and there needs to be a back-end where
-sales staff can add new lines, change prices, and delete out of date
-records. So, you set up the database, provide some default templates
-for the designers to customize, and then write an Apache handler like
-this:
-
- package ProductDatabase;
- use base 'Maypole::Application';
- __PACKAGE__->set_database("dbi:mysql:products");
- ProductDatabase->config->uri_base = "http://your.site/catalogue/";
- ProductDatabase::Product->has_a("category" => ProductDatabase::Category);
- # ...
-
- sub authenticate {
- my ($self, $request) = @_;
- return OK if $request->{ar}->get_remote_host() eq "sales.yourcorp.com";
- return OK if $request->{action} =~ /^(view|list)$/;
- return DECLINED;
- }
- 1;
+This documents the Maypole request object. For user documentation, see
+L<Maypole::Tutorial>.
-You then put the following in your Apache config:
+=head2 CLASS METHODS
- <Location /catalogue>
- SetHandler perl-script
- PerlHandler ProductDatabase
- </Location>
+=head3 config
-And copy the templates found in F<templates/factory> into the
-F<catalogue/factory> directory off the web root. When the designers get
-back to you with custom templates, they are to go in
-F<catalogue/custom>. If you need to do override templates on a
-database-table-by-table basis, put the new template in
-F<catalogue/I<table>>.
+Returns the L<Maypole::Config> object
-This will automatically give you C<add>, C<edit>, C<list>, C<view> and
-C<delete> commands; for instance, a product list, go to
+=head3 setup
- http://your.site/catalogue/product/list
+ My::App->setup();
-For a full example, see the included "beer database" application.
+ Initialise the maypole application and model classes. Your
+ application should
+ call this after setting configuration via L<"config">
-=head1 HOW IT WORKS
+=head3 init
-There's some documentation for the workflow in L<Maypole::Workflow>,
-but the basic idea is that a URL part like C<product/list> gets
-translated into a call to C<ProductDatabase::Product-E<gt>list>. This
-propagates the request with a set of objects from the database, and then
-calls the C<list> template; first, a C<product/list> template if it
-exists, then the C<custom/list> and finally C<factory/list>.
+You should not call this directly, but you may wish to override this to
+add
+application-specific initialisation.
-If there's another action you want the system to do, you need to either
-subclass the model class, and configure your class slightly differently:
+=head3 view_object
- package ProductDatabase::Model;
- use base 'Maypole::Model::CDBI';
+Get/set the Maypole::View object
- sub supersearch :Exported {
- my ($self, $request) = @_;
- # Do stuff, get a bunch of objects back
- $r->objects(\@objects);
- $r->template("template_name");
- }
+=head3 debug
-Then your top-level application package should change the model class:
-(Before calling C<setup>)
+ sub My::App::debug {1}
- ProductDatabase->config->model("ProductDatabase::Model");
+ Returns the debugging flag. Override this in your application class
+ to
+ enable/disable debugging.
-(The C<:Exported> attribute means that the method can be called via the
-URL C</I<table>/supersearch/...>.)
+=head2 INSTANCE METHODS
-Alternatively, you can put the method directly into the specific model
-class for the table:
+=head3 parse_location
- sub ProductDatabase::Product::supersearch :Exported { ... }
+Turns the backend request (e.g. Apache::MVC, Maypole, CGI) into a
+Maypole
+request. It does this by setting the C<path>, and invoking C<parse_path>
+and
+C<parse_args>.
-By default, the view class uses Template Toolkit as the template
-processor, and the model class uses C<Class::DBI>; it may help you to be
-familiar with these modules before going much further with this,
-although I expect there to be other subclasses for other templating
-systems and database abstraction layers as time goes on. The article at
-C<http://www.perl.com/pub/a/2003/07/15/nocode.html> is a great
-introduction to the process we're trying to automate.
+You should only need to define this method if you are writing a new
+Maypole
+backend.
-=head1 USING MAYPOLE
+=head3 path
-You should probably not use Maypole directly. Maypole is an abstract
-class which does not specify how to communicate with the outside world.
-The most popular subclass of Maypole is L<Apache::MVC>, which interfaces
-the Maypole framework to Apache mod_perl; another important one is
-L<CGI::Maypole>. However, if you just don't care, use Maypole::Application,
-and it will choose the right one for you.
+Returns the request path
-If you are implementing Maypole subclasses, you need to provide at least
-the C<parse_location> and C<send_output> methods. You may also want to
-provide C<get_request> and C<get_template_root>. See the
-L<Maypole::Workflow> documentation for what these are expected to do.
+=head3 parse_path
-=cut
+Parses the request path and sets the C<args>, C<action> and C<table>
+properties
-sub get_template_root { "." }
-sub get_request { }
+=head3 table
-sub parse_location {
- die "Do not use Maypole directly; use Apache::MVC or similar";
-}
+The table part of the Maypole request path
-sub send_output {
- die "Do not use Maypole directly; use Apache::MVC or similar";
-}
+=head3 action
+
+The action part of the Maypole request path
+
+=head3 args
+
+A list of remaining parts of the request path after table and action
+have been
+removed
+
+=head3 parse_args
+
+Turns post data and query string paramaters into a hash of C<params>.
+
+You should only need to define this method if you are writing a new
+Maypole
+backend.
+
+=head3 params
+
+Returns a hash of request parameters. The source of the parameters may
+vary
+depending on the Maypole backend, but they are usually populated from
+request
+query string and POST data.
+
+B<Note:> Where muliple values of a parameter were supplied, the
+C<params>
+value
+will be an array reference.
+
+=head3 get_template_root
+
+Implimentation-specific path to template root.
+
+You should only need to define this method if you are writing a new
+Maypole
+backend. Otherwise, see L<Maypole::Config/"template_root">
+
+=head3 is_applicable
+
+Returns a Maypole::Constant to indicate whether the request is valid.
+
+The default implimentation checks that C<$r-E<gt>table> is publicly
+accessible
+and that the model class is configured to handle the C<$r-E<gt>action>
+
+=head3 authenticate
+
+Returns a Maypole::Constant to indicate whether the user is
+authenticated for
+the Maypole request.
+
+The default implimentation returns C<OK>
+
+=head3 model_class
+
+Returns the perl package name that will serve as the model for the
+request. It corresponds to the request C<table> attribute.
+
+=head3 additional_data
+
+Called before the model processes the request, this method gives you a
+chance
+to do some processing for each request, for example, manipulating
+C<template_args>.
+
+=head3 objects
+
+Get/set a list of model objects. The objects will be accessible in the
+view
+templates.
+
+If the first item in C<$r-E<gt>args> can be C<retrieve()>d by the model
+class,
+it will be removed from C<args> and the retrieved object will be added
+to the
+C<objects> list. See L<Maypole::Model> for more information.
+
+=head3 template_args
+
+ $r->template_args->{foo} = 'bar';
+
+ Get/set a hash of template variables.
+
+=head3 template
+
+Get/set the template to be used by the view. By default, it returns
+C<$r-E<gt>action>
+
+=head3 exception
+
+This method is called if any exceptions are raised during the
+authentication
+or
+model/view processing. It should accept the exception as a parameter and
+return
+a Maypole::Constant to indicate whether the request should continue to
+be
+processed.
+
+=head3 error
+
+Get/set a request error
+
+=head3 output
+
+Get/set the response output. This is usually populated by the view
+class. You
+can skip view processing by setting the C<output>.
+
+=head3 document_encoding
+
+Get/set the output encoding. Default: utf-8.
+
+=head3 content_type
+
+Get/set the output content type. Default: text/html
+
+=head3 send_output
+
+Sends the output and additional headers to the user.
=head1 SEE ALSO
-There's more documentation, examples, and a wiki at the Maypole web site:
+There's more documentation, examples, and a wiki at the Maypole web
+site:
-http://maypole.simon-cozens.org/
+http://maypole.perl.org/
L<Maypole::Application>,L<Apache::MVC>, L<CGI::Maypole>.
=head1 THANK YOU
Danijel Milicevic, Jesse Scheidlower, Jody Belka, Marcus Ramberg,
-Mickael Joanne, Simon Flack, Veljko Vidovic and all the others who've helped.
+Mickael Joanne, Simon Flack, Veljko Vidovic and all the others who've
+helped.
=head1 LICENSE
--- /dev/null
+
+=head1 Introduction to the Maypole Request Model
+
+This chapter serves as a gentle introduction to Maypole and setting up
+Maypole applications. We look at Maypole is, how to get it up and
+running, and how to start thinking about building Maypole applications.
+
+=head2 What is Maypole?
+
+Presumably you have some idea of what Maypole is all about, or otherwise
+you wouldn't be reading this manual. But Maypole is good at many
+different things, and you may have accidentally focussed on one aspect
+of Maypole while missing the big picture.
+
+For instance, you may know that Maypole is extremely good at putting web
+front-ends onto databases. This is true, but it's only a part of what
+Maypole does. You may have heard that Maypole is a web application
+framework, which is true, but it doesn't mean very much. There are a
+huge number of things that Maypole can do, because it's very much a
+blank slate. You can make it do what you will. In this manual, we'll be
+making it act as a front-end to a database, as a social network site, as
+an intranet portal, and many other things besides. It is a framework.
+
+I like to think that Maypole is a way of going from a URL to a method
+call to some output. If you have a URL like C</product/order/12>,
+Maypole is a way of having it load up product number 12, call an
+C<order> method, and produce a page about what it's just done. The
+reason Maypole is such a big deal is because it does all this for you.
+You no longer have to care about your web server. You hardly have to
+care about your database. You don't have to care about templating
+modules, parsing CGI parameters, or anything else. You only need to care
+about business logic, and the business logic in this instance is how you
+C<order> a product, and what you need to display about it once you've
+done so. This is what programming should be: only caring about the work
+that distinguishes one program from another.
+
+It does this using a technique called MVC for web applications.
+
+=head2 What is MVC for web applications?
+
+Maypole was originally called C<Apache::MVC>, reflecting its basis in
+the Model-View-Controller design pattern. (I had to change it firstly
+because Maypole isn't tied to Apache, and secondly because
+C<Apache::MVC> is a really dull name.) It's the same design pattern that
+forms the foundation of similar projects in other languages, such as
+Java's Struts framework.
+
+This design pattern is found primarily in graphical applications; the
+idea is that you have a Model class which represents and manipulates
+your data, a View class which is responsible for displaying that data to
+the user, and a Controller class which controls the other classes in
+response to events triggered by the user. This analogy doesn't
+correspond precisely to a web-based application, but we can take an
+important principle from it. As Andy Wardley explains:
+
+ What the MVC-for-the-web crowd are really trying to achieve is a clear
+ separation of concerns. Put your database code in one place, your
+ application code in another, your presentation code in a third place.
+ That way, you can chop and change different elements at will,
+ hopefully without affecting the other parts (depending on how well your
+ concerns are separated, of course). This is common sense and good practice.
+ MVC achieves this separation of concerns as a by-product of clearly
+ separating inputs (controls) and outputs (views).
+
+This is what Maypole does. It has a number of database drivers, a number
+of front-end drivers and a number of templating presentation drivers.
+In common cases, Maypole provides precisely what you need for all of
+these areas, and you get to concentrate on writing just the business
+logic of your application. This is one of the reasons why Maypole lets
+you develop so rapidly: because most of the time, you don't need to do
+any development at all.
+
+=head2 Installing Maypole
+
+The first thing you're going to need to do to get Maypole running is to
+install it. Maypole needs an absolute shedload of Perl modules from CPAN
+to do its job. I am unrepentant about this. Maypole does a lot of work,
+so that you don't have to. This is called code re-use, and if we're
+serious about code re-use, then Maypole should be re-using as much code
+as possible in terms of Perl modules. In another sense, this gives the
+impression that Maypole doesn't actually do all that much itself,
+because all it's doing is gluing together already-existing code. Well,
+welcome to code re-use.
+
+The downside of code re-use is, of course, that you then have to install
+a shedload of Perl modules from CPAN. If you're using OpenBSD or
+FreeBSD, the wonderful ports system will be your friend. There's a
+Maypole port in C<p5-Maypole>. Just type C<make install>.
+
+Debian users, hang in there. There's a package coming.
+
+For other Unices, the L<CPANPLUS> or C<CPAN> modules will help with
+this. If you don't have C<CPANPLUS> installed, my recommendation is to
+use C<perl -MCPAN -e install CPANPLUS> to install it and then throw
+C<CPAN.pm> away. In any case, one of these two should get all that
+Maypole needs:
+
+ % perl -MCPANPLUS -e 'install Maypole'
+ % perl -MCPAN -e 'install Maypole'
+
+I don't know if Maypole works on Windows. I'm not sure I care.
+
+You're also going to need a database server and a web server. For
+databases, I recommend SQLite (if you install the C<DBD::SQLite> module,
+you get the SQLite library for free) for prototyping and mysql for
+production; heavier duty users should use Postgresql or Oracle - Maypole
+should be happy with them all. Maypole is happiest when running under
+Apache C<mod_perl>, with the C<Apache::Request> module installed, but as
+I said, it is a blank slate, and everything is customizable. There is a
+C<CGI::Maypole> frontend available to run as a standalone CGI script.
+
+=head2 The Beer Database example
+
+Throughout this manual, we're going to be referring back to a particular
+application so that we can give concrete examples for the concepts we're
+talking about. We could say "C<related_accessors> returns a list of
+accessors which can be called to return a list of objects in a has-a
+relationship to the original", or we could say "if we call
+C<related_accessors> on while viewing C<brewery>, it returns C<beers>,
+because we can call C<beers> on a C<brewery> object to get a list of
+that berwery's beers."
+
+Because Maypole is all about beer. If you look carefully, you can
+probably see men playing cricket on the village green. The first
+ever Maypole application was written to help me keep track of the many
+different ales available in my area - their styles, their tastes, their
+breweries, prices and so on. Then the more I thought about it, the more
+I thought it was a particularly good data model for demonstrating
+different kinds of database relationships.
+
+We have a C<brewery> table, which has several C<beer>s. We'll call this
+a has-many relationship. The beers each have a C<style>; styles are
+stored in a separate table, so C<beer> has-a C<style>. Beers are in
+several pubs and a pub has several beers, so beers and pubs are in a
+many-to-many relationship. We use a link table called C<handpump> to
+relate pubs to beers.
+
+All in all, this gives us a schema like the following:
+
+ create table brewery (
+ id int not null auto_increment primary key,
+ name varchar(30),
+ url varchar(50),
+ notes text
+ );
+
+ create table beer (
+ id int not null auto_increment primary key,
+ brewery integer,
+ style integer,
+ name varchar(30),
+ url varchar(120),
+ score integer(2),
+ price varchar(12),
+ abv varchar(10),
+ notes text
+ );
+
+ create table handpump (
+ id int not null auto_increment primary key,
+ beer integer,
+ pub integer
+ );
+
+ create table pub (
+ id int not null auto_increment primary key,
+ name varchar(60),
+ url varchar(120),
+ notes text
+ );
+
+ create table style (
+ id int not null auto_increment primary key,
+ name varchar(60),
+ notes text
+ );
+
+If you have C<DBD::SQLite> available, then a database like this will
+be created when Maypole was installed. Let's now see how to set it up
+with a web interface.
+
+=head2 Setting up Maypole
+
+The first thing we need for a Maypole interface to a database is to
+have a database. If you don't have one, now would be a good time to
+create one, using the schema above.
+
+The next thing we need is a module which is going to do all the work.
+Thankfully, it doesn't need to do B<all> the work itself. It's going to be a
+subclass of C<Maypole> or a Maypole front-end like C<Apache::MVC>.
+
+Here's the driver class for our beer database application. We're not
+going to go into much detail about it here; we'll do that in L<Beer.pod>.
+For now, simply admire its brevity, as you realise this is all the code
+you need to write for a simple database front-end:
+
+ package BeerDB;
+ use base 'Apache::MVC';
+ BeerDB->setup("dbi:SQLite:t/beerdb.db");
+ BeerDB->config->{uri_base} = "http://localhost/beerdb/";
+ BeerDB->config->{rows_per_page} = 10;
+ BeerDB->config->{display_tables} = [qw[beer brewery pub style]];
+ BeerDB::Brewery->untaint_columns( printable => [qw/name notes url/] );
+ BeerDB::Style->untaint_columns( printable => [qw/name notes/] );
+ BeerDB::Beer->untaint_columns(
+ printable => [qw/abv name price notes/],
+ integer => [qw/style brewery score/],
+ date => [ qw/date/],
+ );
+
+ use Class::DBI::Loader::Relationship;
+ BeerDB->config->{loader}->relationship($_) for (
+ "a brewery produces beers",
+ "a style defines beers",
+ "a pub has beers on handpumps");
+ 1;
+
+This defines the C<BeerDB> application, which, as it inherits from
+C<Apache::MVC>, will be a mod_perl handler. This means we need to
+tell the Apache configuration about it:
+
+ <Location /beerdb>
+ SetHandler perl-script
+ PerlHandler BeerDB
+ </Location>
+
+And now we need some templates. As we'll see in the chapter on views,
+L<View.pod>, there are several types of template. We're going to copy
+the whole lot from the F<templates/> directory of the Maypole source
+package into the F</beerdb> directory under our web root.
+
+And that's it. We should now be able to go to C<http://localhost/beerdb/>
+and see a menu of things to browse; C<http://localhost/beerdb/beer/list>
+will give a list of beers. There might not be any yet. There's a box
+that lets you add them.
+
+If you have any problems getting to this point, you might want to look
+at L<http://wiki.simon-cozens.org/index.cgi?InstallationIssues>.
+
+Play about with the site. Add some beers. Maybe go out and buy some beers
+to review if you need some inspiration. Don't be ill on my carpet.
+
+=head2 Phases of a Maypole request
+
+Now you should have a feel for what Maypole can do. The important thing
+to know at this point is that this is by no means B<all> that Maypole
+can do. What you've seen in the beer database example is all that Maypole
+can do if you don't customize it at all.
+
+Remember that, for instance, we don't ever tell Maypole what tables our
+database has, or what columns each table has. We don't tell Maypole what
+those tables should be called or how to display them. We don't tell Maypole
+what to do - that we want to list, search, edit and delete beers and breweries.
+Maypole just works that out for itself. We can customize it and have Maypole
+do all sorts of interesting things with our database, and most of the rest
+of this manual will be about how to do that.
+
+In order to do that, we need to look at what Maypole's actually doing.
+
+As mentioned, Maypole is responsible for turning a URL into an object, a
+method call, and some templated output. Here's a handy diagram to
+explain how it does that:
+
+=for html
+<IMG SRC="maypole_process2.png">
+
+Maypole's process revolves around the concept of the Maypole request
+object. This is a little like Apache's request object, but at a much
+higher level - in fact, in C<mod_perl>-based Maypole front-ends, the
+Apache request object is incorporated in the Maypole request object. All
+that Maypole does is gradually flesh out this object until it contains
+something in the C<output> member, and then it is dispatched back to the
+front-end for output.
+
+So to start with, we take the Apache request (or CGI object, or other
+way of isolating what's going on) and break it down. For instance, we
+turn the URL C</beer/view/1> into
+
+ {
+ table => "beer",
+ action => "view",
+ args => [ 1 ]
+ }
+
+Then Maypole will check that C<beer> is a real table, and find the class
+that models it. It also checks whether or not we're allowed to call the
+C<view> method over the network:
+
+ {
+ table => "beer",
+ action => "view",
+ args => [ 1 ],
+ model_class => "BeerDB::Beer"
+ }
+
+Then there's a user-defined authentication method, which by default just
+lets us do anything. Now we hand over to the model class, which loads up
+the object, and decides what template we want to use:
+
+ {
+ table => "beer",
+ action => "view",
+ args => [ ],
+ objects => [ BeerDB::Beer->retrieve(1) ],
+ model_class => "BeerDB::Beer",
+ template => "view"
+ }
+
+Then it calls C<BeerDB::Beer-E<gt>view>, passing in the request object
+as a parameter, and passes the whole lot to the view class for templating.
+In the next two chapters, we'll look at how Maypole's default model and
+view classes generally do what you want them to do.
--- /dev/null
+=head1 The Beer Database, Twice
+
+We briefly introduced the "beer database" example in the L<About.pod>
+material, where we presented its driver class as a fait accompli. Where
+did all that code come from, and what does it actually mean?
+
+=head2 The big beer problem
+
+I have a seriously bad habit. This is not the beer problem; this is a
+programming problem. The bad habit is that when I approach a problem I
+want to solve, I get sidetracked deeper and deeper trying to solve more
+and more generic problems, and then, satisfied with solving the generic
+problem, I never get around to solving the specific problem. I always
+write libraries for people writing libraries, and never write
+applications.
+
+The thing with really good beer is that it commands you to drink more of
+it, and then by the morning you can't remember whether it was any good
+or not. After buying several bottles of some random central African
+lager on a dim recollection that it was really good and having it turn
+out to be abysmal, this really became a problem. If only I could have a
+database that I updated every time I buy a new beer, I'd be able to tell
+whether or not I should buy that Lithuanian porter again or whether it
+would be quicker just to flush my money down the toilet and cut out the
+middle-man.
+
+The only problem with databases on Unix is that there isn't really a
+nice way to get data into them. There isn't really a Microsoft Access
+equivalent which can put a simple forms-based front-end onto an
+arbitrary database, and if there is, I either didn't like it or couldn't
+find it, and after a few brews, you really don't want to be trying to type
+in your tasting notes in raw SQL.
+
+So you see a generic problem arising out of a specific problem here. I
+didn't want to solve the specific problem of the beer database, because
+I'd already had another idea for a database that needed a front-end. So
+for two years, I sat on this great idea of having a database of tasting
+notes for beer. I even bought that damned African beer again. Enough was
+enough. I wrote Maypole.
+
+=head2 The easy way
+
+The first Maypole application was the beer database. We've already met
+it; it looks like this.
+
+ package BeerDB;
+ use base 'Apache::MVC';
+ BeerDB->set_database("dbi:SQLite:t/beerdb.db");
+ BeerDB->config->{uri_base} = "http://localhost/beerdb/";
+ BeerDB->config->{rows_per_page} = 10;
+ BeerDB->config->{display_tables} = [qw[beer brewery pub style]];
+ BeerDB::Brewery->untaint_columns( printable => [qw/name notes url/] );
+ BeerDB::Style->untaint_columns( printable => [qw/name notes/] );
+ BeerDB::Beer->untaint_columns(
+ printable => [qw/abv name price notes/],
+ integer => [qw/style brewery score/],
+ date => [ qw/date/],
+ );
+
+ use Class::DBI::Loader::Relationship;
+ BeerDB->config->{loader}->relationship($_) for (
+ "a brewery produces beers",
+ "a style defines beers",
+ "a pub has beers on handpumps");
+ 1;
+
+Now, we can split this into four sections. Let's look at them one
+at a time.
+
+=head3 Driver setup
+
+Here's the first section:
+
+ package BeerDB;
+ use base 'Apache::MVC';
+ BeerDB->setup("dbi:SQLite:t/beerdb.db");
+
+This is actually all you need for a functional database front-end. Everything
+else is configuration details. This says three things: we're an application
+called C<BeerDB>. This package is called the B<driver class>, because
+it's a relatively small class which defines how the whole application is
+going to run.
+
+The second line says that our front-end is going to be C<Apache::MVC>,
+which is the Apache C<mod_perl> based version of Maypole; there's also
+a CGI version, C<CGI::Maypole>, and a command-line version for
+debugging, C<Maypole::CLI>, but C<Apache::MVC> is usually the one you
+want.
+
+Thirdly we're going to need to set up our database with the given DBI
+connection string. Now the core of Maypole itself doesn't know about
+DBI; as we explained in L<Model.pod>, this argument is passed to our
+model class wholesale. As we haven't said anything about a model
+class, we get the default one, C<Maypole::Model::CDBI>, which takes a
+DBI connect string. So this one line declares that we're using a C<CDBI>
+model class and it sets up the database for us. In the same way, we
+don't say that we want a particular view class, so we get the default
+C<Maypole::View::TT>.
+
+At this point, everything is in place; we have our driver class, it uses
+a front-end, we have a model class and a view class, and we have a data
+source.
+
+=head3 Application configuration
+
+The next of our four sections is the configuration for the application itself.
+
+ BeerDB->config->{uri_base} = "http://localhost/beerdb/";
+ BeerDB->config->{rows_per_page} = 10;
+ BeerDB->config->{display_tables} = [qw[beer brewery pub style]];
+
+Maypole provides a method called C<config> which returns a hash reference
+of the application's whole configuration. We can use this to set some
+parameters; the C<uri_base> is used as the canonical URL of the base
+of this application, and Maypole uses it to construct links.
+
+By defining C<rows_per_page>, we say that any listings we do with the
+C<list> and C<search> templates should be arranged in sets of pages, with
+a maximum of 10 items on each page. If we didn't declare that, C<list>
+would try to put all the objects on one page, which could well be bad.
+
+Finally, we declare which tables we want our Maypole front-end to
+reference. If you remember from the schema, there's a table called
+C<handpump> which acts as a linking table in a many-to-many relationship
+between the C<pub> and C<beer> tables. As it's only a linking table, we
+don't want people poking with it directly, so we exclude it from the
+list of C<display_tables>.
+
+=head3 Editability
+
+The next section is the following set of lines:
+
+ BeerDB::Brewery->untaint_columns( printable => [qw/name notes url/] );
+ BeerDB::Style->untaint_columns( printable => [qw/name notes/] );
+ BeerDB::Beer->untaint_columns(
+ printable => [qw/abv name price notes/],
+ integer => [qw/style brewery score/],
+ date => [ qw/date/],
+ );
+
+As explained in L<StandardTemplates.pod>, this is an set of instructions to
+C<Class::DBI::FromCGI> regarding how the given columns should be edited.
+If we didn't have this section, we'd be able to view and delete records,
+but adding and editing them wouldn't work. It took me ages to work that
+one out.
+
+=head3 Relationships
+
+Finally, we want to explain to Maypole how the various tables relate to
+each other. This is done so that, for instance, when displaying a beer,
+the brewery does not appear as an integer like "2" but as the name of
+the brewery from the C<brewery> table with an ID of 2.
+
+The usual C<Class::DBI> way to do this involves the C<has_a> and
+C<has_many> methods, but I can never remember how to use them, so I came
+up with the C<Class::DBI::Loader::Relationship> module; this was another
+yak that needed shaving on the way to the beer database:
+
+ use Class::DBI::Loader::Relationship;
+ BeerDB->config->{loader}->relationship($_) for (
+ "a brewery produces beers",
+ "a style defines beers",
+ "a pub has beers on handpumps");
+ 1;
+
+C<CDBIL::Relationship> acts on a C<Class::DBI::Loader> object and
+defines relationships between tables in a fairly free-form style.
+The equivalent in ordinary C<Class::DBI> would be:
+
+ BeerDB::Brewery->has_many(beers => "BeerDB::Beer");
+ BeerDB::Beer->has_a(brewery => "BeerDB::Brewery");
+ BeerDB::Style->has_many(beers => "BeerDB::Beer");
+ BeerDB::Beer->has_a(style => "BeerDB::Style");
+
+ BeerDB::Handpump->has_a(beer => "BeerDB::Beer");
+ BeerDB::Handpump->has_a(pub => "BeerDB::Pub");
+ BeerDB::Pub->has_many(beers => [ BeerDB::Handpump => 'beer' ]);
+ BeerDB::Beer->has_many(pubs => [ BeerDB::Handpump => 'pub' ]);
+
+Maypole's default templates will use this information to display, for
+instance, a list of a brewery's beers on the brewery view page.
+
+This is the complete beer database application; Maypole's default templates
+and the actions in the view class do the rest. But what if we want to do a
+little more. How would we begin to extend this application?
+
+=head2 The hard way
+
+Maypole was written because I don't like writing more Perl code than is
+necessary. I also don't like writing HTML. In fact, I don't really get
+on this whole computer thing, to be honest. But we'll start by ways that
+we can customize the beer application simply by adding methods or
+changing properties of the Perl driver code.
+
+The first thing we ought to look into is the names of the columns; most
+of them are fine, but that "Abv" column stands out. I'd rather that was
+"A.B.V.". Maypole uses the C<column_names> method to map between the
+names of the columns in the database to the names it displays in the
+default templates. This is provided by C<Maypole::Model::Base>, and
+normally, it does a pretty good job; it turns C<model_number> into
+"Model Number", for instance, but there was no way it could guess that
+C<abv> was an abbreviation. Since it returns a hash, the easiest way
+to correct it is to construct a hash consisting of the bits it got
+right, and then override the bits it got wrong:
+
+ package BeerDB::Beer;
+ sub column_names {
+ (shift->SUPER::column_names(), abv => "A.B.V.")
+ }
+
+Similarly, the order of columns is a bit wonky. We can fix this by
+overriding the C<display_columns> method; this is also a good way to
+hide away any columns we don't want to have displayed, in the same way
+as declaring the C<display_tables> configuration parameter let us hide
+away tables we weren't using:
+
+ sub display_columns {
+ ("name", "brewery", "style", "price", "score", "abv", "notes")
+ }
+
+Hey, have you noticed that we haven't done anything with the
+beers/handpumps/pubs thing yet? Good, I was hoping that you hadn't.
+Ayway, this is because Maypole can't tell easily that a C<BeerDB::Beer>
+object can call C<pubs> to get a list of pubs. Not yet, at least; we're
+working on it. In the interim, we can explicitly tell Maypole which
+accessors are related to the C<BeerDB::Beer> class like so:
+
+ sub related { "pubs" }
+
+Now when we view a beer, we'll have a list of the pubs that it's on at.
--- /dev/null
+=head1 The Maypole iBuySpy Portal
+
+I think it's good fun to compare Maypole
+
+We begin with a length process of planning and investigating the
+sources. Of prime interest is the database schema and the initial data,
+which we convert to a Mysql database. Converting MS SQL to Mysql is not fun.
+I shall spare you the gore. Especially the bit where the default insert IDs
+didn't match up between the tables.
+
+The C<ibsportal> database has a number of tables which describe how the
+portal should look, and some tables which describe the data that should
+appear on it. The portal is defined in terms of a set of modules; each
+module takes some data from somewhere, and specifies a template to be
+used to format the data. This is quite different from how Maypole
+normally operates, so we have a choice as to whether we're going to
+completely copy this design, or use a more "natural" implementation in
+terms of having the portal display defined as a template itself, with
+all the modules specified right there in Template Toolkit code rather
+than picked up from the database. This would be much faster, since you
+get one shot of rendering instead of having to process each module's
+template independently. The thing is, I feel like showing off
+precisely how flexible Maypole is, so we'll do it the hard way.
+
+The first thing we need to do is get the database into some sort of
+useful shape, and work out the relationships between the tables. This of
+course requires half a day of playing with GraphViz, Omnigraffle and
+mysql, but ended up with something like this:
+
+=for html
+<img src="ibs-schema.png">
+
+This leads naturally to the following driver code:
+
+ package Portal;
+ use base 'Apache::MVC';
+ Portal->setup("dbi:mysql:ibsportal");
+ use Class::DBI::Loader::Relationship;
+ Portal->config->{loader}->relationship($_) for (
+ "A module has a definition", "A module has settings",
+ "A tab has modules", "A portal has tabs",
+ "A role has a portal", "A definition has a portal",
+ "A module has announcements", "A module has contacts",
+ "A module has discussions", "A module has events",
+ "A module has htmltexts", "A module has links",
+ "A module has documents",
+ "A user has roles via userrole"
+ );
+ 1;
+
+As you can see, a portal is made up of a number of different tabs;
+the tabs contain modules, but they're separated into different panes,
+so a module knows whether it belongs on the left pane, the right pane
+or the center. A module also knows where it appears in the pane.
+
+We'll begin by mocking up the portal view in plain text, like so:
+
+ use Portal;
+ my $portal = Portal::Portal->retrieve(2);
+ for my $tab ($portal->tabs) {
+ print $tab,"\n";
+ for my $pane (qw(LeftPane ContentPane RightPane)) {
+ print "\t$pane:\n";
+ for (sort { $a->module_order <=> $b->module_order }
+ $tab->modules(pane => $pane)) {
+ print "\t\t$_:\t", $_->definition,"\n";
+ }
+ }
+ print "\n";
+ }
+
+This dumps out the tabs of our portal, along with the modules in each
+tab and their types; this lets us check that we've got the database
+set up properly. If we have, it should produce something like this:
+
+ Home
+ LeftPane:
+ Quick link: Quicklink
+ ContentPane:
+ Welcome to the IBuySpy Portal: Html Document
+ News and Features: announcement
+ Upcoming event: event
+ RightPane:
+ This Week's Special: Html Document
+ Top Movers: XML/XSL
+
+ ...
+
+Now we want to get the front page up; for the moment, we'll just have it
+display the module names and their definitions like our text mock-up,
+and we'll flesh out the actual modules later.
+
+But before we do that, we'll write a front-end URL handler method, to
+allow us to ape the ASP file names. Why do we want to make a Maypole
+site look like it's running C<.aspx> files? Because we can! - and
+because I want to show we don't necessarily B<have> to follow the
+Maypole tradition of having our URLs look like
+C</I<table>/I<action>/I<id>/I<arguments>>.
+
+ our %pages = (
+ "DesktopDefault.aspx" => { action => "view", table => "tab" },
+ "MobileDefault.aspx" => { action => "view_mobile", table => "tab" },
+ );
+
+ sub parse_path {
+ my $self = shift;
+ $self->{path} ||= "DesktopDefault.aspx";
+ return $self->SUPER::parse_path if not exists $pages{$self->{path}};
+ my $page = $pages{$self->{path}} ;
+ $self->{action} = $page->{action};
+ $self->{table} = $page->{table};
+ my %query = $self->{ar}->args;
+ $self->{args} = [ $query{tabid} || $query{ItemID} || 1];
+ }
+
+ 1;
+
+Here we're overriding the C<parse_path> method which takes the C<path>
+slot from the request and populates the C<table>, C<action> and
+C<arguments> slots. If the user has asked for a page we don't know
+about, we ask the usual Maypole path handling method to give it a try;
+this will become important later on. We turn the default page,
+C<DesktopDefault.aspx>, into the equivalent of C</tab/view/1> unless
+another C<tabid> or C<ItemID> is given in the query parameters; this allows us
+to use the ASP.NET-style C<DesktopDefault.aspx?tabid=3> to select a tab.
+
+Now we have to create our C<tab/view> template; the majority of
+this is copied from the F<DesktopDefault.aspx> source, but our panes
+look like this:
+
+ <td id="LeftPane" Width="170">
+ [% pane("LeftPane") %]
+ </td>
+ <td width="1">
+ </td>
+ <td id="ContentPane" Width="*">
+ [% pane("ContentPane") %]
+ </td>
+ <td id="RightPane" Width="230">
+ [% pane("RightPane") %]
+ </td>
+ <td width="10">
+
+ </td>
+
+The C<pane> macro has to be the Template Toolkit analogue of the Perl code
+we used for our mock-up:
+
+ [% MACRO pane(panename) BLOCK;
+ FOR module = tab.modules("pane", panename);
+ "<P>"; module; " - "; module.definition; "</P>";
+ END;
+ END;
+
+Now, the way that the iBuySpy portal works is that each module has a
+definition, and each definition contains a path to a template:
+C<$module-E<gt>definition-E<gt>DesktopSrc> returns a path name for an C<ascx>
+component file. All we need to do is convert those files from ASP to the
+Template Toolkit, and have Maypole process each component for each module,
+right?
+
+=head2 Components and templates
+
+Dead right, but it was here that I got too clever. I guess it was the word
+"component" that set me off. I thought that since the page was made up of a
+large number of different modules, all requiring their own set of objects, I
+should use a seperate Maypole sub-request for each one, as shown in the
+"Component-based pages" recipe in L<Request.pod>.
+
+So this is what I did. I created a method in C<Portal::Module> that would
+set the template to the appropriate C<ascx> file:
+
+ sub view_desktop :Exported {
+ my ($self, $r) = @_;
+ $r->{template} = $r->objects->[0]->definition->DesktopSrc;
+ }
+
+and changed the C<pane> macro to fire off a sub-request for each module:
+
+ [% MACRO pane(panename) BLOCK;
+ FOR module = tab.modules("pane", panename);
+ SET path = "/module/view_desktop/" _ module.id;
+ request.component(path);
+ END;
+ END; %]
+
+This did the right thing, and a call to C</module/view_desktop/12> would
+look up the C<Html Document> module definition, find the C<DesktopSrc> to
+be F<DesktopModules/HtmlModule.ascx>, and process module 12 with that
+template. Once I had converted F<HtmlModule.ascx> to be a Template Toolkit
+file (and we'll look at the conversion of the templates in a second) it
+would display nicely on my portal.
+
+Except it was all very slow; we were firing off a large number of Maypole
+requests in series, so that each template could get at the objects it
+needed. Requests were taking 5 seconds.
+
+That's when it dawned on me that these templates don't actually need different
+objects at all. The only object of interest that C</module/view_desktop> is
+passing in is a C<module> object, and each template figures everything out by
+accessor calls on that. But we already have a C<module> object, in our C<FOR>
+loop - we're using it to make the component call, after all! Why not just
+C<PROCESS> each template inside the loop directly?
+
+ [% MACRO pane(panename) BLOCK;
+ FOR module = tab.modules("pane", panename);
+ SET src = module.definition.DesktopSrc;
+ TRY;
+ PROCESS $src;
+ CATCH DEFAULT;
+ "Bah, template $src broke on me!";
+ END;
+ END;
+ END; %]
+
+This worked somewhat better, and took request times from 5 seconds down
+to acceptable sub-second levels again. I could take the C<view_desktop>
+method out again; fewer lines of code to maintain is always good. Now
+all that remained to do for the view side of the portal was to convert
+our ASP templates over to something sensible.
+
+=head2 ASP to Template Toolkit
+
+They're all much of a muchness, these templating languages. Some of
+them, though, are just a wee bit more verbose than others. For instance,
+the banner template which appears in the header consists of 104 lines
+of ASP code; most of those are to create the navigation bar of tabs
+that we can view. Now I admit that we're slightly cheating at the moment
+since we don't have the concept of a logged-in user and so we don't
+distinguish between the tabs that anyone can see and those than only an
+admin can see, but we'll come back to it later. Still, 104 lines, eh?
+
+The actual tab list is presented here: (reformated slightly for sanity)
+
+ <tr>
+ <td>
+ <asp:datalist id="tabs" cssclass="OtherTabsBg"
+ repeatdirection="horizontal" ItemStyle-Height="25"
+ SelectedItemStyle-CssClass="TabBg" ItemStyle-BorderWidth="1"
+ EnableViewState="false" runat="server">
+ <ItemTemplate>
+ <a href='<%= Request.ApplicationPath %>/
+ DesktopDefault.aspx?tabindex=<%# Container.ItemIndex %>&tabid=
+ <%# ((TabStripDetails) Container.DataItem).TabId %>' class="OtherTabs">
+ <%# ((TabStripDetails) Container.DataItem).TabName %></a>
+ </ItemTemplate>
+ <SelectedItemTemplate>
+ <span class="SelectedTab">
+ <%# ((TabStripDetails) Container.DataItem).TabName %></span>
+ </SelectedItemTemplate>
+ </asp:datalist>
+ </td>
+ </tr>
+
+But it has to be built up in some 22 lines of C# code which creates and
+populates an array and then binds it to a template parameter. See those
+C<E<lt>%#> and C<E<lt>%=> tags? They're the equivalent of our Template
+Toolkit C<[% %]> tags. C<Request.ApplicationPath>? That's our C<base>
+template argument.
+
+In our version we ask the portal what tabs it has, and display the
+list directly, displaying the currently selected tab differently:
+
+ <table id="Banner_tabs" class="OtherTabsBg" cellspacing="0" border="0">
+ <tr>
+ [% FOR a_tab = portal.tabs %]
+ [% IF a_tab.id == tab.id %]
+ <td class="TabBg" height="25">
+ <span class="SelectedTab">[%tab.name%]</span>
+ [% ELSE %]
+ <td height="25">
+ <a href='[%base%]DesktopDefault.aspx?tabid=[%a_tab.id%]'
+ class="OtherTabs">[%a_tab.name%]</a>
+ [% END %]
+ </td>
+ [% END %]
+ </tr>
+ </table>
+
+This is the way the world should be. But wait, where have we pulled this
+C<portal> variable from? We need to tell the C<Portal> class to put the
+default portal into the template arguments:
+
+ sub additional_data {
+ shift->{template_args}{portal} = Portal::Portal->retrieve(2);
+ }
+
+Translating all the other ASP.NET components is a similar exercise in drudgery;
+on the whole, there was precisely nothing interesting about them at all - we
+merely converted a particularly verbose templating language (and if I never see
+C<asp:BoundColumn> again, it'll be no loss) into a rather more sophisticated
+one.
+
+The simplest component, F<HtmlModule.ascx>, asks a module for its associated
+C<htmltexts>, and then displays the C<DesktopHtml> for each of them in a table.
+This was 40 lines of ASP.NET, including more odious C# to make the SQL calls
+and retrieve the C<htmltexts>. But we can do all that retrieval by magic, so
+our F<HtmlModule.ascx> looks like this:
+
+ [% PROCESS module_title %]
+ <portal:title EditText="Edit" EditUrl="~/DesktopModules/EditHtml.aspx" />
+ <table id="t1" cellspacing="0" cellpadding="0">
+ <tr valign="top">
+ <td id="HtmlHolder">
+ [% FOR html = module.htmltexts; html.DesktopHtml; END %]
+ </td>
+ </tr>
+ </table>
+
+Now I admit that we've cheated here and kept that C<portal:title> tag
+until we know what to do with it - it's obvious that we should turn
+it into a link to edit the HTML of this module if we're allowed to.
+
+The next simplest one actually did provide a slight challenge;
+F<ImageModule.ascx> took the height, width and image source properties
+of an image from the module's C<settings> table, and displayed an C<IMG>
+tag with the appropriate values. This is only slightly difficult because
+we have to arrange the array of C<module.settings> into a hash of
+C<key_name> => C<setting> pairs. Frankly, I can't be bothered to do this
+in the template, so we'll add it into the C<template_args> again. This
+time C<addition_data> looks like:
+
+ sub additional_data {
+ my $r = shift;
+ shift->{template_args}{portal} = Portal::Portal->retrieve(2);
+ if ($r->{objects}->[0]->isa("Portal::Module")) {
+ $r->{template_args}{module_settings} =
+ { map { $_->key_name, $_->setting }
+ $r->{objects}->[0]->settings };
+ }
+ }
+
+And the F<ImageModule.ascx> drops from the 30-odd lines of ASP into:
+
+ [% PROCESS module_title; %]
+ <img id="Image1" border="0" src="[% module_settings.src %]"
+ width="[% module_settings.width %]"
+ height="[% module_settings.height %]" />
+ <br>
+
+Our portal is taking shape; after a few more templates have been translated,
+we now have a complete replica of the front page of the portal and all its
+tabs. It's fast, it's been developed rapidly, and it's less than 50 lines
+of Perl code so far. But it's not finished yet.
+
+=head2 Adding users
+
--- /dev/null
+=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 - 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 student population.
+
+Flox is still in, uh, flux, but it does the essentials. 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...
+
+=head2 Mapping the concepts
+
+Any Maypole application should start with two things: a database schema,
+and some idea of what the pages involved are going to look like.
+Usually, these pages will be tying to displaying or editing some element
+of the database, so these two concepts should come hand in hand.
+
+When I started looking at social networking sites, I began by
+identifying the concepts which were going to make up the tables of the
+application. At its most basic, a site like Orkut or Flox has two
+distinct concepts: a user, and a connection between two users.
+Additionally, there's the idea of an invitation to a new user, which can
+be extended, accepted, declined or ignored. These three will make up the
+key tables; there are an extra two tables in Flox, but they're
+essentially enumerations that are a bit easier to edit: each user has an
+affiliation to a particular college or department, and a status in the
+university. (Undergraduate, graduate, and so on.)
+
+For this first run-through, we're going to ignore the ideas of societies
+and communities, and end up with a schema like so:
+
+ CREATE TABLE user (
+ id int not null auto_increment primary key,
+ first_name varchar(50),
+ last_name varchar(50),
+ email varchar(255),
+ profile text,
+ password varchar(255),
+ affiliation int,
+ unistatus int,
+ status ENUM("real", "invitee"),
+ photo blob,
+ photo_type varchar(30)
+ );
+
+ CREATE TABLE connection (
+ id int not null auto_increment primary key,
+ from_user int,
+ to_user int,
+ status ENUM("offered", "confirmed")
+ );
+
+ CREATE TABLE invitation (
+ id char(32) not null primary key,
+ issuer int,
+ recipient int,
+ expires date
+ );
+
+Plus the definition of our two auxilliary tables:
+
+ CREATE TABLE affiliation (
+ id int not null auto_increment primary key,
+ name varchar(255)
+ );
+
+ CREATE TABLE unistatus (
+ id int not null auto_increment primary key,
+ name varchar(255)
+ );
+
+Notice that, for simplicity, invitations and friendship connections are
+quite similar: they are extended from one user to another. This means
+that people who haven't accepted an invite yet still have a place in the
+user table, with a different C<status>. Similarly, a connection between
+users can be offered, and when it is accepted, its status is changed to
+"confirmed" and a reciprocal relationship put in place.
+
+We also have some idea, based on what we want to happen, of what pages
+and actions we're going to define. Leaving the user aside for the
+moment, we want an action which extends an invitation from the current
+user to a new user. We want a page the new user can go to in order to
+accept that invitation. Similarly, we want an action which offers a
+friendship connection to an existing user, and a page the user can go to
+to accept or reject it. This gives us five pages so far:
+
+ invitation/issue
+ invitation/accept
+
+ user/befriend
+ connection/accept
+ connection/reject
+
+Notice that the C<befriend> action is performed on a user, not a
+connection. This is distinct from C<invitation/issue> because when
+befriending, we have a real user on the system that we want to do
+something to. This makes sense if you think of it in terms of object
+oriented programming - we could say
+
+ Flox::Connection->create(to => $user)
+
+but it's clearer to say
+
+ $user->befriend
+
+Similarly, we could say
+
+ Flox::User->create({ ... })->issue_invitation_to
+
+but it's clearer to say
+
+ Flox::Invitation->issue( to => Flox::User->create({ ... }) )
+
+because it more accurately reflects the principal subject and object of
+these actions.
+
+Returning to look at the user class, we want to be able to view a user's
+profile, edit one's own profile, set up the profile for the first
+time, upload pictures and display pictures. We also need to handle the
+concepts of logging in and logging out.
+
+As usual, though, we'll start with a handler class which sets up the
+database:
+
+ package Flox;
+ use base qw(Apache::MVC);
+ Flox->setup("dbi:mysql:flox");
+ Flox->config->{display_tables} = [qw[user invitation connection]];
+ 1;
+
+Very simple, as these things are meant to be. Now let's build on it.
+
+=head2 Authentication
+
+The concept of a current user is absolutely critical in a site like
+Flox; it represents "me", the viewer of the page, as the site explores
+the connections in my world. We've described the authentication hacks
+briefly in the L<Request.pod> chapter, but now it's time to go into a
+little more detail about how user handling is done.
+
+XXX
+
+We also want to be able to refer to the current user from the templates,
+so we use the overridable C<additional_data> method to give us a C<my>
+template variable:
+
+ sub additional_data {
+ my $r = shift; $r->{template_args}{my} = $r->{user};
+ }
+
+I've called it C<my> rather than C<me> because we it lets us check
+C<[% my.name %]>, and so on.
+
+=head2 Viewing a user
+
+The first page that a user will see after logging in will be their own
+profile, so in order to speed development, we'll start by getting a
+C<user/view> page up.
+
+The only difference from a programming point of view between this action
+and the default C<view> action is that, if no user ID is given, then we
+want to view "me", the current user. Remembering that the default view
+action does nothing, our C<Flox::User::view> action only needs to do
+nothing plus ensure it has a user in the C<objects> slot, putting
+C<$r-E<gt>{user}> in there if not:
+
+ sub view :Exported {
+ my ($class, $r) = @_;
+ $r->{objects} = [ $r->{user} ] unless @{$r->{objects}||[]};
+ }
+
+Maypole, unfortunately, is very good at making programming boring. The
+downside of having to write very little code at all is that we now have
+to spend most of our time writing nice HTML for the templates.
+
+XXX
+
+The next stage is viewing the user's photo. Assuming we've got the photo
+stored in the database already (which is a reasonable assumption for the
+moment since we don't have a way to upload a photo quite yet) then we
+can use the a variation of the "Displaying pictures" hack from the
+Requests chapter:
+
+ 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;
+ } else {
+ # Read no-photo photo
+ $r->{content_type} = "image/png";
+ $r->{output} = slurp_file("images/no-photo.png");
+ }
+ }
+
+We begin by getting a user object, just like in the C<view> action: either
+the user whose ID was passed in on the URL, or the current user. Then
+we check if a C<photo_type> has been set in this user's record. If so,
+then we'll use that as the content type for this request, and the data
+in the C<photo> attribute as the data to send out. The trick here is
+that setting C<$r-E<gt>{output}> overrides the whole view class processing
+and allows us to write the content out directly.
+
+In our template, we can now say
+
+ <IMG SRC="/user/view_picture/[% user.id %]">
+
+and the appropriate user's mugshot will appear.
+
+However, if we're throwing big chunks of data around like C<photo>, it's
+now worth optimizing the C<User> class to ensure that only pertitent
+data is fetched by default, and C<photo> and friends are only fetched on
+demand. The "lazy population" section of C<Class::DBI>'s man page
+explains how to group the columns by usage so that we can optimize
+fetches:
+
+ Flox::User->columns(Primary => qw/id/);
+ Flox::User->columns(Essential => qw/status/);
+ Flox::User->columns(Helpful => qw/ first_name last_name email password/)
+ Flox::User->columns(Display => qw/ profile affiliation unistatus /);
+ Flox::User->columns(Photo => qw/ photo photo_type /);
+
+This means that the status and ID columns will always be retrieved when
+we deal with a user; next, any one of the name, email or password
+columns will cause that group of data to be retrieved; if we go on to
+display more information about a user, we also load up the profile,
+affiliation and university status; finally, if we're throwing around
+photos, then we load in the photo type and photo data.
+
+These groupings are somewhat arbitrary, and there needs to be a lot of
+profiling to determine the most efficient groupings of columns to load,
+but they demonstrate one principle about working in Maypole: this is the
+first time in dealing with Maypole that we've had to explicitly list the
+columns of a table, but Maypole has so far Just Worked. There's a
+difference, though, between Maypole just working and Maypole working
+well, and if you want to optimize your application, then you need to
+start putting in the code to do that. The beauty of Maypole is that you
+can do as much or as little of such optimization as you want or need.
+
+So now we can view users and their photos. It's time to allow the users
+to edit their profiles and upload a new photo.
+
+=head2 Editing users
+
+XXX Editing a profile
+
+I introduced Flox to a bunch of friends and told them to be as ruthless
+as possible in finding bugs and trying to break it. And break it they
+did; within an hour the screens were thoroughly messed up as users had
+nasty HTML tags in their profiles, names, email addresses and so on.
+This spawned another hack in the request cookbook: "Limiting data for
+display". I changed the untaint columns to use C<html> untainting, and
+all was better:
+
+ Flox::User->untaint_columns(
+ html => [qw/first_name last_name profile/],
+ printable => [qw/password/],
+ integer => [qw/affiliation unistatus /],
+ email => [qw/email/]
+ );
+
+The next stage was the ability to upload a photo. We unleash the "Uploading
+files" recipe, with an additional check to make sure the photo is of a
+sensible size:
+
+ use constant MAX_IMAGE_SIZE => 512 * 1024;
+ sub do_upload :Exported {
+ my ($class, $r) = @_;
+ my $user = $r->{user};
+ my $upload = $r->{ar}->upload("picture");
+ if ($upload) {
+ my $ct = $upload->info("Content-type");
+ return $r->error("Unknown image file type $ct")
+ if $ct !~ m{image/(jpeg|gif|png)};
+ return $r->error("File too big! Maximum size is ".MAX_IMAGE_SIZE)
+ if $upload->size > MAX_IMAGE_SIZE;
+
+ my $fh = $upload->fh;
+ my $image = do { local $/; <$fh> };
+
+ use Image::Size;
+ my ($x, $y) = imgsize(\$image);
+ return $r->error("Image too big! ($x, $y) Maximum size is 350x350")
+ if $y > 350 or $x > 350;
+ $r->{user}->photo_type($ct);
+ $r->{user}->photo($image);
+ }
+
+ $r->objects([ $user ]);
+ $r->{template} = "view";
+ }
+
+Now we've gone as far as we want to go about user editing at the moment.
+Let's have a look at the real meat of a social networking site: getting
+other people involved, and registering connections between users.
+
+=head2 Invitations
+
+We need to do two things to invitations working: first provide a way to
+issue an invitation, and then provide a way to accept it. Since what
+we're doing in issuing an invitation is essentially creating a new
+one, we'll use our usual practice of having a page to display the form
+to offer an invitation, and then use a C<do_edit> method to actually do
+the work. So our C<issue> method is just an empty action:
+
+ sub issue :Exported {}
+
+and the template proceeds as normal:
+
+ [% PROCESS header %]
+ <h2> Invite a friend </h2>
+
+ <FORM ACTION="[%base%]/invitation/do_edit/" METHOD="post">
+ <TABLE>
+
+Now we use the "Catching errors in a form" recipe from L<Request.pod> and
+write our form template:
+
+ <TR><TD>
+ First name: <INPUT TYPE="text" NAME="forename"
+ VALUE="[%request.params.forename%]">
+ </TD>
+ <TD>
+ Last name: <INPUT TYPE="text" NAME="surname"
+ VALUE="[%request.params.surname%]">
+ </TD></TR>
+ [% IF errors.forename OR errors.surname %]
+ <TR>
+ <TD><SPAN class="error">[% errors.forename %]</SPAN> </TD>
+ <TD><SPAN class="error">[% errors.surname %]</SPAN> </TD>
+ </TR>
+ [% END %]
+ <TR>
+ ...
+
+Now we need to work on the C<do_edit> action. This has to validate the
+form parameters, create the invited user, create the row in the C<invitation>
+table, and send an email to the new user asking them to join.
+
+We'd normally use C<create_from_cgi> to do the first two stages, but this time
+we handle the untainting manually, because there are a surprising number of
+things we need to check before we actually do the create. So here's the
+untainting of the parameters:
+
+ my ($self, $r) = @_;
+ my $h = CGI::Untaint->new(%{$r->{params}});
+ my (%errors, %ex);
+ for (qw( email forename surname )) {
+ $ex{$_} = $h->extract(
+ "-as_".($_ eq "email" ? "email" : "printable") => $_
+ ) or $errors{$_} = $h->error;
+ }
+
+Next, we do the usual dance of throwing the user back at the form in case
+of errors:
+
+ if (keys %errors) {
+ $r->{template_args}{message} = "There was something wrong with that...";
+ $r->{template_args}{errors} = \%errors;
+ $r->{template} = "issue";
+ return;
+ }
+
+We've introduced a new template variable here, C<message>, which we'll use
+to display any important messages to the user.
+
+The first check we need to do is whether or not we already have a user
+with that email address. If we have, and they're a real user, then we
+abort the invite progress and instead redirect them to viewing that user's
+profile.
+
+ my ($user) = Flox::User->search({ email => $ex{email} });
+ if ($user) {
+ if ($user->status eq "real") {
+ $r->{template_args}{message} =
+ "That user already seems to exist on Flox. ".
+ "Is this the one you meant?";
+
+ $self->redirect_to_user($r,$user);
+ }
+
+Where C<redirect_to_user> looks like this:
+
+ sub redirect_to_user {
+ my ($self, $r, $user) = @_;
+ $r->{objects} = [ $user ];
+ $r->{template} = "view";
+ $r->{model_class} = "Flox::User"; # Naughty.
+ }
+
+This is, as the comment quite rightly points out, naughty. We're currently
+doing a C</invitation/do_edit/> and we want to turn this into a
+C</user/view/xxx>, changing the table, template and arguments all at once.
+To do this, we have to change the Maypole request object's idea of the model
+class, since this determines where to look for the template: if we didn't,
+we'd end up with C<invitation/view> instead of C<user/view>.
+
+Ideally, we'd do this with a Apache redirect, but we want to get that
+C<message> in there as well, so this will have to do. This isn't good practice;
+we put it into a subroutine so that we can fix it up if we find a better way
+to do it.
+
+Anyway, this is what we should do if a user already exists on the system
+and has accepted an invite already. What if we're trying to invite a user but
+someone else has invited them first and they haven't replied yet?
+
+ } else {
+ # Put it back to the form
+ $r->{template_args}{message} =
+ "That user has already been invited; ".
+ "please wait for them to accept";
+ $r->{template} = "issue";
+ }
+ return;
+ }
+
+Race conditions suck.
+
+Okay. Now we know that the user doesn't exist, and so can create the new
+one:
+
+ my $new_user = Flox::User->create({
+ email => $ex{email},
+ first_name => $ex{forename},
+ last_name => $ex{surname},
+ status => "invitee"
+ });
+
+We want to give the invitee a URL that they can go to in order to
+accept the invite. Now we don't just want the IDs of our invites to
+be sequential, since someone could get one invite, and then guess the
+rest of the invite codes. We provide a relatively secure MD5 hash as
+the invite ID:
+
+ my $random = md5_hex(time.(0+{}).$$.rand);
+
+For additional security, we're going to have the URL in the form
+C</invitation/accept/I<id>/I<from_id>/I<to_id>>, encoding the user ids
+of the two users. Now we can send email to the invitee to ask them to
+visit that URL:
+
+ my $newid = $new_user->id;
+ my $myid = $r->{user}->id;
+ _send_mail(to => $ex{email}, url => "$random/$myid/$newid",
+ user => $r->{user});
+
+I'm not going to show the C<_send_mail> routine, since it's boring.
+We haven't actually created the C<Invitation> object yet, so let's
+do that now.
+
+ Flox::Invitation->create({
+ id => $random,
+ issuer => $r->{user},
+ recipient => $new_user,
+ expires => Time::Piece->new(time + LIFETIME)->datetime
+ });
+
+You can also imagine a daily cron job that cleans up the C<Invitation>
+table looking for invitations that ever got replied to within their
+lifetime:
+
+ ($_->expires > localtime && $_->delete)
+ for Flox::Invitation->retrieve_all;
+
+Notice that we don't check whether the ID is already used. We could, but,
+you know, if MD5 sums start colliding, we have much bigger problems on
+our hands.
+
+Anyway, now we've got the invitation created, we can go back to whence we
+came: viewing the original user:
+
+ $self->redirect_to_user($r, $r->{user});
+
+Now our invitee has an email, and goes B<click> on the URL. What happens?
+
+XXX
+
+=head2 Friendship Connections
+
+=head2
--- /dev/null
+=head1 Maypole Model Classes
+
+=head2 Class::DBI
+
+=head2 Maypole::Model::CDBI
+
+Maypole model classes are pretty evil. The very first thing a Maypole
+model class will do in a Maypole application is to cause a load of
+table-based classes to come into being, and then assimilate them.
+
+What I mean by this is that when you set up a Maypole application, in
+your driver class, you'll say something like this:
+
+ BeerDB->setup("dbi:mysql:beerdb");
+
+C<setup> is a Maypole method, and it hands its parameter to the model
+class. In our case, the model class is a DBI connect string, because
+that's what C<Maypole::Model::CDBI>, the C<Class::DBI>-based model
+expects. C<Maypole::Model::CDBI> has a method called C<setup_database>
+that creates all the C<Class::DBI> table classes after connecting to the
+database with that connect string. It does this by using
+C<Class::DBI::Loader>, a utility module which asks a database
+about its schema and sets up classes such as C<BeerDB::Beer> to inherit
+from C<Class::DBI>. This is just doing automatically what we did
+manually in our examples above.
+
+Now it gets interesting. The names of these classes are stashed away in
+the application's configuration, and then Maypole forcibly has these
+classes inherit from the model class. Our C<BeerDB::Beer> now inherits
+both from C<Class::DBI> and C<Maypole::Model::CDBI>.
+
+This is useful for two reasons. The first reason is that
+C<Maypole::Model::CDBI> is stuffed full of C<Class::DBI> goodies that
+make writing Maypole applications a lot easier:
+
+ package Maypole::Model::CDBI;
+ use base qw(Maypole::Model::Base Class::DBI);
+ use Class::DBI::AsForm;
+ use Class::DBI::FromCGI;
+ use Class::DBI::Loader;
+ use Class::DBI::AbstractSearch;
+ use Class::DBI::Plugin::RetrieveAll;
+ use Class::DBI::Pager;
+
+We'll meet most of these goodies in L<StandardTemplates.pod>, where we
+explain how C<Maypole::Model::CDBI> works.
+
+The second reason why we want our table classes to inherit from
+C<Maypole::Model::CDBI> is because C<CDBI> provides a useful set of
+default actions. So what's an action, and why are they useful?
+
+=head2 Extending a model class with actions
+
+Maypole operates primarily by turning URLs into method calls on a model
+class. All that the model stage of Maypole's operation does, when it
+comes down to it, is maintain a mapping of tables to classes, and
+despatch a HTTP request to a method call on the relevant table class. This
+means that if you request a URL such as
+
+ http://localhost/beerdb/brewery/delete/20
+
+Maypole's first stage of operation is to turn that into
+C<BeerDB::Brewery-E<gt>delete(20)>. Now, it's slightly more complex than
+that. Firstly because it doesn't actually pass the parameter C<20>, but
+it passes an object representing row 20 in the database, but we can
+gloss over that for the second. No, the real issue is that Maypole does
+not allow you to call any method in the table class; that would be
+somewhat insecure.
+
+Instead, Maypole makes a distinction between the kind of methods that
+only the class itself and other Perl code can call, and the kind of
+methods that anyone can call from a URL. This latter set of methods are
+called B<exported> methods, and exporting is done with by means of Perl
+attributes. You define a method to be exported like so:
+
+ sub drink :Exported {
+
+This will allow the user to access C</beerdb/beer/drink> over the web.
+An exported method accompanied with a template to render its output is
+sometimes called an B<action>.
+
+Maypole model classes like C<Maypole::Model::CDBI> come with a
+relatively handy set of actions which are all you need to set up a CRUD
+database front-end: viewing a row in a database, editing it, adding a
+new one, deleting, and so on. The most important thing about Maypole,
+though, is that it doesn't stop there. You can add your own.
+
+For instance, in our beer database application, we could create a
+C<BeerDB::Beer> package, and put some additional actions in there.
+
+ package BeerDB::Beer;
+ sub top_five :Exported {
+ my ($class, $r) = @_;
+ $r->{objects} = [ ($r->retrieve_all_sorted_by("score"))[-5..-1] ];
+ }
+
+Our action is called as a class method with the Maypole request object.
+It uses the C<Class::DBI::Plugin::RetrieveAll> module's
+C<retrieve_all_sorted_by> mixin to get the top five scoring beers, and
+puts these in the C<objects> slot of the request of object. Next, the
+view class will come along and process the C<top_five> template with
+these five beers.
+
+We'll look more at how to put together actions in the
+L<StandardTemplates.pod> chapter and our case studies.
+
+=head2 What Maypole wants from a model
+
+=head2 Building your own model class
--- /dev/null
+=head1 Overview of Maypole Documentation
+
+The Maypole documentation is arranged over several files; this is a good
+one to start with.
+
+Once you've read this, you should probably look at L<About.pod>, the
+guide to what Maypole is and how the Maypole request works. It also
+describes how to set up a simple CRUD web application in Maypole.
+
+The next two chapters are quite thorough, and you might want to skip
+over them if you already know how L<Class::DBI> and the L<Template>
+toolkit work. L<Model.pod> and L<View.pod> describe these technologies
+and their relationship to Maypole.
+
+Now we present the default actions and templates - the F<factory>
+templates - in L<StandardTemplates.pod>. When you have read these
+chapters, you are ready to start building more complex applications in
+Maypole than just a simple CRUD interface. L<Beer.pod> reintroduces the
+beer database application and shows how to move from the "magic" of the
+automatically supplied templates and actions to a customized application
+with user-specified actions.
+
+L<Request.pod> contains more information about the Maypole request
+object and provides some cookbook-style techniques: how to provide
+authentication, how to provide a REST interface, how to override various
+stages of the content generation process, and so on.
+
+The final chapters are examples of how to construct large web
+applications in Maypole: L<Flox.pod> describes a "social network" site
+similar to Friendster and Orkut; L<PetStore.pod> implements the Java/C#
+benchmark Pet Store application; and L<BuySpy.pod> implements the
+ASP.NET sample portal application.
--- /dev/null
+=head1 Maypole Request Hacking Cookbook
+
+Hacks; design patterns; recipes: call it what you like, this chapter is a
+developing collection of techniques which can be slotted in to Maypole
+applications to solve common problems or make the development process easier.
+
+As Maypole developers, we don't necessarily know the "best practice" for
+developing Maypole applications ourselves, in the same way that Larry Wall
+didn't know all about the best Perl programming style as soon as he wrote
+Perl. These techniques are what we're using at the moment, but they may
+be refined, modularized, or rendered irrelevant over time. But they've
+certainly saved us a bunch of hours work.
+
+=head2 Frontend hacks
+
+These hacks deal with changing the way Maypole relates to the outside world;
+alternate front-ends to the Apache and CGI interfaces, or subclassing chunks
+of the front-end modules to alter Maypole's behaviour in particular ways.
+
+=head3 Separate model class modules
+
+You want to put all the C<BeerDB::Beer> routines in a separate module,
+so you say:
+
+ package BeerDB::Beer;
+ BeerDB::Beer->has_a(brewery => "BeerDB::Brewery");
+ sub foo :Exported {}
+
+And in F<BeerDB.pm>, you put:
+
+ use BeerDB::Beer;
+
+It doesn't work.
+
+B<Solution>: It doesn't work because of the timing of the module
+loading. C<use Beer::Beer> will try to set up the C<has_a> relationships
+at compile time, when the database tables haven't even been set up,
+since they're set up by
+
+ BeerDB->setup("...")
+
+which does its stuff at runtime. There are two ways around this; you can
+either move the C<setup> call to compile time, like so:
+
+ BEGIN { BeerDB->setup("...") }
+
+or move the module loading to run-time (my preferred solution):
+
+ BeerDB->setup("...");
+ BeerDB::Beer->require;
+
+=head3 Debugging with the command line
+
+You're seeing bizarre problems with Maypole output, and you want to test it in
+some place outside of the whole Apache/mod_perl/HTTP/Internet/browser circus.
+
+B<Solution>: Use the C<Maypole::CLI> module to go directly from a URL to
+standard output, bypassing Apache and the network altogether.
+
+C<Maypole::CLI> is not a standalone front-end, but to allow you to debug your
+applications without having to change the front-end they use, it temporarily
+"borgs" an application. If you run it from the command line, you're expected
+to use it like so:
+
+ perl -MMaypole::CLI=Application -e1 'http://your.server/path/table/action'
+
+For example:
+
+ perl -MMaypole::CLI=BeerDB -e1 'http://localhost/beerdb/beer/view/1?o2=desc'
+
+You can also use the C<Maypole::CLI> module programatically to create
+test suites for your application. See the Maypole tests themselves or
+the documentation to C<Maypole::CLI> for examples of this.
+
+=head3 Changing how URLs are parsed
+
+You don't like the way Maypole URLs look, and want something that either
+fits in with the rest of your site or hides the internal workings of the
+system.
+
+C<Solution>: So far we've been using the C</table/action/id/args> form
+of a URL as though it was "the Maypole way"; well, there is no Maypole
+way. Maypole is just a framework and absolutely everything about it is
+overridable.
+
+If we want to provide our own URL handling, the method to override in
+the driver class is C<parse_path>. This is responsible for taking
+C<$r-E<gt>{path}> and filling the C<table>, C<action> and C<args> slots
+of the request object. Normally it does this just by splitting the path
+on C</>s, but you can do it any way you want, including getting the
+information from C<POST> form parameters or session variables.
+
+For instance, suppose we want our URLs to be of the form
+C<ProductDisplay.html?id=123>, we could provide a C<parse_path> method
+like so:
+
+ sub parse_path {
+ my $r = shift;
+ $r->{path} ||= "ProductList.html";
+ ($r->{table}, $r->{action}) =
+ ($r->{path} =~ /^(.*?)([A-Z]\w+)\.html/);
+ $r->{table} = lc $r->{table};
+ $r->{action} = lc $r->{action};
+ my %query = $r->{ar}->args;
+ $self->{args} = [ $query{id} ];
+ }
+
+This takes the path, which already has the query parameters stripped off
+and parsed, and finds the table and action portions of the filename,
+lower-cases them, and then grabs the C<id> from the query. Later methods
+will confirm whether or not these tables and actions exist.
+
+See L<BuySpy.pod> for another example of custom URL processing.
+
+=head3 Maypole for mobile devices
+
+You want Maypole to use different templates to display on particular
+browsers.
+
+B<Solution>: There are several ways to do this, but here's the neatest
+we've found. Maypole chooses where to get its templates either by
+looking at the C<template_root> config parameter or, if this is not
+given, calling the C<get_template_root> method to ask the front-end to
+try to work it out. We can give the front-end a little bit of help, by
+putting this method in our driver class:
+
+ sub get_template_root {
+ my $r = shift;
+ my $browser = $r->{ar}->headers_in->get('User-Agent');
+ if ($browser =~ /mobile|palm|nokia/i) {
+ "/home/myapp/templates/mobile";
+ } else {
+ "/home/myapp/templates/desktop";
+ }
+ }
+
+(Maybe there's a better way to detect a mobile browser, but you get the
+idea.)
+
+=head2 Content display hacks
+
+These hacks deal primarily with the presentation of data to the user,
+modifying the C<view> template or changing the way that the results of
+particular actions are displayed.
+
+=head3 Null Action
+
+You need an "action" which doesn't really do anything, but just formats
+up a template.
+
+B<Solution>: There are two ways to do this, depending on what precisely
+you need. If you just need to display a template, C<Apache::Template>
+style, with no Maypole objects in it, then you don't need to write any
+code; just create your template, and it will be available in the usual
+way.
+
+If, on the other hand, you want to display some data, and what you're
+essentially doing is a variant of the C<view> action, then you need to
+ensure that you have an exported action, as described in
+L<StandardTemplates.pod>:
+
+ sub my_view :Exported { }
+
+=head3 Template Switcheroo
+
+An action doesn't have any data of its own to display, but needs to display
+B<something>.
+
+B<Solution>: This is an B<extremely> common hack. You've just issued an
+action like C<beer/do_edit>, which updates the database. You don't want
+to display a page that says "Record updated" or similar. Lesser
+application servers would issue a redirect to have the browser request
+C</beer/view/I<id>> instead, but we can actually modify the Maypole
+request on the fly and, after doing the update, pretend that we were
+going to C</beer/view/I<id>> all along. We do this by setting the
+objects in the C<objects> slot and changing the C<template> to the
+one we wanted to go to.
+
+In this example from L<Flox.pod>, we've just performed an C<accept>
+method on a C<Flox::Invitation> object and we want to go back to viewing
+a user's page.
+
+ sub accept :Exported {
+ my ($self, $r) = @_;
+ my $invitation = $r->objects->[0];
+ # [... do stuff to $invitation ...]
+ $r->{objects} = [$r->{user}];
+ $r->{model_class} = "Flox::User";
+ $r->{template} = "view";
+ }
+
+This hack is so common that it's expected that there'll be a neater
+way of doing this in the future.
+
+=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, a Word document, 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->{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 REST
+
+You want to provide a programmatic interface to your Maypole site.
+
+B<Solution>: The best way to do this is with C<REST>, which uses a
+descriptive URL to encode the request. For instance, in L<Flox.pod> we
+describe a social networking system. One neat thing you can do with
+social networks is to use them for reputation tracking, and we can use
+that information for spam detection. So if a message arrives from
+C<person@someco.com>, we want to know if they're in our network of
+friends or not and mark the message appropriately. We'll do this by
+having a web agent (say, L<WWW::Mechanize> or L<LWP::UserAgent>) request
+a URL of the form
+C<http://flox.simon-cozens.org/user/relationship_by_email/person%40someco.com>.
+Naturally, they'll need to present the appropriate cookie just like a
+normal browser, but that's a solved problem. We're just interested in
+the REST request.
+
+The request will return a single integer status code: 0 if they're not
+in the system at all, 1 if they're in the system, and 2 if they're our
+friend.
+
+All we need to do to implement this is provide the C<relationship_by_email>
+action, and use it to fill in the output in the same way as we did when
+displaying a picture. Since C<person%40someco.com> is not the ID of a
+row in the user table, it will appear in the C<args> array:
+
+ use URI::Escape;
+ sub relationship_by_email :Exported {
+ my ($self, $r) = @_;
+ my $email = uri_unescape($r->{args}[0]);
+ $r->{content_type} = "text/plain";
+ my $user;
+ unless (($user) = Flox::User->search(email => $email)) {
+ $r->{content} = "0\n"; return;
+ }
+
+ if ($r->{user}->is_friend($user)) { $r->{content} = "2\n"; return; };
+ $r->{content} = "1\n"; return;
+ }
+
+=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.
+
+Alternatively, if you've already got all the objects you need, you can
+probably just C<[% PROCESS %]> the templates directly.
+
+=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;
+ }
+ ...
+ }
+
+And now your C<sub_authenticate> methods can specify the policy for
+each table:
+
+ sub sub_authenticate { # Ensure we can only create, reject or accept
+ my ($self, $r) = @_;
+ return OK if $r->{action} =~ /^(issue|accept|reject|do_edit)$/;
+ return;
+ }
+
+=head2 Creating and editing hacks
+
+These hacks particularly deal with issues related to the C<do_edit>
+built-in action.
+
+=head3 Limiting data for display
+
+You want the user to be able to type in some text that you're later
+going to display on the site, but you don't want them to stick images in
+it, launch cross-site scripting attacks or otherwise insert messy HTML.
+
+B<Solution>: Use the C<CGI::Untaint::html> module to sanitize the HTML
+on input. C<CGI::Untaint::html> uses C<HTML::Sanitizer> to ensure that
+tags are properly closed and can restrict the use of certain tags and
+attributes to a pre-defined list.
+
+Simply replace:
+
+ App::Table->untaint_columns(
+ text => [qw/name description/]
+ );
+
+with:
+
+ App::Table->untaint_columns(
+ html => [qw/name description/]
+ );
+
+And incoming HTML will be checked and cleaned before it is written to
+the database.
+
+=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.
+
+=head3 Catching errors in a form
+
+A user has submitted erroneous input to an edit/create form. You want to
+send him back to the form with errors displayed against the erroneous
+fields, but have the other fields maintain the values that the user
+submitted.
+
+B<Solution>: This is basically what the default C<edit> template and
+C<do_edit> method conspire to do, but it's worth highlighting again how
+they work.
+
+If there are any errors, these are placed in a hash, with each error
+keyed to the erroneous field. The hash is put into the template as
+C<errors>, and we process the same F<edit> template again:
+
+ $r->{template_args}{errors} = \%errors;
+ $r->{template} = "edit";
+
+This throws us back to the form, and so the form's template should take
+note of the errors, like so:
+
+ FOR col = classmetadata.columns;
+ NEXT IF col == "id";
+ "<P>";
+ "<B>"; classmetadata.colnames.$col; "</B>";
+ ": ";
+ item.to_field(col).as_HTML;
+ "</P>";
+ IF errors.$col;
+ "<FONT COLOR=\"#ff0000\">"; errors.$col; "</FONT>";
+ END;
+ END;
+
+If we're designing our own templates, instead of using generic ones, we
+can make this process a lot simpler. For instance:
+
+ <TR><TD>
+ First name: <INPUT TYPE="text" NAME="forename">
+ </TD>
+ <TD>
+ Last name: <INPUT TYPE="text" NAME="surname">
+ </TD></TR>
+
+ [% IF errors.forename OR errors.surname %]
+ <TR>
+ <TD><SPAN class="error">[% errors.forename %]</SPAN> </TD>
+ <TD><SPAN class="error">[% errors.surname %]</SPAN> </TD>
+ </TR>
+ [% END %]
+
+The next thing we want to do is to put the originally-submitted values
+back into the form. We can do this relatively easily because Maypole
+passes the Maypole request object to the form, and the POST parameters
+are going to be stored in a hash as C<request.params>. Hence:
+
+ <TR><TD>
+ First name: <INPUT TYPE="text" NAME="forename"
+ VALUE="[%request.params.forename%]">
+ </TD>
+ <TD>
+ Last name: <INPUT TYPE="text" NAME="surname"
+ VALUE="[%request.params.surname%]">
+ </TD></TR>
+
+Finally, we might want to only re-fill a field if it is not erroneous, so
+that we don't get the same bad input resubmitted. This is easy enough:
+
+ <TR><TD>
+ First name: <INPUT TYPE="text" NAME="forename"
+ VALUE="[%request.params.forename UNLESS errors.forename%]">
+ </TD>
+ <TD>
+ Last name: <INPUT TYPE="text" NAME="surname"
+ VALUE="[%request.params.surname UNLESS errors.surname%]">
+ </TD></TR>
+
+=head3 Uploading files and other data
+
+You want the user to be able to upload files to store in the database.
+
+B<Solution>: It's messy.
+
+First, we set up an upload form, in an ordinary dummy action. Here's
+the action:
+
+ sub upload_picture : Exported {}
+
+And here's the template:
+
+ <FORM action="/user/do_upload" enctype="multipart/form-data" method="POST">
+
+ <P> Please provide a picture in JPEG, PNG or GIF format:
+ </P>
+ <INPUT TYPE="file" NAME="picture">
+ <BR>
+ <INPUT TYPE="submit">
+ </FORM>
+
+(Although you'll probably want a bit more HTML around it than that.)
+
+Now we need to write the C<do_upload> action. At this point we have to get a
+little friendly with the front-end system. If we're using C<Apache::Request>,
+then the C<upload> method of the C<Apache::Request> object (which
+C<Apache::MVC> helpfully stores in C<$r-E<gt>{ar}>) will work for us:
+
+ sub do_upload :Exported {
+ my ($class, $r) = @_;
+ my $user = $r->{user};
+ my $upload = $r->{ar}->upload("picture");
+
+This returns a C<Apache::Upload> object, which we can query for its
+content type and a file handle from which we can read the data. It's
+also worth checking the image isn't going to be too massive before we
+try reading it and running out of memory, and that the content type is
+something we're prepared to deal with.
+
+ if ($upload) {
+ my $ct = $upload->info("Content-type");
+ return $r->error("Unknown image file type $ct")
+ if $ct !~ m{image/(jpeg|gif|png)};
+ return $r->error("File too big! Maximum size is ".MAX_IMAGE_SIZE)
+ if $upload->size > MAX_IMAGE_SIZE;
+
+ my $fh = $upload->fh;
+ my $image = do { local $/; <$fh> };
+
+Now we can store the content type and data into our database, store it
+into a file, or whatever:
+
+ $r->{user}->photo_type($ct);
+ $r->{user}->photo($image);
+ }
+
+And finally, we use our familiar template switcheroo hack to get back to
+a useful page:
+
+ $r->objects([ $user ]);
+ $r->{template} = "view";
+ }
+
+Now, as we've mentioned, this only works because we're getting familiar with
+C<Apache::Request> and its C<Apache::Upload> objects. If we're planning to use
+C<CGI::Maypole> instead, or want to write our application in a generic way so
+that it'll work regardless of front-end, then we need to replace the C<upload>
+call with an equivalent which uses the C<CGI> module to get the upload data.
+This is convoluted and horrific and we're not going to show it here, but it's
+possible.
+
+Combine with the "Displaying pictures" hack above for a happy time.
--- /dev/null
+
+=head1 Maypole's Standard Templates and Actions
+
+As we saw in our CRUD example, Maypole does all it can to make your life
+easier; this inclues providing a set of default actions and
+factory-supplied templates. These are written in such a generic way,
+making extensive use of class metadata, that they are more or less
+applicable to any table or application. However, in order to progress
+from automatically generated CRUD applications to real customized
+applications, we need to begin by understanding how these default
+actions do their stuff, and how the default templates are put together.
+Once we have an understanding of what Maypole does for us automatically,
+we can begin to customize and create our own templates and actions.
+
+=head2 The standard actions
+
+A simple, uncustomized Maypole model class, such as one of the classes
+in the beer database application, provides the following default actions
+- that is, provides access to the following URLs:
+
+=over 3
+
+=item C</[table]/view/[id]>
+
+This takes the ID of an object in a table, retrieves the object, and
+presents it to the F<view> template.
+
+=item C</[table]/edit/[id]>
+
+This is the same as C<view>, but uses the F<edit> template to provide a
+web form to edit the object; it submits to C<do_edit>.
+
+=item C</[table]/do_edit/[id]>
+
+=item C</[table]/do_edit/>
+
+This provides both editing and row creation facilities.
+
+=item C</[table]/delete/id>
+
+This deletes a row, returning to the C<list> page.
+
+=item C</[table]/list/>
+
+This provides a paged list of the table suitable for browsing.
+
+=item C</[table]/search/>
+
+This handles a search query and presents the search results back to the
+F<list> template.
+
+=back
+
+We'll now look at how these actions are implemented, before moving on to
+take a detailed look at the templates they drive.
+
+=head3 C<view> and C<edit>
+
+These actions are very simple; their job is to take a row ID, turn it
+into an object, and hand it to the template to be displayed. However, as
+taking the first argument and turning it into an object is such a common
+action, it is handled directly by the model class's C<process> method.
+Similarly, the default template name provided by the C<process> method
+is the name of the acction, and so will be C<view> or C<edit>
+accordingly.
+
+So the code required to make these two actions work turns out to be:
+
+ sub view :Exported { }
+ sub edit :Exported { }
+
+That's right - no code at all. This shows the power of the templating
+side of the system. If you think about it for a moment, it is natural
+that these actions should not have any code - after all, we have
+separated out the concerns of "acting" and displaying. Both of these
+"actions" are purely concerned with displaying a record, and don't need
+to do any "acting". Remember that the "edit" method doesn't actually do
+any editing - this is provided by C<do_edit>; it is just another view of
+the data, albeit once which allows the data to be modified later. These
+two methods don't need to modify the row in any way, they don't need to
+do anything clever. They just are.
+
+So why do we need the subroutines at all? If the subroutines did not exist,
+we would be sent to the C<view> and C<edit> templates as would be
+expected, but these templates would not be provided with the right
+arguments; we need to go through the C<process> method in order to turn
+the URL argument into a row and thence into an object to be fed to the
+template. By exporting these methods, even though they contain no code
+themselves, we force Maypole to call C<process> and provide the class
+and object to the templates.
+
+The moral of this story is that if you need to have an action which is
+purely concerned with display, not acting, but needs to receive an ID
+and turn it into an object, then create an empty method. For instance,
+if we want to make an alternate view of a row which only showed the
+important columns, we might create a method
+
+ sub short_view :Exported {}
+
+This will cause the row to be turned into an object and fed to the
+C<short_view> template, and that template would be responsible for
+selecting the particular columns to be displayed.
+
+=head3 C<do_edit>
+
+This action, on the other hand, actually has to do something. If it's
+provided with an ID, this is turned into an object and we're in edit
+mode, acting upon that object. If not, we're in create mode.
+
+ sub do_edit :Exported {
+ my ($self, $r) = @_;
+ my $h = CGI::Untaint->new(%{$r->{params}});
+ my ($obj) = @{$r->objects || []};
+ if ($obj) {
+ # We have something to edit
+ $obj->update_from_cgi($h);
+ } else {
+ $obj = $self->create_from_cgi($h);
+ }
+
+The C<CDBI> model uses L<Class::DBI::FromCGI> to turn C<POST> parameters
+into database table data. This in turn uses C<CGI::Untaint> to ensure
+that the data coming in is suitable for the table. If you're using the
+default C<CDBI> model, then, you're going to need to set up your tables
+in a way that makes C<FromCGI> happy.
+
+=over
+
+=item Digression on C<Class::DBI::FromCGI>
+
+C<CGI::Untaint> is a mechanism for testing that incoming form data
+conforms to various properties. For instance, given a C<CGI::Untaint>
+object that encapsulates some C<POST> parameters, we can extract an
+integer like so:
+
+ $h->extract(-as_integer => "score");
+
+This checks that the C<score> parameter is an integer, and returns it if
+it is; if not, C<< $h->error >> will be set to an appropriate error
+message. Other tests by which you can extract your data are C<as_hex>
+and C<as_printable>, which tests for a valid hex number and an ordinary
+printable string respectively; there are other handlers available on
+CPAN, and you can make your own, as documented in L<CGI::Untaint>.
+
+To tell the C<FromCGI> handler what handler to use for each of your
+columns, you need to use the C<untaint_columns> methods in the classes
+representing your tables. For instance:
+
+ BeerDB::Beer->untaint_columns(
+ integer => ["score", ... ],
+ );
+
+This must be done after the call to C<setup> in your handler, because
+otherwise the model classes won't have been set up to inherit from
+C<Class::DBI::FromCGI>.
+
+Remember that if you want to use drop-downs to set the value of related
+fields, such as the brewery for a beer, you need to untaint these as
+something acceptable for the primary key of that table:
+
+ BeerDB::Beer->untaint_columns(
+ integer => ["score", "brewery", "style" ],
+ ...
+ );
+
+This is usually integer, if you're using numeric IDs for your primary
+key. If not, you probably want C<printable>, but you probably know what
+you're doing anyway.
+
+=back
+
+The data is untainted, and any errors are collected into a hash which is
+passed to the template. We also pass back in the parameters, so that the
+template can re-fill the form fields with the original values. The user
+is then sent back to the C<edit> template.
+
+ if (my %errors = $obj->cgi_update_errors) {
+ # Set it up as it was:
+ $r->{template_args}{cgi_params} = $r->{params};
+ $r->{template_args}{errors} = \%errors;
+ $r->{template} = "edit";
+ }
+
+Otherwise, the user is taken back to viewing the new object:
+
+ } else {
+ $r->{template} = "view";
+ }
+ $r->objects([ $obj ]);
+
+Notice that this does use hard-coded names for the templates to go to next.
+Feel free to override this in your subclasses:
+
+ sub do_edit :Exported {
+ my ($class, $r) = @_;
+ $class->SUPER::do_edit($r);
+ $r->template("my_edit");
+ }
+
+=head3 delete
+
+The delete method takes a number of arguments and deletes those rows from the
+database; it then loads up all rows and heads to the F<list> template.
+You almost certainly want to override this to provide some kind of
+authentication.
+
+=head3 list
+
+Listing, like viewing, is a matter of selecting objects for
+presentation. This time, instead of a single object specified in the
+URL, we want, by default, all the records in the table:
+
+ sub list :Exported {
+ my ($class, $r) = @_;
+ $r->objects([ $self->retrieve_all ])
+ }
+
+However, things are slightly complicated by paging and ordering by
+column; the default implementation also provides a C<Class::DBI::Pager>
+object to the templates and uses that to retrieve the appropriate bit of
+the data, as specified by the C<page> URL query parameter. See the F<pager>
+template below.
+
+=head3 search
+
+Searching also uses paging, and creates a query from the C<POST>
+parameters. It uses the F<list> template to display the objects once
+they've been selected from the database.
+
+=head2 The templates and macros
+
+Once these actions have done their work, they hand a set of objects to
+the templates; if you haven't specified your own custom template
+globally or for a given class, you'll be using the factory specified
+template. Let's take a look now at each of these and how they're put
+together.
+
+The beauty of the factory specified templates is that they make use of
+the classes' metadata as supplied by the view class. Although you're
+strongly encouraged to write your own templates, in which you don't need
+to necessarily be as generic, the factory templates will always do the
+right thing for any class without further modification, and as such are
+useful examples of how to build Maypole templates.
+
+=head3 Commonalities
+
+There are certain common elements to a template, and these are extracted
+out. For instance, all the templates call the F<header> template to
+output a HTML header, and nearly all include the F<macros> template to
+load up some common template functions. We'll look at these common
+macros as we come across them.
+
+=head3 F<view>
+
+=template view
+
+=head3 F<edit>
+
+The F<edit> template is pretty much the same as F<view>, but it uses the
+C<to_field> method on each column of an object to return a C<HTML::Element>
+object representing a form element to edit that property. These elements
+are then rendered to HTML with C<as_HTML>. It expects to see a list of
+editing errors, if any, in the C<errors> template variable:
+
+ FOR col = classmetadata.columns;
+ NEXT IF col == "id";
+ "<P>";
+ "<B>"; classmetadata.colnames.$col; "</B>";
+ ": ";
+ item.to_field(col).as_HTML;
+ "</P>";
+ IF errors.$col;
+ "<FONT COLOR=\"#ff0000\">"; errors.$col; "</FONT>";
+ END;
+ END;
+
+=head3 F<list>
+
+Browsing records and search results are both handled by the F<list> template.
+The C<search> template argument is used to distinguish between the two cases:
+
+ [% IF search %]
+ <h2> Search results </h2>
+ [% ELSE %]
+ <h2> Listing of all [% classmetadata.plural %]</h2>
+ [% END %]
+
+=head1 Customizing Generic CRUD Applications
--- /dev/null
+=head1 Maypole View Classes
+
+In a large application, you will almost certainly want to customize the
+layout and design of the output pages. This task may even be the purview
+of a separate team of HTML designers rather than the programmers. Since
+a typical programmer will try to avoid touching HTML as much as possible
+and a typical designer will try to avoid touching Perl code, programmers
+have evolved a system of templating to separate the concerns of
+programming and designing.
+
+One of the core concepts in Maypole is the I<view class>, and this is
+responsible for routing the data produced in the model class into the
+templates produced by the designers. Of course, there are a great many
+possible templating systems and styles, and so there can be a great many
+possible Maypole view classes. Each view class will take the data from
+the controller, locate a template to be processed, and hand the whole
+lot to its preferred templating module, which will then do the hard work
+of filling in the template and coming up with the output.
+
+You can choose whatever Maypole view class you want, but the default
+view class is C<Maypole::View::TT>, and it feeds its data and templates
+to a module called the Template Toolkit.
+
+=head2 The Template Toolkit
+
+The Template Toolkit, written by Andy Wardley, is a very powerful and
+generic templating system. It provides its own little formatting language
+which supports loops, conditionals, hash and array dereferences and
+method calls, macro processing and a plug-in system to connect it to
+external Perl modules. There are several good introductions to the
+Template Toolkit available: you should have one installed as
+L<Template::Tutorial::Datafile>; there's one at
+L<http://www.perl.com/pub/a/2003/07/15/nocode.html>, and of course
+there's the "Badger Book" - I<The Perl Template Toolkit>, by Andy et al.
+
+We'll present a brief introduction here by deconstructing some of the
+templates used by Maypole applications. For more deconstruction, see
+L<StandardTemplates.pod>, which is an entire chapter dealing with the
+factory supplied templates.
+
+Here's the template which is called for the front page of the standard
+beer database application, C<custom/frontpage>.
+
+ [% INCLUDE header %]
+
+ <h2> The beer database </h2>
+
+ <TABLE BORDER="0" ALIGN="center" WIDTH="70%">
+ [% FOR table = config.display_tables %]
+ <TR>
+ <TD>
+ <A HREF="[%table%]/list">List by [%table %]</A>
+ </TD>
+ </TR>
+ [% END %]
+ </TABLE>
+
+The first thing to note about this is that everything outside of the
+Template Toolkit tags (C<[%> and C<%]>) is output verbatim. That is,
+you're guaranteed to see
+
+ <h2> The beer database </h2>
+
+ <TABLE BORDER="0" ALIGN="center" WIDTH="70%">
+
+in the output somewhere. Inside the tags, magic happens. The first piece
+of magic is the C<[% INCLUDE header %]> directive. This goes away and
+finds a file called F<header> - don't worry about how it finds that yet,
+we'll come to that later on - and processes the file's contents as
+though they were right there in the template. Our F<header> file happens
+not to contain any C<[% %]> tags, but if it did, they would be processed
+in the same way as the ones in F<frontpage>.
+
+The next piece of magic is this line:
+
+ [% FOR table = config.display_tables %]
+
+We're seeing a lot of things here at once. C<config> is where we should
+start looking. This is a template variable, which is what templates are
+all about - templating means getting data from somewhere outside and
+presenting it to the user in a useful way, and the C<config> hash is a
+prime example of data that we want to use. It's actually the hash of
+configuration parameters for this Maypole application, and one of the
+keys in that hash is C<display_tables>, the database tables that we're
+allowed to play with. In the application, we probably said something
+like
+
+ BeerDB->config->{display_tables} = [qw[beer brewery pub style]];
+
+This stores the four values - C<beer>, C<brewery>, C<pub> and C<style> -
+in an array, which is placed in the config hash under the key
+C<display_tables>. Now we're getting them back again.
+
+The Template Toolkit's dot operator is a sort of do-the-right-thing
+operator; we can say C<array.0> to get the first element of an array,
+C<hash.key> to look up the C<key> key in a hash, and C<object.method> to
+call C<method> on an object. So, for instance, if we said
+C<config.display_tables.2>, we'd look up the C<display_tables> key in
+the configuration hash and get our array back, then look up the 2nd
+element and get C<pub>.
+
+The C<FOR> loop will repeat the code four times, setting our new
+variable C<table> to the appropriate array element. This code:
+
+ [% FOR table = config.display_tables %]
+ Hello [% table %]!
+ [% END %]
+
+will produce something like
+
+ Hello beer!
+ Hello brewery!
+ Hello pub!
+ Hello style!
+
+In our case, though, we're printing out a table element linking to each
+database table in turn.
+
+Here's a slightly more complicated example, adapted from F<factory/pager>.
+This template is responsible for printing the little page menu at the
+bottom of a listing if there are more rows in the listing than we want
+on a single page.
+
+ [% PROCESS macros %]
+ <P ALIGN="center">Pages:
+ [%
+ FOREACH num = [pager.first_page .. pager.last_page];
+ IF num == pager.current_page;
+ "["; num; "] ";
+ ELSE;
+ SET args = "?page=" _ num;
+ SET label = "[" _ num _ "]";
+ link(classmetadata.moniker, "list", args, label);
+ END;
+ END;
+ %]
+ </P>
+
+Maypole will be providing a whole bunch of variables to this template,
+and we'll look at them all in a moment, but the only ones we need to care
+about are C<pager> and C<classmetadata>.
+
+We start by loading in a bunch of macros. Macros are Template Toolkit's
+functions - you can provide them some parameters and they'll run a little
+sub-template based on them. The C<macros> file contains some handy macros
+that I've found useful for constructing Maypole templates; again, these
+will be covered in full detail in L<StandardTemplates.pod>.
+
+We're going to be displaying something like this:
+
+ Pages: [1] [2] [3] [4]
+
+with most of those numbers being a link to the appropriate page. This
+mean we're going to have to have a list of numbers, and the C<FOREACH> loop
+provides this: (C<FOREACH> and C<FOR> are identical, just like in Perl.)
+
+ FOREACH num = [pager.first_page .. pager.last_page];
+
+Here we're manually constructing an array of numbers, using the range
+operator (C<..>) to fill in all the numbers from the C<first_page> (1)
+to the C<last_page> (4). The same dot operator is used to ask the C<pager>
+what its C<first_page> and C<last_page> is. Remember when we said
+C<config.display_tables>, we were looking up the C<display_tables> key
+in the C<config> hash? Well, this time we're not looking anything up in
+a hash. C<pager> is an object, and C<first_page> is a method. Thing is,
+you don't have to care. You can pretend it's a hash if you want. The
+syntax is the same, and Template Toolkit knows the right thing to do.
+
+Now we're going to be executing this loop four times, once each for C<num>
+being set to 1, 2, 3, and 4. At some point, we'll come across the page
+that we're actually on right now:
+
+ IF num == pager.current_page;
+
+and in that case, we don't want to produce a link to it. We just want
+to output it as text, surrounded by square brackets:
+
+ "["; num; "] ";
+
+We're using string literals to output the brackets. We don't have to
+do that. We could say it this way:
+
+ [% ...
+ IF num == pager.current_page;
+ %]
+ [ [% num %] ]
+ [% ELSE %]
+ ...
+ [% END %]
+
+But you know, I quite like it my way.
+
+Now if the number we're printing isn't the number of the current page,
+we want to make a link. Here's how we do it:
+
+ SET args = "?page=" _ num;
+ SET label = "[" _ num _ "]";
+ link(classmetadata.table, "list", args, label);
+
+C<SET> declares a new variable of our own. If there was anything called
+C<args> before, there isn't now. It's going to be the result of our
+statement C<"?page=" _ num>. C<_> is the concatenation operator, and
+glues C<?page=> onto the front of our number. So if we want to link to
+page 4, then the C<args> variable will contain C<?page=4>. Similarly,
+the C<label> variable will be C<[4]>.
+
+Now we call a macro, C<link> with these two variables and the value of
+C<classmetadata.table>. This macro takes four arguments, C<table>,
+C<action>, C<args> and C<label>, and constructs a link of the form
+
+ <A HREF="[% config.base_url %]/[% table %]/[% action %][% args %]">
+ [% label %]
+ </A>
+
+In our case, it'll be filled in like so:
+
+ <A HREF="[% config.base_url %]/[% classmetadata.table %]/list?page=4">
+ [ 4 ]
+ </A>
+
+Where C<classmetadata.table> will actually be the name of the current
+table, and C<config.base_url> will be replaced by the appropriate URL for
+this application.
+
+=head2 Locating Templates
+
+Another feature of C<Maypole::View::TT> which may not be present in
+alternate view class implementations - although they are strongly
+encouraged to provide it - is the way that templates are located.
+(Remember, I B<did> say I'd tell you about that later.) Template Toolkit
+allows whatever uses it to provide a path for template files to be
+located in. C<Maypole::View::TT> feeds it up to three possible
+directories to look things up in, and it will try to find a template in
+each of these in turn.
+
+When you configure a Maypole application, you can tell it the base
+directory of your templates like so:
+
+ BeerDB->config->{template_root} = "/var/www/beerdb/templates";
+
+If you don't do this, most Maypole front-ends will use the current
+directory, which is generally what you want anyway. Off this directory,
+Maypole will look for a set of subdirectories.
+
+For instance, I said we were in the middle of processing the front page
+and looking up a template file called F<header>. Maypole will first look
+for this file in the F<custom> subdirectory. (say,
+F</var/www/beerdb/templates/custom>) If it doesn't find one, then it
+looks in the F<factory> subdirectory. If it doesn't find one there, then
+it gives up and dies with an error. But that's your fault, since you've
+called for a template which doesn't exist. Don't do that.
+
+This behaviour means that you can provide your own site-specific
+templates, but if you don't do so, then you get to use a generic one
+provided by Maypole. Maypole's "factory setting" templates are written
+in such a way as to try and do the right thing no matter what your
+application does. They are occasionally successful at this.
+
+Now the front page was a pretty simple example, since Maypole only looks
+up two directories. In most cases, it checks an additional directory,
+and this directory depends entirely on what Maypole is doing.
+
+If you're writing an e-commerce application, for example, you may well
+have a table which represents the product catalogue and all the products
+you can buy. Let's call this the C<product> table. You'll also have a
+data source which is specific to the user which contains all the
+products that they're buying on this particular visit to the site. In
+time-honoured tradition, we'll call this the C<basket> table.
+
+Now it ought to be reasonably apparent that you don't want the basket
+to be displayed in exactly the same way as the product catalogue. The
+templates for C<product/list> and C<basket/list> need to be different.
+This is where the third directory comes in. The other directory, which
+Maypole checks very first of all, is specific to the table that you're
+viewing. So if you go to C<http://your.shop.com/basket/list>, Maypole
+will look in the F<basket> directory first for a file called F<list>,
+and second in the F<custom> directory for a site-wide list template,
+and then fall-back to the F<factory> directory for a generic list
+template. It should be obvious that you probably want to provide all
+of F<basket/list>, F<basket/view>, F<product/list>, F<product/view>
+and any other combination of classes and actions that you can think of.
+
+=head2 What Maypole provides to a template
+
+C<Maypole::View::TT> provides quite a variety of template variables to
+the template. As these are the building blocks of your pages, it's worth
+looking at precisely what variables are available.
+
+The most important variable is called C<objects>, and is a list of all
+the objects that this page is going to deal with. For instance,
+in the template F</beer/view>, C<objects> will contain the C<BeerDB::Beer>
+object for the 23rd item in the database, while F</brewery/list> will
+fill C<objects> will all the breweries; or at least, all the breweries
+on the current page.
+
+This variable is so important that to help design templates with it,
+C<Maypole::View::TT> provides a helpful alias to it depending on
+context. For instance, if you're writing your own F</brewery/list>
+template, the data in C<objects> is also available in a template
+variable called C<breweries>. If you're working on F</brewery/view>,
+though, it's available in C<brewery>, since there's only one brewery to
+be displayed.
+
+Additionally, you can get the base URL for the application from the
+C<base> template variable; this allows you to construct links, as we
+saw earlier:
+
+ <A HREF="[% base %]/brewery/edit/[% brewery.id %]">Edit this brewery</A>
+
+You can also get at the rest of the configuration for the site with the
+C<config> variable as we saw above, and the entire request object in
+C<request>, should you really need to poke at it. (I've only found this
+useful when working with authentication modules which stash a current user
+object in C<request.user>.)
+
+To allow the construction of the "generic" templates which live in
+F<factory>, Maypole also passes in a hash called C<classmetadata>,
+which contains all sorts of useful information about the class under
+examination:
+
+=over 3
+
+=item C<table>
+
+This is the name of the table that is represented by the class.
+
+=item C<class>
+
+This is the Perl's idea of the class; you don't need this unless you're
+doing really tricky things.
+
+=item C<moniker>
+
+This is a more human-readable version of the table name, that can be
+used for display.
+
+=item C<plural>
+
+The same, but a correctly-formed plural. For instance, "breweries".
+
+=item C<columns>
+
+The list of columns for display; see the section "Customizing Generic
+CRUD Applications" in L<StandardTemplates.pod>.
+
+=item C<colnames>
+
+This is a hash mapping the database's name for a column to a more
+human-readable name. Again, see "Customizing Generic CRUD Applications>.
+
+=item C<cgi>
+
+This is a slightly trickier one. It is a hash mapping column names to
+a C<HTML::Element> suitable for entering data into a new instance of
+that class. That is, for the C<beer> table, C<classmetadata.cgi.style>
+should be a C<HTML::Element> object containing a drop-down list of
+beer styles. This is explained in L<StandardTemplates.pod>.
+
+=item C<description>
+
+This is the human-readable description provided by a class.
+
+=item C<related_accessors>
+
+This is a list of accessors which can be called on an object to get
+lists of other things that this object "has". For instance, on a
+brewery, it would return C<beers>, since calling C<brewery.beers> would
+give you a list of beers produced by the brewery. Note that this only
+caters for accessors defining one-to-many relationships, not the
+ordinary one-to-one relationships, such as C<style>.
+
+=back
+
+=head2 Other view classes
+
+Please note that these template variables, C<config>, C<classmetadata>,
+C<objects> and its user-friendly alias, as well as the rest of them are
+a function of one particular view class, the default
+C<Maypole::View::TT> class. Other view classes may need to present an
+entirely different set of template variables, since the default ones
+might not make sense. The templates may look wildly different in other
+view class implementations. But that's OK, because you couldn't
+necessarily use the same templates with a different templating system
+anyway.
+
+For instance, in really dumb templating languages which can't handle
+dereferencing hashes or arrays - no wait, that's most of them - passing
+in a hash reference like C<classmetadata> won't help you since you can't
+get at any of its elements. So you'll need to take a look at the
+documentation for the appropriate view class to see what template
+variables it provides.
+
+So if, for some perverse reason, the Template Toolkit just isn't good
+enough for you, then you can set your own view class while configuring
+your application:
+
+ package BeerDB;
+ use base 'Apache::MVC';
+ ...
+ BeerDB->setup("dbi:SQLite:t/beerdb.db");
+ BeerDB->config->{uri_base} = "http://localhost/beerdb/";
+ BeerDB->config->{rows_per_page} = 10;
+ BeerDB->config->{view} = "Maypole::View::Mason";
+
+Where do these alternate view classes come from? Gentle reader, they
+come from B<you>.
+
+=head2 Building your own view class
+
+I<You should probably skip this section for the first few readings of this manual. It's only intended for people extending Maypole.>
+
+Imagine you've found a brand new templating system that's B<much better>
+than the Template Toolkit. I know I'm stretching your imagination a bit
+here, but try. You'd like to use it with Maypole, which means writing your
+own view class. How is it done?
+
+We'll demonstrate by implementing a view class for C<HTML::Mason>,
+although no value judgement is implied. C<HTML::Mason> is a templating
+system which embeds pure Perl code inside its magic tags. The good side
+of this is that it can get into hash references and objects, and so
+providing C<classmetadata>, C<config> and the Maypole request object
+will work out just fine. The down side is that C<HTML::Mason> is used to
+running more or less standalone, and having all the template variables
+it wants already at its disposal through CGI parameters and the like, so
+we have to fiddle a bit to get these variables into our template.
+
+The key to building view classes is C<Maypole::View::Base>. This is the
+base class that you're going to inherit from and, to be honest, it does
+pretty much everything you need. It provides a method called C<vars>
+which returns a hash of all the template variables described above, so
+it would be good to feed those into C<HTML::Mason>. It also provides a
+C<paths> method which turns returns the full filesystem path of the
+three possible template paths as shown above. Again, it would be good to
+use this as our component paths if we can. It also has some methods we
+can override if we want to, but they're not massively important, so you
+can see L<Maypole::View::Base> for more about them.
+
+The module will do the right thing for us if we agree to provide a
+method called C<template>. This is responsible for taking the Maypole
+request object (of which more later) and putting the appropriate output
+either into C<$r-E<gt>{output}> or C<$r-E<gt>{error}>, depending, of
+course, whether things are OK or whether we got an error.
+
+Thankfully, C<HTML::Mason> makes things really easy for us. We B<can>
+use multiple template roots, so we can use the C<paths> method; we
+B<can> pass in a hash full of interesting data structures, so we can use
+the C<vars> method too. In fact, we have to do very little to make
+C<Maypole::View::Mason> work. Which is somewhat annoying, because it
+makes a boring example. But it means I can leave the fun ones to you!
+
+The doing-the-templating, in Mason and in any templating system, depends on
+three things: the paths that we're going to use to find our templates, the
+template name that we've been asked to fill out, and the set of variables that
+are going to be fed to the template. We'll assemble these for reference:
+
+ sub template {
+ my ($self, $r) = @_;
+ my @paths = $self->paths($r);
+ my $template = $r->template;
+ my %vars = $self->args($r);
+
+We'll also declare somewhere to temporarily store the output:
+
+ my $output;
+
+Now comes the part where we have to actually do something
+templating-language specific, so we open up our copy of "Embedding Perl
+in HTML with Mason" and find the bit where it talks about running Mason
+standalone. We find that the first thing we need to do is create a
+C<HTML::Mason::Interp> object which knows about the component roots.
+There's a slight subtlety in that the component roots have to be
+specified as an array of arrays, with each array being a two-element
+list of label and path, like so:
+
+ comproot => [
+ [ class => "/var/www/beerdb/templates/brewery" ],
+ [ custom => "/var/www/beerdb/templates/custom" ],
+ [ factory => "/var/www/beerdb/templates/factory" ],
+ ]
+
+We also find that we can set the output method here to capture Mason's
+output into a scalar, and also that we can tell Mason to generate
+sensible error messages itself, which saves us from having to worry
+about catching errors. At the end of all this, we come up with a
+constructor for our C<HTML::Mason::Interp> object which looks like this:
+
+ my $label = "path0";
+ my $mason = HTML::Mason::Interp->new(
+ comproot => [ map { [ $label++ => $_ ] } @paths ],
+ output_method => \$output,
+ error_mode => "output"
+ );
+
+The next thing we need to do is run the template with the appropriate
+template variables. This turns out to be really easy:
+
+ $mason->exec($template, %vars);
+
+Now we've got the data in C<$output>, we can put it into the request object,
+and return a true value to indicate that we processed everything OK. (If there
+was an error, then Mason will have produced some suitable output, so we can
+pretend that everything's OK anyway.)
+
+ $r->{output} = $output;
+ return 1;
+
+And that's all we need to do. Barely twenty lines of code for the finished
+product. Wasn't that easy? Don't you feel inspired to write Maypole view
+classes for your favourite templating language? Well, don't let me stop you!
+Patches are always welcome!