この記事はTransformation callbacksという以下の記事を日本語に翻訳したものです。 Transformation Callbacks
変形コールバック
この記事はArvind Satyanarayanの書いた同じトピックのブログ記事に触発されたもので、かなりそこから引用している。
MTはユーザーインタフェースをカスタマイズする沢山の方法がある。 その中には
- カスタムのアプリケーションを作って、全く新しい画面を追加
- 代替テンプレートをすでにあるアプリケーションテンプレートに上書き
- 標準的Perl APIを用いてUIを修正する事ができる「変形コールバック」を定義する
この文章はこのうちの3番目(変形コールバック)を使うための案内です。どうすればMTのUIに要素を追加するできるかを、沢山の例を通して紹介します。そうすることで使用感が向上するでしょう。
変形コールバックの概観
主に2種類の変形コールバックがあります。それぞれ異なったAPIを利用しています。
- 正規表現コールバック
- DOMコールバック
変形コールバックの登録
DOMメソッドを使うためにはMT::Templateオブジェクトにアクセスする必要がある。 トランスフォーマー・プラグインでは、template_paramコールバックの中に フックする必要がありますが、渡された最後の要素がMT::Templateオブジェクトです。 (この行、意味不明)以下の例はMTの「Edit Entry」画面を修正する方法です。 最初にすることは、変形コールバックをレジストリに追加することです。
sub init_registry {
my $plugin = shift;
$plugin->registry({
callbacks => {
'MT::App::CMS::template_param.edit_entry'
=> sub { $plugin->edit_entry(@_); }
}
});
}
変形コールバックを登録する書式は簡単です。最初にCMSのパッケージ名を参照し、 そこに(ファイルシステムに存在する)修正したいテンプレート名を付加します。 上記の場合、CMSオブジェクトはMT::App::CMS::template_paramで、 edit entry 画面のテンプレート名はedit_entryです。
変形コールバックのコードサンプル
上記のコード例が参照する実際のコールバックは次のようになるでしょう。
sub edit_entry {
my $plugin = shift;
my ($cb, $app, $param, $tmpl) = @_;
# $tmpleは指定したMT::Templateオブジェクトです!
# 煩雑なDOMについては以下。
}
コールバックには4つのパラメータが渡される。
- コールバックオブジェクト - 実際のMT::Callbackオブジェクト
- appコンテキスト - アプリケーションの現在の状態、これは設定変数などにアクセスするのを許す
- パラメータのハッシュ - これは表示されるか修正されるかする現在の画面に提供されるパラメータ
- テンプレートオブジェクト - 現在のページを表現するMT::Templateオブジェクト
DOM変形コールバック
「DOM」というのはDocument Object Modelの略で、ページの要素を要素の名前、 その種類、そのIDなどに基づいて探索したり修正したりする手法です。 この概念の簡単な説明を以下に記します。
- quirksmode.org's Introduction to the DOM - 強く推奨、 quirksmode はJavaScriptのチュートリアルと情報を提供する最良のサイトの一つです。
- W3C School's XML DOM Tutorial
- Mozilla's DOM Developer Docs
DOMノードの評価
前に指摘したように、最初のステップはテンプレートから修正部位(マーカー)を得る事です。そのためのメソッドはいくつかあります。
$tmpl->getElementByID('id') - このメソッドは与えられたidに合致するノード(テンプレートタグ)を返します。記事作成画面で、basename app:settingタグがマーカーなら、単に次のようにします。
my $basename_setting = $tmpl->getElementById('basename');
$tmpl->getElementsByTagName('include') - これはテンプレートの全てのタグを検索して与えられたタグ名と合致するものを見つけ出すメソッドです。(MTやmt:などのテンプレートの名前空間を含んではいけません)注意:このタグは合致するタグの配列を返します。(ページ上にたくさんの同じテンプレートタグが存在し得ます)
my @includes = $tmpl->getElementsByTagName('include');
$tmple->getElementsByClassName('msg') - クラスの属性が渡された値(msg)と合致するテンプレートタグの配列を返します。
- $tmpl->getElementsByName('submit') - 恐ろしい事に、このメソッドは名前属性が渡された値と合致する(この場合はname="submit")テンプレートタグの配列を返します。
DOMノードの修正
上記の全ての場合で、ひとつのテンプレートタグかテンプレートタグの配列になる。(テクニカルにはテンプレートノードという)それを使用するのには沢山のメソッドがある。(MT::Template::NOdeの定義に基づくTemplate.pm内にある)以下にすぐ使えるふたつを紹介します。
ノードの属性を取得/変更
getAttributeとsetAttributeというメソッドは名前のままの意味で、ノードの属性を操作できます。例えば、新しいhiddenクラスは(スタイルをnoneと設定するよりも)簡単に要素を隠すことができます。たとえば、ある要素をhiddenを使って隠したいとしましょう、その場合は次のようにします。
my $node = $tmpl->getElementById('title');
# 要素がすでに存在するときのためにセットする前に属性を取得します
# そうしてあとはhiddenを付加するだけです
my $class_attr = $node->getAttribute('class');
$class_attr = 'hidden ' . $class_attr;
$node->setAttribute('class', $class_attr);
DOMを移動
DOMは本質的に異なるノードの階層です。よって、DOM内のあるノードにいるとき、簡単に上や下へ移動できます。これをするには以下のようにします。
<mt:loop name="object_loop">
<mtapp:setting
id="$id"
label="Name">
<input type="text" name="name"
value="<mt:var name="name" escape="html">" id="name" />
</mtapp:setting>
<mt:setvarblock name="status_msg_id">
status_msg_<mt:var name="id">
</mt:setvarblock>
<mtapp:statusmsg
id="$status_msg_id"
class="success">
This was created on <mt:var name="created_on">
</mt:appstatusmsg>
</mt:loop>
$object_loopがあるのは
[
{
id => 1,
name => 'melody',
created_on => 20070502
}
]
それではここにこの変形プラグインでできること
# はじめにapp:setting#$idを取得
my $setting = $tmpl->getElementById('1');
# これでloopが得られる
my $loop = $setting->parentNode;
# ループ内の全てのタグの配列が得られる
my $children = $loop->childNodes;
# setvarblockが得られる
my $setvarblock = $setting->nextSibling
上記はとても簡単な例に過ぎず、DOM APIメソッドはJavascript DOM APIに則って他にもあります。
DOMノードを作る
この記事の最初で述べたように、典型的には変形するときはマーカーを取得し、移動し、そして要素を追加するという過程を経る。それでは最後の部分に付いてみてみよう。
簡単にノードを作れる二つのきわめて便利な方法がある。ひとつは名前そのままのcreateElementである。createElementにタグの名前をその属性のリストと一緒に渡せばよい。次のような要素を作りたいとする
<mtapp:setting id="foo_bar" required="1"></mtapp:setting>
変形プラグインのコードは次のよう
my $setting = $tmpl->createElement(
'app:setting',
{ id => 'foo_bar', required => 1 });
次に、ノードの内容をセットするために(コンテナ/ブロックタグにのみ有効)、要素のinnerHTMLメソッドを呼び出す。
my $innerHTML = '<input type="text" name="foo_bar"
value="<mt:var name="foo_bar" escape="html">"
id="foo_bar" />';
$setting->innerHTML($innerHTML);
このperlコードは次のようなソースを生成するのに必須です。
<mtapp:setting id="foo_bar" required="1">
<input type="text" name="foo_bar"
value="<mt:var name="foo_bar" escape="html">"
id="foo_bar" />
</mtapp:setting>
最後のステップは新しく生成した要素のDOMへの挿入です。このために、insertBeforeとinsertAfterという有用なメソッドがあり、新たな要素とマーカーを渡します(これにより要素をマーカーの前か後に挿入します)。例えば、$settingをentrty editing画面のbasenameフィールドの前に加えたい場合、変形プラグインは次のようになります。
# マーカーを取得
my $basename_field = $tmpl->getElementById('basename');
# 要素を生成
my $setting = $tmpl->createElement(
'app:setting',
{ id => 'foo_bar', required => 1 });
my $innerHTML = '<input type="text" name="foo_bar"
value="<mt:var name="foo_bar" escape="html">"
id="foo_bar" />';
$setting->innerHTML($innerHTML);
# basenameの前に要素を付与
$tmpl->insertBefore($setting, $basename_field);
以下のようなソースが生成されます
<mtapp:setting id="foo_bar" required="1">
<input type="text" name="foo_bar"
value="<mt:var name="foo_bar" escape="html">"
id="foo_bar" />
</mtapp:setting>
<mtapp:setting
id="basename"
label="$basename_label"
help_page="entries"
help_section="basename">
<input type="hidden" name="basename_manual"
id="basename_manual" value="0" />
<input type="hidden" name="basename_old" id="basename_old"
value="<$mt:var name="basename_old" escape="html"$>" />
.......
正規表現コールバック
正規表現コールバックはほぼ同じに機能するが、ページ上のテキストは基本的な文字列置換、文字列操作によって変更されます。例えば、正規表現を使ってテンプレートを カスタマイズするための変形コールバックは以下のようになります。
sub my_cb {
my ($cb, $app, $tmpl) = @_;
my $slug = <<END_TMPL;
A whole bunch of HTML here
END_TMPL
$$tmpl =~ s/(<li><mt:__trans phrase=\"Utilities\">\n<ul class=\"sub\">)/$1$slug/;
}