# Blosxom Plugin: rss20
# Author(s): Gavin Carr <gavin@openfusion.com.au>
-# Version: 0.001002
-# Requires: storydate, lastmodified2
-# Suggests: absolute
-# Follows: storydate, lastmodified2
+# Version: 0.002000
+# Requires: storydate
+# Suggests: absolute, storytags
+# Follows: storydate
package rss20;
use strict;
+
use vars qw(
$flavour
$author_email
- $error_email
+ $author_name
+ $webmaster_email
$permalink
$trackback_link
$copyright
- $generator_url
+ $generator
+ $category_list
);
# --- Configuration variables -----
#$flavour = 'rss20';
# What email address should be used as the default author email?
-$author_email = 'author@example.com';
+# Recommended format is 'email (name)', but bare emails or mailto emails are ok too
+$author_email = 'author@example.com (A. U. Thor)';
+
+# What name should be used as the default author name?
+# Define this only if you don't want to disclose an email address in $author_email.
+# It will be ignored if $author_email is set.
+$author_name = '';
# What email address should feed errors be reported to?
-$error_email = '';
+# Recommended format is 'email (name)', but bare emails or mailto emails are ok too
+$webmaster_email = '';
+#$webmaster_email = 'webmaster@example.com (Web Master)';
# What do your story permalinks look like?
# This must be single-quoted, to defer evaluation; blosxom namespace applies
$copyright = '';
# Generator that produced this feed
-$generator_url = "http://blosxom.sourceforge.net/?v=$blosxom::version";
+$generator = "blosxom $blosxom::version";
# --------------------------------
+# __END_CONFIG__
+
+$webmaster_email ||= $author_email;
-$error_email ||= $author_email;
+my $channel_indent = 8;
+my $item_indent = 12;
# Escape <, >, and & to hex-encoded entities for max compatibility in text elements
# See http://www.rssboard.org/rss-profile#data-types-characterdata
_load_templates();
}
+my $do_encode = 0;
sub story {
my ($pkg, $path, $filename, $story_ref, $title_ref, $body_ref) = @_;
return unless $blosxom::flavour eq $flavour;
# Don't double-encode if someone else has already done it
- return unless $blosxom::encode_xml_entities;
+ return unless $do_encode || $blosxom::encode_xml_entities;
+
+ # Because we're inside a loop, we set $do_encode for runs after the first
+ unless ($do_encode) {
+ $do_encode = 1;
+ $blosxom::encode_xml_entities = 0;
+ }
# Encode and reset encode_xml_entities flag
$$title_ref = _escape_text( $$title_ref );
$$body_ref = _escape_html( $$body_ref );
- $blosxom::encode_xml_entities = 0;
+
+ $category_list = '';
+ if ($blosxom::plugins{storytags}) {
+ for (@storytags::taglist) {
+ $category_list .= qq(<category>$_</category>\n) . ' ' x $item_indent;
+ }
+ }
}
# --- Private subroutines
<rss version="2.0"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
- xmlns:admin="http://webns.net/mvcb/"
- xmlns:atom="http://www.w3.org/2005/Atom"
- xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
- xmlns:content="http://purl.org/rss/1.0/modules/content/">
+ xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<title>$blosxom::blog_title</title>
- <link>$blosxom::url</link>
- <description>$blosxom::blog_description</description>
- <dc:date>\$lastmodified2::latest_iso8601</dc:date>
- <dc:language>$blosxom::blog_language</dc:language>
- <dc:creator>mailto:$rss20::author_email</dc:creator>
- <dc:rights>$rss20::copyright</dc:rights>
- <admin:generatorAgent rdf:resource="$rss20::generator_url" />
- <admin:errorReportsTo rdf:resource="mailto:$rss20::error_email" />
- <atom:link href="$blosxom::url$ENV{PATH_INFO}" rel="self" type="application/rss+xml" />
+ <link>$blosxom::url/</link>
+HEAD
+
+ if ($blosxom::path_info) {
+ $blosxom::template{$flavour}{'head'} .=
+ ' ' x $channel_indent .
+ qq(<category>$blosxom::path_info</category>\n);
+ }
+ if ($blosxom::blog_description) {
+ $blosxom::template{$flavour}{'head'} .=
+ ' ' x $channel_indent .
+ qq(<description>$blosxom::blog_description</description>\n);
+ }
+ if ($rss20::author_email) {
+ $blosxom::template{$flavour}{'head'} .=
+ ' ' x $channel_indent .
+ qq(<managingEditor>$rss20::author_email</managingEditor>\n);
+ }
+ elsif ($rss20::author_name) {
+ $blosxom::template{$flavour}{'head'} .=
+ ' ' x $channel_indent .
+ qq(<dc:creator>$rss20::author_name</dc:creator>\n);
+ }
+ if ($rss20::webmaster_email) {
+ $blosxom::template{$flavour}{'head'} .=
+ ' ' x $channel_indent .
+ qq(<webMaster>$rss20::webmaster_email</webMaster>\n);
+ }
+ if ($rss20::copyright) {
+ $blosxom::template{$flavour}{'head'} .=
+ ' ' x $channel_indent .
+ qq(<copyright>$rss20::copyright</copyright>\n);
+ }
+ my $path_info = $blosxom::path_info_orig || "/$blosxom::path_info";
+ $blosxom::template{$flavour}{'head'} .= <<HEAD;
+ <pubDate>\$storydate::latest_rfc822</pubDate>
+ <language>$blosxom::blog_language</language>
+ <generator>$rss20::generator</generator>
+ <atom:link href="$blosxom::url$path_info" rel="self" type="application/rss+xml" />
<sy:updatePeriod>hourly</sy:updatePeriod>
<sy:updateFrequency>1</sy:updateFrequency>
<sy:updateBase>2000-01-01T12:00+00:00</sy:updateBase>
-
HEAD
$blosxom::template{$flavour}{'story'} = <<STORY;
+
<item>
<title>\$title</title>
- <link>$rss20::permalink</link>
- <description>\$body</description>
- <comments>$rss20::trackback_link</comments>
- <guid isPermaLink="true">$rss20::permalink</guid>
- <dc:date>\$storydate::story_iso8601</dc:date>
+ <link>$rss20::permalink</link>
+ <guid isPermaLink="true">$rss20::permalink</guid>
+ <pubDate>\$storydate::story_rfc822</pubDate>
+STORY
+
+ if ($rss20::trackback_link) {
+ $blosxom::template{$flavour}{'story'} .=
+ ' ' x $item_indent .
+ qq(<comments>$rss20::trackback_link</comments>\n);
+ }
+ $blosxom::template{$flavour}{'story'} .= <<STORY;
+ \$rss20::category_list<description>\$body
+ </description>
</item>
STORY
$blosxom::template{$flavour}{'foot'} = <<'FOOT';
- </channel>
-</rss>
+
+ </channel>
+</rss>
FOOT
1;
Default author email address for posts.
-=item $error_email
+=item $webmaster_email
-Email address to use to report errors with the feed (defaults to
-$author_email).
+Email address to use for webmaster responsible for the feed (defaults
+to $author_email).
=item $permalink
# Author(s): Gavin Carr <gavin@openfusion.com.au>
# (based on work by Frank Hecker <hecker@hecker.org>
# and Bob Schumaker <cobblers@pobox.com>)
-# Version: 0.001000
+# Version: 0.002000
# Documentation: See the bottom of this file or type: perldoc storydate
package storydate;
my @strftime_modules = qw(Time::Piece Date::Format POSIX);
# ---------------------------------
+# __END_CONFIG__
# Package variables
use vars qw(
$story_mtime_iso8601
$now_rfc822
$now_iso8601
+ $latest_rfc822
+ $latest_iso8601
%strftime_formats
);
%strftime_formats = (
- rfc822 => "%a, %d %b %Y %T %z",
+ rfc822 => "%a, %d %b %Y %T %z",
# ISO 8601 format (localtime, including time zone offset)
# Format is YYYY-MM-DDThh:mm:ssTZD per http://www.w3.org/TR/NOTE-datetime
iso8601 => "%Y-%m-%dT%T%z",
1;
}
+sub filter {
+ my ($pkg, $files, $others) = @_;
+
+ # Find the latest publish date in our stories
+ my $latest_ts = 0;
+ for (values %$files) {
+ $latest_ts = $_ if $_ > $latest_ts;
+ }
+
+ # Format the 'latest' variables
+ $latest_rfc822 = format_date('rfc822', $latest_ts);
+ $latest_iso8601 = format_date('iso8601', $latest_ts);
+
+ return 1;
+}
+
sub story {
my ($pkg, $path, $filename, $story_ref, $title_ref, $body_ref) = @_;
The current time in ISO8601 format.
+=item $latest_rfc822
+
+The latest story publication date, in RFC822 format.
+
+=item $latest_iso8601
+
+The latest story publication date, in ISO8601 format.
+
=back
In addition, storydate defines a few subroutines that might be useful
# Blosxom Plugin: storytags
# Author(s): Gavin Carr <gavin@openfusion.com.au>
-# Version: 0.001000
+# Version: 0.002000
# Documentation: See the bottom of this file or type: perldoc storytags
# Requires: tags
# Follows: tags
# Uncomment next line to enable debug output (don't uncomment debug() lines)
#use Blosxom::Debug debug_level => 1;
-use vars qw($taglist);
+use vars qw($taglist @taglist);
# --- Configuration variables -----
my $suffix = '. ';
# ---------------------------------
+# __END_CONFIG__
$taglist = '';
my ($pkg, $path, $filename, $story_ref, $title_ref, $body_ref) = @_;
return 1 unless $tags::tag_cache
- && ref $tags::tag_cache
- && keys %{ $tags::tag_cache };
+ && ref $tags::tag_cache
+ && keys %{ $tags::tag_cache };
return 1 unless defined $tags::tag_cache->{"$path/$filename"};
- $taglist = _format_taglist($tags::tag_cache->{"$path/$filename"}->{tags});
+ @taglist = ();
+ @taglist = sort { lc $a cmp lc $b } split /\s*,\s*/,
+ $tags::tag_cache->{"$path/$filename"}->{tags}
+ if defined $tags::tag_cache->{"$path/$filename"}->{tags};
+ $taglist = _format_taglist( \@taglist );
return 1;
}
sub _format_taglist {
my ($tags) = @_;
- $tags = '' unless defined $tags;
- my @tags = sort { lc $a cmp lc $b } split /\s*,\s*/, $tags;
- return '' unless @tags;
+ return '' unless @$tags;
return $prefix
. join(', ',
map { qq(<a href="$blosxom::url/tags/$_" rel="tag">$_</a>) }
- @tags
+ @$tags
)
. $suffix;
}
=head1 NAME
storytags - blosxom plugin to format a per-story $storytags::taglist string
+and @storytags::taglist array of tags
=head1 DESCRIPTION
L<storytags> is a blosxom plugin to format a per-story $storytags::taglist
-string. The string is a comma-separated list of the tags defined for the
-story, prefixed by $storytags::prefix, and suffixed by $storytags::suffix.
-If no tags are defined, then $taglist will be the empty string '' (i.e. no
-prefix and suffix are added).
+string, and a @storytags::taglist array of tags. The $taglist is a
+comma-separated list of the tags defined for the story, prefixed by
+$storytags::prefix, and suffixed by $storytags::suffix. If no tags are
+defined, then $taglist will be the empty string '' (i.e. no prefix and
+suffix are added). @taglist is a simple array of the tags for the story,
+and an empty array if none are set.
The default values for $prefix and $suffix are 'Tags: ' and '. '
respectively, so a typical $taglist might look like: