# Blosxom Plugin: syndicated # Author(s): Adrian Sampson # Version: 0.3 # Documentation: See the bottom of this file or type: perldoc syndicated # Updates: http://adrian.pygmysoftware.com/blog/blosxom/syndicated/ # Copyright 2004 Adrian Sampson # Released under the same License as Blosxom package syndicated; use File::stat; # --- Configurable variables ----- # reference as syndicated(name) in flavour head and foot my %feeds = ( # the name of each feed to reference in templates 'delicious' => { # url of syndicated rss 'url' => 'http://del.icio.us/rss/adrian', # template for items 'template' => '
  • $title
  • '."\n", # items to display (0 for no limit) 'num_display' => 10, # seconds to cache between downloads 'reload_delay' => 30 * 60}, ); # use HTML::Entities (part of HTML::Parser) to decode entities my $decode_entities = 1; # directory for storing feed caches my $cache_dir = "$blosxom::plugin_state_dir/syndicated_cache"; # set the path to curl to use it instead of LWP::Simple # my $curl_path = '/usr/bin/curl'; # probably '/usr/bin/curl' # -------------------------------- # compatibility, reference as $syndicated::content $content; sub start { 1; } sub head { my($pkg, $currentdir, $head_ref) = @_; for $feed_name (keys(%feeds)) { # fill out each feed's html representation if (!-d $cache_dir) { mkdir $cache_dir; } my $cache = "$cache_dir/$feed_name.xml"; my $rss; if (!stat($cache) || time - stat($cache)->mtime >= $feeds{$feed_name}{'reload_delay'}) { if ($curl_path) { $rss = `$curl_path '$feeds{$feed_name}{'url'}' 2>/dev/null`; } else { require LWP::Simple; $rss = LWP::Simple::get($feeds{$feed_name}{'url'}); } open(FILE,'>',$cache); print FILE $rss; close FILE; } elsif (open(FILE,$cache)) { $rss = join '',; close FILE; } my @items = parse_rss($rss); for my $item (@items[0..(($feeds{$feed_name}{'num_display'} && $feeds{$feed_name}{'num_display'} < $#items) ? $feeds{$feed_name}{'num_display'}-1 : @items-1)]) { my $title = $item->{'title'}; my $link = $item->{'link'}; my $description = $item->{'description'}; $feeds{$feed_name}{'content'} .= eval("qq{$feeds{$feed_name}{'template'}}"); } # and insert $$head_ref =~ s/syndicated\($feed_name\)/$feeds{$feed_name}{'content'}/g; } # compatibility $content = $feeds{ (sort(keys(%feeds)))[0] }{'content'}; 1; } sub foot { my($pkg, $currentdir, $foot_ref) = @_; for $feed_name (keys(%feeds)) { $$foot_ref =~ s/syndicated\($feed_name\)/$feeds{$feed_name}{'content'}/g; } 1; } # my extremely simple, extremely slow (presumably) RSS parser # to eliminate dependencies # will probably break on a lot of feeds sub parse_rss { local $_; my @items; my @rss_items = split(/ ]/,shift); for (@rss_items[1..@rss_items-1]) { my %item; $item{'title'} = (/(.*)<\/title>/s)[0]; $item{'link'} = (/<link>(.*)<\/link>/s)[0]; $item{'description'} = (/<description>(.*)<\/description>/s)[0]; if ($decode_entities) { require HTML::Entities; HTML::Entities::decode_entities($item{'title'}); HTML::Entities::encode_entities($item{'title'},'<>&"'); HTML::Entities::decode_entities($item{'link'}); HTML::Entities::encode_entities($item{'link'},'<>&"'); HTML::Entities::decode_entities($item{'description'}); } push @items,\%item; } @items; } 1; __END__ =head1 NAME Blosxom Plug-in: C<syndicated> =head1 SYNOPSIS Purpose: Allows syndication of RSS feeds into flavour headers and footers. =head1 VERSION 0.2 =head1 CONFIGURATION There are a few values that need to be configured before C<syndicated> is useful. =over =item C<%feeds> A hash that describes the feeds to be syndicated. The keys are the names of the feeds as they are referenced in flavour templates. The values are hash references containing information about the feed as follows. =over =item C<url> The URL of the RSS feed to be syndicated. =item C<template> The format for each article. C<$title>, C<$link>, and C<$description> will be replaced with their appropriate values. =item C<num_display> Maximum number of RSS items to display. =item C<reload_delay> The number of seconds to cache the RSS feed before downloading it again. =back =item C<$decode_entities> If set, C<syndicated> will use C<HTML::Entities> to decode entities in the RSS. Otherwise, entities will be left encoded. =item C<$cache_dir> The directory where C<syndicated> should store its cached RSS feeds. =item C<$curl_path> By default, C<syndicated> fetches RSS feeds using C<LWP::Simple>. If C<$curl_path> is set, it will use the C<curl> at that location instead. =back =head1 USAGE In the head.flavour and foot.flavour templates, use C<syndicated(name)> to insert syndicated content from the source defined in the configuration by C<name>. The first feed (alphabetically by name) may be referenced using C<$syndicated::content> for compatibility with older versions. =head1 AUTHOR Adrian Sampson <adrian@pygmysoftware.com> L<http://adrian.pygmysoftware.com/> =head1 BUGS =over =item * RSS parser is unbelievably simplistic and probably breaks. =item * Every feed defined is fetched (if not cached recently) on every page load, even if it is not used in a template. This may or may not be expected behavior. =back Reports of other bugs should be directed to the author. =head1 LICENSE C<syndicated> Copyright 2003, Adrian Sampson (This license is the same as Blosxom's.) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.