# rss2.alp # A plugin for Dave Madison's album which generates an RSS 2.0 feed of changes for each album # Relies on the 'end_album" hook # Inspired by the rss.alp plugin by Jason Dufair # Thanks to David Ljung Madison for bug reports and edits. # # 2008-05-28 Version 1.0.0b6 # # Use and distribute this plugin as per the Artistic License # Copyright (C) 2008 Igor S. Livshits use strict; my $albumNameToken= "#album#"; # a special token to replace with actual album name # start_plugin # Requred initialization # sub start_plugin { my $options= shift; # current options in a hash my $plugin= shift; # the name of this plugin, as invoked my $path= shift; # the path to this plugin, as invoked my $registration= # required plugin registration record { "author" => "Igor S. Livshits", "href" => "http://www.ayradyss.org/", "version" => "1.0", "description" => "Generates RSS 2.0 feeds for recently added or changed items in albums", }; # Scuttle if we failed to load required modules my $rss= album::attempt_require('XML::RSS'); unless ($rss) { # hmm, troubles with XML::RSS? my $error= "Failed to invoke XML::RSS -- is it installed?"; print STDERR "\n[Plugin: $plugin] $error\n"; $registration->{'description'}.= "\n$error"; return $registration; } my $date= album::attempt_require('Date::Format'); unless ($date) { # hmm, troubles with Date::Format? my $error= "Failed to invoke Date::Format -- is it installed?"; print STDERR "\n[Plugin: $plugin] $error\n"; $registration->{'description'}.= "\n$error"; return $registration; } my $stat= album::attempt_require('File::stat'); unless ($stat) { # hmm, troubles with File::stat? my $error= "Failed to invoke File::stat -- is it installed?"; print STDERR "\n[Plugin: $plugin] $error\n"; $registration->{'description'}.= "\n$error"; return $registration; } # Options album::add_option(1, 'title', album::OPTION_STR, default => 'Web Album', usage => "Title of the feed." ); album::add_option(1, 'baseURL', album::OPTION_STR, usage => "URL to the album base." ); album::add_option(1, 'language', album::OPTION_STR, default => 'en-us', usage => "Language setting for the feed." ); album::add_option(1, 'description', album::OPTION_STR, usage => "Description for the feed." ); album::add_option(1, 'copyright', album::OPTION_STR, usage => "Copyright notice for the feed." ); album::add_option(1, 'editor', album::OPTION_STR, usage => "Address of the album's editor." ); album::add_option(1, 'webmaster', album::OPTION_STR, usage => "Address of the site's web master." ); album::add_option(1, 'image', album::OPTION_STR, usage => "Icon for the feed." ); album::add_option(1, 'feedFilename', album::OPTION_STR, default => 'index.rss', usage => "XML file name for the feed." ); album::add_option(1, 'albumNameToken', album::OPTION_STR, default => $albumNameToken, usage => "Placeholder for actual album name during generation." ); album::add_option(1, 'refreshModified', album::OPTION_BOOL, default => 0, usage => "Toggles creation of unique URLs based on modification time." ); # Register the hook for callback before processing each album album::hook($options, 'do_album', \&StartAlbum); # Register the hook for callback after processing each album album::hook($options, 'end_album', \&GenerateFeed); return $registration; } # ListHash # List contents of a hash # sub ListHash { my $hash= shift; # our hash array my $indent= shift; # indentation level my $string= ""; # our accumulated list as a string $indent= "\t" unless $indent; foreach my $key (keys %$hash) { $string.="$indent" . "[$key]: <" . $hash->{$key} . ">\n"; } return $string; } # ComposeURL # Compose a URL from several provided leaves # sub ComposeURL { my $url= shift; # the root my $leaf= undef; # a given leaf in the series while ($leaf= shift) { # keep adding leaves $url.= "/" # insert slashes if ommitted unless $url=~ /\/$/; $leaf=~ s/^\'//; # strip a leading single quotation mark $leaf=~ s/\'$//; # strip a trailing single quotation mark $url.= $leaf; # append the current leaf } return $url; } # Compose XML # Compose the XML object for the RSS feed # sub ComposeXML { my $rss= shift; # the XML object for the current feed my $album= shift; # the name (path) of the current album my $title= shift; # base title for album feeds my $baseURL= shift; # base URL for album feed my $language= shift; # language label for album feeds my $copyright= shift; # copyright notice for album feeds my $description= shift; # description of album feed my $publicationDate= shift; # date of publication for the current feed my $editor= shift; # album feeds editor's address my $webmaster= shift; # site webmaster's address my $rssImage= shift; # image for album feed my $link= # link to the current album ComposeURL($baseURL, $album); $title=~ # substitute any album name tokens with actual album name s/$albumNameToken/$album/ig; $rss->channel(title => $title, link => $link, language => $language, description => $description, copyright => $copyright, pubDate => Date::Format::time2str("%a, %d %b %Y %T %Z", $publicationDate), lastBuildDate => Date::Format::time2str("%a, %d %b %Y %T %Z", time()), docs => "http://blogs.law.harvard.edu/tech/rss", managingEditor => $editor, webMaster => $webmaster, ); $rss->image(title => $title, url => $rssImage, link => $link, description => $description, ) if $rssImage; } # DumpData # Dump data from passed variables and internal data structures # sub DumpData { my $options= shift; # current options from album in a hash my $data= shift; # current data from album in a hash my $hookName= shift; # album's name for the called hook my $directory= shift; # current working directory my $album= shift; # current album my $pictures= # list of pictures as an array pointer $data->{'pics'}; my $directories= $data->{'dirs'}; print "Options:\n", ListHash($options), "\n\n"; print "Data:\n", ListHash($data), "\n\n"; print "Pictures: ", join(", ", @$pictures), "\n\n"; print "Directories:\n\t<", join(">,\n \t<", @$directories), ">\n\n"; print "Objects\n--\n"; foreach my $object (keys %{$data->{'obj'}}) { # each object at this level print "$object\n", ListHash($data->{'obj'}->{$object}); print "\tThumb\n", ListHash($data->{'obj'}->{$object}->{'thumb'}, "\t\t"); print "\tFull\n", ListHash($data->{'obj'}->{$object}->{'full'}, "\t\t"); print "\tMedium\n", ListHash($data->{'obj'}->{$object}->{'medium'}, "\t\t"); print "\tSnapshot\n", ListHash($data->{'obj'}->{$object}->{'snapshot'}, "\t\t"); print "\tURL (image page)\n", ListHash($data->{'obj'}->{$object}->{'URL'}->{'image_page'}, "\t\t"); print "\tURL (album page)\n", ListHash($data->{'obj'}->{$object}->{'URL'}->{'album_page'}, "\t\t"); } print "Paths:\n", ListHash($data->{'paths'}), "\n"; print "Hook: $hookName\n"; print "Directory: $directory\n"; print "Album: $album\n\n\n"; } # StartAlbum # Add the RSS to the album section # sub StartAlbum { my $options= shift; # current options from album in a hash my $data= shift; # current data from album in a hash my $hookName= shift; # album's name for the called hook my $directory= shift; # current working directory my $album= shift; # current album my $title= ""; # title of our RSS feed my $albumNameToken= ""; # placeholder token for the actual album name $title= album::option($options, 'title') || ""; $albumNameToken= album::option($options, 'albumNameToken') || ""; $album=~ s/["><]//g; # customize the title $title=~ s/$albumNameToken/$album/ig; $data->{head}.= "\t\t{'paths'}->{'album_path'}, album::option($options, 'feedFilename')) . "' title='" . $title . "' />\n"; return 0; # don't skip the current album! } # GenerateFeed # Generate an RSS 2.0 feed for each album and save it # sub GenerateFeed { my $options= shift; # current options from album in a hash my $data= shift; # current data from album in a hash my $hookName= shift; # album's name for the called hook my $directory= shift; # current working directory my $album= shift; # current album my $debug= 0; # switch to dump diagnostic data my $title= undef; # title of a feed item my $description= undef; # description of a feed item my $link= undef; # link to the feed item my $permaLink= $link; # a permanent link to the feed item my $date= undef; # modification date of the feed item my $info= undef; # file information for the feed item my $refreshModified= # should I create unique URLs for modified items? album::option($options, 'refreshModified'); my $albumURL= # URL to our album ComposeURL(album::option($options, 'baseURL'), $data->{'paths'}->{'album_path'}); my $rss= # create the RSS feed object new XML::RSS(version => '2.0'); print STDERR "Could not create the RSS object!\n", return unless $rss; # cound not create the RSS object -- scuttle ComposeXML($rss, $album || "album", album::option($options, 'title') || "", album::option($options, 'baseURL') || "", album::option($options, 'language') || "", album::option($options, 'copyright') || "", album::option($options, 'description') || "", time(), album::option($options, 'editor') || "", album::option($options, 'webmaster') || "", album::option($options, 'image') || "", ); foreach my $picture (@{$data->{'pics'}}) { # process each picture in this album $info= File::stat::stat($data->{'obj'}->{$picture}->{'full'}->{'path'}); $title= $data->{'obj'}->{$picture}->{'name'}; $link= ComposeURL($albumURL, $data->{'obj'}->{$picture}->{'URL'}->{'album_page'}->{'image'}); $date= Date::Format::time2str("%a, %d %b %Y %T %Z", $info->mtime); if ($data->{'obj'}->{$picture}->{'is_image'}) { # the item is an image $description= "Image: " . $data->{'obj'}->{$picture}->{'name'} . "
" . "Size: " . $data->{'obj'}->{$picture}->{'full'}->{'x'} . " by " . $data->{'obj'}->{$picture}->{'full'}->{'y'}; } elsif ($data->{'obj'}->{$picture}->{'is_movie'}) { # the item is a movie $description= "Movie: more informative stuff here later..."; } else { # the item is unknown (???) $description= "Unknown: ???"; } if ($data->{'obj'}->{$picture}->{'has_thumb'}) { # insert the thumbnail into the description $description.= "
{$picture}->{'URL'}->{'album_page'}->{'thumb'}) . "' title='" . $title . "' alt='" . $data->{'obj'}->{$picture}->{'file'} . "' />"; } $permaLink= $link; $link.= "#" . $info->mtime # make the URL unique based on the modification date if $refreshModified; $rss->add_item(title => $title, description => $description, link => $link, permaLink => $permaLink, pubDate => $date, ); } foreach my $directory (@{$data->{'dirs'}}) { # process each directory in this album $info= File::stat::stat($data->{'obj'}->{$directory}->{'path'}); $title= $data->{'obj'}->{$directory}->{'name'}; $link= ComposeURL($albumURL, $data->{'obj'}->{$directory}->{'URL'}->{'album_page'}->{'dir'}); $date= Date::Format::time2str("%a, %d %b %Y %T %Z", $info->mtime); $description= "Child album: " . $data->{'obj'}->{$directory}->{'name'} . "
" . "Contains: " . $data->{'obj'}->{$directory}->{'num_pics_str'}; if ($data->{'obj'}->{$directory}->{'has_thumb'}) { # insert the thumbnail into the description $description.= "
{$directory}->{'URL'}->{'album_page'}->{'thumb'}) . "' title='" . $title . "' alt='" . $data->{'obj'}->{$directory}->{'file'} . "' />"; } $permaLink= $link; $link.= "#" . $info->mtime # make the URL unique based on the modification date if $refreshModified; $rss->add_item(title => $title, description => $description, link => $link, permaLink => $permaLink, pubDate => $date, ); } $rss->save($directory . "/" . album::option($options, 'feedFilename')); DumpData($options, $data, $hookName, $directory, $album) if $debug; } # # Required termination # 1;