MovableType でブログ記事をプレビューした際に生成される一時ファイルは、ブログ記事の優先アーカイブで指定したディレクトリ パス(一般的には、個別アーカイブ ファイルが生成されるパス)に生成されます。プレビューを終えた後、この一時ファイルそのものは削除されるのですが、一時ファイルと一緒に掘られたディレクトリは残ったままになります。ブログ記事の個別アーカイブが最終的にそのまま同じディレクトリ パスに生成されれば良いのですが、何らかの事情で個別アーカイブの書き出し先のディレクトリ パスが変わってしまった場合――例えば、ブログ記事の公開日を元にしてディレクトリ パスを決定していて、編集中に公開日を変更した場合――など、プレビュー時に掘られた空のディレクトリがどんどん残ってしまうことになります。
そこで、プレビュー時に生成される一時ファイルを、常に同じディレクトリ パスに書き出すなど、どこか別の場所を指定できれば良いように思えます。このブログ記事では、プレビュー時に生成される一時ファイルを任意の場所に生成できるように指定する方法を紹介しています。
背景
このサイトのブログ記事の運用スタイルとして、ブログ記事の個別アーカイブのディレクトリ パスは、/archive/YYYY/MMDDhhmm/index.php のように、ブログ記事の公開日(mt:EntryDate
)を元に決めています。ブログ記事は、下書き保存とプレビューを繰り返して執筆・推敲を行いますが、この時点では公開日フィールドには適当な日付(おそらくはブログ記事を新規作成した日時)が入力されています。そして、いよいよ公開する段階になってから、公開日をその時点の日時に書き換えて公開しています。そのため、先に述べたように、ブログ記事の執筆中のプレビューで生成された空のディレクトリがどんどん残っていました。
解決方法
プレビュー時に生成される静的ファイルは、個別アーカイブ ファイルが生成されるディレクトリ パス内に生成されるので、個別アーカイブ テンプレートの生成パスを、プレビュー動作時には固定ディレクトリを指すように書き換えます。ここでのポイントは、request.__mode 変数です。
archive/
<mt:if name="request.__mode" eq="preview_entry">
tmp/dummy.php
<mt:else>
<mt:EntryDate format="%Y/%m%d%H%M">/index.php
</mt:if>
解説のため、複数行にインデントしていますが、実際はインデントや改行を含まずに一行で記述します
解説
- プレビュー動作時とそれ以外の時で、個別アーカイブ ファイルのパスを切替えます
- プレビュー動作時、変数 request.__mode が preview_entry にセットされるので、これを利用して条件分岐します。request.__mode は、mt.cgi に渡される __mode パラメータの値です。なお、テンプレート内では preview_template 変数がセットされていますが、アーカイブ ファイルの生成パスを決定する段階では使えませんでした
- プレビュー動作時、一時ファイルは常に archive/tmp/mt-preview-* に作られるようになるので、ブログ記事の公開日を変更しても、ゴミ ディレクトリが残りません。しかし、一時ファイルのファイル名は任意のハッシュ値を含み推測されにくいとはいえ、常に固定ディレクトリに一時ファイルが生成されるので、セキュリティ的には少し弱い気がします
- MT::TemplateMap.file_template が string(255) のため、あまりに長いテンプレートは指定できません。複雑なテンプレート指定は、アーカイブの制御(その4・アーカイブファイルのパスを柔軟にカスタマイズする) - The blog of H.Fujimotoが参考になります。
雑多な調査メモ
- __mode=preview_entry → MT::CMS::Entry.preview → _create_temp_entry, _build_entry_preview
- _create_temp_entry、POST されたデータから MT::Entry オブジェクトを返す
- _build_entry_preview、プレビュー用の一時ファイルを生成して表示する
- 一時ファイルの生成パスは、MT::CMS::Entry._build_entry_preview 内で、$entry.archive_file で決めていて、最終的には MT::WeblogPublisher.archive_file_for が返している
- preview_template 変数は、その後のテンプレートのビルドに必要な変数コンテクストにセットされているが、パスの決定には関係がない
- プレビュー用一時ファイルを $fmgr->put_data する前に $fmgr->mkpath している。ここでディレクトリが掘られている
- MT::App::CMS::remove_preview_file でプレビュー用一時ファイルを $fmgr->delete しているが、ディレクトリについてはノータッチ
- 空のディレクトリを探して削除する、find . -type d -empty -delete
- 掃除し損ねたプレビュー一時ファイルを削除する、find . -type f -name 'mt-preview*' -delete
- プレビュー機能関係のプラグイン
- プレビュー動作か否かの判別について
- 標準の機能だけで実現できるものの bad know-how くさいので、設定画面があるといいなぁ。「プレビュー一時ファイルの場所:自動/ユーザ指定」みたいな感じで