OSDN Git Service

add faq pages and fix wiki-based contents CSS
[newslash/newslash.git] / src / newslash_web / lib / Newslash / Plugin / WikiContentsReader.pm
1 package Newslash::Plugin::WikiContentsReader;
2 use Mojo::Base 'Mojolicious::Plugin';
3
4 use LWP::UserAgent;
5 use HTTP::Request;
6 use HTTP::Headers;
7 use HTML::Filter;
8 use XML::RSS;
9 use File::Path;
10 use File::Basename;
11 use HTTP::Date;
12
13 has "app";
14
15 =encoding utf8
16
17 =head1 NAME
18
19 Newslash::Plugin::WikiContentsReader - Read Wiki Content
20
21 =head1 SYNOPSIS
22
23   # Mojolicious
24   $app->plugin('Newslash::Plugin::WikiContentsReaders');
25
26 =head1 DESCRIPTION
27
28 L<Newslash::Plugin::WikiContentsReaders> is a helper plugin to
29 read wiki content from osdn.jp.
30
31
32 =head1 METHODS
33
34 =head2 register
35
36   $plugin->register(Mojolicious->new);
37
38 Register helpers in L<Mojolicious> application.
39
40 =cut
41
42 sub register {
43     my ($self, $app, $conf) = @_;
44     $self->app($app);
45
46     $app->helper(wiki_reader => sub { state $wiki_reader = $self });
47 }
48
49 =head1 HELPERS
50
51 L<Mojolicious::Plugin::WikiContentsReaders> implements the following helpers.
52
53 =cut
54
55 sub _read {
56     my ($self, $page_name) = @_;
57
58     my $cf = $self->app->config;
59     my $config = $cf->{WikiContents} ||= {};
60     my $con_timeout = $config->{connect_timeout} //= 10;
61     my $req_timeout = $config->{request_timeout} //= 10;
62     my $max_redirects = $config->{max_redirects} //= 10;
63
64     my $http_proxy = $config->{http_proxy};
65     my $https_proxy = $config->{https_proxy};
66
67     my $project = $config->{project_name} //= "";
68     my $wiki_url = "http://osdn.jp/projects/$project/wiki";
69     my $wiki_url_regex = "https?://[0-9a-zA-Z.]*osdn.(jp|net)/projects/$project/wiki/";
70     my $page_url = "$wiki_url/$page_name";
71
72     if (!$project) {
73         $self->app->log->error("WikiContentsReader::_read: no project given.");
74         return;
75     }
76
77     # get content
78     my $ua  = Mojo::UserAgent->new;
79     $ua->connect_timeout($con_timeout);
80     $ua->request_timeout($req_timeout);
81     $ua->max_redirects($max_redirects);
82     $ua->proxy->http($http_proxy) if $http_proxy;
83     $ua->proxy->https($https_proxy) if $https_proxy;
84
85
86     my $tx = $ua->get($page_url);
87     if (!$tx) {
88         $self->app->log->error("WikiContentsReader::_read: get '$page_name' failed.");
89         return;
90     }
91     if ($tx->res->code != 200) {
92         my $code = $tx->res->code;
93         $self->app->log->error("WikiContentsReader::_read: get '$page_name' failed. code: $code");
94         return;
95     }
96
97     my $html = Encode::decode_utf8($tx->res->body);
98
99     # replace anchor URL
100     my $p = Newslash::Plugin::WikiContentsReader::Filter->new;
101     $p->set_replace_url($wiki_url_regex, '/');
102     $p->parse($html);
103
104     my $content = { body => $p->filtered_html,
105                     title => $p->{title},
106                     page_name => $page_name,
107                   };
108
109     if (!$content->{title} && $html =~ m!<title>([^<]+)</title>!si) {
110         $content->{title} = $1;
111     }
112
113     $content->{body} =~ s/\A.*(<div class=\"wiki-content\">.*)<!-- google_ad_section_end -->.*\z/$1/s;
114     if (!$content->{body}) {
115         return;
116     }
117
118     $content->{body} =~ s/<script type='text\/javascript'>.*?<\/script>//sg;
119     if (!$content->{body}) {
120         return;
121     }
122
123     return $content;
124
125 }
126
127 =head2 read($page_name)
128
129   get content from srad wiki (https://osdn.net/projects/slashdotjp/wiki/)
130
131 =cut
132
133 sub get {
134     my ($self, $page_name) = @_;
135     my $cache_key = "wiki_page";
136     my $content = $self->app->ccache->get($cache_key, $page_name);
137
138     if (defined $content) {
139         return $content;
140     }
141
142     $content = $self->_read($page_name);
143     return if !$content;
144
145     $self->app->ccache->set($cache_key, $page_name, $content);
146     return $content;
147 }
148
149
150
151 # filter for anchor URL replace
152 package Newslash::Plugin::WikiContentsReader::Filter;
153 use Mojo::Base 'HTML::Filter';
154
155 sub set_replace_url {
156     my ($self, $from, $to) = @_;
157     $self->{url_from} = $from;
158     $self->{url_to} = $to;
159 }
160
161 sub get_title {
162     return shift->{title};
163 }
164
165 sub start {
166     my ($self, $tagname, $attrs, $attrnames, $text) = @_;
167
168     if ($tagname eq "a" && $self->{url_from} && $self->{url_to} && $attrs->{href}) {
169         if ($attrs->{href} =~ s/^$self->{url_from}//) {
170             $attrs->{href} =~ s!-!/!;
171             $attrs->{href} = "$self->{url_to}$attrs->{href}";
172             $attrs->{href} =~ s!^/+!/!;
173             $attrs->{href} =~ s!^/faq$!/faq/!;
174         }
175         if ($attrs->{href} !~ m!/attach/!) {
176             $text = "<$tagname " . join(" ", map { "$_=\"$attrs->{$_}\"" } @$attrnames) . ">";
177         }
178     } elsif ($tagname eq "form") {
179         $self->{form_seen}++;
180     } elsif ($tagname eq "h1") {
181         $self->{h1_seen}++;
182     }
183
184     $self->output($text);
185 }
186
187 sub end {
188     my $self = shift @_;
189     my ($tagname, $text) = @_;
190
191     if ($tagname eq "form") {
192         $self->{form_seen}--;
193     } elsif ($tagname eq "h1") {
194         $self->{h1_seen}++;
195     }
196
197     $self->output($text);
198 }
199
200 sub text {
201     my $self = shift @_;
202     my ($text) = @_;
203
204     if ($self->{h1_seen} && int($self->{h1_seen}) == 3) {
205         $self->{title} = $text;
206         $self->{h1_seen}++;
207     }
208
209     $self->output($text);
210 }
211
212 sub output { push(@{$_[0]->{fhtml}}, $_[1]) unless ($_[0]->{form_seen}); }
213 sub filtered_html { join("", @{$_[0]->{fhtml}}) }
214
215 1;
216
217 =head1 SEE ALSO
218
219 L<Mojolicious>, L<Mojolicious::Guides>, L<http://mojolicious.org>.
220
221 =cut