MovableType プラグインの作り方 - 第2回:コンテナタグ

Posted by
ぴろり
Posted at
2006/03/30 20:35
Trackbacks
関連記事 (0)
Post Comment
コメントできます
Category
MovableType カテゴリ

 連載2回目です。第1回では、MovableType プラグインの雛型を示すことから始め、続けて簡単な変数タグの説明をしました。
 今回は、これより少し複雑なコンテナタグと条件タグについての説明です。

この記事を Delicious に追加する   このエントリーをはてなブックマークに追加  

目次

 一度では全て書き切れないので、幾つかの連載記事とする予定です。 MovableType のヘルプドキュメント にある内容を網羅し、これに沿った形で書いていくつもりですので、併せて読み進めていただけるとモア・ベターかと思います。 よろしくお付き合いくださいませm(_ _)m

コンテナタグ

 コンテナタグ(container tags)とは、HTML タグで云う所の <div><blockquote> のように、 他の HTML タグやテンプレートタグや文字列を、その中に含むことができるテンプレートタグのことです。 MT のコンテナタグは、その中身(コンテンツ)に対して、何らかの処理を行いたい場合に使用します。
 例えば、<MTEntries> コンテナタグは、 複数のエントリから一つずつエントリを取り出しながら、 その中に含まれる <MTEntryTitle> などのコンテンツを処理します。 また、<MTCalendar> コンテナタグは、 日付を変化させながら、その月の日数分だけ、そのコンテンツを繰返す処理を行います。
 ここでは簡単な例として、コンテンツを <blockquote> で囲むだけの <MTMyBlockquote> コンテナタグを作ってみます。
package MT::Plugin::myplugin_2_1;

use MT::Template::Context;
MT::Template::Context->add_container_tag (MyBlockquote => &my_blockquote);

sub my_blockquote {
	my ($ctx, $args, $cond) = @_;
	my $builder = $ctx->stash('builder');
	my $tokens  = $ctx->stash('tokens');
	my $out = $builder->build ($ctx, $tokens, $cond);
	return $ctx->error ($builder->errstr) if !defined $out;
	'<blockquote>'. $out. '</blockquote>';
}

1;

 そして、何れかのテンプレートに次のように追記し、そのテンプレートを再構築してみましょう。

<MTMyBlockquote>
These string will just be quoted in blockquote section.
</MTMyBlockquote>

コードの詳細

MT::Template::Context->add_container_tag (MyBlockquote => ¥&my_blockquote);
 テンプレートのコンテクストに新しいコンテナタグを追加します。 変数タグの名前は「MTMyBlockquote」で、その処理内容は my_blockquote にある、と云うことです。 変数タグで出てきた add_tag と全く同じです。
my ($ctx, $args, $cond) = @_;
 現在のコンテクストと引数、条件式のハッシュ参照を取得します。やっぱり、おまじないで。
my $builder = $ctx->stash('builder')
my $tokens = $ctx->stash('tokens')
 <MTMyBlockquote> コンテナタグの コンテンツ($tokens)と、 それを処理するためのビルダ($builder)を取得します。
my $out = $builder->build ($ctx, $tokens, $cond);
 ビルダを使ってコンテンツを構築し、その結果を $out に代入します。
return $ctx->error ($builder->errstr) if !defined $out;
 コンテンツ内でテンプレートタグの使い方を間違っていたり、MT に何かエラーが発生した場合、 $out が未定義(undef) になるので、 MT のエラーログにエラー内容($builder->errstr)を返して処理を中断します。
'<blockquote>'. $out. '</blockquote>';
正常に構築されていたら、コンテンツ全体を blockquote タグで括って、 <MTMyBlockquote> コンテナタグの答え(?)とします。

コンテナタグの引数

 変数タグでは、その動作を変更するのに引数を使うことができましたMTMyBlockquote プラグインのソースコードを見ると、 変数タグの時と同じく $args と書かれた部分が見つかります。 お察しのとおり、変数タグと全く同じ方法でコンテナタグでも引数を取ることができるのです。
 そこで、MTMyBlockquote を改造して、 色々な HTML タグでコンテンツを括ることができる <MTAnyBlock> テンプレートタグを作ってみます。
package MT::Plugin::myplugin_2_2;

use MT::Template::Context;
MT::Template::Context->add_container_tag (AnyBlock => &any_block);

sub any_block {
	my ($ctx, $args, $cond) = @_;
	my $builder = $ctx->stash('builder');
	my $tokens  = $ctx->stash('tokens');
	my $out = $builder->build ($ctx, $tokens, $cond);
	return $ctx->error ($builder->errstr) if !defined $out;
	my $tag = $args->{tag} or return $out;
	my $class = $args->{class} or "<$tag>". $out. "</$tag>";
	"<$tag class="$class">". $out. "</$tag>";
}

1;

 そして、何れかのテンプレートに次のように追記し、そのテンプレートを再構築してみましょう。  …このテンプレートタグに意味があるのか、それは考えない方向で(苦笑

<MTAnyBlock tag="blockquote">
  These string will just be quoted in blockquote section.
</MTAnyBlock>
<MTAnyBlock tag="div" class="hoge">
  These string will just be quoted in div section with class name.
</MTAnyBlock>

コードの詳細

my $tag = $args->{tag} or return $out;
 tag="..." で指定された値を $tag に代入します。 何も指定されていない場合は、コンテンツをそのまま返します。
my $class = $args->{class} or "<$tag>". $out. "</$tag>";
 class="..." で指定された値を $class に代入します。 何も指定されていない場合は、コンテンツを $tag タグで括って返します。
"<$tag class=¥"$class¥">". $out. "</$tag>";
コンテンツを $tag$class で括って返します。

コンテナタグ中の変数タグ

 <MTCalendar> コンテナタグの中で使用される <MTCalendarDay> 変数タグのように、 様々に値を変化させるような変数タグはどのように実現すれば良いのでしょうか?
package MT::Plugin::myplugin_2_3;

use MT::Template::Context;
MT::Template::Context->add_container_tag (MyLoop => &my_loop);
MT::Template::Context->add_tag (MyLoopCount => &my_loop_count);

sub my_loop {
	my ($ctx, $args, $cond) = @_;
	my $builder = $ctx->stash('builder');
	my $tokens  = $ctx->stash('tokens');
	my $loop = $args->{count} || 1;
	my $html = '';
	for (my $count = 1; $count <= $loop; $count++) {
		local $ctx->{__stash}{'myplugin::count'} = $count;
		my $out = $builder->build ($ctx, $tokens, $cond);
		return $ctx->error ($builder->errstr) if !defined $out;
		$html .= $out;
	}
	$html;
}

sub my_loop_count {
	my ($ctx, $args) = @_;
	$count = $ctx->stash('myplugin::count');
	return $ctx->error ('MTMyLoopCount must be used in MTMyLoop') if !defined $count;
	$count;
}

1;

 そして、何れかのテンプレートに次のように追記し、そのテンプレートを再構築してみましょう。

<MTMyLoop count="5">
  Loop Count is <MTMyLoopCount><br />
</MTMyLoop>

コードの詳細

my $loop = $args->{count} || 1;
 <MTMyLoop> コンテナタグの引数 count="..." から繰返す回数を取得します。
for (my $count = 1; $count <= $loop; $count++)
$loop 回だけ処理を繰返します。 現在の繰り返し回数は $count です。
local $ctx->{__stash}{'myplugin::count'} = $count;
 現在の繰り返し回数 $countmyplugin::count に記憶しておきます。
$html .= $out;
 この処理で得られた結果を、今までのコンテンツの処理結果の最後に付けたします。
$count = $ctx->stash('myplugin::count');
 myplugin::count に記憶してある内容を $count に代入します。
return $ctx->error ('MTMyLoopCount must be used in MTMyLoop') if !defined $count;
 $count が未定義(!defined) だった場合、 <MTMyLoopCount> 変数タグは <MTMyLoop> コンテナタグの中でしか使えないことを示すエラーメッセージを返します。

条件タグ

 条件タグ(conditional tags)は、<MTEntryIfExtended> のように、 ある条件が成立した場合に、そのコンテンツを表示したい場合に使用します。 条件タグは、コンテナタグと非常に似ています。 例として、その日が日曜日だった場合に成立する <MTIfSunday> 条件タグを作ってみます。
package MT::Plugin::myplugin_2_4;

use MT::Template::Context;
MT::Template::Context->add_conditional_tag (IfSunday => &if_sunday);

sub if_sunday {
	my ($ctx, $args, $cond) = @_;
	my $e = $ctx->stash ('entry') or return $ctx->_no_entry_error('MTIfSunday');
	my $ts = $e->created_on;
	MT::Util::wday_from_ts ($ts =~ m/^(dddd)(dd)(dd)/) == 0;
}

1;

 そして、何れかのテンプレートに次のように追記し、そのテンプレートを再構築してみましょう。

<MTEntries>
  <MTIfSunday>
    <MTEntryTitle> in Wonderful Sunday !<br />
  </MTIfSunday>
</MTEntries>

コードの詳細

my $e = $ctx->stash('entry') or return $ctx->_no_entry_error('MTIfSunday');
 現在のエントリを取得し、$e に代入します。 エントリが無い場合、エラーを返します。
my $ts = $e->created_on;
 エントリ $e の投稿日を $ts に代入します。
MT::Util::wday_from_ts ($ts =‾ m/^(¥d¥d¥d¥d)(¥d¥d)(¥d¥d)/) == 0;
 MT::Util::wday_from_ts 関数を使用して、投稿日の曜日を取得します。 曜日が日曜日(0)の場合には真、日曜日以外(1〜6)の場合には偽を返します。
 条件タグはコンテナタグと非常に似ています。 これを示すために、先の <MTIfSunday> 条件タグをコンテナタグとして無理矢理に書き換えてみました。 それが次のソースコード(sample_2_4a.pl)です。
package MT::Plugin::myplugin_2_4a;

use MT::Template::Context;
MT::Template::Context->add_container_tag (IfSunday => &if_sunday);

sub if_sunday {
	my ($ctx, $args, $cond) = @_;
	my $e = $ctx->stash ('entry') or return $ctx->_no_entry_error('MTIfSunday');
	my $ts = $e->created_on;
	if (MT::Util::wday_from_ts ($ts =~ m/^(dddd)(dd)(dd)/) == 0) {
		my $builder = $ctx->stash('builder');
		my $tokens  = $ctx->stash('tokens');
		my $out = $builder->build ($ctx, $tokens, $cond);
		return $ctx->error ($builder->errstr) if !defined $out;
		return $out;
	}
	'';
}

1;

 日曜日の場合のみ、ビルダでそのコンテンツを構築し、その内容をそのまま返すようにしています。 ビルダやコンテンツの取得部分は、コンテナタグと全く同じです。 そして日曜日以外の場合には、このコンテナタグは空文字を返すようにしています。 その結果、このプラグインは条件タグとして記述した <MTIfSunday> と全く同じ機能を持つことがわかります。
 このように条件タグと同じ機能をコンテナタグを利用して実装することも可能ですが、 単に条件によってコンテンツを表示するか否かを選択したいだけの場合は、条件タグを使用するのが良いでしょう。

 また、変数タグやコンテナタグと同様に、 $args を使用して引数を扱うことができる点も同じです。

コンテナタグ中の条件タグ

 普通の条件タグと特段変わったことはありません。 コンテナタグの中で変数タグを扱ったように、stash を使用して条件判定を行うこともできます。 ここでは、先の <MTMyLoop> で、 <MyLoopCount> の値が偶数か奇数かを判定する条件タグを書いてみましょう。
package MT::Plugin::myplugin_2_3a;

use MT::Template::Context;
MT::Template::Context->add_container_tag (MyLoop => &my_loop);
MT::Template::Context->add_tag (MyLoopCount => &my_loop_count);
MT::Template::Context->add_conditional_tag (IfCountOdd => &my_loop_count_odd_even);
MT::Template::Context->add_conditional_tag (IfCountEven => &my_loop_count_odd_even);

sub my_loop {
	my ($ctx, $args, $cond) = @_;
	my $builder = $ctx->stash('builder');
	my $tokens  = $ctx->stash('tokens');
	my $loop = $args->{count} || 1;
	my $html = '';
	for (my $count = 1; $count <= $loop; $count++) {
		local $ctx->{__stash}{'myplugin::count'} = $count;
		my $out = $builder->build ($ctx, $tokens, $cond);
		return $ctx->error ($builder->errstr) if !defined $out;
		$html .= $out;
	}
	$html;
}

sub my_loop_count {
	my ($ctx, $args) = @_;
	$count = $ctx->stash('myplugin::count');
	return $ctx->error ('MTMyLoopCount must be used in MTMyLoop') if !defined $count;
	$count;
}

sub my_loop_count_odd_even {
	my ($ctx, $args, $cond) = @_;
	$count = my_loop_count ($ctx, $args) or return $count;
	return ($count % 2) == 1 if $ctx->stash('tag') eq 'IfCountOdd';
	return ($count % 2) == 0 if $ctx->stash('tag') eq 'IfCountEven';
	return 0;
}

1;

 そして、この条件タグを使用したテンプレートは次のようになります。

<MTMyLoop count="5">
  Number <MTMyLoopCount> is an
  <MTIfCountOdd>Odd</MTIfCountOdd>
  <MTIfCountEven>Even</MTIfCountEven>
  number.<br />
</MTMyLoop>

サンプルファイルのダウンロード

 この記事で使用したプラグインのプログラムコードを ダウンロードできます。
この記事を Delicious に追加する   このエントリーをはてなブックマークに追加  


この記事を読んだ人はこんな記事も読んでいます記事リコメンデーションについて

カバー画像:MovableType プラグインの作り方 - 第3回:フィルタ

関連記事/トラックバック

関連記事/トラックバックはまだありません

この記事にトラックバックを送るには?

コメントを投稿する

 
 (必須, 匿名可, 公開, トリップが使えます)
 (必須, 匿名可, 非公開, Gravatar に対応しています)
 (必須)
スパム コメント防止のため「投稿確認」欄に ランダムな数字 CAPTCHAについて を入力してから送信してください。お手数ですがご協力のほど宜しくお願いいたします。