1 # Blosxom Plugin: comments_antispam
2 # Author(s): Axel Beckert <abe@deuxchevaux.org> based on work of
3 # Kevin Lyda <kevin@ie.suberic.net> and
4 # Rael Dornfest <rael@oreilly.com>
6 # Documentation: See the bottom of this file or type: perldoc comments
8 package comments_antispam;
13 # --- Configurable variables -----
15 # Where is the old writeback hierarchy?
17 # NOTE: By setting this variable, you are telling the plug-in to go ahead
18 # and create a comments directory for you.
19 #my $writeback_dir = "$blosxom::plugin_state_dir/writeback";
20 my $comments_dir = "$blosxom::plugin_state_dir/comments";
22 # What flavour should I consider an incoming trackback ping?
23 # Otherwise trackback pings will be ignored!
24 my $trackback_flavour = "trackback";
26 # What file extension should I use for writebacks?
27 # Make sure this is different from that used for your Blosxom weblog
28 # entries, usually txt.
29 my $writeback_file_ext = "wb";
30 my $comments_file_ext = "comments";
32 # max comment indent depth
36 my $time_approx_prefix = "some point before ";
37 # time as described by the strftime man page. aAbBcdHIjmMpSUwWxXyYZ%
38 my $time_fmt = "%a, %d %b %Y %H:%M";
39 my $time_unknown = "some point unknown";
41 my $reply_prefix = "Re: ";
43 my @count_noun = ("comments", # if there are no comments
44 "comment", # if there is only 1 comment
45 "comments"); # all the rest
47 # What fields were used in your writeback form and by trackbacks?
48 my @fields = qw! cname url title comment excerpt blog_name parent time !;
50 my $to_mail = 'blog-owner@example.com';
51 my $from_mail = 'blog-server@example.com';
52 my $blacklist_file = "$blosxom::datadir/comments_blacklist";
54 # --- Export variables -----
56 # Comments for a story; use as $comments_antispam::comments in flavour templates
59 # Count of writebacks for a story; use as $comments_antispam::count in flavour templates
63 # parent id and title fields of replies
67 # The path and filename of the last story on the page (ideally, only 1 story
68 # in this view) for displaying the trackback URL;
69 # use as $comments_antispam::trackback_path_and_filename in your foot flavour templates
71 $trackback_path_and_filename;
73 # Response to comments; use as $comments_antispam::comment_response in
80 # Response to a trackback ping; use as $writeback::trackback_response in
81 # head.trackback flavour template
82 $trackback_response =<<"TRACKBACK_RESPONSE";
83 <?xml version="1.0" encoding="iso-8859-1"?>
90 # --- Utility routines -----
92 use CGI qw/:standard/;
94 use Data::Dumper; $Data::Dumper::Indent=1;
95 use POSIX qw/strftime/;
97 my $fh = new FileHandle;
99 # Strip potentially confounding bits from user-configurable variables
100 $writeback_dir =~ s!/$!!; $file_extension =~ s!^\.!!;
101 $comments_dir =~ s!/$!!;
103 # Save Name and URL/Email via cookie if the cookies plug-in is available
108 my($path, $filename, $new) = @_;
110 open(BL, "< $blacklist_file")
111 or die "Can't open blacklist file $blacklist_file: $!";
113 while (my $line = <BL>) {
114 next if $line =~ /^#/; # strip unix like comments
115 $line =~ s/^\s*(.*?)\s*$/$1/; # strip leading and trailing blanks
116 push(@blacklist, split(/\s+/s, $line));
120 foreach my $v (values %$new) {
121 foreach my $crit (@blacklist) {
122 if ($v =~ m($crit)is) {
123 $errormsg = "<br/><br/>Something in your post was on the blacklist. Use the back button and try changing the post to look less like comment spam. It also may be that your hostname or webbrowser is on the blacklist. Bad luck, if that's the case.<br /><br />Sorry for the inconvenience.";
131 foreach ("", split(/\//, $path)) {
133 -d "$comments_dir$p" or mkdir "$comments_dir$p", 0755;
136 # lock and read in comments
137 # TODO: lock file in nfs-safe way - see LockFile::Simple
138 if ($fh->open("$comments_dir$path/$filename.$comments_file_ext")) {
139 eval join("", <$fh>);
142 $saved_comments = {};
145 # determine if this is a reply
146 if (exists($new->{"parent"})) {
147 if (!exists($saved_comments->{$new->{"parent"}})) {
148 # TODO: unlock file in nfs-safe way
151 $prefix = $new->{"parent"}. "-";
152 delete($new->{"parent"});
157 while (exists($saved_comments->{$prefix. $id})) {
163 foreach (keys %{$new}) {
164 $saved_comments->{$id}->{$_} = $new->{$_};
165 $message .= "$_: $new->{$_}\n"; #append data in parallel to flatfile.
167 $saved_comments->{$id}->{"time"} = time();
169 #------------Begin sendmail routine -------------
170 my $msg = Mail::Send->new;
172 $msg->set('From', $from_mail);
173 $msg->subject("Comment to $path/$filename by $new->{cname}");
177 #--------------------------------------------------
179 if ($fh->open(">$comments_dir$path/$filename.$comments_file_ext")) {
180 $fh->print(Data::Dumper->Dump([$saved_comments], ["saved_comments"]));
184 # TODO: unlock file in nfs-safe way
189 my($path, $filename, $id_limit) = @_;
191 # lock and read in comments
192 if ($fh->open("$comments_dir$path/$filename.$comments_file_ext")) {
193 eval join("", <$fh>);
196 $count_noun = $count_noun[$count = 0];
201 my($last_level, $this_level) = (0, 0);
202 my($push) = &$blosxom::template($path, "comments-push", $blosxom::flavour)
204 my($pop) = &$blosxom::template($path, "comments-pop", $blosxom::flavour)
206 my($comment) = &$blosxom::template($path, "comments", $blosxom::flavour)
207 or '<p><b>Name:</b> '.
208 '$comments_antispam::cname<br /><b>URL:</b> '.
209 '$comments_antispam::url<br /><b>Title:</b> '.
210 '$comments_antispam::title<br /><b>Comment:</b> '.
211 '$comments_antispam::comment</p>';
212 my($trackback) = &$blosxom::template($path, "trackback", $blosxom::flavour)
213 or '<p><b>Blog:</b> '.
214 '$comments_antispam::blog_name<br /><b>URL:</b> '.
215 '$comments_antispam::url<br /><b>Title:</b> '.
216 '$comments_antispam::title<br /><b>Excerpt:</b> '.
217 '$comments_antispam::excerpt</p>';
219 # print STDERR "Comment Template:\n'$comment'\n";
222 if (defined($id_limit)) {
227 @aa = split(/-/, $a); @bb = split(/-/, $b);
230 and $i <= $#bb and $aa[$i] == $bb[$i]) {
233 $aa[$i] = -1 if ($i > $#aa);
234 $bb[$i] = -1 if ($i > $#bb);
235 return $aa[$i] <=> $bb[$i];
236 } (keys(%{$saved_comments}));
239 $this_level = split(/-/, $id) - 1;
240 $this_level = $max_level if ($max_level < $this_level);
241 if ($this_level > $last_level) {
242 $tmp = $push x ($this_level - $last_level);
244 $tmp = $pop x ($last_level - $this_level);
246 $last_level = $this_level;
247 if (exists($saved_comments->{$id}->{"blog_name"}) or
248 exists($saved_comments->{$id}->{"excerpt"})) {
253 if (exists($saved_comments->{$id}->{"time"})) {
254 if ($saved_comments->{$id}->{"time"} =~ /^a(.*)/) {
256 $saved_comments->{$id}->{"time"} = $time_approx_prefix;
258 $time = $saved_comments->{$id}->{"time"};
259 $saved_comments->{$id}->{"time"} = "";
261 $saved_comments->{$id}->{"time"}
262 .= strftime($time_fmt, localtime($time));
264 $saved_comments->{$id}->{"time"} = $time_unknown;
266 $saved_comments->{$id}->{"parent"} = $id;
267 $saved_comments->{$id}->{"reply_title"} =
268 ($saved_comments->{$id}->{"title"} =~ /^$reply_prefix/ ?
269 '' : $reply_prefix) . $saved_comments->{$id}->{"title"};
271 $saved_comments->{$id}->{"reply_title_escaped"} =
272 $saved_comments->{$id}->{"reply_title"};
274 $saved_comments->{$id}->{"reply_title_escaped"} =~
275 s([^a-zA-Z0-9_:./])(sprintf('%%%02X', ord($&)))ge;
277 $saved_comments->{$id}->{"base_url"} = $base_url;
278 $parent = $saved_comments->{$id}->{"parent"};
279 # print STDERR "tmp1 ($id):\n$tmp";
280 $tmp =~ s/\$comments_antispam::([_\w]+)/$saved_comments->{$id}->{$1}/ge;
281 # print STDERR "tmp2 ($id):\n$tmp";
285 $comments .= $pop x $last_level;
286 $count = scalar(keys(%{$saved_comments}));
287 $count_noun = $count_noun[$count < 2? $count: 2];
290 # print STDERR 'Comments: '.
291 # Dumper(\@id,$comments,$count,$count_noun,$saved_comments,$tmp);
292 # print STDERR $comments;
296 sub convert_writebacks {
297 my($path, $filename) = @_;
299 # read in old writebacks
300 if ($fh->open("$writeback_dir$path/$filename.$writeback_file_ext")) {
301 $saved_comments = {};
303 $time = (stat("$writeback_dir$path/$filename.$writeback_file_ext"))[9];
304 foreach my $line (<$fh>) {
305 $line =~ /^(.+?):(.*)$/ and $saved_comments->{$id}->{$1} = $2;
307 $saved_comments->{$id}->{$field} =~ s/^ *//;
308 $saved_comments->{$id}->{$field} =~ s/ *$//;
309 if ($saved_comments->{$id}->{$field} eq "") {
310 delete $saved_comments->{$id}->{$field};
312 if ( $line =~ /^-----$/ ) {
313 if (!exists($saved_comments->{$id}->{"time"})) {
314 $saved_comments->{$id}->{"time"} = "a$time";
321 # make sure comments dir exists.
323 foreach ( ("", split /\//, $path) ) {
325 -d "$comments_dir$p" or mkdir "$comments_dir$p", 0755;
327 if ($fh->open(">$comments_dir$path/$filename.$comments_file_ext")) {
328 $fh->print(Data::Dumper->Dump([$saved_comments],
329 ["saved_comments"]));
332 warn "blosxom: comments: convert_writeback: ".
333 "couldn't open comment file '".
334 "$comments_dir$path/$filename.$comments_file_ext\n";
337 warn "blosxom: comments: convert_writeback: ".
338 "couldn't open writeback file '".
339 "$writeback_dir$path/$filename.$writeback_file_ext'\n";
343 # --- Plugin Exports -----
347 # $comments_dir must be set to activate writebacks
348 unless ($comments_dir) {
349 warn "blosxom: comments: \$comments_dir".
350 " is not set; please set it to enable comments.".
351 " Comments are disabled!\n";
355 # the $comments_dir exists, but is not a directory
356 if ( -e $comments_dir and ( !-d $comments_dir or !-w $comments_dir)) {
357 warn "blosxom: comments: \$comments_dir, $comments_dir, must be".
358 " a writeable directory; please move or remove it and Blosxom".
359 " will create it properly for you. Comments are disabled!\n";
363 # the $comments_dir does not yet exist, so Blosxom will create it
364 if ( !-e $comments_dir) {
365 if (mkdir("$comments_dir", 0755)) {
366 warn "blosxom: comments: \$comments_dir, $comments_dir, created.\n"
368 warn "blosxom: comments: There was a problem creating".
369 " \$comments_dir, $comments_dir. Comments are disabled!\n";
373 if (chmod(0755, $comments_dir)) {
374 warn "blosxom: comments: \$comments_dir, $comments_dir, set to".
375 " 0755 permissions.\n"
377 warn "blosxom: comments: Problem setting perms on \$comments_dir".
378 ", $comments_dir. Comments are disabled!\n";
382 #warn "blosxom: comments: comments are enabled!\n";
385 $path_info = path_info();
388 my $postfile = "$blosxom::datadir$path_info";
389 $postfile =~ s/$blosxom::flavour$/$blosxom::file_extension/;
391 my $newpath = readlink $postfile;
392 $path_info =~ s/$blosxom::file_extension$/$blosxom::flavour/;
394 if ($newpath =~ m(^/)) {
395 $newpath =~ s/\Q$blosxom::datadir\E//;
396 $path_info = $newpath;
398 $path_info =~ s(/[^/]*$)();
399 $newpath = "$path_info/$newpath";
400 while ($newpath =~ m(/\.\./)) {
401 $newpath =~ s(/[^/]+/\.\./)(/)s or last;
403 $path_info = $newpath;
407 my($path, $filename) = $path_info =~ m!^(?:(.*)/)?(.*)\.$blosxom::flavour!;
408 $path =~ m!^/! or $path = "/$path";
410 ($path_info_escaped = $path_info)
411 =~ s([^-\w/.])(sprintf('%%%02x',ord($&)))ge;
413 $base_url = $blosxom::url.$path_info_escaped;
415 # Only spring into action if POSTing to the writeback plug-in
416 if (request_method() eq "POST" and
417 (param("plugin") eq "comments"
418 or $blosxom::flavour eq $trackback_flavour) ) {
420 foreach ( @fields ) {
421 $new{$_} = param($_);
422 if (!defined($new{$_}) or length($new{$_}) == 0) {
424 } elsif ($_ eq "url" and $new{$_} !~ /:/
425 and length($new{$_}) > 2) {
426 $new{$_} = "mailto:". $new{$_};
427 } elsif ($_ eq "comment") {
428 $new{$_} =~ s/\n?\r\n?/\n/mg;
429 $new{$_} =~ s/\n\n/<\/p><p>/mg;
430 $new{$_} = "<p>". $new{$_}. "</p>";
434 $new{referrer} = $ENV{HTTP_REFERER} if $ENV{HTTP_REFERER};
435 $new{host} = $ENV{REMOTE_HOST} if $ENV{REMOTE_HOST};
436 $new{host_ip} = $ENV{REMOTE_ADDR} if $ENV{REMOTE_ADDR};
437 $new{host} ||= gethost($new{host_ip})->name;
438 $new{user_agent} = $ENV{HTTP_USER_AGENT} if $ENV{HTTP_USER_AGENT};
440 if (! -f "$comments_dir$path/$filename.$comments_file_ext"
441 and -f "$writeback_dir$path/$filename.$writeback_file_ext") {
442 convert_writebacks($path, $filename);
444 if (save_comment("$comment_dir$path", $filename, \%new)) {
446 $trackback_response =~ s!<error></error>!<error>0</error>!m;
447 $trackback_response =~ s!<message></message>\n!!s;
448 $comment_response = "Thanks for the comment.";
450 # Make a note to save Name & URL/Email if save_preferences checked
451 param("save_preferences") and $cookie++;
452 # Pre-set Name and URL/Email based on submitted values
453 $pref_name = param("cname") || "";
454 $pref_url = param("url") || "";
457 warn "blosxom: comments: start: couldn't >>".
458 " $comments_dir$path/$filename.$file_extension\n";
459 $trackback_response =~ s!<error></error>!<error>1</error>!m;
460 $trackback_response =~ s!<message>trackback error</message>!!m;
461 $comment_response = "There was a problem saving your comment. $errormsg";
470 my($pkg, $path, $filename, $story_ref, $title_ref, $body_ref) = @_;
472 $path =~ s!^/*!!; $path &&= "/$path";
475 my $postfile = "$blosxom::datadir$path/$filename.$blosxom::file_extension";
477 my $newpath = readlink $postfile;
478 $newpath =~ s/\Q$filename.$blosxom::file_extension\E$//;
480 if ($newpath =~ m(^/)) {
481 $newpath =~ s/\Q$blosxom::datadir\E//;
484 $newpath = "$path/$newpath";
485 while ($newpath =~ m(/\.\./)) {
486 $newpath =~ s(/[^/]+/\.\./)(/)s or last;
492 # convert those old writebacks!
493 if (! -f "$comments_dir$path/$filename.$comments_file_ext"
494 and -f "$writeback_dir$path/$filename.$writeback_file_ext") {
495 convert_writebacks($path, $filename);
498 # Prepopulate Name and URL/Email with cookie-baked preferences, if any
499 if ($blosxom::plugins{"cookies"} > 0
500 and my $cookie = &cookies::get("comments") ) {
501 $pref_name ||= $cookie->{"cname"};
502 $pref_url ||= $cookie->{"url"};
506 print_comments($path, $filename, param("parent"));
507 if (defined(param("parent"))) {
508 $parent = param("parent");
509 $title = param("title");
512 $title = $blosxom::title;
514 $title = $reply_prefix.$title if $title !~ /^$reply_prefix/;
516 $trackback_path_and_filename = "$path/$filename";
522 if ($pref_name or $pref_url) {
523 $blosxom::plugins{"cookies"} > 0 and $cookie and &cookies::add(
526 -value=>{ "cname" => $pref_name, "url" => $pref_url },
527 -path=>$cookies::path,
528 -domain=>$cookies::domain,
529 -expires=>$cookies::expires
541 Blosxom Plug-in: comments
545 Provides comments and TrackBacks [http://www.movabletype.org/trackback/].
546 It will also convert your old writebacks.
548 This modified version from Axel Beckert also features mail
549 notification for the blog owner and a comment and trackback spam
552 All comments and TrackBack pings for a particular story are kept in
553 $comments_dir/$path/$filename.comment.
557 Drop this comments plug-in file into your plug-ins directory
558 (whatever you set as $plugin_dir in blosxom.cgi).
560 Comments, being a well-behaved plug-in, won't do anything until you
561 set $comments_dir. If you were using writebacks, you need to set
564 While you can use the same directory as your blosxom $datadir (comments
565 are saved as path/weblog_entry_name.comment), it's probably better to keep
568 Once set, the next time you visit your site, the comments plug-in will
569 perform some checks, creating the $comments_dir and setting appropriate
570 permissions if it doesn't already exist. (Check your error_log for details
571 of what's happening behind the scenes.)
573 Move the contents of the flavours folder included in this distribution
574 into your Blosxom data directory (whatever you set as $datadir in blosxom.cgi).
575 Don't move the folder itself, only the files it contains! If you don't
576 have the the sample flavours handy, you can download them from:
578 FIXME: http://ie.suberic.net/~kevin/blosxom/comments.tar.gz
580 Point your browser at one of your Blosxom entries, specifying the comments
581 flavour (e.g. http://localhost/cgi-bin/blosxom.cgi/path/to/a/post.comment)
589 The blacklist is a list of perl regular expressions separeted by blank
590 characters (e.g. space, tab and newline) and supports shell script
591 like comment with "#" at the beginning of the line. If any of the
592 regexps in the blacklist matches any value (including title, commenty
593 body, poster's name, URL, poster's hostname or user agent), the
594 comment or trackback will not be accepted. So be careful and wise when
595 choosing which words or domains you block. And remember: "." matches
596 any character, so escape it as "\.".
602 acyclovir adipex alprazolam ativan butalbital carisoprodol
603 carisoprodol casino celexa cialis condylox cyclobenzaprine ddrx
604 diazepam didrex diethylpropion diflucan drofa ephedrine ephedrine
605 fioricet flexeril fluoxetine fluoxetine hydrocodone levitra lexapro
606 lipitor lorazepam lortab lrzpm meridia musapuk nextel norvasc paxil
607 penis pharmacy phentermine prilosec propecia prozac renova rkwdl rolex
608 skelaxin tadalafil tenuate tramadol tricyclen triphasil ultracet
609 ultram valium valtrex vaniqa viagra xanax xenical zanaflex zithromax
616 # Hostnames (used example.com here as an example for real regexps :-)
617 \.example.(com|net)\b
623 =head2 MAIL NOTIFICATION
625 Set $to_mail to the receipient and $from_mail to the sender
626 address. Needs perl module Mail::Send installed.
628 =head2 FLAVOUR TEMPLATE VARIABLES
630 Wherever you wish all the comments for a particular story to appear
631 in your flavour templates, use $comments_antispam::comments.
633 A count of WriteBacks for each story may be embedded in your flavour
634 templates with $comments_antispam::count. Based on the @count_noun config array,
635 $comments_antispam::count_noun will reflect a $comments_antispam::count of 0, 1, many.
637 If you'd like, you can embed a "Thanks for the comment." or
638 "There was a problem saving your comment." message after posting with
639 $comments_antispam::comment_response.
641 =head2 SAMPLE FLAVOUR TEMPLATES
643 I've made sample flavour templates available to you to help with any
644 learning curve this plug-in might require.
646 Take a gander at the source HTML/XML for:
648 =item * story.comments, a basic example of a single-entry story
649 flavour with comments embedded. You should not use this as your
650 default flavour since every story on the home page would have comments
651 right there with the story itself.
653 =item * head.trackback, all you need to provide the right response to
654 a TrackBack ping. Notice in story.comments that the auto-discovery
655 and human-readable TrackBack ping URLs point at this flavour.
657 =item * foot.comments provides a simple comment form for posting to the
658 comments plug-in. NOTE: The comments plug-in requires the presence
659 of a "plugin" form variable with the value set to "comments"; this tells
660 the plug-in that it should handle the incoming POSTing data rather than
661 leaving it for another plug-in.
663 =item * comments.comments is a sample flavour file for comments themselves.
664 Think of a comments flavour file as a story flavour file for individual
667 =item * comments-push.comments and comments-pop.comments are sample
668 flavour files to handling threads (descending into a thread and coming
673 =head2 FLAVOURING COMMENTS
675 While the default does a pretty good job, you can flavour your comments
676 in the comments flavour file (e.g. comments.comments) using the following
679 =item * $comments_antispam::name$comments_antispam::blog_name - Name entered in comment
680 form or weblog name used in TrackBack ping.
682 =item * $comments_antispam::url - URL entered in comment form or that of writing
683 citing your weblog entry via TrackBack ping.
685 =item * $comments_antispam::title - Title entered into comment form or that of writing citing your weblog entry via TrackBack ping.
687 =item * $comments_antispam::comment$comments_antispam::excerpt - Comment entered
688 into comment form or excerpt of writing citing your weblog entry via
691 =item * $comments_antispam::pref_name and $comments_antispam::pref_url are prepopulated
692 with the values of the form you just submitted or preferences stored in a
693 'comments' cookie, if you've the cookie plug-in installed an enabled.
697 =head2 INVITING AND SUPPORTING TRACKBACKS
699 You should provide the TrackBack ping URL for each story so that those
700 wanting to TrackBack ping you manually know where to ping.
701 $comments_antispam::trackback_path_and_filename, together with $url and
702 a TrackBack flavour will provide them all they need.
704 e.g. $url$comments_antispam::trackback_path_and_filename.trackback
706 You need to provide an XML response to TrackBack pings to let them
707 know whether or not the ping was successful. Thankfully, the plug-in
708 does just about all the work for you. You should, however, create a
709 head.trackback flavour file (only the head is needed) containing simply:
711 $comments_antispam::trackback_response
713 Be sure that the flavour of the head file (suggested: head.trackback)
714 corresponds to the $trackback_flavour configurable setting otherwise
715 Blosxom will ignore incoming TrackBack pings!
719 Drop comments into your plug-ins directory ($blosxom::plugin_dir).
723 =head2 (REQUIRED) SPECIFYING A COMMENTS DIRECTORY
725 Comments, being a well-behaved plug-in, won't do anything until you set
726 $comments_dir, create the directory, and make it write-able by Blosxom.
727 Make sure to set $writeback_dir if you were using the WriteBack plugin.
729 Create a directory to save comments to (e.g. $plugin_state_dir/comments),
730 and set $comments_dir to the path to that directory.
732 While you can use the same directory as your blosxom $datadir (comments
733 are saved as path/weblog_entry_name.wb), it's probably better to keep
736 The comments plug-in will create the appropriate paths to mimick your
737 $datadir hierarchy on-the-fly. So, for a weblog entry in
738 $datadir/some/path/or/other/entry.txt, comments will be kept in
739 $comments_dir/some/path/or/other/entry.wb.
741 =head2 (OPTIONAL) ALTERING THE TRACKBACK FLAVOUR
743 The $trackback_flavour sets the flavour the plug-in associates with
744 incoming TrackBack pings. Unless this corresponds to the flavour
745 associated with your trackback URL, the comments plug-in will ignore
748 =head2 (OPTIONAL) SPECIFYING AN EXTENSION FOR COMMENT FILES
750 The default extension for comments is comments. You can change this
751 if you wish by altering the $comments_file_ext value. The default for
752 writebacks is wb - changed by changing $writeback_file_ext.
754 =head2 (OPTIONAL) SPECIFYING WHAT FIELDS YOU EXPECT IN YOUR COMMENTS FORM
756 The defaults are probably ok here, but you can specify that the comments
757 plug-in should look for more fields in your comments form by adding to this
758 list. You should keep at least the defaults in place so as not to break
761 my @fields = qw! name url title comment excerpt blog_name !;
763 Second part of the version number is the comments plugin version on
764 which it is based upon.
768 Axel Beckert <blosxom@deuxchevaux.org>, http://noone.org/blog
769 Kevin Lyda <kevin@ie.suberic.net>, http://ie.suberic.net/~kevin/cgi-bin/blog
770 Rael Dornfest <rael@oreilly.com>, http://www.raelity.org/
774 Latest version can be found at http://noone.org/blosxom/comments_antispam
778 Homepage: http://noone.org/blog?-tags=comments_antispam
780 Blosxom Home/Docs/Licensing: http://www.raelity.org/apps/blosxom/
781 Blosxom Download: http://blosxom.sourceforge.net/
782 Blosxom Plugin Docs: http://www.raelity.org/apps/blosxom/plugin.shtml
783 Blosxom User Group: http://blosxom.ookee.com/blog
784 WriteBack Plugin: http://www.raelity.org/apps/blosxom/downloads/plugins/writeback.zip
788 Address bug reports and comments to the Blosxom mailing list
789 [http://www.yahoogroups.com/group/blosxom].
793 Blosxom and this Blosxom Plug-in
794 Copyright 2003, Rael Dornfest
795 Copyright 2005-2006, Axel Beckert
797 Permission is hereby granted, free of charge, to any person obtaining a
798 copy of this software and associated documentation files (the "Software"),
799 to deal in the Software without restriction, including without limitation
800 the rights to use, copy, modify, merge, publish, distribute, sublicense,
801 and/or sell copies of the Software, and to permit persons to whom the
802 Software is furnished to do so, subject to the following conditions:
804 The above copyright notice and this permission notice shall be included
805 in all copies or substantial portions of the Software.
807 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
808 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
809 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
810 THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
811 OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
812 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
813 OTHER DEALINGS IN THE SOFTWARE.