21 September 2013

MovableTypeで一括文字列置換(データベースファイルを直接編集)

Movable Typeの記事中の文字列を、データベースファイルを直接編集することで一括修正するスクリプト。

・過去の記事 『MovableTypeの「改行を変換する」でコード記述部分まで変換されたのを自動修正する』 のコードを流用する。

■ 検証環境

・Movable Type 3.33 と Movable Type 5.01 の双方で確認
・SQLite データベース

■ 検索置換サンプル

変換前(赤いところを削除)

<a target="_top" … 他の属性等 href="index.html"><img alt="戻る" src="/pics/btn_up.gif" height="25" width="25" />一つ前のメニューに戻る</a>

変換後(青の部分を追加)

<a class="link_return" href="index.html">一つ前のメニューに戻る</a>

■ 検索置換項目を画面表示する(プレビュー)

mt_export_btnup.plをダウンロードする

このスクリプトを、データベースファイル(mtdata.db)のあるフォルダにコピーして、 perl mt_export_btnup.pl > ../test.htmlのように実行する。結果はエクスポートして、ブラウザからのほうが見やすい。

mt_export_btnup.pl
#!/usr/bin/perl
 
use warnings;
use strict; # 変数の宣言を強制する
use DBI; # DBI モジュールを利用する
use HTML::Entities;
 
my $dbh = undef;
my $sth = undef;
my $strQuery = "";
my $strTmp = "";
my $strDsn = 'DBI:SQLite:dbname=./mt-copy.db'; # DSN
 
print   "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n".
        "<html>\n".
        "<head>\n".
        "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" />\n".
        "<meta http-equiv=\"Content-Language\" content=\"ja\" />\n".
        "<style type=\"text/css\">\n".
        "<!--\n".
        "table, td, tr { border:solid 1px maroon;font-size:10px;border-collapse: collapse; }\n".
        "-->\n".
        "</style>\n".
        "<title></title>\n".
        "</head>\n".
        "<body>\n".
        "<table>\n";
 
eval{
    # DBに接続
    $dbh = DBI->connect($strDsn, "", "", {PrintError => 1, AutoCommit => 0});
    if(!$dbh) { die();}
 
    # データを読み込む
    $strQuery = "select entry_id,entry_text,entry_text_more from mt_entry where entry_text like '%\"/pics/btn_up.gif\"%' or entry_text_more like '%\"/pics/btn_up.gif\"%'";
    $sth = $dbh->prepare($strQuery);
    if($sth){ $sth->execute();}
 
    my $counter = 0;
    while(my $arrData = $sth->fetchrow_arrayref()) {
        $counter++;
        my ($id, $text, $text_more) = @$arrData;
        my @arr_textline = split(/\n/, $text."\n".$text_more);
        print "<tr><td>".$id."</td><td><pre>";
        my $flag_incode = 0;
        foreach my $text_line (@arr_textline) {
            chomp $text_line;
            if($text_line =~ m/<a\s*.*href\s*="[^"]*"><img\s*.*\s*src\s*=\s*"\/pics\/btn_up.gif"\s*height="25"\s*width="25"\s*\/>/){
                print "<span style=\"color:green;\">";
                print encode_entities($text_line, '&<>\\\"\'');
                print "</span>";
                print "\n";
            }
        }
        print "</pre></td></tr>\n";
    }
 
    if($sth){ $sth->finish();}
 
    # DBを閉じる
    $dbh->disconnect();
 
    print "</table>\n".
            "<p>total count = ".$counter."</p>\n".
            "</body>\n".
            "</html>\n";
 
};
if($@){
    # evalによるDBエラートラップ:エラー時の処理
    $dbh->disconnect();
    print("DBI error : ".$@."\n");
}
 
exit(0);

■ 置換処理

プレビューで置換ヶ所を確認したら、次のファイルを実行する。

mt_fix_entry_btnup.plをダウンロードする

mt_export_btnup.pl
#!/usr/bin/perl
 
use warnings;
use strict;
use DBI;
use HTML::Entities;
 
my $dbh = undef;
my $sth = undef;
my $strQuery = "";
my $strTmp = "";
my $strDsn = 'DBI:SQLite:dbname=./mtdata.db'; # DSN
 
my @arr_entry = ();     # 修正すべきentry_id
 
eval{
    # DBに接続
    $dbh = DBI->connect($strDsn, "", "", {PrintError => 1, AutoCommit => 0});
    if(!$dbh) { die();}
 
    # データを読み込む
    $strQuery = "select entry_id,entry_text,entry_text_more from mt_entry where entry_text like '%\"/pics/btn_up.gif\"%' or entry_text_more like '%\"/pics/btn_up.gif\"%'";
    $sth = $dbh->prepare($strQuery);
    if($sth){ $sth->execute();}
 
    my $counter = 0;
    while(my $arrData = $sth->fetchrow_arrayref()) {
        my ($id, $text, $text_more) = @$arrData;
        my @arr_textline = split(/\n/, $text."\n".$text_more);
        my $flag_incode = 0;
        foreach my $text_line (@arr_textline) {
            chomp $text_line;
            if($text_line =~ m/<a\s*.*href\s*="[^"]*"><img\s*.*\s*src\s*=\s*"\/pics\/btn_up.gif"\s*height="25"\s*width="25"\s*\/>/){
                $flag_incode = 1;
                push(@arr_entry, $id);
                $counter++;
                last;
            }
        }
    }
 
    if($sth){ $sth->finish();}
 
    print "found ".$counter."topics\n";
 
    foreach my $id (@arr_entry) {
        print $id." ...";
 
        # 指定されたentry_idが複数ないか、データ件数を確認する
        $strQuery = "select count(*) from mt_entry where entry_id = ?";
        $sth = $dbh->prepare($strQuery);
        $sth->bind_param(1, $id, DBI::SQL_INTEGER);
        if($sth){ $sth->execute();}
        my @arr = $sth->fetchrow_array();
        if($sth){ $sth->finish();}
 
        if($arr[0] != 1){
            print "count(entry_id=".$id.") = ".$arr[0]." , is not only one row\n";
            last;
        }
 
        # 指定されたentry_idの内容を読み込む
        $strQuery = "select entry_id,entry_text,entry_text_more from mt_entry where entry_id = ?";
        $sth = $dbh->prepare($strQuery);
        $sth->bind_param(1, $id);
        if($sth){$sth->execute();}
        my $arrData = $sth->fetchrow_arrayref();
        if($sth){ $sth->finish();}
        my ($id, $text, $text_more) = @$arrData;
 
        # entry_textとentry_text_moreを修正する
        $text = fix_text($text);
        $text_more = fix_text($text_more);
 
        # 指定されたentry_idの内容を更新する
        $strQuery = "update mt_entry set entry_text = ?, entry_text_more = ? where entry_id = ?";
        $sth = $dbh->prepare($strQuery);
        $sth->bind_param(1, $text, DBI::SQL_VARCHAR);
        $sth->bind_param(2, $text_more, DBI::SQL_VARCHAR);
        $sth->bind_param(3, $id, DBI::SQL_INTEGER);
        if($sth){$sth->execute();}
        if($sth){ $sth->finish();}
 
        print "\n";
    }
 
    $dbh->commit();
    # DBを閉じる
    $dbh->disconnect();
};
if($@){
    # evalによるDBエラートラップ:エラー時の処理
    $dbh->disconnect();
    print("DBI error : ".$@."\n");
}
 
exit(0);
 
#
# <img alt="戻る" src="/pics/btn_up.gif" height="25" width="25" /> を削除する
# それを囲む <a href="..."> を <a class="link_return" href="..."> に書き換える
#
sub fix_text {
    my $text = shift;
    my @arr_textline = split(/\n/, $text);
    $text = "";
    my $flag_incode = 0;
    foreach my $text_line (@arr_textline) {
        chomp $text_line;
        if($text_line =~ m/<a\s*.*href\s*="[^"]*"><img\s*.*\s*src\s*=\s*"\/pics\/btn_up.gif"\s*height="25"\s*width="25"\s*\/>/){
            $text_line =~ s/<a\s*.*href\s*="([^"]*)"><img\s*alt="戻る"\s*src\s*=\s*"\/pics\/btn_up.gif"\s*height="25"\s*width="25"\s*\/>/<a class="link_return" href="$1">/;
        }
        $text .= ($text_line . "\n");
    }
 
    return $text;
}