28 March 2013

MovableTypeの「改行を変換する」でコード記述部分まで変換されたのを自動修正する

MovableTypeで「改行を変換する」オプションを選択すると、 <pre> の間にある文章(ソースコード記述)まで“改行変換”されて表示がおかしくなる。

※ 「改行を変換する」オプションは標準設定値のため、特に明示しなければこのオプションで構築しているはず…

MovableTypeのデータベースファイルを直接開いて、一括して修正するスクリプトを作成した。(この修正後、MovableTypeの管理画面で“全て再構築”すれば、作業完了する)

■ MovableTypeでの記述例と表示サンプル

(1)改行だけの行が存在する場合

<div class="code">

#!/usr/bin/perl

use warnings;
use strict;
use DBI;
use HTML::Entities;


my $dbh = undef;

</div>

このソースコードをMovableTypeで「改行を変換する」をONにして変換(構築)すると

<div class="code">

<p>#!/usr/bin/perl</p>

<p>use warnings;<br />
use strict;<br />
use DBI;<br />
use HTML::Entities;</p>

<p>my $dbh = undef;<br />

20130328-mt-wospace.jpg
表示サンプル

(2)改行だけの行に1この空白文字を付加した場合

<div class="code">

#!/usr/bin/perl

use warnings;
use strict;
use DBI;
use HTML::Entities;

my $dbh = undef;

</div>

このソースコードをMovableTypeで「改行を変換する」をONにして変換(構築)すると

<div class="code">
#!/usr/bin/perl

use warnings;
use strict;
use DBI;
use HTML::Entities;

my $dbh = undef;

20130328-mt-wspace.jpg
表示サンプル

■ MovableTypeの全エントリを一括修正するPerlスクリプト

MovableTypeのデータベースを直接開いて修正する。このサンプルスクリプトは、SQLite3のデータベースファイル mtdata.sqliteの存在するディレクトリで実行すると仮定している。

mt_fix_entry_code.plをダウンロードする(<div class="code"> 〜 </div> 版)

#!/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.sqlite'; # 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 '%\"code\"%' or entry_text_more like '%\"code\"%'";
$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/class\s*=\s*"code"/){ $flag_incode = 1; }
if($flag_incode && $text_line =~ m/<\/div>/){ $flag_incode = 0; }
if(length($text_line)<=0 && $flag_incode){
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);

#
# <div class="code"> 〜 </div> で囲まれた領域で改行のみの行に1文字分の空白を追加する
#
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/class\s*=\s*"code"/){ $flag_incode = 1; }
if($flag_incode && $text_line =~ m/<\/div>/){ $flag_incode = 0; }
if(length($text_line)<=0 && $flag_incode){ $text_line = " "; }
$text .= ($text_line . "\n");
}
return $text;
}

■ コード記述のある記事のみを抜き出してHTML化するスクリプト

mt_export_entry_code.plをダウンロードする(<div class="code"> 〜 </div> 版)
mt_export_entry_pre.plをダウンロードする(<pre> 〜 </pre> 版)

#!/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=./mtdata.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 '%\"code\"%' or entry_text_more like '%\"code\"%'";
$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/class\s*=\s*"code"/){ $flag_incode = 1; }
if($flag_incode && $text_line =~ m/<\/div>/){ $flag_incode = 0; }
if(length($text_line)<=0 && $flag_incode){ print "<span style=\"background-color:red;\"> </span>"; }
if($flag_incode){ print "<span style=\"color:green;\">"; }
print encode_entities($text_line, '&<>\\\"\'');
if($flag_incode){ 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);