我的 Recent Backlinks 自從升級到 wordpress 2.3 之後,就一直有問題,會多顯示出 kramer_pre%--> 的字樣。用這個奇怪的字樣去找,發現是 Kramer 這個 plug-in 的問題。當初會裝主要是因為 trackback 壞掉了,所以乾脆裝了 Kramer,會直接到網上找 back links,找到後代替 trackback auto-discovery 機制,插入資料到 wp_comments 表格。既然 Kramer 壞掉了,只好先拔掉再說。

沒想到拔掉 Kramer 之後,問題依舊,即使關掉 get-recent-comments 的 cache 也一樣。於是就直接進 database 裡找,發現不曉得為甚麼,Kramer 把製造出來的 wp_comments.comment_content 內容,前後加上了 <!--%kramer-pre%--><!--%kramer_post%-->。所以,只好辛苦地,寫個 script 修正 database:

#!/usr/bin/perl -w

use strict;
use utf8;
use File::Basename;
use Getopt::Long;
use DBI;

my ($__exe_name__) = (basename($0));
my ($__revision__) = ('$Rev: 26 $' =~ m/(\d+)/o);
my ($__rev_date__) = ('$Date: 2007-11-02 15:41:31 +0800 (五, 02 11 2007) $' =~ m/(\d{4}-\d{2}-\d{2})/o);

sub usage
{
    print STDERR <<"EOF";
Usage: $__exe_name__ [ <option> ... ] <db-name>

Query wordpress database <db-name> and fix bad wp_comments.comment_content
generated by Kramer plug-in.

Options:

  --help                   Show this help messages.
  -h,--hostname <db-host>  Connect to database hosted at <db-host>.
  -u,--username <db-user>  Use username <db-user> to log into database.
  -p,--password <db-pass>  Use password <db-pass> to log into database.
  -v,--verbose             Show verbose messages.

Revision: r$__revision__ ($__rev_date__)
EOF
    exit;
}

sub msg_exit
{
    my $ex = ((scalar(@_) > 0) ? shift @_ : 0);
    foreach my $m (@_) {
        print STDERR "ERROR: $m\n";
    }
    print STDERR <<"EOF";
Usage: $__exe_name__ [ <option> ... ] <db-name>
Type '$__exe_name__ --help' for usage.
EOF
    exit($ex);
}

my $opt_verbose = 0;
my %db_options;
if (!GetOptions('help'           => sub { usage; },
                'h|hostname=s'   => sub { $db_options{'DB_HOST'} = $_[1]; },
                'u|username=s'   => sub { $db_options{'DB_USER'} = $_[1]; },
                'p|password=s'   => sub { $db_options{'DB_PASS'} = $_[1]; },
                'v|verbose'      => \$opt_verbose)) {
    msg_exit(0);
}
$db_options{'DB_NAME'} = shift @ARGV or msg_exit(1, 'Missing <db-name>.');

my $dbh = DBI->connect(
    sprintf('DBI:mysql:database=%s;host=%s',
            $db_options{'DB_NAME'},
            $db_options{'DB_HOST'}),
    $db_options{'DB_USER'},
    $db_options{'DB_PASS'}
);

my @update_statements;

my $sth = $dbh->prepare(<<SQL
SELECT *
FROM   wp_comments
WHERE  comment_type ="pingback" AND
       comment_content LIKE "<!--%"
SQL
);
$sth->execute();
while (my $row = $sth->fetchrow_hashref()) {
    my @sqlpart_setval;
    my @sqlpart_where;
    foreach my $f (keys %$row) {
        my $v = $row->{$f};
        if ($f eq 'comment_content') {
#           push(@sqlpart_setval, sprintf('-- WAS: "%s"', $v));
            $v =~ s/^<!--%kramer-pre%-->//o;
            $v =~ s/<!--%kramer-post%-->$//o;
            push(@sqlpart_setval, sprintf('%s = %s', $f, $dbh->quote($v)));
        }
        else {
            push(@sqlpart_where, sprintf('%s = %s', $f, $dbh->quote($v)));
        }
    }
    push(@update_statements, sprintf(
        "UPDATE\n\t%s\nSET\n\t%s\nWHERE\n\t%s;",
        $arg_tab_name,
        join(",\n\t",    @sqlpart_setval),
        join(" AND\n\t", @sqlpart_where)
    ));
}
$sth->finish();

foreach my $sql (@update_statements) {
    print "-- --------------------------------------------- --\n$sql\n\n";
}

$dbh->disconnect();

利用這個 script,可以 dump 出一個 SQL 檔,備份好 wordpress 之後,把這個 SQL 檔餵進資料庫即修正完畢。

清掉 get-recent-comments 的 cache 之後,奇怪的字樣雖然不見了,不過正常該有的內容,卻又被截掉了最前面 5 個字元。繼續用 comment_excerpt 當關鍵字翻 get-recent-comments.php,發現了和 5 相關的下面這段程式:

        if ($comment->comment_type == 'pingback')
        {

            $comment_type = "Pingback";
            list($comment_author,$trackback_title) = kjgrc_parse_pingback($comment->comment_author);
            if(strpos($comment_excerpt,'[...]') == 0)
                $comment_excerpt = trim(substr($comment_excerpt,5));
            if(strpos($comment_excerpt,'[...]') == strlen($comment_excerpt)-5)
                $comment_excerpt = trim(substr($comment_excerpt,0,strlen($comment_excerpt)-5));
        }

意思是說,如果 $comment_excerpt 最前面的 [...] 與最後面的 [...] 移掉。看到 strpos() 我就感到不對,查了一下文件,strpos() 若找不到就會回傳 FALSE,而 FALSE == 0 會得到真值,於是$comment_excerpt 前面的 5 個字元就被砍掉了。解法很簡單,把 == 改成 === 就可以了,如下:

--- get-recent-comments.php.ori 2007-11-02 17:31:56.000000000 +0800
+++ get-recent-comments.php     2007-11-02 17:32:13.000000000 +0800
@@ -1248,7 +1248,7 @@

                        $comment_type = "Pingback";
                        list($comment_author,$trackback_title) = kjgrc_parse_pingback($comment->comment_author);
-                       if(strpos($comment_excerpt,'[...]') == 0)
+                       if(strpos($comment_excerpt,'[...]') === 0)
                                $comment_excerpt = trim(substr($comment_excerpt,5));
                        if(strpos($comment_excerpt,'[...]') == strlen($comment_excerpt)-5)
                                $comment_excerpt = trim(substr($comment_excerpt,0,strlen($comment_excerpt)-5));

至此,問題全部解決,同時回應給 get-recent-comments team。不過,又回想了一下,共 5 個字元被砍掉,那一開始的那個奇怪的 kramer-pre%--> 字樣,不就是 Kramer 塞進來的 <!--%kramer-pre%--> 扣掉前面 5 個字元嗎?

繞了一圈才發現,原來問題的癥結在於 get-recent-comments,而不是 Kramer,我錯怪它了。不過我還沒決定是否要把 Kramer 裝回去就是了。