【しばらく編集不可モードで運営します】 編集(管理者用) | 差分 | 新規作成 | 一覧 | RSS | FrontPage | 検索 | 更新履歴

plugin/rssview.pl - RSSを取得しRecentChangesの書式で表示するプラグイン。

RSSを取得しRecentChangesの書式で表示するプラグイン。

要Jcode、HTTP::Lite、Time::Localモジュール。

use strict;

package rssview;

use Time::Local;
use HTTP::Lite;
use Jcode;

my $subject_delimiter = $main::subject_delimiter;
my $kanjicode = $main::kanjicode;
my $rss_dir = './rss';

$rss_dir = &path_canonical($rss_dir);

sub plugin_block {
    my ($escaped_argument) = @_;
    $escaped_argument =~ s/(?:\x0D\x0A|[\x0D\x0A])?$/,/;
    my ($target, $num) = map {/^"(.*)"$/ ? scalar($_ = $1, s/""/"/g, $_) : $_} ($escaped_argument =~ /("[^"]*(?:""[^"]*)*"|[^,]*),/g);
    my $rss = &fetch($target);
    return '<div class="rssview">('.&main::escape($rss).')</div>' if ($rss =~ /^Error : /);
    my @items = &parse_rss($rss, $num);
    return "<div class=\"rssview\">(no items found)</div>" unless (@items);
    my $result = "<div class=\"rssview\"><ul class=\"rssview\">";
    foreach my $item (@items) {
        next unless ($item->{'link'} and $item->{'title'});
        my $title = sprintf('<a href="%s">%s</a>', $item->{'link'}, $item->{'title'});
        my $date = ($item->{'date'}) ? '<span class="date">'.$item->{'date'}.'</span> ' : '';
        my $desc = ($item->{'description'}) ? $subject_delimiter.$item->{'description'} : '';
        $result .= "\n<li class=\"rssview\">$date$title$desc</li>";
    }
    $result .= "\n</ul>";
    if ($kanjicode eq 'sjis') { $result = jcode($result, 'euc')->sjis; }
    elsif ($kanjicode eq 'utf8') { $result = jcode($result, 'euc')->utf8; }
    return $result;
}

sub fetch {
    my $target = shift;
    my $result = "";
    if ($target =~ /^http:\/\//i) {
        return &fetch_url($target);
    } elsif ($rss_dir) {
        my $path = &path_canonical($target);
        if (index($path, $rss_dir) != 0) {
            $result = "Error : '$path' is not in '$rss_dir'.";
        } elsif (not -f $path) {
            $result = "Error : '$path' is not exist.";
        } else {
            $result = &read_file($path);
        }
    }
    return $result;
}

sub fetch_url {
    my $url = shift;
    my $http = new HTTP::Lite;
    my $result = $http->request($url);
    return "Error : $result" unless ($result == 200);
    return jcode($http->body())->euc;
}

sub read_file {
    my $file = shift;
    open(IN, $file);
    my $result = join('', <IN>);
    close(IN);
    return jcode($result)->euc;
}

sub parse_rss {
    my ($rss, $num) = @_;
    my @items = ();
    return unless ($rss);
    $num = 0 unless ($num =~ /^\d+$/);
    foreach my $item ($rss =~ /<item\b.*?>.*?<\/item>/gis) {
        my $parsed = {};
        foreach my $tag qw(title link description dc:date) {
            if ($item =~ /<$tag\b.*?>(.*?)<\/$tag>/is) {
                $parsed->{$tag} = &sanitize($1);
            }
        }
        $parsed->{'date'} = &date_convert($parsed->{'dc:date'});
        push(@items, $parsed);
        last if ($num and @items >= $num);
    }
    return @items;
}

sub sanitize {
    my $str = shift;
    # remove tags and unescape
    my $re_tag_    = q{[^"'<>]*(?:"[^"]*"[^"'<>]*|'[^']*'[^"'<>]*)*(?:>|(?=<)|$(?!\n))}; #'};
    my $re_comment = '<!(?:--[^-]*-(?:[^-]+-)*?-(?:[^>-]*(?:-[^>-]+)*?)??)*(?:>|$(?!\n)|--.*$)';
    my $re_tag     = qq{$re_comment|<$re_tag_};
    $str =~ s/$re_tag//g;
    # resanitize
    my %unescaped = ('lt' => '<', 'gt' => '>', 'quot' => '"', 'apos' => "'", 'copy' => '(c)', 'amp' => '&');
    $str =~ s/&(lt|gt|quot|apos|copy|amp);/$unescaped{$1}/gio;
    $str =~ &main::escape($str);
    return $str;
}

sub date_convert {
    my $date = shift;
    if ($date =~ /^(\d{4})(?:-(\d{2})(?:-(\d{2})(?:T(\d{2}):(\d{2})(?::(\d{2})(?:\.(\d))?)?(Z|([+-]\d{2}):(\d{2}))?)?)?)?$/) {
        my ($year, $month, $day, $hour, $min, $sec, $wday) = ($1, ($2 ? $2 : 1), ($3 ? $3 : 1), $4, $5, $6);
        my $offset = (abs($8) * 60 + $9) * ($8 >= 0 ? 60 : -60) if ($7);
        my $time_exist = ($4 =~ /\d/) ? 1 : 0;
        my $time = ($7) ? &Time::Local::timegm($sec, $min, $hour, $day, $month - 1, $year)
                        : &Time::Local::timelocal($sec, $min, $hour, $day, $month - 1, $year);
        $time -= $offset;
        ($sec, $min, $hour, $day, $month, $year, $wday) = localtime($time);
        $wday = (qw(Sun Mon Tue Wed Thu Fri Sat))[$wday];
        $date = sprintf('%04d-%02d-%02d (%s)', $year + 1900, $month + 1, $day, $wday);
        $date .= sprintf(' %02d:%02d:%02d', $hour, $min, $sec) if ($time_exist);
        return $date;
    } else {
        return '';
    }
}

sub path_canonical {
    my $path  = shift;
    my @parts = ($path =~ /^\//) ? ('') : ();
    foreach my $part (split('/', $path)) {
        if ($part eq '..') {
            if    (@parts == 0)                     { @parts = ('..'); }
            elsif (@parts == 1 and $parts[0] eq '') { return undef; }
            elsif ($parts[$#parts] eq '..')         { push(@parts, '..'); }
            else                                    { pop(@parts); }
        } elsif ($part and $part ne '.') {
            push(@parts, $part);
        }
    }
    $path = (@parts) ? join('/', @parts) : '.';
    return $path;
}


sub plugin_usage {
    return {
        name => 'rssview',
        version => '0.0',
        author => 'Makio Tsukamoto <walrus@digit.que.ne.jp>',
        syntax => '#rssview(url [,maxnum])',
        description => 'Rss Viewer',
        example => '#rssview(http://digit.que.ne.jp/work/?RssPage,5)',
    };
}

1;