On Wed, 17 Dec 2003, Andy Wardley wrote: > Simon Wilcox wrote: > > I've written a Plugin which provides an easy means to work with a sitemap > > described by an xml file. > > Me too. Several times, each time slightly different :-)
:-) > Can you post the code and/or a description of the API first? OK. Here it is. Be gentle. > My most recent half-assed implementation (attached) doesn't both with > this abstraction and uses a quick XML::Simple hack in an all-in-one > plugin to do the job. But it's not very good and really should be > better. Perhaps that's why I never sent it to CPAN - because I always > thought I'd improve on it one day (or better yet, someone would beat me > to it - long live Simon the Sitemap Pumpkin!). Feel free to rip out and > reuse any bits you want. OK, I already did ! Added proper TT exceptions when called from TT and dieing when not. Comments welcome. Simon.
package Template::Plugin::XML::Sitemap; use strict; use XML::XPath; use base qw/ Template::Plugin /; our $VERSION = 0.01; =head1 NAME Template::Plugin::XML::Sitemap - Manipulate xml based sitemaps. =head1 SYNOPSIS [% USE sitemap = XML.Sitemap( '/path/to/sitemap.xml' ) %] [% FOREACH node = sitemap.breadcrumb( uri ) %] <a href="[% node.uri %]">[% node.label %]</a>[% " :: " UNLESS loop.last %] [% END %] <ul> [% FOREACH node = sitemap.submenu( uri ) %] <li><a href="[% node.uri %]">[% node.label %]</a></li> [% END %] </ul> =head1 DESCRIPTION This module provides a simple interface for template authors to generate breadcrumb trails and sub menus. Under the hood it uses XML::XPath to parse and navigate a tree of nodes. This module is designed to be used as a Template Toolkit plugin but it can also be used outside of TT. See the C<new> method below. =head1 XML FORMAT Here is a complete example of a sitemap file. <sitemap site="www.peacedirect.org"> <node uri="/" label="Home"> <node uri="/peacedirect/" label="Peace Direct"> <node uri="/peacedirect/aboutus.html" label="About us" /> <node uri="/peacedirect/howwework.html" label="How we work" > <node uri="/peacedirect/otherorgs.html" label="Other organisations" /> </node> <node uri="/peacedirect/events/" label="Events" /> <node uri="/peacedirect/thankyous.html" label="Thank yous" /> </node> <node uri="/news/" label="News & Articles"> <node uri="/news/news.html" label="News index" /> <node uri="/news/pressinfo.html" label="Press information" /> </node> </node> </sitemap> =head1 METHODS =over 4 =item * C<new( '/path/to/sitemap.xml' )> =item * C<[% USE sitemap = XML.Sitemap( 'path/to/sitemap.xml' ) %]> Parses the xml file using XML::XPath and returns an instance of Template::Plugin::XML::Sitemap. Throws an exception if it can't parse the xml. Template Toolkit will automatically pass it's context to the C<new> method. If it does then it will be stored and used to throw TT exceptions on errors. Otherwise it defaults to using C<die>. =item * C<breadcrumb( $uri )> =item * C<[% sitemap.breadcrumb( uri ) %]> Returns a reference to an array of hashes containing links from the root node to the supplied uri. It will throw a Template exception if the uri cannot be found. (Outside of TT it will C<die>). Calling C<$sitemap-E<gt>breadcrumb( '/peacedirect/' )> on the example sitemap above returns the following data structure [ { uri => '/', label => 'Home', }, { uri => '/peacedirect/', label => 'Peace Direct', }, ] =item * C<submenu( $uri )> =item * C<[% sitemap.submenu( uri ) %]> Returns a reference to an array of hashes containing the nodes beneath C<uri>. The data structure and the error methods are the same as C<breadcrumb>. =head1 CAVEATS This module hasn't been tested heavily in a dynamic environment so there are no performance benchmarks. It is likely to perform badly due to the XML parsing but it works well enough in command line mode. Some caching of the data probably wouldn't go amiss either ! =head1 AUTHOR Simon Wilcox [EMAIL PROTECTED] http://www.digitalcraftsmen.net/perl =head1 COPYRIGHT This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. The full text of the license can be found in the LICENSE file included with this module. =head1 SEE ALSO perl(1). =cut sub new { my ($class, $context, $datafile) = @_; unless( ref( $context )) { $datafile = $context; $context = undef; } $class->throw( "No sitemap file" ) unless defined $datafile; my $self = bless ({ _context => $context }, ref ($class) || $class); $self->{ _xp } = XML::XPath->new( $datafile ); return ($self); } sub _findme { my ($self, $uri) = @_; my $xp = $self->{ _xp }; $uri =~ s/\/index\.html$/\//; # strip trailing filename my $xpath = qq{//[EMAIL PROTECTED]"$uri"]}; (my $node) = ( $xp->findnodes( $xpath )); $self->throw( "Can't find $uri using $xpath" ) unless $node; return $node; } sub throw { my $self = shift; if ( defined $self->{ _context } ) { $self->{ _context }->throw( sitemap => join( ''. @_ )); }else{ die "Sitemap: ".join( '', @_ ); } } sub breadcrumb { my ($self, $uri) = @_; my $node = $self->_findme( $uri ); my @trail = (); while (defined $node) { unshift @trail, { uri => $node->getAttribute( 'uri' ), label => $node->getAttribute( 'label' ) }; $node = $node->getParentNode; last unless $node->getName eq 'node'; } return [EMAIL PROTECTED]; } sub submenu { my ($self, $uri) = @_; my $thisnode = $self->_findme( $uri ); my $xp = $self->{ _xp }; my $subnodes = $xp->find( 'node', $thisnode ); my @submenu = (); foreach my $node ($subnodes->get_nodelist) { push @submenu, { uri => $node->getAttribute( 'uri' ), label => $node->getAttribute( 'label' ) }; } return [EMAIL PROTECTED]; } 1; #this line is important and will help the module return a true value __END__