linkerは「人と人」「人と情報」「人と物」をつなぐデザインユニットです。

なかなか奥が深そうなMTのプレビュー機能

みなさん、こんにちは、まーしーです。
今日は「Movable Type Advent Calendar 2012」の10日目の記事として書いてみます。

仕事ではいろいろな案件でMTを使うことが多いです。
MTMLを編集してhtmlを生成するというのは問題ないのですが、実際の運用を想定した管理画面でのプレビューでよく問題になることがあるので少し見てみました。
恥ずかしながらプレビューへの対応は後手になってしまったり、そもそも対応をしていなかったりなど色々なのですが、みなさんはどうでしょうか?

個別記事がある場合のプレビュー

MTの標準テンプレートで見てみます。
個別記事ページを生成する場合はこのような感じで問題なくプレビューできます。

121210 01

他のブログを読み込むとか、表示させるところに分岐が入ったりすると引っかかることもあるかもしれませんが、コンテキストはブログ記事なのでそこまで大変じゃないでしょう。

ブログ記事テンプレートを使わない場合

データの管理はブログ記事でするけど、個別の記事ページを作成しない、という場合もあるかと思います。
一覧形式で表示させるといった感じですね。

そういった場合、ブログ記事のアーカイブテンプレート自体がないと思います。
その場合、ブログ記事をプレビューした場合はこのようになると思います。

121210 02

タイトルと本文はプレビューできていますが、こんな状態で納品したらクレームが来るだけなので何かしら対策が必要になります。

ブログ記事アーカイブを削除したのでこうなりますが、ひとまずブログ記事アーカイブはデフォルトのテンプレートに戻しておいて、公開の状態を「公開しない」にしておくとよいでしょう。

記事一覧をプレビューする

ブログ記事の個別ページは持たせないけど、一覧形式で表示させるためにブログ記事を使用する場合のプレビューを考えてみましょう。
これまでと同じようにMT標準のテンプレートで考えてみます。
メインページのように新着を表示させる、という前提です。

その場合はまずはメインページのテンプレートの内容をそのままブログ記事アーカイブ(「公開しない」設定になっている)に持って行けばいいでしょう。

121210 03

これでとりあえずプレビュー時にはこのテンプレートが使われることになります。

121210 04

プレビューも問題なさそうですね。

新規記事をプレビューする

リスト形式でプレビューできるようになりました。
ただ、新規に記事を作成して、保存前の状態だとこれではプレビューができません。

今の状態は

<mt:Entries>
<$mt:Include module="ブログ記事の概要"$>
</mt:Entries>

こんなかんじでリストにして表示させているわけですが保存前だとmt:Entriesでは出てきません。
プレビュー自体はブログ記事アーカイブと同じコンテキストなので

<$mt:Include module="ブログ記事の概要"$>
<mt:Entries>
<$mt:Include module="ブログ記事の概要"$>
</mt:Entries>

このようにmt:Entriesの中とプレビュー用にもう一つ追加しておくとよいでしょう。

121210 05

プレビューも問題なさそうですね。

下書き保存をしてある記事をプレビューする場合もこの方法で問題ないです。

未保存のEntryIDは -1

参考までに、未保存のブログ記事の場合、そのEntryIDは -1になります。
なので

<mt:if tag="entryid" eq="-1">
<$mt:Include module="ブログ記事の概要"$>
</mt:if>

といった条件分岐が可能になります。

ただし、下書き保存をしている場合は何かしらのIDをもっているのでこれでは分岐ができません。

未保存のブログ記事のEntryStatusはPublish

下書きかどうかを判定するには<$MTEntryStatus$>というファンクションタグがあるので、この値で判定が可能です。

<mt:If tag="EntryStatus" eq="Draft">
〜〜〜
</mt:If>

下書きの判定は可能ですが、記事を新規に作成して未保存の場合、Publishが返ってくるので要注意です。
entryidが-1かどうかの判定と組み合わせるといいでしょう。

プレビューする記事が最新ではない場合

ここまではプレビューする記事が最新の場合を想定してきました。
ただ、運用によってはプレビューするタイミングが最新の場合だけ、とは限らないでしょう。

プレビューしている記事が最新じゃなくても一番上にでて、実際表示される際は該当の箇所に表示、ということが許されるのであれば、ここまでの方法でひとまずは問題なさそうです。

プレビューする記事の状況として考えられるのは
・記事が未保存の場合
・記事が保存済みで下書き状態
・記事が公開中で編集してプレビューする場合
が考えられます。

日付順に並ぶ前提では、記事が未保存の場合は最新になることが多いと思います。
記事が公開中で編集してプレビューする場合はmt:Entriesのリストのなかで表示されるので問題ないです。

なので、「記事が保存済みで下書き状態」という状況を考えてみます。

<mt:Entries>のなかでは取得できないので一旦ハッシュに入れることにします。
ソースはこんな感じですね。

<mt:EntryID setvar="_entryID"><mt:EntryID setvar="_curID">
<mt:SetVarBlock name="previewlist" key="$_entryID"><mt:EntryDate format="%Y%m%d%H%M"></mt:SetVarBlock>
<mt:Entries><mt:EntryID setvar="_entryID">
<mt:SetVarBlock name="previewlist" key="$_entryID"><mt:EntryDate format="%Y%m%d%H%M"></mt:SetVarBlock>
</mt:Entries>
<mt:loop name="previewlist" sort_by="value numeric reverse"><mt:Var name="__key__" setvar="_key">
<mt:If name="_key" eq="$_curID">
<$mt:Include module="ブログ記事の概要"$>
<mt:Else>
<mt:Entries id="$_key"><$mt:Include module="ブログ記事の概要"$></mt:Entries>
</mt:If>
</mt:loop>

まず最初の

<mt:EntryID setvar="_entryID"><mt:EntryID setvar="_curID">
<mt:SetVarBlock name="previewlist" key="$_entryID"><mt:EntryDate format="%Y%m%d%H%M"></mt:SetVarBlock>

の部分でプレビューしている記事のentryIDをkeyにセット(_entryID)して、値として日付をpreviewlistというハッシュにセットしておきます。
プレビューしている記事のIDは_curIDにもセットしておきます。

<mt:Entries><mt:EntryID setvar="_entryID">
<mt:SetVarBlock name="previewlist" key="$_entryID"><mt:EntryDate format="%Y%m%d%H%M"></mt:SetVarBlock>
</mt:Entries>

この部分で他の公開済みの記事についても同じようにそれぞれentryIDと日付を1組としてハッシュに入れておきます。

あとはハッシュにいれた物を取り出します。

<mt:loop name="previewlist" sort_by="value numeric reverse"><mt:Var name="__key__" setvar="_key">
<mt:If name="_key" eq="$_curID">
<$mt:Include module="ブログ記事の概要"$>
<mt:Else>
<mt:Entries id="$_key"><$mt:Include module="ブログ記事の概要"$></mt:Entries>
</mt:If>
</mt:loop>

という感じでループ内で値(日付)でソートして取り出していきます。

取り出す際に

<mt:Entries id="$_key"><$mt:Include module="ブログ記事の概要"$></mt:Entries>

という感じでmt:Entriesにidを指定して取り出しますが、下書き保存の記事についてはこれでは取り出せないので、

<mt:If name="_key" eq="$_curID">
<$mt:Include module="ブログ記事の概要"$>
<mt:Else>
<mt:Entries id="$_key"><$mt:Include module="ブログ記事の概要"$></mt:Entries>
</mt:If>

という感じで分岐をさせてそのまま表示させます。
ループのなかですが、このプレビューがこの記事のコンテキストになっているのでこれで大丈夫です。

ということで下書きの記事をプレビューしてみます。

121210 06

このようなかんじで下書き状態のテストエントリー4をプレビューしてみます。

121210 07

下書き状態のテストエントリー4も表示される場所でプレビューできました。

簡単なリスト形式でのプレビューの話でした

ということで、簡単なリスト形式のプレビューについて書いてみました。
他のカテゴリーを引っ張るとか、他のブログのカスタムフィールドのチェックが入った物を取り出すとか色々な条件が絡んでくると結構面倒です。

カスタムフィールドの値でピックアップしたものを何件か表示させていた場合
<mt:Entries field:customfieldbasename="foo">
みたいな感じでmt:Entriesでは簡単に取り出せますが、プレビューする記事の状況によっては取り出せません。

プレビューしたい記事自体にカスタムフィールドの値にチェックが入っているかどうかの判定が必要になります。
チェックが入っているということはピックアップして表示するわけですが、ピックアップの件数が5件と決まっていて、公開済みの記事で5件表示されている場合はプレビューの分を差し引く必要があります。
ここでの並び順とかも問題になるでしょう。

単純にカスタムフィールドの値のチェックだけでなく、記事の状況によっての分岐が入るので、仕組みは難しくはないですが、色々な条件を想定した分岐が必要になります。

html側でphp includeさせている場合などもプレビューの場合はphp includeが使えないのでテンプレートモジュールも用意しておいてそれを読み込む、といったことが必要になります。

プレビューテンプレートかどうかの分岐は
<mt:if name="preview_template">
になりますね。

プレビューについてMTQにあがっていたのですが、

プレビュー機能について - MTQ | Movable Type 5 ユーザーコミュニティ
http://communities.movabletype.jp/2012/10/post-682.html

多分この質問をされた方は記事のプレビューとリストになってる方のプレビューもしたい、ということなのではないかと思います。
なんかやればできるんじゃないかなぁという気もしなくもないですが、簡単にはできなさそうな気もします。
だったら一旦ステージングを挟むなどした方がスムーズにできるかもしれませんね。

どういうプレビューにするかというところまで要件定義の時点で詰められればいいきもしますが、なかなかそうはいかないことも多そうです。
今回書いてみたソースなども無駄に複雑な気がするのでもっといい解決策がありそうな気もしています。
プレビューはまだまだ奥が深そうです。