Home

JeffreyFrancesco.org

zenback を導入してみました - powered by blosxom なサイト編

Movable Type でおなじみのシックスアパート社さんが、zenback という新サービスを開始されております。どのようなものかというのは、以下 http://zenback.jp/ より引用。

zenbackは、ブログ記事のテンプレートにスクリプトコードを埋め込むことで利用できます。ブログの記事の下や横に、

  • その記事に関係する自分のブログ記事
  • その記事に関係する他のzenbackユーザーのブログ記事
  • その記事についての最新のTwitterのつぶやき
  • その記事についてのはてなブックマーク
  • その記事の最新のアクセス元(リファラ)

を表示します。

発表とほぼ同時にベータテストの申し込みをしていたのですが、この度利用準備が整ったという事で、早速導入してみました。

サービスが利用できるブログの条件も、基本的には以下の二つだけ。

  1. 個別記事表示用テンプレートの HTML をカスタマイズできて
  2. テンプレートに JavaScript のコードを貼ることができる

まあウチは blosxom などという(今や誰も選択肢として考えないような…汗)ブログツールを使っておりますが、かなり自由度は高いツールなので当然上の二つはクリアできてるし大丈夫。若干工夫が必要ですけど、その辺りも含めて簡単に設置方法の説明を。

Continue reading

feedback プラグイン導入メモ - (3)

前回の続き。

実は feedback プラグインを導入した最大の動機が、Akismet による強力なスパム対策。既に WordPress に標準で提供され (有効化は必要ですが) 実績を上げているとなれば、Blosxom でも使いたくなるというのが人情というもので(?)

実際に有効化するためには、Net::Akismet という Perl モジュールが必要です。モジュールがインストールできる環境にあればインストール、でなければ blosxom.cgi 本体と同じ階層に Net というディレクトリを作り、その下に Akismet.pm を置いても OK です。API Key の取得も忘れずに (取得方法の解説サイト)。

/cgi-bin + blosxom.cgi
         |
         - /Net        - Akismet.pm

で、実際にスパムと判断されそうな適当なコメント ("casino" とか "poker" とかのキーワードを入れたもの) を投稿してみましたが…普通に通ってしまいます。アレ? ローカルでやってるからダメなの? なら実際に構築したサイトの方でテスト。

…やっぱり普通に通ってしまいます。適当な他のサイトからスパムっぽいコメントを拾ってきて、そのままコピペして投稿しても通ってしまう。ダメじゃん。

で、サーバのエラーログを見てみますと、何か Use of uninitialized value in string eq ていうエラーが一杯出てる。「初期化されていない変数が比較に使われてる」というエラーだそうで。エラーが出てる部分のプラグインのスクリプトは:

    return 1 if $akismet->check(%fields) eq 'true';

ということは、$akismet->check(%fields) が初期化されていないって事でしょうか? その少し前のコードにて:

    # Attempt to connect to the Askimet service.
    my $akismet = Net::Akismet->new(KEY => $wordpress_api_key,
                    URL => $blosxom::url);
    unless ($akismet) {
    warn "feedback: Akismet key verification failed\n";
    return 0;
    }

    # Set up fields to be verified. Note that we do not use the REFERRER,
    # PERMALINK, or COMMENT_AUTHOR_EMAIL fields supported by Akismet.

    my %fields = (USER_IP => $ENV{'REMOTE_ADDR'});
    if ($is_comment) {
    $fields{COMMENT_TYPE} = 'comment';
    $fields{COMMENT_CONTENT} = $$params_ref{'comment'};
    $fields{COMMENT_AUTHOR} = $$params_ref{'name'};
    $fields{COMMENT_AUTHOR_URL} = $$params_ref{'url'};
    } else {
    $fields{COMMENT_TYPE} = 'trackback';
    $fields{COMMENT_CONTENT} =
        $$params_ref{'title'} . "\n" . $$params_ref{'excerpt'};
    $fields{COMMENT_AUTHOR} = $$params_ref{'blog_name'};
    $fields{COMMENT_AUTHOR_URL} = $$params_ref{'url'};
    }

ってやってるんだけど。で、上記と同じように %fields を埋めたテストスクリプトを書いて走らせてみると、どうも $akismet->check(%fields)undef を返しているようです。うーん…という訳で、Net::Akismet の和訳ドキュメントを読んでみると:

COMMENT_USER_AGENT

必須。コメント投稿者のユーザーエージェント文字列を示します。

ってなってる。改めて先のコードを見直すと、%fieldsUSER_IP は設定されてるけど、COMMENT_USER_AGENT が設定されていません。なるほど、必須パラメータが抜けてるので、undef を返してるんですね。という訳で、コードを以下のように手直し。

    my %fields = (USER_IP => $ENV{'REMOTE_ADDR'});
    # 追加部分
    $fields{COMMENT_USER_AGENT} = $ENV{'HTTP_USER_AGENT'}

これで改めてテストコメントを投稿しますと、今度はちゃんと弾いてくれました。ほっ。

次回は、コメントの処理まわりをもう少し弄ってますので、その辺の解説を。でも一旦この話題はしばらく休むかも。

feedback プラグイン導入メモ - (2)

前回書いたように修正しておけば、とりあえず謎な文字化けは回避できるけど、トラックバックが有効にしてあるサイトなどでは、トラックバック元の文字コードが UTF-8 じゃなければ文字化けが発生しますし、普通に個別ページからコメントする分にはまず問題ないけどそうじゃない場合もある (悪意でわざと文字コードを変えてコメント投稿をされるなど)ので、それに対しては我々の方で対策してやる必要があります。

feedback プラグインのコードを読んでみると、受け取ったコメントやトラックバックのサニタイズ処理を行なっているのは save_feedbacksave_preview の2つのサブルーチンで、それぞれ1157行目と1216行目で:

    $value = escapeHTML($value);

とやってるだけなので、この部分に文字コード判別と変換の処理を加えておきます (ちなみにこの部分は前回書いた通り、自前の escape_HTML サブルーチンで処理するように変更します)。という訳で、変更後の save_feedbacksave_preview サブルーチンの該当部分は:

    my $charset = getcode(\$value);
    $value = Jcode->new(\$value, $charset)->utf8
        if $charset and $charset !~ /^(ascii|utf8)$/;
    $value = escape_HTML($value);

という風に変更しました。始めに getcode で判別した文字コードを変数 $charset に格納した後、$charset があって (つまり undef でない) かつ ascii utf8 以外の時にのみ、Jcode で UTF-8 に変換、その後 escape_HTML してサニタイズ処理を行なう、という具合です。

SuiteX サーバには Perl 5.8.7 がインストールされているので Encode モジュールを使っても良いんでしょうが、UTF-8 フラグが今一理解できてない頃に修正したので、Jcode モジュールを使ってます。まあどちらもインストールされているのですが。

次回は Akismet によるスパム対策の有効化について書く予定です。

feedback プラグイン導入メモ - (1)

という訳で、1回目。

ダウンロードしたのをそのまま plugin ディレクトリに放り込んだだけでは、日本語環境だと少し不具合が起こりました。デフォルトの状態でブラウザからテスト投稿してみると、文字コードの変換は全く起こってない状態にも関わらず、何故か一部のコメントが文字化けしてしまうんです。

色々調べてみると、feedback プラグインは投稿されたコメントを処理する際に、& < > " を実体参照化するために CGI モジュール内の escapeHTML サブルーチンに処理を任せているのですが、どうやらこのサブルーチンの仕様が悪さをしているようで

escapeHTML() を読むと、文字コードが "ISO-8859-1" か "WINDOWS-1252" だった場合に、 "\x8b" と "\x9b" という二文字をエスケープしてそれぞれ "‹" と "›" にしている。コメントによると、 "# Handle bug in some browsers with Latin charsets" だそうで、ブラウザのバグに対処するためのコードらしい。

この "\x8b" "\x9b" という二文字は、 Terminal.app で文字エンコーディングを「欧米 (Windows Latin 1)」にしたときに以下のようにすると表示される文字(UTF-8 だと ? になってしまう)。 wordpress で作ってあるブログのタイトル部分によく使われている、 < と > の小さいっぽい文字のようだ。

ということなのですが、おそらくプラグインから escapeHTML に処理を渡す部分で文字コード (UTF-8) を指定して渡していないため、デフォルトの "ISO-8859-1" と判断されて上記引用部分のエスケープ処理がされ、この二つのバイトを含む文字が化けてしまうのだと思います。なので、プラグインの内部に新規で escape_HTML というサブルーチンを作り:

sub escape_HTML {
    my $text = shift;

    $text =~ s/&/&amp;/mg;
    $text =~ s/</&lt;/mg;
    $text =~ s/>/&gt;/mg;
    $text =~ s/"/&quot;/mg;
    $text =~ s/'/&#39;/mg;  # 無くてもいいかもしれないけど一応

    return $text;
}

んで、コメント処理 etc. で escapeHTML を呼び出しているところを escape_HTML に変更し、実体参照化をプラグイン内部で行なってやるようにすることで対処しておくことにしました。オリジナルのコードでいうと 1157 行目辺りと 1216 行目辺りの2ヶ所になりますね。やってることはほぼ同じ、余計な処理だけ省いたってだけなんですが (笑) とりあえずこれで普通の文字化け (謎) は回避することが出来ます。

という訳で、次回は普通じゃない文字化け (?) への対処法を、近いうちに書きます。

Blosxom v2.1 の設定ファイル

こんな事書いた次のエントリで、さっそく全く関係ない話題をだすオレ。

SourceForge で開発が続けられている Blosxom ですが、version 2.1 で各種の設定を別ファイルにする (事も出来る) 機能が追加されています。「事も出来る」なので、別に今まで通り Blosxom のスクリプト内部の設定を変更しても構わないのですが、今後の色々な手間 (アップグレードとかサーバ移転だとか) の事も考えると、自分は別ファイルに分けておいた方が楽かなあと。Blosxom Starter Kit もそうなってるし (と、無理に正当化を試みる)。

で、その設定ファイルを読みに行く仕組みですが、その部分のスクリプトはこんな感じ。

# Load configuration from $ENV{BLOSXOM_CONFIG_DIR}/blosxom.conf, if it exists
my $blosxom_config;
if ( $ENV{BLOSXOM_CONFIG_FILE} && -r $ENV{BLOSXOM_CONFIG_FILE} ) {
    $blosxom_config = $ENV{BLOSXOM_CONFIG_FILE};
    ( $config_dir = $blosxom_config ) =~ s! / [^/]* $ !!x;
}
else {
    for my $blosxom_config_dir ( $ENV{BLOSXOM_CONFIG_DIR}, $datadir,
    '/etc/blosxom', '/etc' )
    {
        if ( -r "$blosxom_config_dir/blosxom.conf" ) {
            $config_dir     = $blosxom_config_dir;
            $blosxom_config = "$blosxom_config_dir/blosxom.conf";
            last;
        }
    }
}

言葉でいえば、

  1. 環境変数 BLOSXOM_CONFIG_FILE が設定されていればそのファイル
  2. 環境変数 BLOSXOM_CONFIG_DIR ディレクトリにあるファイル blosxom.conf
  3. $datadir ディレクトリにあるファイル blosxom.conf
  4. /etc/blosxom ディレクトリにあるファイル blosxom.conf
  5. /etc ディレクトリにあるファイル blosxom.conf

の順番に設定ファイルを探しに行き、見つかればそれを $blosxom_config 変数に格納して、直後にそれを require すると。ここで面白いのは、設定ファイルを探す処理と同時に、設定ファイルが置かれるディレクトリをグローバル変数である $config_dir に格納するところ。グローバル変数って事は、他のプラグインでも利用することが出来るって事で、例えば :

  • Blosxom 内の $datadir を、設定ファイルだけが置かれているディレクトリに (e.g. /virtual/foo/settings)
  • blosxom.conf ファイル内の $datadir は、実際にエントリが置かれるディレクトリに (e.g. /virtual/foo/entries)

こんな風にしておけば、$config_dir は Blosxom 内の $datadir に設定された値になり、本来の $datadirblosxom.conf で設定した値で上書きされるので、環境変数を自由に設定できないレンタルサーバを利用している場合でも、設定ファイルとエントリファイルを別ディレクトリに置くことが出来ます。同時に例えば config プラグインとか使ってるなら、プラグインのスクリプトを :

my $path = $blosxom::path_info;
$path &&= "/$path";

for (;;) {
do {
  -e "$blosxom::config_dir$path/config.$blosxom::flavour"
    and eval { require "$blosxom::config_dir$path/config.$blosxom::flavour" }
    and ( $@ ? warn $@ : last );
  -e "$blosxom::config_dir$path/config"
    and eval { require "$blosxom::config_dir$path/config" }
    and ( $@ ? warn $@ : last );
} while ($path =~ s/(\/*[^\/]*)$// and $1);
last;
}

1;

こんな感じに変更すれば、config.flavour なんかも blosxom.conf と同じディレクトリに置けるので、これに flavourdir プラグインなんかも組み合わせれば、$datadirの中身がエントリファイルだけになって、非常にすっきりとするよっていう訳です。

で、ウチのディレクトリは参考までにこんな感じ。ディレクトリ名は適当。ちなみに data ディレクトリは Web アクセス出来ない位置で覗けません。Blosxom 本体も blosxom.cgi というファイル名ではないです。

/virtual - /my_dir  + /cgi-bin - blosxom.cgi
                    |
                    + /data    + /entries  - (entry files)
                    |          |
                    |          + /flavours - (flavour files)
                    |          |
                    |          + /plugins  - (plugins)
                    |          |
                    |          + /settings + blosxom.conf
                    |                      + (config files)
                    |
                    + /home    - (.htaccess, css etc.)

.htaccess は、URL から /cgi-bin/blosxom.cgi を隠すために置いてますが、それなら SetEnv BLOSXOM_CONFIG_FILE ... も一緒に書けば済むだろとか突っ込まないで下さい。大人の事情があるんです(何

feedback プラグイン導入メモ - 前書き

Blosxom でサイト作ってる人の殆どは、コメント機能をつけるのに writeback プラグイン (もしくはその派生品) を使っていると思うのですが、ウチで使ってるのは feedback プラグインです。わざわざマイノリティにならなくてもいいのに、とか思う。

後発なだけあって機能は充実してるけど、後発なだけあって解説記事も少ないので (既に writeback 使っている人はわざわざ置き換えて試さないだろうし、新規で Blosxom なサイトを作る人が導入するにしても、既にネット上にさまざまな解説記事が充実してる writeback を選択するよな普通)、ローカルで実験中にもさまざまな問題にぶつかり、試行錯誤しながら何とか導入してたりします。

なので、もしこれから新規で Blosxom なサイトを構築しようとする人が feedback プラグインを使ってみようと考えた時に、もしかしたら何かの役に立てばと思い、自分の試行錯誤の結果をメモしておこうと考えてみたのがこのエントリなんですが、既にどんな改造・修正をしているか忘れてしまったのと、ここまで文章書くのだけでもう3日ほど推敲してはっきり言って疲れたので(ぉ 今後何回かに分けて、思い出しながらエントリを書いていこうと思います。

べっ別に更新するために同じネタを引っ張ろうって訳じゃないんだからっ(ツンデレ

はてなスターつけた

設置したのはいいけど、星が一個も付かなくてちょっぴり悲しい思いをしそうな気がしないでもない。

そんな事はさておき、はてなスターを設置するには「はてなスターをブログに設置するには」に書かれている JavaScript を head 内に埋め込めばいいのだけど、対応ブログサービス以外で設置するにはカスタマイズを行なわないといけない。まあ大抵の場合は、Hatena.Star.SiteConfigentryNodes を自分のサイトに合わせてきちんと設定してやればいいだけだ。

ただ、ウチの場合 vicuna CMS を元にした Flavour を使っていて、一覧ページと個別ページでエントリの見出しレベルが変わってくる (具体的には、一覧ページでは h2 要素・個別ページでは h1 要素がエントリの見出しになる) ので、どちらか一方に合わせてしまうと、もう一方では意図した通りの部分に表示されなくなるということになってしまう。

これを回避するために、最初は一覧ページと個別ページの Flavour を別にしなくちゃダメかな…と思っていたのだけど、別に分けなくても entryNodes の各要素を interpolate_fancy プラグインで条件分岐して出してやれば良いことに気付いたので、最終的には以下のようなコードを書いて対応した。なるべく楽したかったので pagetype プラグインと組み合わせていますが。

<!-- 実際のコードは改行せずに一行で書いています -->
<script type="text/javascript">
Hatena.Star.SiteConfig = {
    entryNodes: {
        <?$pagetype::pagetype ne="story">'div.entry'</?>
        <?$pagetype::pagetype eq="story">'div#main'</?>: {
            uri: <?$pagetype::pagetype ne="story">'h2 a'</?>
                 <?$pagetype::pagetype eq="story">'window.location'</?>,
            title: <?$pagetype::pagetype ne="story">'h2'</?>
                   <?$pagetype::pagetype eq="story">'h1'</?>,
            container: <?$pagetype::pagetype ne="story">'h2'</?>
                       <?$pagetype::pagetype eq="story">'h1'</?>
        }
    }
};
</script>

こう書いておけば、一覧ページでは、

entryNodes : {
    'div.entry': {
        url: 'h2 a',
        title: 'h2',
        container: 'h2'
    }
}

個別ページでは、

entryNodes : {
    'div#main': {
        url: 'window.location',
        title: 'h1',
        container: 'h1'
    }
}

が出力されて、どちらの場合もエントリ見出しの後ろにはてなスターが設置できて、めでたしめでたしと。

素直に Flavour 分けた方がはるかに楽です。

Home

About Author
Syndication

Return to page top