X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FXML%2FFeed%2FRSS.pm;h=432b6af1ad7de814691174926381222c77385000;hb=33d4cb3f33502990083b6f240b0764c77d62444a;hp=3f992efd3c98f0091b16ad8da258c10e6ed4dc4c;hpb=fe71566dc49fb1adac7816ecb10aa3574a21cc08;p=catagits%2FXML-Feed.git diff --git a/lib/XML/Feed/RSS.pm b/lib/XML/Feed/RSS.pm index 3f992ef..432b6af 100644 --- a/lib/XML/Feed/RSS.pm +++ b/lib/XML/Feed/RSS.pm @@ -1,19 +1,32 @@ -# $Id: RSS.pm 942 2004-12-31 23:01:21Z btrott $ +# $Id: RSS.pm 1934 2006-04-22 05:13:55Z btrott $ package XML::Feed::RSS; use strict; use base qw( XML::Feed ); -use XML::RSS; use DateTime::Format::Mail; use DateTime::Format::W3CDTF; +our $PREFERRED_PARSER = "XML::RSS"; + +sub init_empty { + my ($feed, %args) = @_; + $args{'version'} ||= '2.0'; + eval "use $PREFERRED_PARSER"; die $@ if $@; + $feed->{rss} = $PREFERRED_PARSER->new(%args); + $feed->{rss}->add_module(prefix => "content", uri => 'http://purl.org/rss/1.0/modules/content/'); + $feed->{rss}->add_module(prefix => "dcterms", uri => 'http://purl.org/dc/terms/'); + $feed->{rss}->add_module(prefix => "atom", uri => 'http://www.w3.org/2005/Atom'); + $feed->{rss}->add_module(prefix => "geo", uri => 'http://www.w3.org/2003/01/geo/wgs84_pos#'); + $feed; +} + sub init_string { my $feed = shift; my($str) = @_; - my $rss = $feed->{rss} = XML::RSS->new; + $feed->init_empty; if ($str) { - $rss->parse($$str); + $feed->{rss}->parse($$str); } $feed; } @@ -21,35 +34,89 @@ sub init_string { sub format { 'RSS ' . $_[0]->{rss}->{'version'} } ## The following elements are the same in all versions of RSS. -sub title { $_[0]->{rss}->channel('title') } -sub link { $_[0]->{rss}->channel('link') } -sub description { $_[0]->{rss}->channel('description') } +sub title { shift->{rss}->channel('title', @_) } +sub link { shift->{rss}->channel('link', @_) } +sub description { shift->{rss}->channel('description', @_) } + +# This doesn't exist in RSS +sub id { } ## This is RSS 2.0 only--what's the equivalent in RSS 1.0? -sub copyright { $_[0]->{rss}->channel('copyright') } +sub copyright { shift->{rss}->channel('copyright', @_) } ## The following all work transparently in any RSS version. sub language { - $_[0]->{rss}->channel('language') || - $_[0]->{rss}->channel->{dc}{language} + my $feed = shift; + if (@_) { + $feed->{rss}->channel('language', $_[0]); + $feed->{rss}->channel->{dc}{language} = $_[0]; + } else { + $feed->{rss}->channel('language') || + $feed->{rss}->channel->{dc}{language}; + } +} + +sub self_link { + my $feed = shift; + + if (@_) { + my $uri = shift; + + $feed->{rss}->channel->{'atom'}{'link'} = + { + rel => "self", + href => $uri, + type => "application/rss+xml", + }; + } + + return $feed->{rss}->channel->{'atom'}{'link'}; } + sub generator { - $_[0]->{rss}->channel('generator') || - $_[0]->{rss}->channel->{'http://webns.net/mvcb/'}{generatorAgent}; + my $feed = shift; + if (@_) { + $feed->{rss}->channel('generator', $_[0]); + $feed->{rss}->channel->{'http://webns.net/mvcb/'}{generatorAgent} = + $_[0]; + } else { + $feed->{rss}->channel('generator') || + $feed->{rss}->channel->{'http://webns.net/mvcb/'}{generatorAgent}; + } } sub author { - $_[0]->{rss}->channel('webMaster') || - $_[0]->{rss}->channel->{dc}{creator}; + my $feed = shift; + if (@_) { + $feed->{rss}->channel('webMaster', $_[0]); + $feed->{rss}->channel->{dc}{creator} = $_[0]; + } else { + $feed->{rss}->channel('webMaster') || + $feed->{rss}->channel->{dc}{creator}; + } } sub modified { - my $rss = $_[0]->{rss}; - if (my $ts = $rss->channel('pubDate')) { - return DateTime::Format::Mail->parse_datetime($ts); - } elsif ($ts = $rss->channel->{dc}{date}) { - return DateTime::Format::W3CDTF->parse_datetime($ts); + my $rss = shift->{rss}; + if (@_) { + $rss->channel('pubDate', + DateTime::Format::Mail->format_datetime($_[0])); + ## XML::RSS is so weird... if I set this, it will try to use + ## the value for the lastBuildDate, which I don't want--because + ## this date is formatted for an RSS 1.0 feed. So it's commented out. + #$rss->channel->{dc}{date} = + # DateTime::Format::W3CDTF->format_datetime($_[0]); + } else { + my $date; + eval { + if (my $ts = $rss->channel('pubDate')) { + $date = DateTime::Format::Mail->parse_datetime($ts); + } elsif ($ts = $rss->channel->{dc}{date}) { + $date = DateTime::Format::W3CDTF->parse_datetime($ts); + } + }; + return $date; } } @@ -57,74 +124,166 @@ sub entries { my $rss = $_[0]->{rss}; my @entries; for my $item (@{ $rss->{items} }) { - push @entries, XML::Feed::RSS::Entry->wrap($item); + push @entries, XML::Feed::Entry::RSS->wrap($item); } @entries; } -package XML::Feed::RSS::Entry; +sub add_entry { + my $feed = shift; + my $entry = shift || return; + $entry = $feed->_convert_entry($entry); + $feed->{rss}->add_item(%{ $entry->unwrap }); +} + +sub as_xml { $_[0]->{rss}->as_string } + +package XML::Feed::Entry::RSS; use strict; use XML::Feed::Content; use base qw( XML::Feed::Entry ); -sub title { $_[0]->{entry}{title} } -sub link { $_[0]->{entry}{link} || $_[0]->{entry}{guid} } +sub init_empty { $_[0]->{entry} = { } } + +sub title { + my $entry = shift; + @_ ? $entry->{entry}{title} = $_[0] : $entry->{entry}{title}; +} + +sub link { + my $entry = shift; + if (@_) { + $entry->{entry}{link} = $_[0]; + ## For RSS 2.0 output from XML::RSS. Sigh. + $entry->{entry}{permaLink} = $_[0]; + } else { + $entry->{entry}{link} || $entry->{entry}{guid}; + } +} sub summary { - my $item = $_[0]->{entry}; - ## Some RSS feeds use for a summary, and some use it - ## for the full content. Pretty gross. We don't want to return the - ## full content if the caller expects a summary, so the heuristic is: - ## if the contains both a and one of the elements - ## typically used for the full content, use as the summary. - my $txt; - if ($item->{description} && - ($item->{'http://purl.org/rss/1.0/modules/content/'}{encoded} || - $item->{'http://www.w3.org/1999/xhtml'}{body})) { - $txt = $item->{description}; + my $item = shift->{entry}; + if (@_) { + $item->{description} = ref($_[0]) eq 'XML::Feed::Content' ? + $_[0]->body : $_[0]; + ## Because of the logic below, we need to add some dummy content, + ## so that we'll properly recognize the description we enter as + ## the summary. + if (!$item->{content}{encoded} && + !$item->{'http://www.w3.org/1999/xhtml'}{body}) { + $item->{content}{encoded} = ' '; + } + } else { + ## Some RSS feeds use for a summary, and some use it + ## for the full content. Pretty gross. We don't want to return the + ## full content if the caller expects a summary, so the heuristic is: + ## if the contains both a and one of the elements + ## typically used for the full content, use as summary. + my $txt; + if ($item->{description} && + ($item->{content}{encoded} || + $item->{'http://www.w3.org/1999/xhtml'}{body})) { + $txt = $item->{description}; + } + XML::Feed::Content->wrap({ type => 'text/plain', body => $txt }); } - XML::Feed::Content->wrap({ type => 'text/plain', body => $txt }); } sub content { - my $item = $_[0]->{entry}; - my $body = - $_[0]->{entry}{'http://purl.org/rss/1.0/modules/content/'}{encoded} || - $_[0]->{entry}{'http://www.w3.org/1999/xhtml'}{body} || - $_[0]->{entry}{description}; - XML::Feed::Content->wrap({ type => 'text/html', body => $body }); + my $item = shift->{entry}; + if (@_) { + my $c = ref($_[0]) eq 'XML::Feed::Content' ? $_[0]->body : $_[0]; + $item->{content}{encoded} = $c; + } else { + my $body = + $item->{content}{encoded} || + $item->{'http://www.w3.org/1999/xhtml'}{body} || + $item->{description}; + XML::Feed::Content->wrap({ type => 'text/html', body => $body }); + } } sub category { - $_[0]->{entry}{category} || $_[0]->{entry}{dc}{subject}; + my $item = shift->{entry}; + if (@_) { + $item->{category} = $item->{dc}{subject} = $_[0]; + } else { + $item->{category} || $item->{dc}{subject}; + } } sub author { - $_[0]->{entry}{author} || $_[0]->{entry}{dc}{creator}; + my $item = shift->{entry}; + if (@_) { + $item->{author} = $item->{dc}{creator} = $_[0]; + } else { + $item->{author} || $item->{dc}{creator}; + } } ## XML::RSS doesn't give us access to the rdf:about for the , ## so we have to fall back to the element in RSS 1.0 feeds. sub id { - $_[0]->{entry}{guid} || $_[0]->{entry}{link}; + my $item = shift->{entry}; + if (@_) { + $item->{guid} = $_[0]; + } else { + $item->{guid} || $item->{link}; + } } sub issued { - if (my $ts = $_[0]->{entry}{pubDate}) { - my $parser = DateTime::Format::Mail->new; - $parser->loose; - return $parser->parse_datetime($ts); - } elsif ($ts = $_[0]->{entry}{dc}{date}) { - return DateTime::Format::W3CDTF->parse_datetime($ts); + my $item = shift->{entry}; + if (@_) { + $item->{dc}{date} = DateTime::Format::W3CDTF->format_datetime($_[0]); + $item->{pubDate} = DateTime::Format::Mail->format_datetime($_[0]); + } else { + ## Either of these could die if the format is invalid. + my $date; + eval { + if (my $ts = $item->{pubDate}) { + my $parser = DateTime::Format::Mail->new; + $parser->loose; + $date = $parser->parse_datetime($ts); + } elsif ($ts = $item->{dc}{date}) { + $date = DateTime::Format::W3CDTF->parse_datetime($ts); + } + }; + return $date; } } sub modified { - if (my $ts = $_[0]->{entry}{'http://purl.org/rss/1.0/modules/dcterms/'}{modified}) { - return DateTime::Format::W3CDTF->parse_datetime($ts); + my $item = shift->{entry}; + if (@_) { + $item->{dcterms}{modified} = + DateTime::Format::W3CDTF->format_datetime($_[0]); + } else { + if (my $ts = $item->{dcterms}{modified}) { + return eval { DateTime::Format::W3CDTF->parse_datetime($ts) }; + } + } +} + +sub lat { + my $item = shift->{entry}; + if (@_) { + $item->{geo}{lat} = $_[0]; + } else { + return $item->{geo}{lat}; } } +sub long { + my $item = shift->{entry}; + if (@_) { + $item->{geo}{long} = $_[0]; + } else { + return $item->{geo}{long}; + } +} + + 1;