blib/lib/Slash/OurNet.pm | |||
---|---|---|---|
Criterion | Covered | Total | % |
statement | 63 | 66 | 95.4 |
branch | 1 | 2 | 50.0 |
condition | 1 | 6 | 16.6 |
subroutine | 22 | 23 | 95.6 |
pod | n/a | ||
total | 87 | 97 | 89.6 |
line | stmt | bran | cond | sub | pod | time | code |
---|---|---|---|---|---|---|---|
1 | package Slash::OurNet; | ||||||
2 | 1 | 1 | 7032 | use 5.006; | |||
1 | 4 | ||||||
1 | 58 | ||||||
3 | |||||||
4 | our $VERSION = '1.41'; | ||||||
5 | |||||||
6 | 1 | 1 | 5 | use strict; | |||
1 | 2 | ||||||
1 | 38 | ||||||
7 | 1 | 1 | 6 | use warnings; | |||
1 | 5 | ||||||
1 | 49 | ||||||
8 | 1 | 1 | 5 | no warnings qw(once redefine); | |||
1 | 1 | ||||||
1 | 35 | ||||||
9 | |||||||
10 | 1 | 1 | 934 | use Date::Parse; | |||
1 | 9975 | ||||||
1 | 166 | ||||||
11 | 1 | 1 | 1059 | use Date::Format; | |||
1 | 3435 | ||||||
1 | 76 | ||||||
12 | 1 | 1 | 10 | use File::Spec; | |||
1 | 1 | ||||||
1 | 21 | ||||||
13 | 1 | 1 | 9 | use File::Basename; | |||
1 | 2 | ||||||
1 | 124 | ||||||
14 | 1 | 1 | 1112 | use Lingua::ZH::Wrap; | |||
1 | 22854 | ||||||
1 | 205 | ||||||
15 | |||||||
16 | 1 | 33 | 1 | 11 | use constant PATH => File::Spec->rel2abs(dirname($ENV{SCRIPT_FILENAME} or $0)); | ||
1 | 2 | ||||||
1 | 191 | ||||||
17 | 1 | 1 | 7 | use constant SLASH => $ENV{SLASH_USER}; | |||
1 | 2 | ||||||
1 | 62 | ||||||
18 | |||||||
19 | 1 | 1 | 1052 | use lib PATH . '/lib'; | |||
1 | 4100 | ||||||
1 | 9 | ||||||
20 | 1 | 1 | 167 | use base 'Locale::Maketext'; | |||
1 | 2 | ||||||
1 | 1973 | ||||||
21 | use Locale::Maketext::Lexicon { | ||||||
22 | 1 | 14 | en => [ Gettext => PATH . '/po/en.po' ], | ||||
23 | zh_tw => [ Gettext => PATH . '/po/zh_tw.po' ], | ||||||
24 | zh_cn => [ Gettext => PATH . '/po/zh_cn.po' ], | ||||||
25 | 1 | 1 | 24660 | }; | |||
1 | 6193 | ||||||
26 | |||||||
27 | 1 | 1 | 10768 | use if SLASH, 'Slash'; | |||
1 | 22 | ||||||
1 | 14 | ||||||
28 | 1 | 1 | 66 | use if SLASH, 'Slash::DB'; | |||
1 | 3 | ||||||
1 | 4 | ||||||
29 | 1 | 1 | 39 | use if SLASH, 'Slash::Display'; | |||
1 | 2 | ||||||
1 | 4 | ||||||
30 | 1 | 1 | 43 | use if SLASH, 'base' => qw(Slash::DB::Utility Slash::DB::MySQL); | |||
1 | 2 | ||||||
1 | 4 | ||||||
31 | 1 | 1 | 39 | use if !SLASH, 'Slash::OurNet::Standalone'; | |||
1 | 2 | ||||||
1 | 5 | ||||||
32 | Slash::OurNet::Standalone->import unless SLASH; | ||||||
33 | |||||||
34 | $Lingua::ZH::Wrap::columns = 75; | ||||||
35 | $OurNet::BBS::Client::NoCache = 1; # avoids bloat | ||||||
36 | |||||||
37 | our %Lexicon = ( _AUTO => 1 ); | ||||||
38 | our ($TopClass, $MailBox, $Organization, @Connection, $SecretSigils, | ||||||
39 | $BoardPrefixLength, $GroupPrefixLength, $Strip_ANSI, $Use_RealEmail, | ||||||
40 | $Thread_Prev, $Date_Prev, $Thread_Next, $Date_Next, $Language, $Colors, | ||||||
41 | $DefaultUser, %CachedTop, $LanguageHandle, $SourceEncoding, $Theme, | ||||||
42 | $TrappedExcept, $ALLBBS); | ||||||
43 | |||||||
44 | sub loc { | ||||||
45 | 1 | 1 | 399 | use Encode; | |||
1 | 2 | ||||||
1 | 172 | ||||||
46 | 0 | 0 | 0 | return decode_utf8(($LanguageHandle ||= __PACKAGE__->get_handle($Language))->maketext(@_)); | |||
47 | } | ||||||
48 | |||||||
49 | 1 | 50 | 1 | 45 | BEGIN { do(PATH . '/ournet.conf'); die $@ if $@ } | ||
1 | 36 | ||||||
50 | |||||||
51 | 1 | 1 | 1946 | use OurNet::BBS $SourceEncoding; | |||
0 | |||||||
0 | |||||||
52 | |||||||
53 | sub new { | ||||||
54 | return unless @_; # to satisfy pudge's automation scripts | ||||||
55 | my ($class, $name) = splice(@_, 0, 2); | ||||||
56 | |||||||
57 | no warnings 'once'; | ||||||
58 | my $self = { | ||||||
59 | bbs => OurNet::BBS->new(@_), | ||||||
60 | virtual_user => $name, | ||||||
61 | }; | ||||||
62 | |||||||
63 | return bless($self, $class); | ||||||
64 | } | ||||||
65 | |||||||
66 | sub article_save { | ||||||
67 | my ($self, $group, $board, $child, $artid, $reply, | ||||||
68 | $title, $body, $state, $name, $nick) = @_; | ||||||
69 | |||||||
70 | $child ||= 'articles'; | ||||||
71 | my $artgrp = $self->{bbs}{boards}{$board}; | ||||||
72 | |||||||
73 | $body = ($body); | ||||||
74 | |||||||
75 | # honor 75-column tradition of legacy BBS systems | ||||||
76 | $body = wrap('','', $body) if $body and length($body) > 75; | ||||||
77 | |||||||
78 | no warnings 'uninitialized'; | ||||||
79 | my $offset = sprintf("%+0.4d", getCurrentUser('off_set') / 36); | ||||||
80 | $offset =~ s/([1-9][0-9]|[0-9][1-9])$/$1 * 0.6/e; | ||||||
81 | |||||||
82 | if ($Use_RealEmail and SLASH) { | ||||||
83 | $name = getCurrentUser('realemail'); | ||||||
84 | $nick = getCurrentUser('nickname'); | ||||||
85 | } | ||||||
86 | |||||||
87 | # we could ignore the $reply until a Reply-To header is supported | ||||||
88 | my $article = { | ||||||
89 | header => { | ||||||
90 | From => "$name ($nick)", | ||||||
91 | Subject => $title || '', | ||||||
92 | Board => $board, | ||||||
93 | Date => timeCalc( | ||||||
94 | scalar localtime, "%a %b %e %H:%M:%S $offset %Y" | ||||||
95 | ), | ||||||
96 | }, | ||||||
97 | body => $body || '', | ||||||
98 | }; | ||||||
99 | |||||||
100 | my $error; # error message | ||||||
101 | |||||||
102 | $error .= loc('Please enter a subject. ') |
||||||
103 | unless (length($article->{header}{Subject})); | ||||||
104 | $error = ' ' unless $state; | ||||||
105 | |||||||
106 | $PerlIO::via::trap::PASS = 1 if $TrappedExcept eq 'post'; | ||||||
107 | $artgrp->{articles}{$artid || ''} = $article unless $error; | ||||||
108 | $PerlIO::via::trap::PASS = 0 if $TrappedExcept eq 'post'; | ||||||
109 | |||||||
110 | return ($article, $error); | ||||||
111 | } | ||||||
112 | |||||||
113 | sub article { | ||||||
114 | my ($self, $group, $board, $child, $artid, $reply) = @_; | ||||||
115 | my (@related, $artgrp, $is_reply); | ||||||
116 | |||||||
117 | # put $reply to $name and set flag for further processing | ||||||
118 | $is_reply++ if !defined($artid) and defined($artid = $reply); | ||||||
119 | return unless defined $artid; # happens when a new article's made | ||||||
120 | |||||||
121 | $child ||= 'articles'; | ||||||
122 | $artgrp = $self->{bbs}{($child eq 'mailbox') ? 'users' : 'boards'}{$board}; | ||||||
123 | |||||||
124 | foreach my $chunk (split('/', $child)) { | ||||||
125 | $artgrp = ($chunk =~ /^\d+$/ ? $artgrp->[$chunk] : $artgrp->{$chunk}); | ||||||
126 | } | ||||||
127 | |||||||
128 | # number OR name | ||||||
129 | my $article = ($artid =~ /^\d+$/ ? $artgrp->[$artid] : $artgrp->{$artid}); | ||||||
130 | |||||||
131 | my $related = ($is_reply || $child =~ m/^archives/) ? [] : $self->related_articles( | ||||||
132 | [ group => $group, board => $board, child => $child ], | ||||||
133 | $artgrp, $article, | ||||||
134 | ); # do not calculate related article during reply | ||||||
135 | |||||||
136 | return ($self->mapArticle( | ||||||
137 | $group, $board, $child, $artid, $article, $is_reply | ||||||
138 | ), $related); | ||||||
139 | } | ||||||
140 | |||||||
141 | sub related_articles { | ||||||
142 | my ($self, $params, $artgrp, $article) = @_; | ||||||
143 | return unless $article; | ||||||
144 | |||||||
145 | my $header = $article->{header}; | ||||||
146 | my $recno = $article->recno; | ||||||
147 | my $size = $#{$artgrp}; | ||||||
148 | my $title = $header->{Subject}; | ||||||
149 | my $related = []; | ||||||
150 | |||||||
151 | $title = "Re: $title" unless substr($title, 0, 4) eq 'Re: '; | ||||||
152 | |||||||
153 | my %cache; | ||||||
154 | |||||||
155 | # grepping for thread_prev | ||||||
156 | if ($Thread_Prev) { foreach my $i (reverse(($recno - 5) .. ($recno - 1))) { | ||||||
157 | next if $i < 0; | ||||||
158 | my $art = $artgrp->[$i]; | ||||||
159 | my $title2 = $art->{header}->{Subject}; | ||||||
160 | next unless $title eq $title2 or $title eq "Re: $title2"; | ||||||
161 | pushy(\%cache, $related, $params, $Thread_Prev, $art); | ||||||
162 | last; | ||||||
163 | } } | ||||||
164 | |||||||
165 | pushy(\%cache, $related, $params, $Date_Prev, $artgrp->[$recno - 1]) | ||||||
166 | if $Date_Prev and $recno; | ||||||
167 | |||||||
168 | if ($Thread_Next) { foreach my $i (($recno + 1) .. ($recno + 5)) { | ||||||
169 | next if $i > $size - 1; | ||||||
170 | my $art = $artgrp->[$i]; | ||||||
171 | my $title2 = $art->{header}{Subject}; | ||||||
172 | next unless $title eq $title2 or $title eq "Re: $title2"; | ||||||
173 | pushy(\%cache, $related, $params, $Thread_Next, $art); | ||||||
174 | last; | ||||||
175 | } } | ||||||
176 | |||||||
177 | pushy(\%cache, $related, $params, $Date_Next, $artgrp->[$recno + 1]) | ||||||
178 | if $Date_Next and $recno < $size - 1; | ||||||
179 | |||||||
180 | return $related; | ||||||
181 | } | ||||||
182 | |||||||
183 | sub pushy { | ||||||
184 | my ($cache, $self, $params, $relation, $art) = @_; | ||||||
185 | return unless defined $art; | ||||||
186 | |||||||
187 | my $name = $art->name; | ||||||
188 | return if $cache->{$name}++; | ||||||
189 | my $header = $art->{header}; | ||||||
190 | my $author = $art->{author}; | ||||||
191 | $author =~ s/(?:\.\.?bbs)?\@.+/\./; | ||||||
192 | |||||||
193 | push @{$self}, { | ||||||
194 | @{$params}, | ||||||
195 | relation => $relation, name => $name, header => $header, | ||||||
196 | author => $author | ||||||
197 | } unless $params->[5] ne 'articles' and $art->REF =~ /Group/; | ||||||
198 | } | ||||||
199 | |||||||
200 | sub board { | ||||||
201 | my ($self, $group, $board, $child, $begin) = @_; | ||||||
202 | my ($artgrp, $bm, $title, $etc); | ||||||
203 | |||||||
204 | my $PageSize = 20; | ||||||
205 | if ($child eq 'mailbox') { | ||||||
206 | $artgrp = $self->{bbs}{users}{$board}; | ||||||
207 | $bm = $board; | ||||||
208 | $title = $MailBox; | ||||||
209 | $etc = ''; | ||||||
210 | } | ||||||
211 | else { | ||||||
212 | $artgrp = $self->{bbs}{boards}{$board}; | ||||||
213 | $bm = $artgrp->{bm}; | ||||||
214 | $title = $artgrp->{title}; | ||||||
215 | if ($etc = $artgrp->{etc_brief}) { | ||||||
216 | $etc = (split(/\n\n+/, $etc, 2))[1]; | ||||||
217 | $etc =~ s/\x1b\[[\d\;]*m//g; | ||||||
218 | $etc =~ s/\n+/ /g; |
||||||
219 | } | ||||||
220 | } | ||||||
221 | |||||||
222 | die "no such board" unless $artgrp; | ||||||
223 | return unless $artgrp; | ||||||
224 | |||||||
225 | die "permission denied" | ||||||
226 | if $child ne 'mailbox' and $SecretSigils and | ||||||
227 | index(substr($artgrp->{title}, 0, index($artgrp->{title}, ' ')), $SecretSigils) > -1; | ||||||
228 | |||||||
229 | foreach my $chunk (split('/', $child)) { | ||||||
230 | $artgrp = $artgrp->{$chunk}; | ||||||
231 | } | ||||||
232 | |||||||
233 | die "permission denied" | ||||||
234 | if $artgrp->can('readlevel') and $artgrp->readlevel and $artgrp->readlevel ne 4294967295; | ||||||
235 | |||||||
236 | my $reversed = ($child eq 'articles' or $child eq 'mailbox'); | ||||||
237 | my $size = eval { $#{$artgrp} } || 0; | ||||||
238 | |||||||
239 | $begin = $reversed ? ($size - $PageSize + 1) : 0 | ||||||
240 | unless defined $begin; | ||||||
241 | |||||||
242 | my @pages; | ||||||
243 | |||||||
244 | foreach my $page (1..(int($size / $PageSize)+1)) { | ||||||
245 | my $thisbegin = $reversed | ||||||
246 | ? ($size - ($page * $PageSize) + 1) | ||||||
247 | : (($page - 1) * $PageSize + 1); | ||||||
248 | my $iscurpage = ($thisbegin == $begin); | ||||||
249 | push @pages, { | ||||||
250 | number => $page, | ||||||
251 | begin => $thisbegin, | ||||||
252 | iscurpage => $iscurpage, | ||||||
253 | }; | ||||||
254 | } | ||||||
255 | |||||||
256 | $size = $begin + $PageSize - 1 if ($begin + $PageSize - 1 <= $size); | ||||||
257 | $begin = 0 if $begin < 0; | ||||||
258 | |||||||
259 | my $message = "| $board | ". | ||||||
260 | (($artgrp->name or $child eq 'mailbox') | ||||||
261 | ? $title : substr($title, $BoardPrefixLength)). | ||||||
262 | " | $bm | "; |
||||||
263 | $message .= $etc if defined $etc; | ||||||
264 | |||||||
265 | my @range = $reversed | ||||||
266 | ? reverse ($begin .. $size) : ($begin.. $size); | ||||||
267 | |||||||
268 | local $_; | ||||||
269 | return ($message, ($#pages ? \@pages : undef), $self->mapArticles( | ||||||
270 | $group, $board, $child, \@range, | ||||||
271 | map { eval { $artgrp->[$_] } || 0 } @range | ||||||
272 | )); | ||||||
273 | } | ||||||
274 | |||||||
275 | sub group { | ||||||
276 | my ($self, $group, $board) = @_; | ||||||
277 | my $boards; | ||||||
278 | |||||||
279 | $self->{bbs}{groups}->toplevel($TopClass); | ||||||
280 | |||||||
281 | if ($board eq $TopClass) { | ||||||
282 | $boards = $self->{bbs}{groups}; | ||||||
283 | } | ||||||
284 | else { | ||||||
285 | # $group =~ s|^\Q$TopClass\E/?||; | ||||||
286 | $boards = ($group =~ m!^\Q$TopClass\E/?$!) | ||||||
287 | ? $self->{bbs}{groups}{$board} | ||||||
288 | : $self->{bbs}{groups}{$group}{$board}; | ||||||
289 | } | ||||||
290 | |||||||
291 | my ($thisgroup, $title, $bm, $etc); | ||||||
292 | |||||||
293 | if ($board eq $TopClass) { | ||||||
294 | $bm = 'SYSOP'; | ||||||
295 | $title = 'All Boards'; | ||||||
296 | } | ||||||
297 | elsif ($title = $boards->{title}) { | ||||||
298 | $bm = $boards->{bm}; # XXX! | ||||||
299 | $bm = $boards->{owner}; | ||||||
300 | $etc = $self->{bbs}{boards}{$board}{etc_brief} | ||||||
301 | if exists $self->{bbs}{boards}{$board}; | ||||||
302 | } | ||||||
303 | |||||||
304 | local $_; | ||||||
305 | $title ||= ''; | ||||||
306 | my $title2 = substr($title, $GroupPrefixLength); | ||||||
307 | $title2 =~ s|^[^/]+/\s+||; # XXX: melix special case | ||||||
308 | |||||||
309 | my $message = "| $board | ". | ||||||
310 | ( $title2 || $Organization) . | ||||||
311 | ($bm ? " | $bm | " : ' | '); |
||||||
312 | $message .= $etc if defined $etc; | ||||||
313 | |||||||
314 | # $boards->refresh; | ||||||
315 | |||||||
316 | return ($self->mapBoards( | ||||||
317 | "$group/$board", | ||||||
318 | map { | ||||||
319 | $boards->{$_} | ||||||
320 | } sort { | ||||||
321 | uc($a) cmp uc($b) | ||||||
322 | } grep { | ||||||
323 | $_ !~ /^(?:owner|id|title)$/ | ||||||
324 | } keys (%{$boards}), | ||||||
325 | ), $message); | ||||||
326 | } | ||||||
327 | |||||||
328 | sub top { | ||||||
329 | my $self = shift; | ||||||
330 | |||||||
331 | # XXX kludge! | ||||||
332 | my $top = $self->{bbs}{files}{'@-day'} || $self->{bbs}{files}{day}; | ||||||
333 | my $brds = $self->{bbs}{boards}; | ||||||
334 | my @ret; | ||||||
335 | |||||||
336 | if (($self->{top} || '') eq $top) { | ||||||
337 | @ret = $CachedTop{"@Connection"}; | ||||||
338 | } | ||||||
339 | else { while ( | ||||||
340 | $top =~ s/^.*?32m([^\s]+).*?33m\s*([^\s]+)\n.*?37m\s*([^\x1b]+?)\x20*\x1b//m | ||||||
341 | ) { | ||||||
342 | my ($board, $author, $title) = ($1, $2, $3); | ||||||
343 | my $artgrp = $brds->{$board}{articles}; | ||||||
344 | |||||||
345 | foreach my $art (reverse(0..$#{$artgrp})) { | ||||||
346 | my $article = $artgrp->[$art]; | ||||||
347 | next unless ($article->{title} eq $title); | ||||||
348 | push @ret, $article; | ||||||
349 | last; | ||||||
350 | } | ||||||
351 | } | ||||||
352 | $CachedTop{"@Connection"} = \@ret; | ||||||
353 | $self->{top} = $top; | ||||||
354 | } | ||||||
355 | |||||||
356 | return $self->mapArticles($TopClass, '', 'articles', [], @ret); | ||||||
357 | } | ||||||
358 | |||||||
359 | sub mapArticles { | ||||||
360 | my ($self, $group, $board, $child, $range) = splice(@_, 0, 5); | ||||||
361 | |||||||
362 | local $_; | ||||||
363 | return [ map { | ||||||
364 | my $recno = shift(@{$range}); | ||||||
365 | my ($type, $title, $date, $author, $board, $artid); | ||||||
366 | |||||||
367 | if (UNIVERSAL::isa($_, 'UNIVERSAL')) { | ||||||
368 | $type = ($_->REF =~ /Group/) ? 'group' : 'article'; | ||||||
369 | $title = $_->{title}; | ||||||
370 | $title =~ s/\x1b\[[\d\;]*m//g; | ||||||
371 | $date = $_->{date}, | ||||||
372 | $author = $_->{author}; | ||||||
373 | $author =~ s/(?:\.bbs)?\@.+//; | ||||||
374 | $board = $board || $_->board; | ||||||
375 | $artid = $_->name; | ||||||
376 | } | ||||||
377 | else { # deleted article | ||||||
378 | $type = 'deleted'; | ||||||
379 | $title = loc('<< This article has been deleted >>'); | ||||||
380 | $board = $board; | ||||||
381 | $author = ' '; | ||||||
382 | $date = ' '; | ||||||
383 | } | ||||||
384 | |||||||
385 | { | ||||||
386 | title => $title, | ||||||
387 | child => $child, | ||||||
388 | group => $group, | ||||||
389 | type => $type, | ||||||
390 | date => $date, | ||||||
391 | author => $author, | ||||||
392 | board => $board, | ||||||
393 | name => $artid, | ||||||
394 | recno => $recno, | ||||||
395 | articles_count => $type eq 'group' ? $#{$_} : 1, | ||||||
396 | } | ||||||
397 | } @_ ]; | ||||||
398 | } | ||||||
399 | |||||||
400 | sub mapArticle { | ||||||
401 | my ($self, $group, $board, $child, $artid, $article, $is_reply) = @_; | ||||||
402 | my $header = { %{$article->{header} || {}} }; | ||||||
403 | my $title = $header->{Subject} || '(untitled)'; | ||||||
404 | $header->{Subject} =~ s/\x1b\[[\d\;]*m//g; | ||||||
405 | |||||||
406 | return { | ||||||
407 | body => txt2html($article, $is_reply), | ||||||
408 | header => $header, | ||||||
409 | title => $title, | ||||||
410 | board => $board, | ||||||
411 | group => $group, | ||||||
412 | child => $child, | ||||||
413 | name => $artid, | ||||||
414 | }; | ||||||
415 | } | ||||||
416 | |||||||
417 | sub mapBoards { | ||||||
418 | my $self = shift; | ||||||
419 | my $group = shift; | ||||||
420 | |||||||
421 | my (@group, @board); | ||||||
422 | local $_; | ||||||
423 | no strict 'refs'; | ||||||
424 | |||||||
425 | foreach (@_) { | ||||||
426 | my $type = 'board'; | ||||||
427 | my $board; | ||||||
428 | my $etc; | ||||||
429 | my ($title, $date, $bm); | ||||||
430 | |||||||
431 | if ($_->REF =~ /Group$/) { | ||||||
432 | $title = $_->{title} or next; | ||||||
433 | |||||||
434 | $board = $1 if $title =~ s|^([^/]+)/\s+||; | ||||||
435 | $bm = $_->{owner}; | ||||||
436 | $bm = '' if $bm =~ /\W/; # XXX melix 0.8 bug | ||||||
437 | |||||||
438 | $type = 'group'; | ||||||
439 | } | ||||||
440 | else { | ||||||
441 | $board = $_->board or next; | ||||||
442 | if ($etc = $_->{etc_brief}) { | ||||||
443 | $etc = (split(/\n\n+/, $etc, 2))[1]; | ||||||
444 | $etc =~ s/\n+/\n/g; | ||||||
445 | } | ||||||
446 | $bm = $_->{bm}, | ||||||
447 | $title = substr($_->{title}, $BoardPrefixLength) or next; | ||||||
448 | } | ||||||
449 | |||||||
450 | next if $SecretSigils and index(substr($_->{title}, 0, index($_->{title}, ' ')), $SecretSigils) > -1; | ||||||
451 | |||||||
452 | next if $TopClass and $board eq $TopClass; | ||||||
453 | |||||||
454 | my $entry = { | ||||||
455 | title => $title, | ||||||
456 | bm => $bm, | ||||||
457 | etc_brief => $etc, | ||||||
458 | group => $group, | ||||||
459 | board => $board, | ||||||
460 | type => $type, | ||||||
461 | # archives_count => ( | ||||||
462 | # ($type eq 'group') ? '' : $#{$_->{archives}} | ||||||
463 | # ), | ||||||
464 | # articles_count => ( | ||||||
465 | # ($type eq 'group') ? ' ' : $#{$_->{articles}} | ||||||
466 | # ), | ||||||
467 | }; | ||||||
468 | |||||||
469 | if ($type eq 'group') { | ||||||
470 | push @group, $entry; | ||||||
471 | } | ||||||
472 | else { | ||||||
473 | push @board, $entry; | ||||||
474 | } | ||||||
475 | } | ||||||
476 | |||||||
477 | return [ @group, @board ]; | ||||||
478 | } | ||||||
479 | |||||||
480 | sub txt2html { | ||||||
481 | my ($article, $is_reply) = @_; | ||||||
482 | |||||||
483 | # reply mode decorations | ||||||
484 | my $body = $article->{body}; | ||||||
485 | |||||||
486 | if ($is_reply) { | ||||||
487 | $body =~ s/^(.+)\n+--+\n.+/$1/sg; | ||||||
488 | $body =~ s/\n+/\n: /g; | ||||||
489 | $body =~ s/\n: : : .*//g; | ||||||
490 | $body =~ s/\n: : ※ .*//g; | ||||||
491 | $body =~ s/: \n+/\n/g; | ||||||
492 | $body = sprintf(loc("*) %s wrote:")."\n: %s", $article->{header}{From}, $body); | ||||||
493 | } | ||||||
494 | elsif ($Strip_ANSI) { | ||||||
495 | require HTML::FromText; | ||||||
496 | |||||||
497 | $body =~ s/\x1b\[.*?[mJH]//g; | ||||||
498 | $body = HTML::FromText::text2html( | ||||||
499 | $body, | ||||||
500 | metachars => 1, urls => 1, | ||||||
501 | email => 1, underline => 1, | ||||||
502 | lines => 1, spaces => 1, | ||||||
503 | ); | ||||||
504 | |||||||
505 | $body =~ s/ | ||||||
506 | $body =~ s/<\/TT>//g; | ||||||
507 | |||||||
508 | $body = << "."; | ||||||
509 | |||||||
510 | $body | ||||||
511 | |||||||
512 | . | ||||||
513 | } | ||||||
514 | else { | ||||||
515 | require HTML::FromANSI; | ||||||
516 | $body = HTML::FromANSI::ansi2html($body); | ||||||
517 | } | ||||||
518 | |||||||
519 | return $body; | ||||||
520 | } | ||||||
521 | |||||||
522 | 1; | ||||||
523 | |||||||
524 | __END__ |