App-PFT
view release on metacpan or search on metacpan
bin/pft-gen-rss view on Meta::CPAN
B<pft gen-rss>
=head1 DESCRIPTION
This command will generate an XML file according to the RSS (Really Simple
Syndacation) specification.
The default behaviour is to position the generated file in the root of the
C<build> directory, and named C<feed.rss>. The generated feed will contain
one C<item> element for each of last 10 blog entries.
The global PFT configuration allows to change the default path of the file
and the number of listed blog entries (see L<pft-init(1)>).
=head1 OPTIONS
=over
=item B<--help>
Show this guide
=back
=head1 EXIT STATUS
=over
=item * 1 in case of option parsing failure.
=item * 2 if it was impossible to construct the filesystem tree.
=item * 3 in case of corrupt configuration.
=back
=head1 BUGS
In PFT blog entries are timestamped with a day resolution. There is no
obvious way to timestamp the publishing date (C<pubDate>) of blog entries
with a better time resolution, so the hour is rounded up to midnight.
The generated RSS feed will use the publishing date of the most recent entry
as C<lastBuildDate>. This makes the implementation simple, but yields
inaccurate results should the user update an old blog entry.
=head1 SEE ALSO
L<pft(1)>, L<pft-init(1)>
=cut
use strict;
use warnings;
use utf8;
use v5.16;
use Carp;
use Digest::MD5;
use HTML::Escape qw/escape_html/;
use PFT::Tree;
use POSIX qw/strftime/;
use Encode;
use Encode::Locale;
use File::Spec::Functions qw/catfile/;
use File::Path qw/make_path/;
use File::Basename qw/dirname/;
use Pod::Usage;
use Getopt::Long;
Getopt::Long::Configure qw/bundling/;
GetOptions(
'help|h!' => sub {
pod2usage
-exitval => 1,
-verbose => 2,
-input => App::PFT::help_of 'gen-rss',
},
) or exit 1;
my $tree = eval{ PFT::Tree->new } || do {
say STDERR $@ =~ s/ at.*$//rs;
exit 2
};
my $conf = eval{ $tree->conf } || do {
say STDERR 'Configuration error: ', $@ =~ s/ at.*$//rs;
exit 3;
};
my $digest = Digest::MD5->new;
my $site_title = $conf->{site}{title};
my $site_url = $conf->{site}{url};
my $feed_path = $conf->{site}{feed}{path} || "feed.rss";
my $feed_url = "$site_url/$feed_path";
my $encoding = $conf->{site}{encoding};
my $description = $conf->{site}{feed}{description} || "News from $site_title";
my $length = $conf->{site}{feed}{length} || 10;
my $outfile;
do {
my $path = catfile($tree->dir_build, $feed_path);
make_path encode(locale_fs => dirname($path));
open($outfile, ">:encoding($encoding)", encode(locale_fs => $path))
or die "opening $path $!";
select $outfile;
};
sub node_to_href {
my $node = shift;
confess unless $node;
join '/', $site_url, do {
my $hdr = $node->header;
my $k = $node->content_type;
if ($k =~ /::Blog$/) {(
'blog',
sprintf('%04d-%02d', $hdr->date->y, $hdr->date->m),
sprintf('%02d-%s.html', $hdr->date->d, $hdr->slug),
)} elsif ($k =~ /::Month$/) {(
'blog',
sprintf('%04d-%02d.html', $hdr->date->y, $hdr->date->m),
)} elsif ($k =~ /::Page$/) {(
'pages',
$hdr->slug . '.html',
)} elsif ($k =~ /::Tag$/) {(
'tags',
$hdr->slug . '.html',
)} elsif ($k =~ /::Picture$/) {(
'pics',
$node->content->relpath
)} elsif ($k =~ /::Attachment$/) {(
'attachments',
$node->content->relpath
)} else { die $k }
}
}
print <<"END";
<?xml version="1.0" encoding="$encoding"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<title>$site_title</title>
<link>$site_url</link>
<generator>App::PFT</generator>
<description>$description</description>
<atom:link href="$feed_url" rel="self" type="application/rss+xml"/>
END
my $first = 1;
foreach my $node ($tree->content_map->blog_recent($length)) {
my $guid;
my $pubDate;
$digest->add($node->title);
$guid = $digest->hexdigest;
$digest->reset;
$pubDate = do {
my($y, $m, $d) = @{$node->date};
strftime("%a, %d %b %Y %H:%M:%S %z", 0, 0, 0, $d, $m, $y - 1900)
};
my $content = escape_html $node->html(\&node_to_href);
if ($first) {
$first = 0;
say "<lastBuildDate>$pubDate</lastBuildDate>"
}
say '<item>',
' <title>', $node->title, '</title>',
' <guid isPermaLink="false">', $guid, '</guid>',
' <pubDate>', $pubDate, '</pubDate>',
' <link>', node_to_href($node), '</link>',
' <description>', $content, '</description>',
'</item>'
}
print <<END;
</channel>
</rss>
END
close $outfile;
( run in 1.540 second using v1.01-cache-2.11-cpan-75ffa21a3d4 )