50f243942a198f57c7a02f2c2e3dee6137387c19
[catagits/XML-Feed.git] / lib / XML / Feed / Format / RSS.pm
1 # $Id$
2
3 package XML::Feed::Format::RSS;
4 use strict;
5
6 use base qw( XML::Feed );
7 use DateTime::Format::Mail;
8 use DateTime::Format::W3CDTF;
9 use XML::Atom::Util qw(iso2dt);
10 use XML::Feed::Enclosure;
11 use XML::Feed::Entry::Format::RSS;
12
13 our $PREFERRED_PARSER = "XML::RSS";
14
15
16 sub identify {
17     my $class   = shift;
18     my $xml     = shift;
19     my $tag     = $class->_get_first_tag($xml);
20     return ($tag eq 'rss' || $tag eq 'RDF');
21 }
22
23 sub init_empty {
24     my ($feed, %args) = @_;
25     $args{'version'} ||= '2.0';
26     eval "use $PREFERRED_PARSER"; die $@ if $@;
27     $feed->{rss} = $PREFERRED_PARSER->new(%args);
28     $feed->{rss}->add_module(prefix => "content", uri => 'http://purl.org/rss/1.0/modules/content/');
29     $feed->{rss}->add_module(prefix => "dcterms", uri => 'http://purl.org/dc/terms/');    
30     $feed->{rss}->add_module(prefix => "atom", uri => 'http://www.w3.org/2005/Atom');
31     $feed->{rss}->add_module(prefix => "geo", uri => 'http://www.w3.org/2003/01/geo/wgs84_pos#');
32     $feed;
33 }
34
35 sub init_string {
36     my $feed = shift;
37     my($str) = @_;
38     $feed->init_empty;
39     my $opts = {
40          hashrefs_instead_of_strings => 1,
41     };
42     $opts->{allow_multiple} = [ 'enclosure' ] if $XML::Feed::MULTIPLE_ENCLOSURES;
43     if ($str) {
44         $feed->{rss}->parse($$str, $opts );
45     }
46     $feed;
47 }
48
49 sub format { 'RSS ' . $_[0]->{rss}->{'version'} }
50
51 ## The following elements are the same in all versions of RSS.
52 sub title       { shift->{rss}->channel('title', @_) }
53 sub link        {
54     my $link = shift->{rss}->channel('link', @_);
55     $link =~ s/^\s+//;
56     $link =~ s/\s+$//;
57     return $link;
58 }
59 sub description { shift->{rss}->channel('description', @_) }
60 sub updated     { shift->modified(@_) }
61
62 # This doesn't exist in RSS
63 sub id          { }
64
65 ## This is RSS 2.0 only--what's the equivalent in RSS 1.0?
66 sub copyright   { shift->{rss}->channel('copyright', @_) }
67
68 sub base {
69     my $feed = shift;
70     if (@_) {
71         $feed->{rss}->{'xml:base'} = $_[0];
72     } else {
73         $feed->{rss}->{'xml:base'};
74     }
75 }
76
77 ## The following all work transparently in any RSS version.
78 sub language {
79     my $feed = shift;
80     if (@_) {
81         $feed->{rss}->channel('language', $_[0]);
82         $feed->{rss}->channel->{dc}{language} = $_[0];
83     } else {
84         $feed->{rss}->channel('language') ||
85         $feed->{rss}->channel->{dc}{language};
86     }
87 }
88
89 sub self_link {
90     my $feed = shift;
91
92     if (@_) {
93         my $uri = shift;
94
95         $feed->{rss}->channel->{'atom'}{'link'} =
96         {
97             rel => "self",
98             href => $uri,
99             type => "application/rss+xml",
100         };
101     }
102
103     return $feed->{rss}->channel->{'atom'}{'link'};
104 }
105
106 # This doesn't exist in RSS
107 sub first_link { };
108 sub last_link { };
109 sub previous_link { };
110 sub next_link { };
111 sub current_link { };
112 sub prev_archive_link { };
113 sub next_archive_link { };
114
115 sub generator {
116     my $feed = shift;
117     if (@_) {
118         $feed->{rss}->channel('generator', $_[0]);
119         $feed->{rss}->channel->{'http://webns.net/mvcb/'}{generatorAgent} =
120             $_[0];
121     } else {
122         $feed->{rss}->channel('generator') ||
123         $feed->{rss}->channel->{'http://webns.net/mvcb/'}{generatorAgent};
124     }
125 }
126
127 sub author {
128     my $feed = shift;
129     if (@_) {
130         $feed->{rss}->channel('webMaster', $_[0]);
131         $feed->{rss}->channel->{dc}{creator} = $_[0];
132     } else {
133         $feed->{rss}->channel('webMaster') ||
134         $feed->{rss}->channel->{dc}{creator};
135     }
136 }
137
138 sub modified {
139     my $rss = shift->{rss};
140     if (@_) {
141         $rss->channel('pubDate',
142             DateTime::Format::Mail->format_datetime($_[0]));
143         ## XML::RSS is so weird... if I set this, it will try to use
144         ## the value for the lastBuildDate, which I don't want--because
145         ## this date is formatted for an RSS 1.0 feed. So it's commented out.
146         #$rss->channel->{dc}{date} =
147         #    DateTime::Format::W3CDTF->format_datetime($_[0]);
148     } else {
149         my $date;
150         eval {
151             if (my $ts = $rss->channel('pubDate')) {
152                 $ts =~ s/^\s+//;
153                 $ts =~ s/\s+$//;
154                 $date = DateTime::Format::Mail->parse_datetime($ts);
155             } elsif ($ts = $rss->channel->{dc}{date}) {
156                 $ts =~ s/^\s+//;
157                 $ts =~ s/\s+$//;
158                 $date = DateTime::Format::W3CDTF->parse_datetime($ts);
159             }
160         };
161         return $date;
162     }
163 }
164
165 sub entries {
166     my $rss = $_[0]->{rss};
167     my @entries;
168     for my $item (@{ $rss->{items} }) {
169         push @entries, XML::Feed::Entry::Format::RSS->wrap($item);
170                 $entries[-1]->{_version} = $rss->{'version'};           
171     }
172     @entries;
173 }
174
175 sub add_entry {
176     my $feed  = shift;
177     my $entry = shift || return;
178     $entry    = $feed->_convert_entry($entry);
179     $feed->{rss}->add_item(%{ $entry->unwrap });
180 }
181
182 sub as_xml { $_[0]->{rss}->as_string }
183
184 1;
185