Cleaner
[catagits/XML-Feed.git] / lib / XML / Feed / Format / Atom.pm
CommitLineData
3353d70c 1# $Id$
0d5e38d1 2
729cd7a8 3package XML::Feed::Format::Atom;
0d5e38d1 4use strict;
5
6use base qw( XML::Feed );
7use XML::Atom::Feed;
8use XML::Atom::Util qw( iso2dt );
9use List::Util qw( first );
ecac864a 10use DateTime::Format::W3CDTF;
0d5e38d1 11
9a36f82c 12use XML::Atom::Entry;
13XML::Atom::Entry->mk_elem_accessors(qw( lat long ), ['http://www.w3.org/2003/01/geo/wgs84_pos#']);
4800b535 14
5383a560 15use XML::Atom::Content;
16
9b6bc912 17sub identify {
18 my $class = shift;
19 my $xml = shift;
20 my $tag = $class->_get_first_tag($xml);
21 return ($tag eq 'feed');
22}
23
24
973e1f9e 25sub init_empty {
4e9c4625 26 my ($feed, %args) = @_;
27 $args{'Version'} ||= '1.0';
28
29 $feed->{atom} = XML::Atom::Feed->new(%args);
973e1f9e 30 $feed;
31}
32
0d5e38d1 33sub init_string {
34 my $feed = shift;
35 my($str) = @_;
fe71566d 36 if ($str) {
37 $feed->{atom} = XML::Atom::Feed->new(Stream => $str)
38 or return $feed->error(XML::Atom::Feed->errstr);
39 }
0d5e38d1 40 $feed;
41}
42
43sub format { 'Atom' }
44
973e1f9e 45sub title { shift->{atom}->title(@_) }
0d5e38d1 46sub link {
973e1f9e 47 my $feed = shift;
48 if (@_) {
49 $feed->{atom}->add_link({ rel => 'alternate', href => $_[0],
50 type => 'text/html', });
51 } else {
4679cf3f 52 my $l = first { !defined $_->rel || $_->rel eq 'alternate' } $feed->{atom}->link;
973e1f9e 53 $l ? $l->href : undef;
54 }
55}
9a36f82c 56
57sub self_link {
58 my $feed = shift;
59 if (@_) {
60 my $uri = shift;
61 $feed->{atom}->add_link({type => "application/atom+xml", rel => "self", href => $uri});
62 return $uri;
63 }
64 else
65 {
66 my $l =
67 first
68 { !defined $_->rel || $_->rel eq 'self' }
69 $feed->{atom}->link;
70 ;
71
72 return $l ? $l->href : undef;
73 }
74}
75
973e1f9e 76sub description { shift->{atom}->tagline(@_) }
77sub copyright { shift->{atom}->copyright(@_) }
78sub language { shift->{atom}->language(@_) }
79sub generator { shift->{atom}->generator(@_) }
e8fcbc5b 80sub id { shift->{atom}->id(@_) }
81sub updated { shift->{atom}->updated(@_) }
82sub add_link { shift->{atom}->add_link(@_) }
5383a560 83sub base { shift->{atom}->base(@_) }
973e1f9e 84
85sub author {
86 my $feed = shift;
87 if (@_ && $_[0]) {
c4d4c98e 88 my $person = XML::Atom::Person->new(Version => 1.0);
973e1f9e 89 $person->name($_[0]);
90 $feed->{atom}->author($person);
91 } else {
92 $feed->{atom}->author ? $feed->{atom}->author->name : undef;
93 }
94}
95
5383a560 96
97
98
973e1f9e 99sub modified {
100 my $feed = shift;
101 if (@_) {
ecac864a 102 $feed->{atom}->modified(DateTime::Format::W3CDTF->format_datetime($_[0]));
973e1f9e 103 } else {
1ee56ab5 104 return iso2dt($feed->{atom}->modified) if $feed->{atom}->modified;
105 return iso2dt($feed->{atom}->updated) if $feed->{atom}->updated;
106 return undef;
973e1f9e 107 }
0d5e38d1 108}
0d5e38d1 109
c4d4c98e 110sub entries {
0d5e38d1 111 my @entries;
112 for my $entry ($_[0]->{atom}->entries) {
729cd7a8 113 push @entries, XML::Feed::Entry::Format::Atom->wrap($entry);
0d5e38d1 114 }
c4d4c98e 115
0d5e38d1 116 @entries;
117}
118
973e1f9e 119sub add_entry {
33d4cb3f 120 my $feed = shift;
121 my $entry = shift || return;
122 $entry = $feed->_convert_entry($entry);
973e1f9e 123 $feed->{atom}->add_entry($entry->unwrap);
124}
125
126sub as_xml { $_[0]->{atom}->as_xml }
127
729cd7a8 128package XML::Feed::Entry::Format::Atom;
0d5e38d1 129use strict;
130
131use base qw( XML::Feed::Entry );
132use XML::Atom::Util qw( iso2dt );
a749d9b9 133use XML::Feed::Content;
973e1f9e 134use XML::Atom::Entry;
0d5e38d1 135use List::Util qw( first );
136
4800b535 137use constant ACTIVITY_NAMESPACE_URI => "http://activitystrea.ms/spec/1.0/";
138use constant ACTIVITY_NAMESPACE => XML::Atom::Namespace->new(
139 'activity' => ACTIVITY_NAMESPACE_URI,
140);
141
973e1f9e 142sub init_empty {
143 my $entry = shift;
c4d4c98e 144 $entry->{entry} = XML::Atom::Entry->new(Version => 1.0);
973e1f9e 145 1;
146}
147
4800b535 148sub format { 'Atom' }
3bdbab6f 149
4800b535 150sub title { shift->{entry}->title(@_) }
151sub source { shift->{entry}->source(@_) }
e8fcbc5b 152sub updated { shift->{entry}->updated(@_) }
4800b535 153sub base { shift->{entry}->base(@_) }
e8fcbc5b 154
0d5e38d1 155sub link {
973e1f9e 156 my $entry = shift;
157 if (@_) {
158 $entry->{entry}->add_link({ rel => 'alternate', href => $_[0],
159 type => 'text/html', });
160 } else {
4679cf3f 161 my $l = first { !defined $_->rel || $_->rel eq 'alternate' } $entry->{entry}->link;
973e1f9e 162 $l ? $l->href : undef;
163 }
0d5e38d1 164}
a749d9b9 165
166sub summary {
973e1f9e 167 my $entry = shift;
168 if (@_) {
ac9492d2 169 my %param;
170 if (ref($_[0]) eq 'XML::Feed::Content') {
171 %param = (Body => $_[0]->body);
172 } else {
173 %param = (Body => $_[0]);
174 }
175 $entry->{entry}->summary(XML::Atom::Content->new(%param, Version => 1.0));
973e1f9e 176 } else {
ac9492d2 177 my $s = $entry->{entry}->summary;
178 # map Atom types to MIME types
179 my $type = ($s && ref($s) eq 'XML::Feed::Content') ? $s->type : undef;
180 if ($type) {
181 $type = 'text/html' if $type eq 'xhtml' || $type eq 'html';
182 $type = 'text/plain' if $type eq 'text';
183 }
184 my $body = $s;
185 if (defined $s && ref($s) eq 'XML::Feed::Content') {
186 $body = $s->body;
187 }
188 XML::Feed::Content->wrap({ type => $type,
189 body => $body });
973e1f9e 190 }
a749d9b9 191}
192
4f413435 193my %types = (
194 'text/xhtml' => 'xhtml',
195 'text/html' => 'html',
196 'text/plain' => 'text',
197);
198
a749d9b9 199sub content {
973e1f9e 200 my $entry = shift;
201 if (@_) {
202 my %param;
5383a560 203 my $base;
973e1f9e 204 if (ref($_[0]) eq 'XML::Feed::Content') {
4f413435 205 if (defined $_[0]->type && defined $types{$_[0]->type}) {
206 %param = (Body => $_[0]->body, Type => $types{$_[0]->type});
207 } else {
208 %param = (Body => $_[0]->body);
209 }
5383a560 210 $base = $_[0]->base if defined $_[0]->base;
973e1f9e 211 } else {
c4d4c98e 212 %param = (Body => $_[0]);
973e1f9e 213 }
c4d4c98e 214 $entry->{entry}->content(XML::Atom::Content->new(%param, Version => 1.0));
5383a560 215 $entry->{entry}->content->base($base) if defined $base;
973e1f9e 216 } else {
217 my $c = $entry->{entry}->content;
c4d4c98e 218
219 # map Atom types to MIME types
220 my $type = $c ? $c->type : undef;
221 if ($type) {
222 $type = 'text/html' if $type eq 'xhtml' || $type eq 'html';
223 $type = 'text/plain' if $type eq 'text';
224 }
225
226 XML::Feed::Content->wrap({ type => $type,
5383a560 227 base => $c ? $c->base : undef,
973e1f9e 228 body => $c ? $c->body : undef });
229 }
a749d9b9 230}
0d5e38d1 231
232sub category {
973e1f9e 233 my $entry = shift;
0d5e38d1 234 my $ns = XML::Atom::Namespace->new(dc => 'http://purl.org/dc/elements/1.1/');
973e1f9e 235 if (@_) {
a0cca2a4 236 $entry->{entry}->add_category({ term => $_ }) for @_;
237 return 1
973e1f9e 238 } else {
a0cca2a4 239
240
241 my @category = ($entry->{entry}->can('categories')) ? $entry->{entry}->categories : $entry->{entry}->category;
242 my @return = @category
243 ? (map { $_->label || $_->term } @category)
244 : $entry->{entry}->getlist($ns, 'subject');
245
bf34c07e 246 return wantarray? @return : $return[0];
973e1f9e 247 }
248}
249
250sub author {
251 my $entry = shift;
252 if (@_ && $_[0]) {
c4d4c98e 253 my $person = XML::Atom::Person->new(Version => 1.0);
973e1f9e 254 $person->name($_[0]);
255 $entry->{entry}->author($person);
256 } else {
257 $entry->{entry}->author ? $entry->{entry}->author->name : undef;
258 }
259}
260
261sub id { shift->{entry}->id(@_) }
262
263sub issued {
264 my $entry = shift;
265 if (@_) {
ecac864a 266 $entry->{entry}->issued(DateTime::Format::W3CDTF->format_datetime($_[0])) if $_[0];
973e1f9e 267 } else {
268 $entry->{entry}->issued ? iso2dt($entry->{entry}->issued) : undef;
269 }
0d5e38d1 270}
271
973e1f9e 272sub modified {
273 my $entry = shift;
274 if (@_) {
ecac864a 275 $entry->{entry}->modified(DateTime::Format::W3CDTF->format_datetime($_[0])) if $_[0];
973e1f9e 276 } else {
1ee56ab5 277 return iso2dt($entry->{entry}->modified) if $entry->{entry}->modified;
278 return iso2dt($entry->{entry}->updated) if $entry->{entry}->updated;
279 return undef;
973e1f9e 280 }
281}
0d5e38d1 282
9a36f82c 283sub lat {
284 my $entry = shift;
285 if (@_) {
286 $entry->{entry}->lat($_[0]) if $_[0];
287 } else {
288 $entry->{entry}->lat;
289 }
290}
291
292sub long {
293 my $entry = shift;
294 if (@_) {
295 $entry->{entry}->long($_[0]) if $_[0];
296 } else {
297 $entry->{entry}->long;
298 }
299}
300
4800b535 301sub activity_verbs {
302 my $entry = shift;
303 $entry->_activity('verb', @_);
304}
305sub activity_object_types {
306 my $entry = shift;
307 $entry->_activity('object-type', @_);
308}
309
310sub _activity {
311 my $entry = shift->{entry};
312 my $name = shift;
313
314 if (@_) {
315 my @things = @_;
316
317 # Remove all of the existing elements.
318 my @existing = XML::Atom::Util::childlist($entry->elem, ACTIVITY_NAMESPACE_URI(), $name);
319 foreach my $elem (@existing) {
320 $entry->elem->removeChild($elem);
321 }
322
323 foreach my $thing (@things) {
324 $entry->set(ACTIVITY_NAMESPACE(), $name, $thing, undef, 1);
325 }
326 }
327 else {
328 # FIXME: This currently returns all decendent things, not just
329 # children. This might get troublesome if, for example,
330 # there's ever an activity entry with an activity as its
331 # object, or something crazy like that.
332 return $entry->getlist(ACTIVITY_NAMESPACE(), $name);
333 }
334}
335
336sub activity_object {
337 my $entry = shift->{entry};
338
339 if (@_) {
340 # Need to accept any arbitrary XML::Feed::Entry and turn it into
341 # an XML::Atom::Entry here, then call:
342 # $entry->set(ACTIVITY_NAMESPACE, 'object', $xml_atom_entry);
343 die "setting activity_object is not yet implemented";
344 } else {
345 my ($object_elem) = XML::Atom::Util::childlist($entry->elem, ACTIVITY_NAMESPACE_URI(), 'object');
346 if (defined $object_elem) {
347 my $ret = XML::Atom::Entry->new(Elem => $object_elem);
348 # If we're holding an activity:object element then our primary
349 # namespace will be set wrong, so let's put it back.
350 XML::Atom::Util::set_ns($ret, { Version => "1.0" });
351 return XML::Feed::Entry::Format::Atom->wrap($ret);
352 }
353 else {
354 return undef;
355 }
356 }
357}
358
d161335a 359sub thumbnail_image {
360 my $entry = shift;
361 if (@_) {
362 $entry->{entry}->add_link({ rel => 'alternate', href => $_[0],
363 type => 'text/html', });
364 } else {
365 my $l = first { $_->rel eq 'preview' && $_->type =~ m!^image/! } $entry->{entry}->link;
366
367 if ($l) {
368 # FIXME: This method for getting the attributes only works if
369 # XML::Atom is using LibXML; XML::Atom doesn't provide
370 # a proper way to get a namespaced attribute.
371 return {
372 url => $l->href,
373 type => $l->type,
374 width => $l->elem->getAttributeNS("http://purl.org/syndication/atommedia", "width"),
375 height => $l->elem->getAttributeNS("http://purl.org/syndication/atommedia", "height"),
376 };
377 }
378 else {
379 return undef;
380 }
381 }
382}
0d5e38d1 3831;