1 # $Id: RSS.pm 1934 2006-04-22 05:13:55Z btrott $
3 package XML::Feed::RSS;
6 use base qw( XML::Feed );
7 use DateTime::Format::Mail;
8 use DateTime::Format::W3CDTF;
10 our $PREFERRED_PARSER = "XML::RSS";
13 my ($feed, %args) = @_;
14 $args{'version'} ||= '2.0';
15 eval "use $PREFERRED_PARSER"; die $@ if $@;
16 $feed->{rss} = $PREFERRED_PARSER->new(%args);
17 $feed->{rss}->add_module(prefix => "content", uri => 'http://purl.org/rss/1.0/modules/content/');
18 $feed->{rss}->add_module(prefix => "dcterms", uri => 'http://purl.org/dc/terms/');
19 $feed->{rss}->add_module(prefix => "atom", uri => 'http://www.w3.org/2005/Atom');
20 $feed->{rss}->add_module(prefix => "geo", uri => 'http://www.w3.org/2003/01/geo/wgs84_pos#');
29 $feed->{rss}->parse($$str);
34 sub format { 'RSS ' . $_[0]->{rss}->{'version'} }
36 ## The following elements are the same in all versions of RSS.
37 sub title { shift->{rss}->channel('title', @_) }
38 sub link { shift->{rss}->channel('link', @_) }
39 sub description { shift->{rss}->channel('description', @_) }
41 # This doesn't exist in RSS
44 ## This is RSS 2.0 only--what's the equivalent in RSS 1.0?
45 sub copyright { shift->{rss}->channel('copyright', @_) }
47 ## The following all work transparently in any RSS version.
51 $feed->{rss}->channel('language', $_[0]);
52 $feed->{rss}->channel->{dc}{language} = $_[0];
54 $feed->{rss}->channel('language') ||
55 $feed->{rss}->channel->{dc}{language};
65 $feed->{rss}->channel->{'atom'}{'link'} =
69 type => "application/rss+xml",
73 return $feed->{rss}->channel->{'atom'}{'link'};
80 $feed->{rss}->channel('generator', $_[0]);
81 $feed->{rss}->channel->{'http://webns.net/mvcb/'}{generatorAgent} =
84 $feed->{rss}->channel('generator') ||
85 $feed->{rss}->channel->{'http://webns.net/mvcb/'}{generatorAgent};
92 $feed->{rss}->channel('webMaster', $_[0]);
93 $feed->{rss}->channel->{dc}{creator} = $_[0];
95 $feed->{rss}->channel('webMaster') ||
96 $feed->{rss}->channel->{dc}{creator};
101 my $rss = shift->{rss};
103 $rss->channel('pubDate',
104 DateTime::Format::Mail->format_datetime($_[0]));
105 ## XML::RSS is so weird... if I set this, it will try to use
106 ## the value for the lastBuildDate, which I don't want--because
107 ## this date is formatted for an RSS 1.0 feed. So it's commented out.
108 #$rss->channel->{dc}{date} =
109 # DateTime::Format::W3CDTF->format_datetime($_[0]);
113 if (my $ts = $rss->channel('pubDate')) {
114 $date = DateTime::Format::Mail->parse_datetime($ts);
115 } elsif ($ts = $rss->channel->{dc}{date}) {
116 $date = DateTime::Format::W3CDTF->parse_datetime($ts);
124 my $rss = $_[0]->{rss};
126 for my $item (@{ $rss->{items} }) {
127 push @entries, XML::Feed::Entry::RSS->wrap($item);
136 $feed->{rss}->add_item(%{ $entry->unwrap });
139 sub as_xml { $_[0]->{rss}->as_string }
141 package XML::Feed::Entry::RSS;
144 use XML::Feed::Content;
146 use base qw( XML::Feed::Entry );
148 sub init_empty { $_[0]->{entry} = { } }
152 @_ ? $entry->{entry}{title} = $_[0] : $entry->{entry}{title};
158 $entry->{entry}{link} = $_[0];
159 ## For RSS 2.0 output from XML::RSS. Sigh.
160 $entry->{entry}{permaLink} = $_[0];
162 $entry->{entry}{link} || $entry->{entry}{guid};
167 my $item = shift->{entry};
169 $item->{description} = ref($_[0]) eq 'XML::Feed::Content' ?
171 ## Because of the logic below, we need to add some dummy content,
172 ## so that we'll properly recognize the description we enter as
174 if (!$item->{content}{encoded} &&
175 !$item->{'http://www.w3.org/1999/xhtml'}{body}) {
176 $item->{content}{encoded} = ' ';
179 ## Some RSS feeds use <description> for a summary, and some use it
180 ## for the full content. Pretty gross. We don't want to return the
181 ## full content if the caller expects a summary, so the heuristic is:
182 ## if the <entry> contains both a <description> and one of the elements
183 ## typically used for the full content, use <description> as summary.
185 if ($item->{description} &&
186 ($item->{content}{encoded} ||
187 $item->{'http://www.w3.org/1999/xhtml'}{body})) {
188 $txt = $item->{description};
190 XML::Feed::Content->wrap({ type => 'text/plain', body => $txt });
195 my $item = shift->{entry};
197 my $c = ref($_[0]) eq 'XML::Feed::Content' ? $_[0]->body : $_[0];
198 $item->{content}{encoded} = $c;
201 $item->{content}{encoded} ||
202 $item->{'http://www.w3.org/1999/xhtml'}{body} ||
203 $item->{description};
204 XML::Feed::Content->wrap({ type => 'text/html', body => $body });
209 my $item = shift->{entry};
211 $item->{category} = $item->{dc}{subject} = $_[0];
213 $item->{category} || $item->{dc}{subject};
218 my $item = shift->{entry};
220 $item->{author} = $item->{dc}{creator} = $_[0];
222 $item->{author} || $item->{dc}{creator};
226 ## XML::RSS doesn't give us access to the rdf:about for the <item>,
227 ## so we have to fall back to the <link> element in RSS 1.0 feeds.
229 my $item = shift->{entry};
231 $item->{guid} = $_[0];
233 $item->{guid} || $item->{link};
238 my $item = shift->{entry};
240 $item->{dc}{date} = DateTime::Format::W3CDTF->format_datetime($_[0]);
241 $item->{pubDate} = DateTime::Format::Mail->format_datetime($_[0]);
243 ## Either of these could die if the format is invalid.
246 if (my $ts = $item->{pubDate}) {
247 my $parser = DateTime::Format::Mail->new;
249 $date = $parser->parse_datetime($ts);
250 } elsif ($ts = $item->{dc}{date}) {
251 $date = DateTime::Format::W3CDTF->parse_datetime($ts);
259 my $item = shift->{entry};
261 $item->{dcterms}{modified} =
262 DateTime::Format::W3CDTF->format_datetime($_[0]);
264 if (my $ts = $item->{dcterms}{modified}) {
265 return eval { DateTime::Format::W3CDTF->parse_datetime($ts) };
271 my $item = shift->{entry};
273 $item->{geo}{lat} = $_[0];
275 return $item->{geo}{lat};
280 my $item = shift->{entry};
282 $item->{geo}{long} = $_[0];
284 return $item->{geo}{long};