#!/usr/bin/perl # Bloxsom 3.0 Plugin: AuthComments # Cache for speeding up # Author: Victor Hsieh # Tue, 22 Mar 2005 00:08:15 +0800 # 修一下 comment 的 layout, 例如在最前跟最後加上 hr package Blosxom::Plugin::AuthComments; use strict; use CGI qw/:standard/; use CGI::Session qw/-ip_match/; use Fcntl qw/:flock/; use File::Spec; use POSIX qw/ strftime /; use Shell; use DBI; use GD::SecurityImage; use Blosxom; use constant EXPIRE => '+30m'; use constant FONT => '/home/victor/.fonts/simhei.ttf'; use constant CODELENGTH => 4; use constant CHARACTER => ('A'..'Z'); use constant LOGFILE => '/home/victor/public_html/blog/comment.log'; use constant DBDIR => '/home/victor/public_html/blog'; my $flavour = 'html'; my $authimage_url = 'http://victor.csie.org/blog/authimg.pl'; my $dbh; # # SQL operation # BEGIN { my $firsttime = ! -f 'comments.db'; $dbh = DBI->connect('dbi:SQLite:dbname=comments.db') or "connect: $DBI::errstr"; $dbh->{f_dir} = DBDIR; $dbh->{RaiseError} = 1; if ($firsttime) { # XXX AUTO_INCREMENT ? $dbh->do('CREATE TABLE comments ( uniqueid INTEGER PRIMARY KEY, article TEXT, name TEXT, link TEXT, comment TEXT, time INTEGER)'); } } END { $dbh->disconnect; } sub insert_comments($) { my $hash = shift; my $sth = $dbh->prepare("INSERT INTO comments (article, name, link, comment, time) VALUES (?, ?, ?, ?, ?)"); my @params = qw/ article name link comment time /; $sth->execute( map { $hash->{$_} } @params ); } sub select_comments($) { my ($hash) = shift; my $condition; for my $key (qw/ article name link comment time /) { $condition .= " AND $key = " . $dbh->quote($hash->{$key}) if exists $hash->{$key}; } $condition =~ s/^ AND/ WHERE/ if ($condition); my $sth = $dbh->prepare("SELECT uniqueid, article, name, link, comment, time FROM comments $condition"); $sth->execute; return $sth->fetchall_hashref('uniqueid'); } # # rendering # sub output_header { my $self = shift; if (param('action') eq 'post') { my $cookie = CGI::cookie(CGISESSID => $self->{state}->{current_entry}->{Plugin}->{AuthComments}->{sessionid}); $self->{response}->{content_type}->{rendered} .= "\r\nSet-cookie: $cookie"; } Blosxom::output_header($self); } =cut There are 5 main function: add_comment add given comment to the filesystem. show_comments show all the comments. comment_form show the comment HTML form for users. show_brief_comments_info show brief comment information, it's "post number" now. return_authimg return the authority image. If you want to change the behavior, you can modify the following code. =cut sub form { my $self = shift; my $action = param('action'); $self->{state}->{current_entry}->{Plugin}->{AuthComments}->{form} = ''; if ($action eq 'submit') { add_comment($self); show_comments($self); } elsif ($action eq 'post') { show_comments($self); comment_form($self); ++$self->{state}->{stop}->{entries}; } =cut elsif ($action eq 'authimg') { return_authimg($self); } =cut elsif (condition_showing_comments($self)) { show_comments($self); show_brief_comments_info($self); } else { show_comments($self); } } sub condition_showing_comments { my $self = shift; return $self->{request}->{path_info} =~ /\.$flavour/o; } sub show_brief_comments_info { my ($self, $comments) = @_; my $path = File::Spec->catfile($self->{state}->{current_entry}->{path}, $self->{state}->{current_entry}->{fn}); my $param = "action=post&article=$path"; my $count = 0; $comments ||= select_comments({ article => $path }); $count = scalar keys %$comments; $self->{state}->{current_entry}->{Plugin}->{AuthComments}->{form} .= qq{comment($count)}; } sub render_comment { my $comment = shift; $_ = POSIX::strftime '%a, %d %b %Y %H:%M:%S %z', localtime($comment->{time}); return < $comment->{name} says at $_
$comment->{comment}
COMMENT } sub show_comments { my $self = shift; my $file = File::Spec->catfile($self->{state}->{current_entry}->{path}, $self->{state}->{current_entry}->{fn}); show_brief_comments_info($self); my $comments = select_comments({ article => $file }); my $ref = \$self->{state}->{current_entry}->{Plugin}->{AuthComments}->{form}; $$ref = < STYLE $$ref .= "
" if keys %$comments; for my $id (sort { $comments->{$a}->{time} <=> $comments->{$b}->{time}} keys %$comments) { $$ref .= render_comment($comments->{$id}); } $$ref .= "
" if keys %$comments; $$ref .= ""; } sub return_code { my @character = (CHARACTER); return join '', map { $character[int rand scalar @character] } (1 .. CODELENGTH); } sub tmpdir { $_ = File::Spec->catdir(File::Spec->tmpdir(), 'comments'); -d $_ or mkdir $_; return $_; } sub start_session { my ($self, $article) = @_; my $cgi = new CGI; my $session = new CGI::Session(undef, $cgi, { Directory => tmpdir() }); $session->expire(EXPIRE); $session->param("_code", return_code()) unless $session->param("_code"); $session->param("_article", $article); return $session; } sub attach_session { my $sid = cookie('CGISESSID') || param('CGISESSID') || return undef; return new CGI::Session(undef, $sid, { Directory => tmpdir() }); } sub comment_form { my $self = shift; my $ref = \$self->{state}->{current_entry}->{Plugin}->{AuthComments}->{form}; my $article = param('article'); $article =~ s!^/!!; $_ = $article; s#$#.txt#; return unless -f File::Spec->catfile($self->{settings}->{find_entries_dir}, $_); $self->{state}->{current_entry}->{Plugin}->{AuthComments}->{sessionid} = start_session($self, $_)->id; $$ref .= <
Your Name:
Your Website (optional):
Code: (Enter this code) authimage

FORM } sub add_comment { my $self = shift; my $session = attach_session(); my %data; return unless $session; for (qw/ author article url code comment /) { goto failed unless $_; $data{$_} = param($_); $data{$_} =~ s/&/&/g; $data{$_} =~ s//>/g; } $data{file} = File::Spec->catfile($self->{settings}->{find_entries_dir}, $session->param('_article')); goto failed unless (checking($session, \%data)); $_ = '/'.$session->param('_article'); s/\.txt$//; insert_comments({ article => $_, name => $data{author}, link => $data{url}, comment => $data{comment}, time => time, }); log_comment(LOGFILE, "$data{author} post at ".localtime().", $data{file}\n"); $session->delete(); return 1; failed: $session->delete(); warn "$data{file}: $!"; return 0; } sub append { my ($file, @msg) = @_; my $fh; warn $file; open($fh, '>>', $file) or warn "$file: $!" and return; flock $fh, LOCK_EX; print $fh $_ for (@msg); flock $fh, LOCK_UN; close($fh); } sub log_comment { append(@_); } sub checking { my ($session, $data) = @_; return -f $data->{file} && $session->param('_code') eq $data->{code}; } # # Authority image generator # sub return_authimg { my $image = GD::SecurityImage->new( width => 90, height => 40, lines => 7, font => FONT, ptsize => 24, rndmax => CODELENGTH, ); my $session = attach_session(); if ($session) { $image->random($session->param('_code')); $image->create('ttf', randomstyle(), randomcolor(64), randomcolor(256, 64)); $image->particle(50, 100); print "Content-type: image/jpeg\r\n\r\n"; binmode(STDOUT); my ($image_data) = $image->out( force => 'jpeg' ); print $image_data; } else { print "Content-type: text/plain\r\n\r\nNot available"; } } sub randomstyle { @_ = qw/ box rect circle /; return $_[int rand(scalar @_)]; } sub randomcolor($;$) { my ($max, $min) = @_; $min ||= 0; @_ = map { $_ = int rand($max - $min) + $min } (1..3); return \@_; } 1; __END__ =head1 NAME Blosxom 3.0 Plugin: AuthComments =head1 SYNOPSIS AuthComments is the comment plugin for Blosxom 3.0 . It provides B as well to keep the spam from bots. The image is generated using C, comments are stored in a SQLite. So you need to install C and C first. =head1 INSTALL First, you have to replace C with C in handler.flow for cookie of the HTTP header. Second, put C in your handler.entry . If you want to change the behavior, just go ahead to change the constant at the beginning and form(). Please read form() for more specific information. The next step is to add $Plugin::AuthComments::form into your entry.flavour . $Plugin::AuthComments::form will be the HTML form, the brief description, or the whole comments. In addtion, you need a simple perl script to respone the image request. This is my example: #!/usr/bin/perl require 'plugins/AuthComments.pm'; Blosxom::Plugin::AuthComments::return_authimg(); And then define $authimage_url correctly. =head1 Constant =over 4 =item FONT The ttf font which you want to use in authority image. You HAVE to override it. =item EXPIRE Expire time of the session, such as '+30m'. =item CODELENGTH The length of the authority code. Default is 4. =item CHARACTER Characters which will be used in the code. Default is ('A'..'Z'); =item LOGFILE Post log of the comment. You HAVE to override it. =head1 AUTHOR Victor Hsieh , L, L =head1 SEE ALSO Blosxom Home/Docs/Licensing: L =head1 COPYRIGHT Copyright (c) 2005. Victor Hsieh. All rights reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. See http://www.perl.com/perl/misc/Artistic.html =cut