Movable Type で、編集画面とプレビュー画面を同期する FastestPreview というプラグインを公開しました。

FastestPreview

できること

  • 編集画面で変更した内容を、リアルタイムでプレビュー画面に反映させることができます。
  • mt-config.cgi の設定を調整する必要がありますが、基本的にはインストールするだけで利用できます。
  • 独自のテンプレートを使っている場合でも同期できます。
  • 管理画面のドメインとブログのドメインが異なっていても同期できます。
    • このケースでは、CGIPath や StaticWebPathに絶対パスを指定する必要があります。

デモ

http://screencast.com/t/uBUjtBtS

インストール

  1. GitHubからzipファイルをダウンロードしてください。リリース一覧
  2. ダウンロードしたファイルを展開してください。
  3. pluginsディレクトリとmt-static/pluginsディレクトリにアップロードしてください。

    インストール後のディレクトリの配置は以下のようになります。

    $MT_HOME/
        plugins/
            FastestPreview/
        mt-static/
            plugins/
                FastestPreview/
    
  4. mt-config.cgi の設定を変更してください。
    1. "CGIPath" (もしくは "AdminCGIPath") と "StaticWebPath" が、http からはじまる絶対URLで指定さていることを確認してください。(/ からはじまる相対パスの場合は、ブログのドメインが異なる場合に正しく動作しません)
    2. PreviewInNewWindow に 1 を設定してください。(PreviewInNewWindow のドキュメント)

サポートしているタグ

以下のタグが埋め込まれている部分が同期されます。(モディファイアが付いていないタグのみが同期されます)

  • mt:EntryTitle
  • mt:EntryBody
    • Markdown と Textile も部分的にサポートされています
  • mt:EntryMore
  • mt:EntryExcerpt
    • 「本文」から自動生成はされません
  • mt:EntryKeywords
  • mt:PageTitle
  • mt:PageBody
  • mt:PageMore
  • mt:PageExcerpt
  • mt:PageKeywords
  • カスタムフィールドで生成されたタグ
    • テキスト
    • テキスト(複数行)

サポートしている出力タイプ

  • スタティックパブリッシング
  • ダイナミックパブリッシング

動作環境

  • MT6

外部ライブラリ

このプラグインにはフォーマットの変換を行うために以下の外部ライブラリが含まれています。

Movable Type に「パス別」というマッピングのタイプを追加する、MappingBasedArchive というプラグインを公開しました。ざっくりいうと「カスタムフィールドの値ごとにアーカイブを作ることができる」ようになるプラグインです。

MappingBasedArchive

screenshot-ja-shadow.png

できること

  • 「出力されるパス」を基準にして、記事をまとめてアーカイブすることができます。
  • カスタムフィールドの設定から簡単にパスを設定することができます。
    • 特に以下のタイプのカスタムフィールドが登録されていると、選択肢から選ぶだけで利用することができます。
      • ドロップダウン
      • ラジオボタン
  • AnotherCustomFields にも対応しており、カスタムフィールドと同様「ドロップダウン」と「ラジオボタン」のフィールドがある場合、選択肢に表示されます。
  • カスタムフィールドのそのままの値を使うだけでなく、例えば数値のフィールドであれば範囲で区切ってアーカイブにすることもできます。
  • 任意の順番でアーカイブを並べ替えることができます。
  • アーカイブに名前を付けることができます。

デモ

http://screencast.com/t/BTpd3ofH3P

インストール

  1. GitHubからzipファイルをダウンロードしてください。zipファイル
  2. ダウンロードしたファイルを展開してください。
  3. MTのpluginsディレクトリにアップロードしてください。

インストール後のディレクトリの配置は以下のようになります。

$MT_HOME/
    plugins/
        MappingBasedArchive/

基本的な使いかた

  1. 「ドロップダウン」タイプのカスタムフィールドを作成します。Create Custom Field
  2. 「パス別」のアーカイブマッピングを作成します。 Create Archive Mapping
  3. 「パス」を選択します。(デフォルトで選択されているので、そのままでも大丈夫です。)Choose Output Path
  4. 鉛筆のアイコンをクリックして、詳細情報を編集することもできます。アーカイブファイルを出力するだけであれば編集する必要はありませんが、<mt:ArchiveList> を利用する場合にはここでマッピングに「名前」を付けておく必要があります。Edit Template Map
  5. テンプレートを保存して、再構築してください。
  6. ナビゲーションにアーカイブファイルの一覧を入れる場合には、以下のように書くことができます。
<mt:ArchiveList name="EntryDataTypeMapping">
<a href="<mt:ArchiveLink />"><mt:ArchiveTitle /></a>
</mt:ArchiveList>

応用的な使いかた

範囲でまとめる

  1. 「テキスト」タイプのカスタムフィールドを作成してください。
    • このフィールドには数値のみを入れるものとします。
  2. 「パス別」のマッピングを作成してください。
  3. 「パス」を次のように設定してください。<mt:NumberField regex_replace="/\d\d$/","00"/>/%i

以下のようなアーカイブファイルが出力されます。

  • 100/index.html (100〜199の値)
  • 200/index.html (200〜299の値)
  • ...

親カテゴリでまとめる

  1. 「パス別」のマッピングを作成してください。
  2. 「パス」を次のように設定してください。 <mt:EntryCategories><mt:ParentCategory><mt:CategoryBasename /></mt:ParentCategory></mt:EntryCategories>/%i

サポートしているアーカイブ系のタグ

  • mt:ArchiveList
  • mt:ArchiveTitle
  • mt:ArchiveCount
  • mt:ArchiveNext
  • mt:ArchivePrevious
  • mt:ArchiveLink

よくある(ありそうな)質問と答え

  • 1つのマッピングから複数のアーカイブファイルを書き出すことはできますか?
    • できません。

サポートしている出力タイプ

  • スタティックパブリッシング
  • ダイナミックパブリッシング

動作環境

  • MT6

Reply.pm で MT を対話的に操作

  • 投稿日:
  • by
  • カテゴリ:

MT::Object をワンライナーしやすくする拡張書いた - あと味 を読んで面白いなと思ったので、コンソールつながりで、対話的に MT を操作する方法を紹介します。

やっていることはとても簡単で、以下のようなスクリプトを書いて、Reply.pm を使って対話環境を作って操作をしています。

これを「mt-repl」として保存しておいて、MT のルートディレクトリで実行すると、$mt が初期化された状態で対話環境がスタートします。ちょこっとオブジェクトを調べたり作ったりする限りには、割りと便利に使えると思います。

ポイントは MT_HOME を明示的にしているところで、MT_HOME を指定しないと MT がオブジェクトにカスタムフィールドを生やしてくれないので、このようにしています。

Movable Type でデバッグを捗らせる - 2013

  • 投稿日:
  • by
  • カテゴリ:

この記事について

これは Movable Type Advent Calendar 2013 で枠に空きができた場合に備えてストックしていた記事なのですが、全ての枠が埋まったようなので放出します。

実は、2012年にもMovable Type でデバッグを捗らせるという記事を書いたのですが、似たようなテーマの小ネタになります。(内容は異なります)

自分の開発環境について

私は普段、MacBookPro 上で MT を動かして開発を行っています。以前はリモートのサーバーで環境を作り ssh で接続して開発を行っていたのですが、ネットワークが不安定な場所でも開発できると便利だなというところもあり、現在はその環境に落ち着いています。

以下のトピックスはそのような環境を前提にした話題になります。

Warning が発生したら通知する

プラグインを開発する場合、開発者としては Warning はできるだけ早めに潰しておきたいものである一方で、(特に POST のリクエストなどは) 気をつけていないと見逃してしまいがちであると思います。

そこで、Perl ではプログラムで Warning を補足することが可能なので、Warning が発生したら Mac の通知センターに通知するプラグインを作ってみました。(Mavericks以上が必要です)

こんな感じで通知されるようになります。

WarningsNotification.pl-2.png

プラグインは以下のファイルを plugins に入れれば動作するようになります。

外部から開発環境にアクセスする

IE での動作を確認したいときなどは、開発環境に外部からアクセスできると便利です。ただそのたびに hosts ファイルで指定するのも面倒ですし、また開発環境の80番ポートを外部に開放するのもできれば避けたいところだと思います。

そこで私は、外部からアクセスする場合には以下のような方法をとっています。

  1. VPS などのリモートサーバーを用意する
  2. リモートサーバーにドメインを割り当てる (proxy.home.example.com など)
  3. リモートサーバーではそのドメインへのアクセスを、8001番ポートへのプロキシする
  4. アクセスさせたい時にだけ、開発環境からサーバーに接続してポートフォワーディングを行う
    ssh proxy.home.example.com -R 8001:localhost:80

このように設定することで、インターネットに接続されていればどのような環境からでも、proxy.home.example.com へアクセスするだけで開発環境へ接続することが可能になります。

この記事について

この記事は Movable Type Advent Calendar 2013 の3日目の記事です。

概要

Data API をキャッシュする仕組みとしては既に PHP を使ったものなどがあるようですが、この記事では .htaccess を使い、以下のような方針でキャッシュする方法を紹介します。

  • クライアント側の設定 (mt-data-api.cgi へのエンドポイント) を変更をせずにキャッシュを利用できる
  • 共用サーバーでも利用できる
  • プログラムのレイヤまで降りずに Apache から直接データを返すことができる

サンプルコード

.htaccess とプラグインのサンプルコードが以下の URL から参照できます。

DataAPIPublicCache

ここからはサンプルコードを参照しながら説明をしていきます。

キャッシュの戦略

キャッシュをする際にまず考慮しなくてはならないのが、以下の点になります。

  • MT 側でデータが更新されたらキャッシュがクリアされること
  • 閲覧に認証が必要なデータをキャッシュしてしまい、未認証のユーザーに見られてしまうことがないこと

この記事で紹介する方法では、それぞれ以下の方針で対応をしています。

  • データが更新された場合にはプラグインを使ってキャッシュをクリアする
  • 閲覧に認証が必要なデータはキャッシュしない

.htaccess の戦略

静的なファイルを mt-static/support/data-api-public-cache/ 以下に書き出し、mt-data-api.cgi/v1/... へのアクセスを .htaccess を使ってマッピングして、ファイルが作成済みの場合にのみそれを返すようにします。

キャッシュファイルへのマッピングは一般的にはURLとパラメータキーにして、それを MD5 などでハッシュ化することが多いと思いますが、.htaccess ではそういった操作はできないので基本的にはパラメータをそのままファイル名にしています。

ただパラメータを直接ファイル名にすると任意の名前でファイルを作成できてしまい「/..」などで問題が発生しそうだったので、キャッシュする際にパラメータの文字列をホワイトリストで制限した上で、「/..」と「%」をエンコードする形をとっています。

ファイル名の制限について

ファイル名に関して一つのポイントとして、OS やファイルシステムにより文字数に制限がある場合があります。一般の UNIX 的な環境だと、個々のディレクトリやファイルの文字数の制限は 255 文字程度のようなので、名前が制限内に収まるように適当にディレクトリに分割する必要があります。(トータルの長さは 255 文字を超えても問題ないので)

またディレクトリを含めたトータルの長さについても、1023 文字などの制限がある場合があるようです。手元の Linux 環境では特にこの点は工夫をしなくても Apache 側、Perl 側、共に1023 文字より長くても大丈夫でしたが、Mac 環境では Apache 側でファイルを見つけられなくなりました。この記事ではこの問題に関しては解決策を用意できていないので、この問題にあたった場合には制限事項となっています。

.htaccess の内容

.htaccess の内容は以下のようなものになります。

RewriteEngine On
RewriteBase /mt/

RewriteCond %{HTTP:X-MT-Authorization} =""
RewriteCond %{REQUEST_METHOD} =GET
RewriteCond %{QUERY_STRING} ^[a-zA-Z0-9!""\$\%\&\'\(\)\=\~\^\|\\\{\[\`\@\}\]\*\:\+\;\_\?\/\>\.\<\,-]*$
RewriteRule data-api.cgi/(v.*) mt-static/support/data-api-public-cache/$1/%{QUERY_STRING}c.js [N,E=HTTP_X_DATA_API_PUBLIC_CACHE_TRY:1]

RewriteRule (mt-static/support/data-api-public-cache/.*?)%(.*c.js)/v\d+ "$1 $2" [N]
RewriteRule (mt-static/support/data-api-public-cache/.*?)/\.\.(.*c.js)/v\d+ $1/_..@$2 [N]
RewriteRule (mt-static/support/data-api-public-cache/.*?)(/[^/]{250})([^/].*c.js)/v\d+ $1$2/$3 [N]

RewriteCond %{SCRIPT_FILENAME} !-f
RewriteCond %{ENV:HTTP_X_DATA_API_PUBLIC_CACHE_TRY} =1
RewriteRule mt-static/support/data-api-public-cache/(.*c.js)/v\d+ mt-data-api.cgi [L,E=HTTP_X_DATA_API_PUBLIC_CACHE_FILENAME:$1]

RewriteCond %{SCRIPT_FILENAME} !-f
RewriteCond %{ENV:HTTP_X_DATA_API_PUBLIC_CACHE_TRY} !=1
RewriteRule mt-static/support/data-api-public-cache/ - [R=403,L]

静的ファイルの書き出し

基本的には単純にプラグインで Data API の出力をフックし、未認証の場合にファイルに書き出すだけです。

書き出すファイル名の生成は Perl 側で実装すると .htaccess とルーチンが重複するので、.htaccess から渡すようにします。ただその際、リクエストパラメータとして渡すと外部からファイル名を指定できてしまう可能性があり危険なので、環境変数として渡すようにしています。(そのため PSGI 環境では動作しません、CGI 環境でのみ動作します)

環境変数として渡す場合に一つ注意が必要なのは、CGI が SuExec 環境で実行されていると渡すことのできる名前に制限があるという点です。任意の値を渡す場合には HTTP_X_ で始まるようにする必要があります。

性能の比較

パラメータを MD5 でハッシュ化して readfile するような簡単な PHP を追加して、「この記事の .htaccess を使った方法」「PHP」「CGI」「PSGI」で、いくつかのパラメータのパターンでリクエストにかかる時間を比較してみました。

fields=id,title,excerptfields=...&search=あfields=...&search=あ...(5文字)fields=...&search=あ...(10文字)
.htaccess 0.394 0.424 0.474 0.565
PHP 0.46 0.469 0.469 0.464
CGI 408.793 422.225 421.653 417.161
PSGI 57.613 64.643 62.714 63.373
  • "ab -c 1" で計測した "Time per request" の値です
  • .htaccess と PHP に関してはキャッシュは作成済みの状態での値です
  • 開発環境の MacBookPro で計測した簡易的なものなので、絶対値にはあまり意味はありません

.htaccess と PHP の比較

.htaccess と PHP の結果をグラフにしたものが以下の図になります。

htaccess-php.png

PHP はパラメータの長さによらずほぼ一定の速度ですが、.htaccess の方は長くなるに従って (特に%が増えることによって) 内部でのリダイレクトの回数が増えるために急激に遅くなっています。

パラメータが長いことを前提とした場合には、以下の様な書き方をすることである程度速度の低下を抑えることもできますが、.htaccess の記述が増える分パラメータが短い場合の性能が悪化してしまいます。

RewriteRule (mt-static/support/data-api-public-cache/.*?)%(.*?)%(.*?)%(.*?)%(.*?)%(.*?)%(.*?)%(.*?)%(.*c.js)/v\d+ "$1 $2 $3 $4 $5 $6 $7 $8 $9" [N]
RewriteRule (mt-static/support/data-api-public-cache/.*?)%(.*?)%(.*?)%(.*?)%(.*c.js)/v\d+ "$1 $2 $3 $4 $5" [N]
RewriteRule (mt-static/support/data-api-public-cache/.*?)%(.*?)%(.*c.js)/v\d+ "$1 $2 $3" [N]
RewriteRule (mt-static/support/data-api-public-cache/.*?)%(.*c.js)/v\d+ "$1 $2" [N]

この記事の方法を使う限り、パラメータが長くなるにつれて性能が大きく悪化するのは避けられなそうです。

参考URL

次の記事について

次の記事(4日目)を書く人がまだ決まっていないようです!どなたかよろしくお願いいたします。

Google Maps v3 でなぜか shadow がつかないとき

  • 投稿日:
  • by

2013年11月9日現在、

<script
  type="text/javascript"
  src="http://maps.googleapis.com/maps/api/js?sensor=SET_TO_TRUE_OR_FALSE">
</script>

で読み込むと、オーバーレイ のサンプルに倣って以下のように指定しても、

new google.maps.Marker({
    position: myLatLng,
    map: map,
    icon: image,
    shadow: shadow
});

shadowが有効にならないようです。

<script
  type="text/javascript"
  src="http://maps.googleapis.com/maps/api/js?v=3&sensor=SET_TO_TRUE_OR_FALSE">
</script>

とすると有効になります。

どうやら、

  • v=n を指定しない -> Experimental Versionがロードされる
  • v=3 を指定する -> v3のRelease Versionがロードされる

という動作になっていて、現在のExperimental Versionである3.14.12だと、サンプルにある指定方法だとshadowが反映されないバグ(か仕様変更)があることが原因のようです。

つまり、v=nを指定していない場合には以前は表示されていたとしても現在は表示されていないかもしれないので、Google Map v3でshadowを使っている人は確認した方がいいかもしれません。

MTとPSGIミドルウェア

  • 投稿日:
  • by
  • カテゴリ:

MTでは5.2.4からレジストリを使ってPSGIミドルウェアを各アプリケーションに適用できるようになっているので、この仕組を使ってMTを拡張する方法をいくつか考えてみました。(検証したのはMT6 Beta1です)
公式ドキュメント

PSGIミドルウェアとは

PSGIミドルウェアとは、PSGIアプリケーションに対してリクエスト時の環境やレスポンスを動的に書き換えるための仕組みです。PSGIミドルウェアを使うことでレスポンスのHTMLを書き換えたり、認証のレイヤーを別に挟んだりということができます。MTはプラグインによる拡張の方法が充実しているので、機能の拡張としてはそれで事足りる部分も多いですが、

  • PSGIミドルウェアの資産(MTに限らない)を利用することができる
  • 拡張するためにMTの知識を必要としない

という点がPSGIミドルウェアを利用するメリットであると思います。

逆にMTでPSGIミドルウェアを使う場合にあらかじめ考慮しておくべきところとしては、

  • そもそもPSGIアプリケーションとして実行していないと(CGIで実行している)と利用できない。

という点があります。

以下、ミドルウェアの適用例をいくつか紹介していきます。

MTのアカウント情報を使ってBASIC認証を設定

PlackにはPlack::Middleware::Auth::Basicというミドルウェアが同梱されていて、これを使うと簡単にBASIC認証を設定することができます。以下のような config.yaml でプラグインを作成すると、

  • mt-search.cgi
  • mt-data-api.cgi

に対してBASIC認証が必要になり、MTアカウントの「ユーザー名」と「APIパスワード」が送られてきた場合にだけ結果を得ることができるようになります。

「options」にサブルーチンを渡すパターンとしては「code」と「handler」があり、この違いが少しややこしいですが、

  • code: 初期化時に一度だけ実行され、サブルーチンから返された値がミドルウェアに渡される
  • handler: コードのリファレンスがミドルウェアに渡される

という違いがあります。

プラグイン: P-M-Auth-Basic

デバッグ情報を表示

開発時に有用なミドルウェアもたくさん公開されており、Plack::Middleware::DebugRequestParamsはその一つです。以下のような config.yaml でプラグインを作成すると、環境変数のDebugModeが有効である場合に、リクエストパラメータの一覧が標準エラー出力(多くの場合にはstarmanを実行した端末)に表示されるようになります。

「condition」はPlack::Builder::add_middleware_ifに渡され、リクエストの度に評価されます。

ライセンス的に問題のないミドルウェアであればプラグインのextlibに同梱することもできるので(他のプラグインとのバージョンのコンフリクトは気にする必要はありますが)、配布する場合にはexlibに同梱してしまうのもMTらしい形だと思います。

プラグイン: P-M-DebugRequestParams

mt-search.cgiの検索条件にエイリアスを設定

PSGIミドルウェアを使えることの最大のメリットは既存のPSGIの資産を利用できることだと思いますが、専用のPSGIを新しく書くことも簡単です。例えば以下のようなコードを書くと、mt-search.cgiの検索条件へのエイリアスを作成することができます。

config.yamlを以下のように書き、

mt-config.cgiで以下のように設定すれば、

http://host/mt/search/food

で、

http://host/mt/mt-search.cgi?search=food&IncludeBlogs=1&limit=20

にアクセスしたのと同じ結果が得られるようになります。

プラグイン: P-M-AliasedSearch

サーバーのステータスを取得

PSGIミドルェアの中に、Plack::Middleware::ServerStatus::Liteというサーバーのステータスを取得するものがあります。以下のような config.yaml でプラグインを作成するとServerStatus::Lite を適用することができるのですが、mt.cgi/server-status のようなURLになってしまうので、これはあまりいい感じではありません。

PSGIサーバー全体に適用するような場合には「サクッとPSGIなMTOSの開発環境を用意する方法」でも紹介されているような、専用の.psgiファイルを作成して起動する方がいいと思います。

その他

その他、MTと一緒に利用できそうなプラグインをいくつか紹介します。

  • Plack::Middleware::JSONP
    • JSON形式で返されるレスポンスを、JSONPで呼び出せるように変換します。
    • DataAPIからのレスポンスに適用することができそうです。
  • Plack::Middleware::Deflater
    • レスポンスデータをGzipやDeflate圧縮します。