# 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;