File Coverage

blib/lib/XML/RSS/LibXML/V2_0.pm
Criterion Covered Total %
statement 15 118 12.7
branch 0 34 0.0
condition 0 10 0.0
subroutine 5 14 35.7
pod 0 9 0.0
total 20 185 10.8


line stmt bran cond sub pod time code
1             # $Id$
2             #
3             # Copyright (c) 2005-2007 Daisuke Maki
4             # All rights reserved.
5              
6             package XML::RSS::LibXML::V2_0;
7 1     1   2330 use strict;
  1         3  
  1         33  
8 1     1   6 use warnings;
  1         1  
  1         25  
9 1     1   5 use base qw(XML::RSS::LibXML::ImplBase);
  1         2  
  1         585  
10 1     1   812 use DateTime::Format::W3CDTF;
  1         218193  
  1         23  
11 1     1   1722 use DateTime::Format::Mail;
  1         3720  
  1         21  
12              
13             my %DcElements = (
14             (map { ("dc:$_" => [ { module => 'dc', element => $_ } ]) }
15             qw(language rights date publisher creator title subject description contributer type format identifier source relation coverage)),
16             );
17            
18             my %SynElements = (
19             (map { ("syn:$_" => [ { module => 'syn', element => $_ } ]) }
20             qw(updateBase updateFrequency updatePeriod)),
21             );
22             my $format_dates = sub {
23             my $v = eval {
24             DateTime::Format::Mail->format_datetime(
25             DateTime::Format::W3CDTF->parse_datetime($_[0])
26             );
27             };
28             if ($v && ! $@) {
29             $_[0] = $v;
30             }
31             };
32              
33             my %ChannelElements = (
34             %DcElements,
35             %SynElements,
36             (map { ($_ => [ $_ ]) } qw(title link description)),
37             language => [ { module => 'dc', element => 'language' }, 'language' ],
38             copyright => [ { module => 'dc', element => 'rights' }, 'copyright' ],
39             pubDate => {
40             candidates => [ 'pubDate', { module => 'dc', element => 'date' } ],
41             callback => $format_dates,
42             },
43             lastBuildDate => {
44             candidates => [ { module => 'dc', element => 'date' }, 'lastBuildDate' ],
45             callback => $format_dates,
46             },
47             docs => [ 'docs' ],
48             managingEditor => [ { module => 'dc', element => 'publisher' }, 'managingEditor' ],
49             webMaster => [ { module => 'dc', element => 'creator' }, 'webMaster' ],
50             category => [ { module => 'dc', element => 'category' }, 'category' ],
51             generator => [ { module => 'dc', element => 'generator' }, 'generator' ],
52             ttl => [ { module => 'dc', element => 'ttl' }, 'ttl' ],
53             );
54              
55             my %ItemElements = (
56             %DcElements,
57             enclosure => ['enclosure'],
58             map { ($_ => [$_]) }
59             qw(title link description author category comments pubDate)
60             );
61              
62             my %ImageElements = (
63             (map { ($_ => [$_]) } qw(title url link width height description)),
64             %DcElements,
65             );
66            
67             my %TextInputElements = (
68             (map { ($_ => [$_]) } qw(title link description name)),
69             %DcElements
70             );
71              
72             sub definition
73             {
74             return +{
75 0     0 0   channel => {
76             title => '',
77             'link' => '',
78             description => '',
79             language => undef,
80             copyright => undef,
81             managingEditor => undef,
82             webMaster => undef,
83             pubDate => undef,
84             lastBuildDate => undef,
85             category => undef,
86             generator => undef,
87             docs => undef,
88             cloud => '',
89             ttl => undef,
90             image => '',
91             textinput => '',
92             skipHours => '',
93             skipDays => '',
94             },
95             image => bless ({
96             title => undef,
97             url => undef,
98             'link' => undef,
99             width => undef,
100             height => undef,
101             description => undef,
102             }, 'XML::RSS::LibXML::ElementSpec'),
103             skipDays => bless ({
104             day => undef,
105             }, 'XML::RSS::LibXML::ElementSpec'),
106             skipHours => bless ({
107             hour => undef,
108             }, 'XML::RSS::LibXML::ElementSpec'),
109             textinput => bless ({
110             title => undef,
111             description => undef,
112             name => undef,
113             'link' => undef,
114             }, 'XML::RSS::LibXML::ElementSpec'),
115             };
116             }
117              
118             sub parse_dom
119             {
120 0     0 0   my $self = shift;
121 0           my $c = shift;
122 0           my $dom = shift;
123              
124 0           $c->reset;
125 0           $c->version('2.0');
126 0           $c->encoding($dom->encoding);
127 0           $self->parse_base($c, $dom);
128 0           $self->parse_namespaces($c, $dom);
129 0           $self->parse_channel($c, $dom);
130 0           $self->parse_items($c, $dom);
131 0           $self->parse_misc_simple($c, $dom);
132             }
133              
134              
135             sub parse_channel
136             {
137 0     0 0   my ($self, $c, $dom) = @_;
138              
139 0           my $xc = $c->create_xpath_context($c->{namespaces});
140              
141 0           my ($root) = $xc->findnodes('/rss/channel', $dom);
142 0           my %h = $self->parse_children($c, $root, './*[name() != "item"]');
143              
144 0           foreach my $type (qw(day hour)) {
145 0           my $field = 'skip' . ucfirst($type) . 's';
146 0 0         if (my $skip = delete $h{$field}) {
147 0 0         if (ref $skip ne 'HASH') {
    0          
148             # warn "field $field has invalid entry (does this RSS validate?)";
149             } elsif (! UNIVERSAL::isa($skip, 'XML::RSS::LibXML::ElementSpec')) {
150 0 0         $c->$field(UNIVERSAL::isa($skip, 'XML::RSS::LibXML::MagicElement') ? $skip : %$skip);
151             }
152             }
153             }
154              
155 0           foreach my $field (qw(textinput image)) {
156 0 0         if (my $v = $h{$field}) {
157 0 0         if (ref $v ne 'HASH') {
    0          
158             # warn "field $field has invalid entry (does this RSS validate?)";
159             } elsif (! UNIVERSAL::isa($v, 'XML::RSS::LibXML::ElementSpec')) {
160 0 0         $c->$field(UNIVERSAL::isa($v, 'XML::RSS::LibXML::MagicElement') ? $v : %$v);
161             }
162             }
163             }
164 0           $c->channel(%h);
165             }
166              
167             sub parse_items
168             {
169 0     0 0   my ($self, $c, $dom) = @_;
170 0           my @items;
171 0           my $version = $c->version;
172 0           my $xc = $c->create_xpath_context($c->{namespaces});
173 0           my $xpath = '/rss/channel/item';
174 0           foreach my $item ($xc->findnodes($xpath, $dom)) {
175 0           my $i = $self->parse_children($c, $item);
176 0           $self->add_item($c, $i);
177             }
178             }
179              
180             sub parse_misc_simple
181             {
182 0     0 0   my ($self, $c, $dom) = @_;
183              
184 0           my $xc = $c->create_xpath_context($c->{namespaces});
185 0           foreach my $node ($xc->findnodes('/rss/*[name() != "channel" and name() != "item"]', $dom)) {
186 0           my $h = $self->parse_children($c, $node);
187 0           my $name = $node->localname;
188 0           my $prefix = $node->getPrefix();
189              
190 0 0         $name = 'textinput' if $name eq 'textInput';
191              
192 0 0         if ($prefix) {
193 0   0       $c->{$prefix} ||= {};
194 0           $self->store_element($c->{$prefix}, $name, $h);
195              
196             # XML::RSS requires us to allow access to elements both from
197             # the prefix and the namespace
198 0   0       $c->{$c->{namespaces}{$prefix}} ||= {};
199 0           $self->store_element($c->{$c->{namespaces}{$prefix}}, $name, $h);
200             } else {
201 0           $self->store_element($c, $name, $h);
202             }
203             }
204             }
205              
206             sub create_dom
207             {
208 0     0 0   my ($self, $c) = @_;
209              
210 0           my $dom = $self->SUPER::create_dom($c);
211 0           my $root = $dom->getDocumentElement();
212 0           my $xc = $c->create_xpath_context(scalar $c->namespaces);
213 0           my($channel) = $xc->findnodes('/rss/channel', $dom);
214              
215 0 0         if (my $image = $c->image) {
216 0           my $inode = $dom->createElement('image');
217 0           $self->create_element_from_spec($image, $dom, $inode, \%ImageElements);
218 0           $self->create_extra_modules($image, $dom, $inode, $c->namespaces);
219 0           $channel->appendChild($inode);
220             }
221              
222 0 0         if (my $textinput = $c->textinput) {
223 0           my $inode = $dom->createElement('textInput');
224 0           $self->create_element_from_spec($textinput, $dom, $inode, \%TextInputElements);
225 0           $self->create_extra_modules($textinput, $dom, $inode, $c->namespaces);
226 0           $channel->appendChild($inode);
227             }
228              
229 0           return $dom;
230             }
231              
232             sub create_rootelement
233             {
234 0     0 0   my ($self, $c, $dom) = @_;
235 0           my $root = $dom->createElement('rss');
236 0           $root->setAttribute(version => '2.0');
237 0 0         if (my $base = $c->base) {
238 0           $root->setAttribute('xml:base' => $base);
239             }
240 0           $dom->setDocumentElement($root);
241             }
242              
243             sub create_channel
244             {
245 0     0 0   my ($self, $c, $dom) = @_;
246              
247 0           my $root = $dom->getDocumentElement();
248 0           my $channel = $dom->createElement('channel');
249              
250 0           $self->create_element_from_spec($c->channel, $dom, $channel, \%ChannelElements);
251              
252 0           foreach my $type (qw(day hour)) {
253 0           my $field = 'skip' . ucfirst($type) . 's';
254 0           my $skip = $c->$field;
255 0 0 0       if ($skip && defined $skip->{$type}) {
256 0           my $sd = $dom->createElement($field);
257 0           my $d = $dom->createElement($type);
258 0           $d->appendChild($dom->createTextNode($skip->{$type}));
259 0           $sd->appendChild($d);
260 0           $channel->appendChild($sd);
261             }
262             }
263 0           $root->appendChild($channel);
264             }
265              
266             sub create_items
267             {
268 0     0 0   my ($self, $c, $dom) = @_;
269              
270 0           my ($channel) = $dom->findnodes('/rss/channel');
271 0           foreach my $i ($c->items) {
272 0           my $item = $dom->createElement('item');
273 0           $self->create_element_from_spec($i, $dom, $item, \%ItemElements);
274 0           $self->create_extra_modules($i, $dom, $item, $c->namespaces);
275 0           my $guid = $i->{guid};
276 0 0         if (defined $guid) {
277 0           my $guid_element = $dom->createElement('guid');
278 0 0         if (eval { $guid->isa('XML::RSS::LibXML::MagicElement') }) {
  0            
279 0           my $isperma = 'true';
280 0 0 0       if (! $guid->{isPermaLink} || $guid->{isPermaLink} ne 'true') {
281 0           $isperma = 'false';
282             }
283 0           $guid_element->setAttribute(isPermaLink => $isperma);
284 0           $guid_element->appendChild($dom->createTextNode($guid->toString));
285             } else {
286 0           $guid_element->setAttribute(isPermaLink => "false");
287 0           $guid_element->appendChild($dom->createTextNode($guid));
288             }
289 0           $item->appendChild($guid_element);
290             }
291              
292 0           $channel->appendChild($item);
293             }
294             }
295              
296             1;