# 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 <http://www.dufair.org/>
#
#   2007.12.26 Version 1.0.0b3
#   
#   Use and distribute this plugin as per the Artistic License
#   Copyright (C) 2007 Igor S. Livshits <mailto:album-rss@ayradyss.org>

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 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 give 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);

  use Date::Format;             # pretty date output
  $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        => time2str("%a, %d %b %Y %T %Z", $publicationDate),
                lastBuildDate  => 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";
}


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

  ComposeXML($rss,
             $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'),
    );

  use File::stat;               # a better file stat

  foreach my $picture (@{$data->{'pics'}})
  {                             # process each picture in this album
    $info= stat($data->{'obj'}->{$picture}->{'full'}->{'path'}); 
    $title= $data->{'obj'}->{$picture}->{'name'};
    $link= ComposeURL($albumURL, $data->{'obj'}->{$picture}->{'URL'}->{'album_page'}->{'image'});
    $date= 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'} .
        "<br />" .
        "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.= "<br /><a href='" . $link . "'><img src='" . 
        ComposeURL($albumURL, $data->{'obj'}->{$picture}->{'URL'}->{'album_page'}->{'thumb'}) .
        "' title='" . $title . "' alt='" .  $data->{'obj'}->{$picture}->{'file'} . "' /></a>";
    }
    $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= stat($data->{'obj'}->{$directory}->{'path'}); 
    $title= $data->{'obj'}->{$directory}->{'name'};
    $link= ComposeURL($albumURL, $data->{'obj'}->{$directory}->{'URL'}->{'album_page'}->{'dir'});
    $date= time2str("%a, %d %b %Y %T %Z", $info->mtime);
    $description= "Child album: " . $data->{'obj'}->{$directory}->{'name'} .
      "<br />" .
      "Contains: " . $data->{'obj'}->{$directory}->{'num_pics_str'};
    if ($data->{'obj'}->{$directory}->{'has_thumb'})
    {                           # insert the thumbnail into the description
      $description.= "<br /><a href='" . $link . "'><img src='" . 
        ComposeURL($albumURL, $data->{'obj'}->{$directory}->{'URL'}->{'album_page'}->{'thumb'}) .
        "' title='" . $title . "' alt='" .  $data->{'obj'}->{$directory}->{'file'} . "' /></a>";
    }
    $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;

