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