root/trunk/plagger/lib/Plagger/Plugin/Subscription/OPML.pm

Revision 1500 (checked in by miyagawa, 14 years ago)

rewrite OPML parser using XML::LibXML::SAX. YAY! Fixes #383

  • Property svn:keywords set to Id Revision
Line 
1 package Plagger::Plugin::Subscription::OPML;
2 use strict;
3 use base qw( Plagger::Plugin );
4
5 use Plagger::Util;
6 use URI;
7 use XML::LibXML::SAX;
8
9 our $HAS_LIBERAL;
10 BEGIN {
11     eval { require XML::Liberal; $HAS_LIBERAL = 1 };
12 }
13
14 sub register {
15     my($self, $context) = @_;
16
17     $context->register_hook(
18         $self,
19         'subscription.load' => \&load,
20     );
21 }
22
23 sub load {
24     my($self, $context) = @_;
25     my $uri = URI->new($self->conf->{url})
26         or $context->error("config 'url' is missing");
27
28     $self->load_opml($context, $uri);
29 }
30
31 sub load_opml {
32     my($self, $context, $uri) = @_;
33
34     my $xml = Plagger::Util::load_uri($uri, $self);
35
36     if ($HAS_LIBERAL) {
37         my $parser = XML::Liberal->new('LibXML');
38         my $doc = $parser->parse_string($xml);
39         $xml = $doc->toString;
40     }
41
42     my $handler = Plagger::Plugin::Subscription::OPML::SAXHandler->new(
43         callback => sub { $context->subscription->add(@_) },
44     );
45
46     my $parser  = XML::LibXML::SAX->new(Handler => $handler);
47        $parser->parse_string($xml);
48 }
49
50 package Plagger::Plugin::Subscription::OPML::SAXHandler;
51 use base qw( XML::SAX::Base );
52
53 sub new {
54     my $class = shift;
55     bless { @_ }, $class;
56 }
57
58 sub start_element {
59     my $self = shift;
60     my($ref) = @_;
61
62     if ($ref->{LocalName} eq 'outline') {
63         if (_attr($ref, 'htmlUrl', 'xmlUrl')) {
64             my $feed = Plagger::Feed->new;
65             $feed->url(_attr($ref, 'xmlUrl'));
66             $feed->link(_attr($ref, 'htmlUrl'));
67             $feed->title(_attr($ref, 'title', 'text'));
68             $feed->tags([ grep { defined && $_ ne 'Subscriptions' } @{$self->{containers}} ]);
69             $self->{callback}->($feed);
70         } else {
71             my $tag = _attr($ref, 'title', 'text');
72             push @{$self->{containers}}, $tag;
73         }
74     }
75 }
76
77 sub end_element {
78     my $self = shift;
79     my($ref) = @_;
80
81     if ($ref->{LocalName} eq 'outline') {
82         pop @{$self->{containers}};
83     }
84 }
85
86 sub _attr {
87     my($ref, @attr) = @_;
88
89     for my $attr (@attr) {
90         return $ref->{Attributes}->{"{}$attr"}->{Value}
91             if defined $ref->{Attributes}->{"{}$attr"};
92     }
93
94     return;
95 }
96
97 package Plagger::Plugin::Subscription::OPML;
98
99 1;
100
101 __END__
102
103 =head1 NAME
104
105 Plagger::Plugin::Subscription::OPML - OPML subscription
106
107 =head1 SYNOPSIS
108
109   - module: Subscription::OPML
110     config:
111       url: http://example.com/mySubscriptions.opml
112
113 =head1 DESCRIPTION
114
115 This plugin creates Subscription by fetching remote OPML file by HTTP
116 or locally (with C<file://> URI). It supports nested folder structure
117 of OPML subscription.
118
119 =head1 AUTHOR
120
121 Tatsuhiko Miyagawa
122
123 =head1 SEE ALSO
124
125 L<Plagger>, L<XML::LibXML::SAX>
126
127 =cut
Note: See TracBrowser for help on using the browser.