Perl を1行も書かずに Movable Type のプラグインのような機能を実装する

これは Movable Type Advent Calendar 2020 の 6 日目 の記事です。12月6日にこの Movable Type のアドベントカレンダーで記事を書くのも、今年で9年目!我ながら偉いです、自分。

さて、Movable Type のプラグインを作るとき、どんなにシンプルなプラグインでも Perl を書いて機能を実装する必要があります。これは多くの人にとって割と敷居が高い部分だと思います。

僕自身も MT のプラグインを作るとき以外で Perl を使うことはありませんので、Perl の書き方を忘れてしまうこともあります。それでも MT のプラグインを簡単に書けたら、テンプレートで複雑な処理を汗水流して書かなくても済むこともしばしば。そこで今日は、ちょっと裏技的な(?)方法で、Perl を一切書かずに Movable Type のプラグインのような機能を実装する方法をご紹介します。

概要

今回の仕組みは、下記の SystemCommand プラグインを利用して再構築の中で PHP のコードを実行してその結果を出力する、という仕組みで実現します。

SystemCommand プラグイン

今日の機能は SystemCommand プラグインを利用して動かします。事前にこのプラグインをインストールして、mt-config.cgi に下記の設定を追記してください。

SystemCommand 1

実装する機能

今回は例として、記事の公開日が再構築時点で N 日以内であれば New マークを出力する、という機能にしてみます(本来であれば JavaScript で実装した方がリアルタイムに日付を判定できますが)。

PHP ファイルを作成

まずは PHP のファイルを置くディレクトリを作成します。場所はどこでも良いですが、今回は Movable Type のインストールディレクトリに scripts というディレクトリを作成し、その中に new-mark.php というファイルを作成します。

new-mark.php の中身は下記のようにしておきます。

<?php

$res = '<span>New!</span>';
echo $res;

コマンドラインで実行してみる

上記の new-mark.php をコマンドラインで実行してみます。

流れは下記のようになります。

# 上記で作成した scripts ディレクトリに移動
cd PATH-TO-MOVABLETYPE/scripts
php new-mark.php

これで <span>New!</span> と出力されれば成功です。

MT テンプレートを実装する

次に MT のテンプレートを書いていきます。

テンプレートはシンプルに、リストでタイトルと記事へのリンクを出力し、投稿日が 7 日以内であれば New マークを出力するようにします。大枠は下記の通りです。

<ul>
<mt:Entries>
    <li><a href="<mt:EntryPermalink>"><mt:EntryTitle /></a>(<mt:EntryDate format="%Y-%m-%d" />)<mt:system command="$cmd_new_mark" /></li>
</mt:Entries>
</ul>

サンプル記事が古かったので最初の3つだけ 7 日以内の投稿に変更しました。出力結果は下図の通りです。まだ New マークはどこにも表示されていません。

20201206104541 min

上記のテンプレートの中の <mt:system command="$cmd_new_mark" /> 部分が今回の肝です。SystemCommand プラグインが提供する mt:system タグを利用すると command モディファイアに渡したコマンド、上記のケースだと cmd_new_mark という変数に入ったコマンドを再構築のタイミングで実行できるのです。

とうわけで、この cmd_new_mark という変数に、先程作成した PHP のファイルを実行するためのコマンドをセットすれば良いので、それを先程のテンプレートに追加します。

なお、開発中はどんなコマンドが実行されるのかを <mt:Var name="cmd_new_mark" /> で出力して確認しながら実装すると良いです。

<ul>
    <mt:Entries>
        <li>
            <mt:SetVarBlock name="cmd_new_mark">php <mt:CGIServerPath />/scripts/new-mark.php</mt:SetVarBlock>
            <a href="<mt:EntryPermalink>"><mt:EntryTitle /></a>(<mt:EntryDate format="%Y-%m-%d" />)<mt:system command="$cmd_new_mark" />
        </li>
    </mt:Entries>
</ul>

このテンプレートの出力結果は下図の通りです。とりあえず今はすべてに New! が表示されていれば成功です。

20201206105936 min

引数で「公開日時」を渡す

さて、次は「記事の公開日時が 7 日以内であれば〜」という条件を加えていきますが、記事の公開日時が 7 日以内かどうかを判定するために、PHP にその情報を渡す必要があります。PHP をコマンドラインで実行するときは、コマンドの後ろにスペースで区切って情報を渡すことで引数を渡せます。

そこで、MT のテンプレートのコマンドの定義部分を下記のようにして、記事の公開日時を引数として渡します。このとき、encode_php="q" でエスケープするのを忘れずに。

<ul>
    <mt:Entries>
        <li>
            <mt:SetVarBlock name="cmd_new_mark">php <mt:CGIServerPath />/scripts/new-mark.php '<mt:EntryDate format_name="iso8601" encode_php="q" />'</mt:SetVarBlock>
            <a href="<mt:EntryPermalink>"><mt:EntryTitle /></a>(<mt:EntryDate format="%Y-%m-%d" />)<mt:system command="$cmd_new_mark" />
        </li>
    </mt:Entries>
</ul>

PHP コードの実装

ここまでの MT テンプレートにより、PHP ファイルが記事の公開日時の情報を引数として渡されて実行されます。コマンドラインの引数は $argv[1] のようにして取得できますので、PHP のコードを下記のように変更すれば、「記事の公開日時が 7 日以内であれば New マークを表示する」という条件を加えられます。PHP の解説はコード中のコメントを参照してください。

<?php

// タイムゾーンを明記(僕のローカル環境はタイムゾーンがニュージーランドなので・・・)
date_default_timezone_set('Asia/Tokyo');

// 記事の公開日時
$entryDate = new DateTime($argv[1]);

// 現在時刻
$now = new DateTime();

// 記事の公開日時と現在時刻の差を取得
$diff = $now->diff($entryDate);

// 差の日数は $diff->days で取れるので、それが 7日以内であれば New マークを出力
if ($diff->days <= 7) {
    $res = '<span style="color: red; font-weight: bold;">New!</span>';
}
else {
    $res = '';
}

echo $res;

これで下図のように出力されました。バッチリですね!

20201206113918 min

まとめ

いかがでしょうか? Perl のコードは一切出てきませんし、Movable Type のプラグインを作るお作法すら不要です。この方法を利用すれば Movable Type が一層使いやすくなるのではないでしょうか。

今回は単純な実装でしたが、もっと複雑な処理も可能ですし、composer で他のライブラリを簡単に利用することもできるので、PHP のエコシステムの恩恵に預かることもできます。ただし、システムコマンドを実行するので、ユーザーが入力した情報をコマンドに入れるのは避けるか、きちんとエスケープして、思わぬ事故につながらないように注意しましょう。

ぜひお試しください!