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