3 package XML::Feed::Format::Atom;
6 use base qw( XML::Feed );
8 use XML::Atom::Util qw( iso2dt );
9 use List::Util qw( first );
10 use DateTime::Format::W3CDTF;
13 XML::Atom::Entry->mk_elem_accessors(qw( lat long ), ['http://www.w3.org/2003/01/geo/wgs84_pos#']);
15 use XML::Atom::Content;
20 my $tag = $class->_get_first_tag($xml);
21 return ($tag eq 'feed');
26 my ($feed, %args) = @_;
27 $args{'Version'} ||= '1.0';
29 $feed->{atom} = XML::Atom::Feed->new(%args);
37 $feed->{atom} = XML::Atom::Feed->new(Stream => $str)
38 or return $feed->error(XML::Atom::Feed->errstr);
45 sub title { shift->{atom}->title(@_) }
49 $feed->{atom}->add_link({ rel => 'alternate', href => $_[0],
50 type => 'text/html', });
52 my $l = first { !defined $_->rel || $_->rel eq 'alternate' } $feed->{atom}->link;
53 $l ? $l->href : undef;
61 $feed->{atom}->add_link({type => "application/atom+xml", rel => "self", href => $uri});
68 { !defined $_->rel || $_->rel eq 'self' }
72 return $l ? $l->href : undef;
76 sub description { shift->{atom}->tagline(@_) }
77 sub copyright { shift->{atom}->copyright(@_) }
78 sub language { shift->{atom}->language(@_) }
79 sub generator { shift->{atom}->generator(@_) }
80 sub id { shift->{atom}->id(@_) }
81 sub updated { shift->{atom}->updated(@_) }
82 sub add_link { shift->{atom}->add_link(@_) }
83 sub base { shift->{atom}->base(@_) }
88 my $person = XML::Atom::Person->new(Version => 1.0);
90 $feed->{atom}->author($person);
92 $feed->{atom}->author ? $feed->{atom}->author->name : undef;
102 $feed->{atom}->modified(DateTime::Format::W3CDTF->format_datetime($_[0]));
104 return iso2dt($feed->{atom}->modified) if $feed->{atom}->modified;
105 return iso2dt($feed->{atom}->updated) if $feed->{atom}->updated;
112 for my $entry ($_[0]->{atom}->entries) {
113 push @entries, XML::Feed::Entry::Format::Atom->wrap($entry);
121 my $entry = shift || return;
122 $entry = $feed->_convert_entry($entry);
123 $feed->{atom}->add_entry($entry->unwrap);
126 sub as_xml { $_[0]->{atom}->as_xml }
128 package XML::Feed::Entry::Format::Atom;
131 use base qw( XML::Feed::Entry );
132 use XML::Atom::Util qw( iso2dt );
133 use XML::Feed::Content;
134 use XML::Atom::Entry;
135 use List::Util qw( first );
137 use constant ACTIVITY_NAMESPACE_URI => "http://activitystrea.ms/spec/1.0/";
138 use constant ACTIVITY_NAMESPACE => XML::Atom::Namespace->new(
139 'activity' => ACTIVITY_NAMESPACE_URI,
144 $entry->{entry} = XML::Atom::Entry->new(Version => 1.0);
148 sub format { 'Atom' }
150 sub title { shift->{entry}->title(@_) }
151 sub source { shift->{entry}->source(@_) }
152 sub updated { shift->{entry}->updated(@_) }
153 sub base { shift->{entry}->base(@_) }
158 $entry->{entry}->add_link({ rel => 'alternate', href => $_[0],
159 type => 'text/html', });
161 my $l = first { !defined $_->rel || $_->rel eq 'alternate' } $entry->{entry}->link;
162 $l ? $l->href : undef;
170 if (ref($_[0]) eq 'XML::Feed::Content') {
171 %param = (Body => $_[0]->body);
173 %param = (Body => $_[0]);
175 $entry->{entry}->summary(XML::Atom::Content->new(%param, Version => 1.0));
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;
181 $type = 'text/html' if $type eq 'xhtml' || $type eq 'html';
182 $type = 'text/plain' if $type eq 'text';
185 if (defined $s && ref($s) eq 'XML::Feed::Content') {
188 XML::Feed::Content->wrap({ type => $type,
194 'text/xhtml' => 'xhtml',
195 'text/html' => 'html',
196 'text/plain' => 'text',
204 if (ref($_[0]) eq 'XML::Feed::Content') {
205 if (defined $_[0]->type && defined $types{$_[0]->type}) {
206 %param = (Body => $_[0]->body, Type => $types{$_[0]->type});
208 %param = (Body => $_[0]->body);
210 $base = $_[0]->base if defined $_[0]->base;
212 %param = (Body => $_[0]);
214 $entry->{entry}->content(XML::Atom::Content->new(%param, Version => 1.0));
215 $entry->{entry}->content->base($base) if defined $base;
217 my $c = $entry->{entry}->content;
219 # map Atom types to MIME types
220 my $type = $c ? $c->type : undef;
222 $type = 'text/html' if $type eq 'xhtml' || $type eq 'html';
223 $type = 'text/plain' if $type eq 'text';
226 XML::Feed::Content->wrap({ type => $type,
227 base => $c ? $c->base : undef,
228 body => $c ? $c->body : undef });
234 my $ns = XML::Atom::Namespace->new(dc => 'http://purl.org/dc/elements/1.1/');
236 $entry->{entry}->add_category({ term => $_ }) for @_;
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');
246 return wantarray? @return : $return[0];
253 my $person = XML::Atom::Person->new(Version => 1.0);
254 $person->name($_[0]);
255 $entry->{entry}->author($person);
257 $entry->{entry}->author ? $entry->{entry}->author->name : undef;
261 sub id { shift->{entry}->id(@_) }
266 $entry->{entry}->issued(DateTime::Format::W3CDTF->format_datetime($_[0])) if $_[0];
268 $entry->{entry}->issued ? iso2dt($entry->{entry}->issued) : undef;
275 $entry->{entry}->modified(DateTime::Format::W3CDTF->format_datetime($_[0])) if $_[0];
277 return iso2dt($entry->{entry}->modified) if $entry->{entry}->modified;
278 return iso2dt($entry->{entry}->updated) if $entry->{entry}->updated;
286 $entry->{entry}->lat($_[0]) if $_[0];
288 $entry->{entry}->lat;
295 $entry->{entry}->long($_[0]) if $_[0];
297 $entry->{entry}->long;
303 $entry->_activity('verb', @_);
305 sub activity_object_types {
307 $entry->_activity('object-type', @_);
311 my $entry = shift->{entry};
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);
323 foreach my $thing (@things) {
324 $entry->set(ACTIVITY_NAMESPACE(), $name, $thing, undef, 1);
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);
336 sub activity_object {
337 my $entry = shift->{entry};
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";
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);
359 sub thumbnail_image {
362 $entry->{entry}->add_link({ rel => 'alternate', href => $_[0],
363 type => 'text/html', });
365 my $l = first { $_->rel eq 'preview' && $_->type =~ m!^image/! } $entry->{entry}->link;
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.
374 width => $l->elem->getAttributeNS("http://purl.org/syndication/atommedia", "width"),
375 height => $l->elem->getAttributeNS("http://purl.org/syndication/atommedia", "height"),