BLOG

Go to top


MT::Boostrap を利用して独自アプリケーションを作成する場合には、MT::Comments のソースが結構役に立つ

June 21, 2008 1:47 PM

A-From を公開している ARK-Web の進地さんが毎週金曜日の午前 10:30 から開催してらっしゃる MT プラグイン勉強会 のストリーミングを見るのが最近の週末の日課(もっと言えばささやかな現実逃避)になっています。

ここ何週間かは「MT::Boostrap を利用して独自アプリケーションを作成する」ことがテーマなのですが、何度かこういったアプリケーションを作成していて気がついた点、役に立ちそうな点をまとめてみます。

独自(?)アプリケーションの代表はやっぱりコメント投稿

Movable Type の外から(実際には「外」ではないのですが) Movable Type のライブラリを利用した CGI アプリケーションを作成する場合、コメント投稿の際の動作を定義しているソースコードが役立ちます。この、コメント投稿に関するスクリプトは、「MTを格納しているディレクトリ/lib/MT/App/Comments.pm」に記述されています。

初期化処理

初期化処理には、アプリケーションの初期化である init ルーチン(メソッド)と、リクエストの初期化である init_request ルーチン(メソッド)の二つがあります。公開されているアプリケーションでは、init_request でメソッドの追加を行っているものが多いですが、Comments.pm では init の方でメソッドの追加を行っています。Comments.pm の該当箇所は以下です。

sub init {
    my $app = shift;
    $app->SUPER::init(@_) or return;
    $app->add_methods(
        login            => \&login,
        login_external   => \&login_external,
        do_login         => \&do_login,
        signup           => \&signup,
        do_signup        => \&do_signup,
        register         => \&register,
        do_register      => \&do_register,
        preview          => \&preview,
        post             => \&post,
        handle_sign_in   => \&handle_sign_in,
        cmtr_name_js     => \&commenter_name_js,
        edit_profile     => \&edit_commenter_profile,
        save_profile     => \&save_commenter_profile,
        red              => \&do_red,
        generate_captcha => \&generate_captcha,
    );
    $app->{template_dir} = 'comment';
    $app->init_commenter_authenticators;
    $app->init_captcha_providers();
    $app;
}

sub init_request {
    my $app = shift;
    $app->SUPER::init_request(@_);
    $app->set_no_cache;
    $app->{default_mode} = 'post';
    my $q = $app->param;

    ## We don't really have a __mode parameter, because we have to
    ## use named submit buttons for Preview and Post. So we hack it.
    if (   $q->param('post')
        || $q->param('post_x')
        || $q->param('post.x') )
    {
        $app->mode('post');
    }
    elsif ($q->param('preview')
        || $q->param('preview_x')
        || $q->param('preview.x') )
    {
        $app->mode('preview');
    }
    elsif ($q->param('reply')
        || $q->param('reply_x')
        || $q->param('reply.x') )
    {
        $app->mode('reply');
    }
    elsif ($q->param('reply_preview')
        || $q->param('reply_preview_x')
        || $q->param('reply_preview.x') )
    {
        $app->mode('reply_preview');
    }
    elsif ( $app->path_info =~ /captcha/ ) {
        $app->mode('generate_captcha');
    }
}

init_request の方では何を定義しているのかというと、押されたボタンによってどのモード(init で追加されたメソッド)を実行するのかを定義しています。

$app->mode('モード名');

とすることで、クライアントがそのモードにリダイレクトされます。コメント投稿の際、Form 要素には確認用ボタンと、送信用ボタンの二つがあるため、このような分岐を行っているようですが、同じような、確認画面をはさむアプリケーションを作成する際は参考にさせてもらいましょう。こうしておくことにより、Form 要素内で

<input type="hidden" name="__mode" value="preview">

とか書いて、こっそりモード情報を送る必要はなくなります(実際には同じことですが、ソースコードが若干のぞかれにくくなります)。また、$app の受け渡しの観点からしても、$app->mode によってリダイレクトしてやる方が望ましい理由があるのですが、この点はあんまりよく調べていないので割愛します。

ここで注意が必要なのは、たとえば画面の再読み込みによる二重投稿を避ける目的で、完了画面はリダイレクトさせる場合があります。例を挙げると、以下のように init で 投稿開始画面である entry、確認画面である preview、保存処理を行う save、保存処理完了後やエラーが発生した場合にリダイレクトして結果表示する result をそれぞれ定義した場合、

sub init {
    my $app = shift;
    $app->SUPER::init(@_) or return;
    $app->add_methods( entry => \&_entry );
    $app->add_methods( preview => \&_preview );
    $app->add_methods( save => \&_save );
    $app->add_methods( result => \&_result );
    $app;
}

リダイレクトの無限ループを回避するため、result モードではリダイレクトがかからないようにしておいてやる必要があります。

sub init_request {
    my $app = shift;
    $app->SUPER::init_request(@_);
    $app->set_no_cache;
    $app->{default_mode} = 'entry';
    return 1 if $app->mode eq 'result'; << こんな感じで
    my $q = $app->param;
	if ( $q->param( 'preview' ) ) {
        $app->mode('preview');
	}
	if ( $q->param( 'save' ) ) {
        $app->mode('save');
	}
}

result モードでは、Movable Type 管理画面のように、パラメータによってエラー表示や、完了表示に変わるようなテンプレートを読み込んで表示するようにすればよいでしょう。テンプレートについては下で触れます。

ログインを要求する

Comments.pm には記述されていませんが、ログインを要する処理にしたい場合は、init ルーチンの add_methods と並べて、以下のように書いておくことで、ログイン状態でない場合はログインフォームを返すことができます。

$app->{requires_login} = 1;

また、ログイン状態の要求をモード単位で設定してやることもできます。その場合には、以下のように記述します。

$app->add_methods( result => {
                                    code           => \&_result,
                                    requires_login => 0,
                                 });
$app->{requires_login} = 1;

上記のようにすると、結果表示を行う result モードではログインは必要ないが、その他の処理ではログインが必要、ということになります。このあたりは、MT::App::CMS (「MTを格納しているディレクトリ/lib/MT/App/CMS.pm」) の core_methods ルーチンが参考になると思います。

送信モードによるエラー処理

フォームに入力された情報を post 送信のみに限定したい場合は、Comments.pm の 209 行目にあるような、以下の処理を入れるとよいでしょう。$app->request_method() で、POST か GET かが取得できます。

return $app->error($app->translate("Invalid request"))
    if $app->request_method() ne 'POST';

プレビュー画面の構築と表示

これには、Comments.pm の 1056 行目から始まる do_preview ルーチンが参考になります。1109 行目から始まる以下の部分で、MT::Template オブジェクトを作成しています。

$tmpl = MT::Template->load({ type => 'comment_preview', << コメント・プレビュー・テンプレートのこと
                             blog_id => $entry->blog_id })
    or return $app->error($app->translate(
        "You must define a Comment Preview template."));

ここで、$tmpl に代入された MT::Temaplte オブジェクトをもとに、1114 行目の、以下のコードのような感じで確認画面を構築するわけですが、

require MT::Blog;
my $blog = MT::Blog->load($entry->blog_id);
my %cond;
my $html = $tmpl->build($ctx, \%cond);

コメント・プレビュー・テンプレート内でエントリーやコメントに関するテンプレートタグが使えるように、$ctx にエントリーのオブジェクトや、コメントのオブジェクトを入れています。1071 行目で、

my $ctx = MT::Template::Context->new;

として $ctx に新規作成した MT::Template::Context のオブジェクトを代入後、1080 行目で

$ctx->stash('comment_preview', $comment);

1092 行目で

$ctx->stash('entry', $entry);

などとしています(他にもあるのですが割愛します)。

独自アプリケーション内で同じようなことをすることを考える場合、システム・テンプレートは追加できないようなので、使用するテンプレートをテンプレートモジュールとして入れておけば、

my $tmpl = MT::Template->load(
    {
        name    => '[テンプレートモジュールの名前]',
        type    => 'custom',
        blog_id => '[ブログID]'
    }
);

のようにして $tmpl に MT::Template のオブジェクトを代入することができますし、<mt:var> や <mt:if> などで使える値を入れておくのであれば(これは Comments.pm には書かれていませんが)、

$tmpl->param(
    {   'mode' => 'preview', }
);

などとして値を入れておいてやれば、テンプレート内で

<mt:var name="mode">

のようにすることで、

preview

と出力されます。こうしておくことにより、アプリケーション制作過程とテンプレート制作過程を切り離してやることが可能になります。

長くなったので、今回はこのへんで…

Comments


Contact me

Copyright © 2005 - 2017 okayama All rights reserved.