How to Write a Plugin


プラグインの書き方

概要

プラグインは、Movable Typeのパブリッシング・プラットフォームを、数ある方法のひとつで拡張するソフトウエアです。 プラグインは、Movable Typeの新しいテンプレート・タグを作成できます。また、既存のタグに「グローバル・フィルターと呼ばれる」アトリビュート(属性)を足すこともできます。さらにエントリーのテキストを構築中に処理できます。また、コールバックをインストールして、データベースのイベント発生時にオブジェクトを処理できます。 プラグイン

MT::Pluginクラス

Movable Type 3.0に新しく搭載されたMT::Pluginクラスは、ユーザーと対話するプラグインのすべてのデータをカプセル化します。 これによってユーザーは、インストールされたプラグインをGUIで操作できるようになりました。

どんなプラグインをインストールしているか常にわかるように、どのプラグインにもMT::Pluginオブジェクトを登録することを推奨します。 しかし、プラグインがこのように登録できなくても、プラグインの他の機能は動作するはずです。

とりわけ、MT::Pluginは、プラグインの名前とバージョン、短い説明、およびドキュメントと設定インタフェースへのリンク情報を保持します。 この情報を供給するために、プラグインが採用できる方法は2種類以上あります。

一通りでない方法

まず、プラグインに、MT::Pluginのサブクラスを記述して、methods name()、description()、doc_link()、およびconfig_link()を実装し、該当するデータをそれぞれに返す方法があります。

また、MT::Pluginのインスタンスを作成し、そのメソッドを使って該当するフィールドを設定することもできます。

$plugin = new MT::Plugin();
$plugin->name("Arthur Dent's Duplicator Plugin, v.0.45");
$plugin->description("Duplicates weblog posts with a minimum of fuss.");

インタフェースへのプラグインの追加

MT::Pluginオブジェクトが作成されると、そのプラグインはMovable Typeのメソッド、add_plugin_slug($plugin)を呼び出して、Movable Typeのインタフェースにおける足場を確立します。 このメソッドは、「メインメニュー」にスロットを作成します。そこには、プラグインの名前と説明が表示され、設定インタフェースとドキュメントのページへのリンクが含まれます。

プラグイン・ファイルには、pl の拡張子が付くはずです(プラグインの検索時には、他の拡張子を持つファイルは無視されます)。 設定ページには通常cgiの拡張子が付きます。これはウェブサーバーがそのページをCGIプログラムとして実行するために必要です。 ただしウェブサーバーによっては、CGIプログラムにplの拡張子を採用しないとCGIプログラムが機能しないものもあります。 したがって、ファイルの拡張子を除いた名前の部分は、独自の名称にしておく方がよいでしょう。

タグの追加

Movable Typeにはさまざまなテンプレート・タグが搭載されているので、プラグインの作成者やデザイナーは、それらを使ってカスタマイズされた外見と使い勝手を持つサイトを作り上げることができます。

プラグインの作成者は、新しいテンプレート・タグを加えてコンピューター処理できる文字列を生成することで、システムを拡張できます。 タグは、MT::Template::Contextのadd_tagおよびadd_conditional_tagメソッドを使って追加します。

MT::Template::Context->add_tag($name, \&subroutine)

add_tagはシステムに単純な「変数タグ」を登録します。 このタグの一例は<$MTEntryTitle$>です。

$nameは接頭語、MTを省いたタグ名で、\&subroutineはサブルーチン(匿名または名前付き)へのリファレンスです。 \&subroutineはエラー(「エラーの取り扱い」を参照)、または定義済みのスカラー値(数値または文字列)を返します。undefを返すとエラーとして扱われるので、その代わりに常時、空の文字列を返します。

例:

MT::Template::Context->add_tag(ServerUptime => sub { `uptime` });

このタグは、<$MTServerUptime$>としてテンプレートに使います。

このサブルーチン・リファレンスは2つの引数を渡されます。 ひとつはテンプレートといっしょに構築するMT::Template::Contextオブジェクトで、もうひとつはテンプレート・タグを介して渡される引数を含むハッシュへのリファレンスです。 たとえば、タグの<$MTFooBar$>が以下のように呼び出されたときは、

<$MTFooBar baz="1" quux="2"$>

このタグに登録されたサブルーチンへの2番目の引数は、以下のようになります。

{
    'quux' => 2,
    'bar' => 1
};

MT::Template::Context->add_container_tag($name, \&subroutine)

「コンテナ・タグ」をテンプレート・システムに登録します。 コンテナ・タグは通常、ループまたは条件文を表すのに使います。 実際にはadd_container_tagをループに使い、add_conditional_tagを条件文に使います。こうしておくと、ほとんどの条件文のタグ・ハンドラーが同じ構造をしているため、後の面倒な作業をほとんど処理してくれます。

$nameは接頭語、MTを省いたタグ名で、\&subroutineはサブルーチン(匿名または名前付き)へのリファレンスです。 \&subroutineはエラー(「エラーの取り扱い」を参照)、または定義済みのスカラー値(数値または文字列)を返します。undefを返すとエラーとして扱われるので、その代わりに常時、空の文字列を返します。

このサブルーチン・レファレンスは2つの引数を渡されます。 ひとつはテンプレートといっしょに構築するMT::Template::Contextオブジェクトで、もうひとつはテンプレート・タグに渡される引数を含むハッシュへのリファレンスです。

コンテナ・タグは一般にループを表すので、サブルーチン内にループ構造を使い一連のアイテムをループさせて、各アイテムのコンテナ内に使うテンプレート・タグを構築する必要があります。 これらの内部に使うテンプレート・タグは、すでにトークンのリストへコンパイルされています。 したがって後は、MT::Builderオブジェクトを使って、このトークンのリストをスカラー文字列に構築し、その文字列を出力値に加えるだけです。 トークンのリストは$ctx->stash('tokens')にあり、MT::Builderオブジェクトは$ctx->stash('builder')にあります。

たとえば、タグの<MTLoop>が以下のように使われたときは、

<MTLoop>
The value of I is: <$MTLoopIValue$>
</MTLoop>

以下は、このタグセットの実装例です。

MT::Template::Context->add_container_tag(Loop => sub {
    my $ctx = shift;
    my $res = '';
    my $builder = $ctx->stash('builder');
    my $tokens = $ctx->stash('tokens');
    for my $i (1..5) {
        $ctx->stash('i_value', $i);
        defined(my $out = $builder->build($ctx, $tokens))
            or return $ctx->error($builder->errstr);
        $res .= $out;
    }
    $res;
});
MT::Template::Context->add_tag(LoopIValue => sub {
    my $ctx = shift;
    $ctx->stash('i_value');
});

<$MTLoopIValue$>は単純な変数タグです。 <MTLoop>はコンテナ・タグとして登録され、1から5までをループし、各番号に付き、<MTLoop></MTLoop>の間でトークンのリストを構築します。 これは、$builder->buildの起動ごとに返るエラー値をチェックします。

上のタグは、以下を表示します。

The value of I is: 1
The value of I is: 2
The value of I is: 3
The value of I is: 4
The value of I is: 5

MT::Template::Context->add_conditional_tag($name, $condition)

「コンテナ・タグ」をテンプレート・システムに登録します。

条件タグは実際にはコンテナ・タグのひとつですが、条件タグを書く作業を簡単にするため、add_conditional_tagメソッドを使うことができます。 $nameは接頭語、MTを省いたタグ名で、$conditionは、条件が合致するかどうかで真か偽の値を返すサブルーチンへのリファレンスです。 条件が合致すると、タグのブロックと条件タグ内にあるマークアップが実行され表示されます。合致しないと、無視されます。

たとえば、以下のコードは2つの条件タグを登録します。

MT::Template::Context->add_conditional_tag(IfYes => sub { 1 });
MT::Template::Context->add_conditional_tag(IfNo => sub { 0 });

<MTIfYes>は常に1を返すので、常にその内容は表示されます。<MTIfNo>は常に0を返すので、その内容はまったく表示されません。 したがってこれらのタグを以下のように使った場合は、

<MTIfYes>Yes, this appears.</MTIfYes>
<MTIfNo>No, this doesn't appear.</MTIfNo>

"Yes, this appears.”だけが表示されます。

もっとおもしろい例は、<MTEntryIfTitle>のタグを加えて、エントリー・コンテキストに使うことです。すると、エントリーにタイトルがある場合はその内容が表示されます。

MT::Template::Context->add_conditional_tag(EntryIfTitle => sub {
            my $e = $_[0]->stash('entry') or return;
            defined($e->title) && $e->title ne '';
        });

このタグを使った一例です。

<MTEntries>
  <MTEntryIfTitle>
    This entry has a title: <$MTEntryTitle$>
  </MTEntryIfTitle>
</MTEntries>

処理後のタグの出力

新しいタグを追加するほかに、プラグインはどんなタグにも適用できるアトリビュート(属性)を追加できます。 アトリビュートに関連したコードは呼び出されると、タグの出力を変換します。

MT::Template::Context->add_global_filter($name, \&subroutine)

$nameはたとえば、”encode_html”などのタグ名を表します。 encode_html=valueのアトリビュートを含むMT(Movable Type)テンプレート・タグはどれも、与えられたサブルーチンを起動します。

コード・リファレンスの\&subroutineは以下のように呼び出されます。

$string = &subroutine($string, $attribute_value, $context)

$stringパラメータは、変換するテキストを示します。 $attribute_valueは、このコードの起動時にアトリビュートに与えられる値です。

<MTEntryTitle encode_html=1>

&subroutineは、$attribute_value1に設定されると起動します。 サブルーチンの最後の引数である$contextは、タグが使われたコンテキストについての情報を含むMT::Template::Contextオブジェクトへのリファレンスです。

プラグインのコールバック

ほとんどのMT::Objectの動作は、プラグインへのコールバックを引き起こします。 この動作による特筆すべき機能として、データベースの記録が変更すると通知が受けられること、およびデータベースに流れているデータの前処理と後処理ができることが挙げられます。

コールバックを追加するには、以下のようにMT::Objectサブクラスのadd_callbackメソッドを起動します。

MT::Foo->add_callback("pre_save", <priority>,
                      <plugin object>, \&callback_function);

最初の引数はフックポイントの名前です。 MT::Objectサブクラスは、以下の各操作におけるpre_およびpost_フックポイントを持っています。

load
save
remove
remove_all
(load_iter操作はloadコールバックを呼び出します)

2番目の引数である <priority>は、コールバックを呼び出す際の相対的な優先順位を示します。 通常、値は1から10の間に設定されます。 優先順位1のコールバックは優先順位2のコールバックより前に呼び出され、2は3より前に呼び出され、以下同様に続きます。

最初または最後に実行する必要があることを知っているプラグインは、それぞれ優先順位0または11を使用できます。 優先順位0のコールバックは、他のコールバックよりも先に実行します。そして2つのコールバックが同じ値を使おうとすると、エラーが発生します。 優先順位11もまた排他的で、そのコールバックは最後に実行します。

どのコールバックの優先順位が特別かを覚えておくにはどうしたよいでしょうか。 たとえば、ギターのアンプには1から10まで音量を調節できるつまみがあります。 しかし、私達のアンプは、一部のロックスターのアンプと同様、11の音量まで上がります。 優先順位11のコールバックは、「最も音量の大きな」最も強力なコールバックです。これは、pre-opコールバックの場合、オブジェクトがデータベースに保存される直前に呼び出され、post-opコールバックの場合、オブジェクトが返される直前に呼び出されるからです。 優先順位0のコールバックは、「最も音量の低い」コールバックで、それに続くコールバックによって完全に圧倒されます。 優先順位0は、あなたのプラグインを他のプラグインとうまく機能させたいと願うならば、あなたのプラグインにとってよい選択になるかもしれません。 正しい優先順位を指定できるかどうかの鍵は、あなたのプラグインが他のプラグインとどのような関係にあるかを考え、あなたのプラグインをユーザーが最大限利用できるよう、経験に基づいて優先順位を調節していくことに尽きます。

<plugin object>は、プラグインについての情報を提供するタイプMT::Pluginのオブジェクトです。 これは、エラー・メッセージにプラグインの名前を含めるために使います。

<callback function>は呼び出されるサブルーチンのコード・リファレンスです。 この関数の引数は、操作によって異なりますが(詳細は「MT::Callback」を参照)、どのケースも最初のパラメータはMT::Callbackオブジェクトそのものです。

sub my_callback {
    my ($cb, ...) = @_;
      if ( <error condition> ) {
        return $cb->error("Error message");
    }
}

正確にはコールバックの戻り値は無視されます。 MT::Callbackオブジェクト(この場合は$cb)のerror()メソッドを呼び出すと、Movable Typeのアクティビティ・ログにエラー・メッセージが生成します。

別のエラーの扱い方はdieを呼び出す方法です。 コールバックがdieすると、MTはアクティビティ・ログにエラーを警告しますが、引き続きMT::Objectの動作を処理します。 したがってそのほかのコールバックは依然として実行され、データベースの操作も続いています。

コールバックの優先順位

プラグインの作成者は、コールバックを登録するたびに、そのコールバックに優先順位を指定し、プラグインが実行する順序を管理します。 優先順位は1から10の間に設定されます。優先順位1のコールバックはどのイベントよりも先に実行され、優先順位10のコールバックはどのイベントよりも後に実行されます。

プラグインを書くときには、それが他のプラグインとどのように関係するかを考慮します。 プラグインが比較的ゆるやかなデータ変換しか要しない場合もあることでしょう。また、プラグインが他のプラグインでデータが使えない状態になるほど、動的な場合もあることでしょう。 より動的なプラグインを使う場合は、その'save'コールバックに高い優先順位を設定してください。

エラーの取り扱い

プラグインのコールバックがdieしても、Movable Typeは引き続き実行し他のコールバックを呼び出します。

ただし、1つのコールバックが別の返ってきたコールバックに依存している場合、コールバックの1つは、何らかの理由で実行されないことがあります。 たとえば、load()に対し実行するコールバックとsave()に対し実行するコールバックの、2つの対称的なコールバックがある場合、saveのコールバックに失敗すると、データベースのデータはloadのコールバックが必要とする形態で得られないこともあります。

テキスト・フィルター

Movable Typeは、エントリーをフォーマット・プロパティを使って作成する上で役立つ、拡張可能なテキスト・フィルターを種々用意しています。 HTMLでエントリーを記入する代わりに、テキスト・フィルターを選ぶことができます。テキスト・フィルターは、記号の一部を洗練されたフォーマット・コマンドに置き換え、エントリー・テキストを変換することができます。 テキスト・フィルターは、エントリー編集画面のポップアップ・メニューに表示されるので、エントリーごとに選択できます。

テキスト・フィルターの追加

テキスト・フィルターはMT->add_text_filter()を呼び出して、以下のように追加します。

MT->add_text_filter($key, {label => $label, 
                            on_format => <executable code>});

$labelは、人間が読めるテキストでフィルターをユーザーに特定します。このテキストはエントリー編集画面のポップアップ・メニューに表示されます。 $keyは、HTMLのnameアトリビュートに使われるIDで、メニューのフィルターは$keyの値によってアルファべット順に並び替えられます。

on_formatキーの値に渡される値はコード・リファレンスです。 このコード・リファレンスは呼び出されると、エントリー・テキストを表示する前に変換します。

このコード・リファレンスは、エントリー編集画面を除き、エントリーが表示されるところであればどこでも呼び出されます。 たとえば、エントリー確認(プレビュー)、<MTEntryBody>タグの結果、トラックバックping、ニュースフィードなどです。

Movable Typeのアプリケーション・ページへのアクションの追加

Movable Typeのアプリケーション・インタフェースのページの一部を使うと、あるタイプのオブジェクトを一覧表示したり、オブジェクトを編集したりすることが可能になります。 こうしたページの多くは、ユーザーがオブジェクトに対しアクションを追加する形式で自然な拡張を許可しています。

これらの1ページにアクションを追加するには、MTクラスのadd_plugin_actionメソッドを呼び出します。

MT->add_plugin_action('entry', $link,
                      'Add one xyzzy monster to this entry');

上記のentryはリンクが表示されるページを示します。 単純なオブジェクト・タイプを渡すと、そのタイプのオブジェクトを「編集」するページにアクションのリンクが表示されるはずです。 そのほか使用可能な値には、commentcategorytemplateauthorなどがあります。

さらに、以下の値のいずれかを最初の引数に渡すと、オブジェクトのリストの1つにアクション・リンクを張ることができます: list_commentslist_commenterslist_entries


Copyright (c) 2001-2004 Six Apart. All Rights Reserved.