Japanese plugin dev 2 1 - Hiranyaloka/Documentation GitHub Wiki
今回は既に「テストドリブンでのプラグインの開発について」で紹介しているファンクションタグについて、改めて解説させていただきます。
ファンクションタグとは、タグの内部処理を行い、その結果をブログ記事やウェブページなどに出力するものです。
前回のファンクションタグ"<MTHelloWorld>"は、特に処理をせず毎回"Hello, world!"を出力するだけの物でしたが、今回はデータベース処理なども含めて解説します。
仕様としては以下を満たす実装を行います。
- システムのプラグイン設定画面での表示
- プラグイン名:「サンプルプラグイン ファンクションタグ 1.0」
- 詳細:「ファンクションタグ テストプラグイン」
- 詳細>ドキュメント:「http://www.example.com/docs/」
- 詳細>プラグイン作者:「http://www.example.com/about/」
- リソース:「タグ:<$MTCSSByCategory$>」
- ブログ記事アーカイブのテンプレートの<head>に"<MTCSSByCategory>"タグを追加するだけ
- ブログ記事にカテゴリが無い場合は、何も出力しない
- ブログ記事にカテゴリがある場合は以下の処理をする
- カテゴリの親子関係を確認する
- 親カテゴリの場合は、"cat-($category->basename)"というフォルダ名を作成する
- 親カテゴリがある場合は、"cat-($parent-&t;basename)-($category->basename)"というフォルダ名を作成する
- 親子関係は孫や孫の子など階層に制限無し
- このフォルダ名から、CSS名をmt-confing.cgiに設定されているStaticWebPath配下のファイルとヒモ付けます
- mt-config.cgiに設定されているStaticWebPath配下の"support"ディレクトリ以下のCSSにひもづける 例)"/mt-static/support/css/cat-foo-subfoo/screen.css"
- cssファイルを配置するHTMLタグを出力する "<link rel=“stylesheet” href=“/mt-static/support/css/cat-foo-subfoo/screen.css” type=“text/css” />"
- 書き込まれたCSSファイルのパスにCSSを配置すると、CSSの要素が新設や上書きされる
- このタグはブログ記事アーカイブ以外、例えばカテゴリアーカイブなどでは動作せずエラーとなります。
“$MT_DIR/t/mysql-test.cfg”に以下の一行を追加
StaticWebPath /mt-static/
従来と同じく"$MT_DIR/t/"配下になります。
例)00-compile.t, 01-tags.t
$MT_DIR/
|__ plugins/
|__ MyPlugin05/
|__ t/
|_ 00-compile.t
|_ 01-tags.t
前々回行ったMyPlugin03と基本的に同じです。各オブジェクトが正しくロードされているかを確認します。
use strict;
use lib qw( t/lib lib extlib );
use warnings;
use MT;
use Test::More tests => 5;
use MT::Test;
ok(MT->component ('MyPlugin05'), "MyPlugin05 plugin loaded correctry");
require_ok('MyPlugin05::L10N');
require_ok('MyPlugin05::L10N::ja');
require_ok('MyPlugin05::L10N::en_us');
require_ok('MyPlugin05::Tags');
1;
テストするのは以下の3項目です。
- 空文字列をインプットすると、空文字列が戻ってくる。
- 親子カテゴリを持ったエントリー1件を取得し、CSSのリンク"<link rel=“stylesheet” href=“/mt-static/support/css/cat-foo-subfoo/screen.css” type=“text/css” />"が戻ってくる。
- カテゴリを持たないエントリー1件を取得し、空文字列が戻ってくる。
(前略)
#===== Edit here
my $test_json = <<'JSON';
[
{ "r" : "1", "t" : "", "e" : ""},
{ "r" : "1", "t" : "<mt:Entries id=\"7\"><MTCSSByCategory></mt:Entries>",
"e" : "<link rel=\"stylesheet\" href=\"/mt-static/support/css/cat-foo-subfoo/screen.css\" type=\"text/css\" />"},
{ "r" : "1", "t" : "<mt:Entries id=\"8\"><MTCSSByCategory></mt:Entries>", "e" : ""}
]
JSON
#=====
前々章で作成したMyPlugin03を元にプラグインを作成します。
グローバル・モディファイアの追加には以下のように"tags"=> “modifier”=> “モディファイア名” => $プラグイン名::ハンドラ名 を記述します。
id: MyPlugin05
name: <__trans phrase="Sample Plugin function tag">
version: 1.0
description: <__trans phrase="_PLUGIN_DESCRIPTION">
author_name: <__trans phrase="_PLUGIN_AUTHOR">
author_link: http://www.example.com/about/
doc_link: http://www.example.com/docs/
l10n_class: MyPlugin05::L10N
tags:
function:
CSSByCategory: $MyPlugin05::MyPlugin05::Tags::_hdlr_cssbycategory
package MyPlugin05::L10N; use strict; use base 'MT::Plugin::L10N'; 1;
package MyPlugin05::L10N::en_us;
use strict;
use base 'MyPlugin05::L10N';
use vars qw( %Lexicon );
%Lexicon = (
'_PLUGIN_DESCRIPTION' => 'Sample function tag',
'_PLUGIN_AUTHOR' => 'Plugin author',
);
1;
package MyPlugin05::L10N::ja;
use strict;
use base 'MyPlugin05::L10N::en_us';
use vars qw( %Lexicon );
%Lexicon = (
'Sample Plugin function tag' => 'サンプルプラグイン ファンクションタグ',
'_PLUGIN_DESCRIPTION' => 'ファンクションタグ テストプラグイン',
'_PLUGIN_AUTHOR' => 'プラグイン作者',
);
1;
package MyPlugin05::Tags;
use strict;
sub _hdlr_cssbycategory {
my ($ctx, $args) = @_;
my $entry = $ctx->stash('entry')
|| $ctx->error(MT->translate('You used an [_1] tag outside of the proper context.', 'CSSByCategory'));
my $cat = $entry->category() || return;
my @basenames = ();
while (1){
if ($cat->parent() == 0) {
push (@basenames, $cat->basename());
last;
}
push (@basenames, $cat->basename());
$cat = MT::Category->load($cat->parent());
}
my $cat_tmp = 'cat-' . (join '-', reverse @basenames);
$cat_tmp .= '/screen.css';
my $mt = MT->instance;
my $css_dir = $mt->config('StaticWebPath') || '/mt-static/';
unless ($css_dir =~ m/\/$/) {
$css_dir .= '/';
}
$css_dir .= 'support/css/';
return '<link rel="stylesheet" href="' . $css_dir . $cat_tmp . '" type="text/css" />';
}
1;
- 初めにpackage宣言をし、"use strict;"しています。

- ハンドラ関数宣言と呼び出し時の"$ctx", “$args”の代入です。

- $ctx->stash(‘entry’)とすると、今処理をしているEntryオブジェクトが取得できます。
Entryオブジェクトが取得できない場合、エラーを出力します。

- Entryオブジェクトからカテゴリを取得します。取得できない場合はブログ記事にカテゴリが設定されていないため、何も出力せず処理を抜けます(return)。

- @basenames配列を用意し、ブログ記事に設定されているカテゴリの"basename(カテゴリアーカイブに用いるURL PATH)" を孫>子>親の順に格納していきます。
カテゴリの"parent"には親カテゴリのIDが入っています。親の場合は"0"が入っているのでそこで処理を終了します。
カテゴリを呼び出すには19行目の"MT::Category->load($cat_id);“を用います。今回は子や孫カテゴリですので、親カテゴリのカテゴリ idである”$cat->parent()"をloadの引数として渡します。

- @basename配列の順を「孫>子>親」からreverseの利用で「親>子>孫」の順に変更。
変更後、’-’でjoinし文字列にします。その上で初めに"cat-“を、終わりに”/screen.css"を追加し$cat_tmpに代入します。

- “MT->instance”でアプリケーションの情報を取得し、その中のStaticWebPath設定を$css_dirに代入します。StaticWebPathの設定がされていない場合’/mt-static/‘を指定します。
$css_dirの末尾に’/’があるか確認し、無い場合は付加します。その後’support/css/’を追加しています。

- 最後にPATHやタグを追加しreturnします。
returnされる値は’<link rel=“stylesheet” href=“/mt-static/support/css/cat-foo-subfoo/screen.css” type=“text/css/”>’のような形になります。

ファイル名はファンクションタグである事と<MTCSCByCategory>タグである事から、function.mtcssbycategory.phpとなります。
<?php
function smarty_function_mtcssbycategory($args, &$ctx) {
$entry = $ctx->stash('entry');
if (!$entry) {
return $ctx->error('You used an MTCSSByCategory tag outside of the proper context.');
}
$cat = $entry->category();
if (!$cat) {
return;
}
while (1) {
if ($cat->parent == 0) {
$basenames[] = $cat->basename;
break;
}
$basenames[] = $cat->basename;
$loaded = $cat->Load("category_id = " . $cat->parent);
}
$cat_tmp = 'cat-' . join ('-', array_reverse($basenames));
$cat_tmp .= '/screen.css';
$mt = MT::get_instance();
$css_dir = $mt->config('StaticWebPath') or '/mt-static/';
if (!ereg('\/$', $css_dir)) {
$css_dir .= '/';
}
$css_dir .= 'support/css/';
return '<link rel="stylesheet" href="' . $css_dir . $cat_tmp . '" type="text/css" />';
}
?>
functionタグの作り方はPerl版と大きな差がありません。大きく違っている場所を 強調 しました。
- function名は smarty 記法にのっとって smarty_function_mtcssbycategory となります。一緒に呼び出し時の"$args",“$ctx”の取得を行います。Perl版とは位置が逆になっている事に注意してください。
-
$ctx->stash('entry')とすると、今処理をしているEntryオブジェクトが取得できます。Entryオブジェクトが取得できない場合、エラーを出力します。
- Entryオブジェクトからカテゴリを取得します。取得できない場合はブログ記事にカテゴリが設定されていないため、何も出力せず処理を抜けます(return)。
-
basenames[] 配列を用意し、ブログ記事に設定されているカテゴリの"basename(カテゴリアーカイブに用いるURL PATH)"を孫>子>親の順に格納していきます。
カテゴリの"parent"には親カテゴリのIDが入っています。親の場合は"0"が入っているのでそこで処理を終了します。

$catは既にカテゴリオブジェクトなので"$cat->Load()"でデータベースから値の取得ができます。何も無いところから新しくカテゴリオブジェクトをロードするには以下のようにする必要があります。
$category = new Category;
$loaded = $category→Load("category_id = " . $cat→parent);
$cat = $category;
- basename[]配列の順を「孫>子>親」から array_reverse の利用で「親>子>孫」の順に変更。変更後、’-’でjoinし文字列にします。その上で初めに"cat-“を、終わりに”/screen.css"を追加し$cat_tmpに代入します。
-
“MT::get_instance()” でアプリケーションの情報を取得し、その中のStaticWebPath設定を$css_dirに代入します。StaticWebPathの設定がされていない場合’/mt-static/‘を指定します。
$css_dirの末尾に’/’があるか確認し、無い場合は付加します。その後’support/css/’を追加しています。
- 最後にPATHやタグを追加しreturnします。
returnされる値は’<link rel=“stylesheet” href=“/mt-static/support/css/cat-foo-subfoo/screen.css” type=“text/css/”>’のような形になります。
$MT_DIR/
|__ plugins/
|__ MyPlugin05/
|__ config.yaml
|__ lib/
| |_ MyPlugin05/
| |__ L10N.pm
| |_ L10N/
| | |_ en_us.pm
| | |_ ja.pm
| |_ Tags.pm
|__ php/
| |_function.mtcssbycategory.php
|__ t/
|_00-compile.t
|_01-tags.t
では、作成したプラグインに対してテストを行います。
$ cd $MT_DIR $ perl plugins/MyPlugin05/t/00-compile.t 1..5 ok 1 - MyPlugin05 plugin loaded correctry ok 2 - require MyPlugin05::L10N; ok 3 - require MyPlugin05::L10N::ja; ok 4 - require MyPlugin05::L10N::en_us; ok 5 - require MyPlugin05::Tags;
$ perl plugins/MyPlugin05/t/01-tags.t 1..7 ok 1 - 'blog-name' template found ok 2 - Test blog loaded ok 3 - Test entry loaded ok 4 - perl test 1 ok 5 - perl test 2 ok 6 - perl test 3 ok 7 - ok - php test 1 ok - php test 2 ok - php test 3
$ prove plugins/MyPlugin05/t/*.t plugins/MyPlugin05/t/00-compile....ok plugins/MyPlugin05/t/01-tags.......ok All tests successful. Files=2, Tests=12, 27 wallclock secs (12.70 cusr + 5.31 csys = 18.01 CPU)
テストでの確認は終了しました。実際に管理画面からブログ記事アーカイブの"<title>“タグの上に”<MTCSSByCategory>“タグを記入し、再構築すると”<title>"タグの上にカテゴリ毎のCSSファイルのリンクが書き込まれます。
- 記入
(前略)
<MTCSSByCategory>
<title><$mt:EntryTitle encode_html="1"$> - <$mt:BlogName encode_html="1"$></title>
</head>
(後略)
- 出力
(前略)
<link rel="stylesheet" href="/mt-static/support/css/cat-foo-subfoo/screen.css" type="text/css" />
<title>Verse 4 - none</title>
</head>
(後略)
- MTの管理画面が利用できる環境を用意します。(新規作成することを推奨します。)
- クラシック ウェブサイト > クラシックブログを作成します。
- 今回作成したMyPlugin05をPlugin配下に配置します。
- ブログ記事 アーカイブのテンプレートに、上記の形で<title>の上に<MTCSSByCategory>を記入し保存します。
- ブログ記事メニューのカテゴリから、親カテゴリ"foo"、子カテゴリ"subfoo"を作成してください。
- ブログ記事を3つ投稿します。1.カテゴリなし、2.カテゴリ"foo"、3.カテゴリ"subfoo"
- 各ブログ記事の出力結果の<title>タグの1行上をチェックします。(StaticWebPathが/mt-static/だった場合)
- カテゴリなし:空行
- 親カテゴリ:<link rel=“stylesheet” href=“/mt-static/support/css/cat-foo/screen.css” type=“text/css” />
- 子カテゴリ:<link rel=“stylesheet” href=“/mt-static/support/css/cat-foo-subfoo/screen.css” type=“text/css” />
- “/mt-static/support/”以下にディレクトリ"css/cat-foo"と"css/cat-foo-subfoo"を作成します。
- 作成したディレクトリにCSSファイル “screen.css”を作成します。
- 簡単な例
- /mt-static/support/css/cat-foo/screen.css
#container-inner,
#content {
background-color: #008080;
}
- /mt-static/support/css/cat-foo-subfoo/screen.css
#container-inner,
#content {
background-color: #008000;
}
- /mt-static/support/css/cat-foo/screen.css
- これらのファイルを置き、ブラウザで閲覧する事でCSSの内容がカテゴリごとに上書きされている事がわかります。実際に利用される際には、基本となるのデザイン(カテゴリが無いもの)を作成し、そこからカテゴリ毎にCSSの上書きを行うといった手順で作業を行ってください。
少し盛り沢山な内容でしたがいかがだったでしょうか?ファンクションタグを"Hellow, world!"だけで終わらせるのはもったいないので、実際に使えるネタとしてあげてみました。またファンクションタグの場合、Perl版とPHP版の違いが大きくない事も見て取れたのではないでしょうか。
現在のコードですと、エラーハンドリング(問題を検知すること)や、設定など実装されていない部分はありますし、ブログ記事には利用できますが、カテゴリアーカイブでは利用できないといった問題もあります。
この続きは是非ご自分で改造し、活用してみてください。
- プラグイン開発のためのファーストステップ
- レジストリ、YAMLについて
- 環境変数について
- プラグインのローカライゼーションについて
- テストドリブンでのプラグインの開発について
- グローバル・モディファイアプラグインの開発について
- ファンクションタグ プラグインの開発について
- ブロックタグ プラグインの開発について
- コンディショナルタグ プラグインの開発について
- プラグインのデバッグ
- プラグインの設定方法
- コールバックとフックポイント
- スケジュールタスクの開発
- MTオブジェクトの利用方法
- 独自オブジェクトの作成
- 新規アプリケーションの作成
- Transformerプラグインの開発
- 管理画面のメニュー修正
- リストアクションの追加
- 動作モードの追加とモーダルウィンドウの表示
- 外部Web APIとの連携
- 権限とロール