AMP向け個別記事テンプレートを作ってみる
AMP(Accelerated Mobile Pages) は、モバイル環境で高速表示を実現するプロジェクトです。これに対応する Movable Type用のテンプレートを作ってみることにしました。
AMPに関する詳細は以下にあります。
Movable Type で対応させたといった記事です。
- Google AMP HTML に対応してみた件 - LIQUID BLOG
- AMP (Accelerated Mobile Pages) HTML を出力するようにしてみたけど面倒くさかった話 | WWW WATCH
基本としては、個別アーカイブにテンプレート組んで、AMPの決まりごとに沿うように出力の調整をかけるというものです。
その際に、アーカイブマッピングでAMP用のパーマリンク設定となるよう、設定を行い、記事アーカイブのメインと同時にAMP用アーカイブテンプレートが作成されるという形です。
個人的には、出力については、ダイナミックパブリッシングにしておくことで、再構築などに負担がかからないのではないかと思ってます(キャッシュが効いてるという前提ならば)。
以上踏まえて、サンプルというか、実際ブログで使ってるものベースのテンプレートを以下においてあります。
(Google Analytics や アドセンスなど付随するモジュール群は入ってません)
あと、定期的に変更したり元が変更したら反映するようにしますのであくまでもサンプルとしてです。mt:Ignoreのコメントは説明のためのもので、実際のテンプレートには入れていません。
以下、作った時点の方針のようなものです。
- 編集画面で管理するものとして、なるべく手間のいらないように、include テンプレートしない作りにした
- 禁止属性等は正規表現にて、置換・削除し、マッチパターン、リプレイスメントは変数代入にした
- ダイナミック動作想定で極力DBアクセスが少ないよう最低限なテンプレートタグで組んだ
- CSS や JSON は出力時に、改行・空白を詰めるようにした(編集画面で見やすく、サイズ縮小出力できる、よくある手法です)。
Movable Type 5:演習:Website に置くsaitemap.xml テンプレート内のウェブページリスト
ウェブマスターツールに送信するなどする、sitemap.xmlにウェブサイト下のウェブページを載せたい時のテンプレートの記述です。
以下を前提とします
- sitemap.xml はウェブサイトテンプレートの管理でカスタムインデックスとして作成する
- ブログはウェブサイト配下にあり、ブログを上位階層にあげていないこと
- ウェブページはすべてをアーカイブテンプレートのウェブページで作成されているものとする。ディレクトリトップとなるページを index というファイル名で作成する(テンプレートタグだけでインデックステンプレートの Last Modifiedを得ようとすると面倒なので)
手順は、ウェブサイト(サイトのトップに該当するページ)の管理画面のテンプレートの管理を開き、任意のテンプレート名でsitemap.xml を作成します。
再構築さえしなければ一旦保存にしておいてもかまいません。
作成例
<?xml version='1.0' encoding='UTF-8'?> <urlset xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd" xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"> <mt:Websites> <MTIfArchiveTypeEnabled archive_type="Page"><MTPages lastn="0"> <url> <loc><$MTPagePermalink encode_xml="1"$></loc> <lastmod><$MTPageModifiedDate format="%Y-%m-%dT%H:%M:%S"$><$MTBlogTimezone$></lastmod> </url> </MTPages></MTIfArchiveTypeEnabled> </mt:Websites> </urlset>
*この例は、ウェブページのみ対象にしてるが、実際の運用ではブログとその配下のウェブページなども含めることになるので、MTBlogs タグのループも追加するようになる。
ちなみにここではseoについて解説するつもりはないので、詳細までは触れませんが、複数サイトマップを作成して管理する方法は以下に記されています。
簡単な説明
MTWebSites : ウェブサイトを対象とする
MTIfArchiveTypeEnabled : ウェブページアーカイブが利用可能かどうか
MTPages : ウェブページアーカイブを対象にループ
MTPagePermalink: ウェブサイト下のウェブページに対するパーマリンク ( loc要素 )
MTPageModifiedDate: ウェブページの最終更新日 フォーマットはW3C Datetime 形式エンコード (英文ドキュメント) ( lastmod 要素)
テンプレートができたら、とりあえずプレビューでみてみる。MTのプレビューのソースの確認は、フレーム内のほうのソースを確認する。
パースエラーがおこっていると、ブラウザで警告がでたりするのでそのときは修正する。開始・終了タグの対応が違っていないかよく確認する。
問題がなければ、再構築してファイルを出力する。この出力されたファイル(sitemap.xml)を、Google ウェブマスターツールの、[最適化] - [サイトマップ] の [サイトマップの追加/テスト] クリックした時のフォームに入力するか、既に作成済ならば、リストに出ているサイトマップファイルをクリックして、サイトマップをテストをクリックしてエラーがでていないか確認する。
mtVicuna simple のウェブページ一覧ウィジェットで除外したいフォルダのあるときは
mt.Vicuna simple にある、ウェブページ一覧を表示するウィジェットで一部のフォルダを除外してフォルダリストを表示させるひとつの方法です
この件はおそらく Movable Type からフォルダを作成しなければ、そのフォルダはフォルダまわりのテンプレートタグが認識できないので、そのような運用をするほうがセオリーぽいですが、細かなツッコミはこの際なかったことにして、とりあえずどうしたら指定したフォルダを一覧で表示しないように出来るか、というのをやっていきます。
mt.Vicuna simple のページ一覧ウィジェットでは、MTPages によるウェブページリストと、MTTopLevelFolders による、フォルダリストで構成されています。
今回カスタマイズをおこなうのが、MTTopLevelFoldersによるループの部分ということになります。
上のキャプチャ中のリンクが入ってない部分を取り除きたい、というわけです。
ウィジェットテンプレート中の、
<li class="page page-<$MTPageID$>">
よりも先のループの部分にフィルタをかける必要があります。
フォルダラベルを評価してフィルタリングしたいので、トップレベルフォルダーから、cur_folder
に変数セットしている箇所以下に次のタグを追加します。
<MTTopLevelFolders> <MTFolderLabel setvar="cur_folder" /><MTSetVar name="folder_index_id" value="" /><MTSetVar name="folder_page_count" value="0" /> <mt:Unless name="cur_folder" like="^(aaa|bbb|ccc)">
aaa、bbb、cccはそれぞれフォルダラベルを指します。除外したい分だけlikeモディファイアに書き加え、各フォルダラベルは | でつなぎます。
MTUnlessの終了タグは、ul 要素の終了タグの前で MTTopLevelFolders 内にする必要がありますので、以下の場所になります。
</MTPages> </li> <$MTSubFolderRecurse$></mt:Unless> </MTTopLevelFolders>
演習:特定カテゴリ下のサブカテゴリのリストを指定数でグループ化して出力する
Movable Typeのコミュニティサイト、MTQで質問のあったケースの演習をおこないました。
すでに模範解答がでているようですので、詳細はそちらに譲ります。このような場合は剰余演算を使って行なうのが通例のようです。
__counter__、__last__ といった特殊な変数が利用できないケースでは、変数をセットして自前のカウンターを用意する必要があるのでこれがプログラミング的要素がでてきて厄介なのであります。
ここでは、余りを求めないでおこなうというやり方でやってみるという課題にしておきます。
考えたこと
- ループの4回目の先頭に</ul> がつくというルールにすればいい(わりとこういう考え方がポイント)。
- カウント数は変数なので使い回していく
- リストを出力させるごとに、カウンターを 0・1・ 2 ...という具合に振っていく
- カウンターが 3(つぎのグループの先頭) になったら </ul>をつけて カウンター0 からスタートさせる
作成したテンプレートサンプル
- ※テスト環境用のモノなので実際の運用のための最適化をしていません。
- ※先の命題のとおりで、ある親カテゴリ下に属する子カテゴリのみをリスト表示するためだけのテンプレートです。
- ※ 条件判断のMT:Unlessは好みでそうしているだけなので、MT:Ifでnot equal な判断でも同じです
<h3>サブカテゴリのリスト</h3> <mt:Setvar name="cont" value="0"> <MTSubCategories category="サブカテゴリの親のカテゴリ"> <mt:If name="cont" eq="0"> <ul class="group"> </mt:If> <li><$mt:CategoryLabel$> ($contの値=<$mt:Var name="cont"$>)</li> <mt:Unless name="cont" eq="3"><mt:SetVar name="cont" value="1" op="+"></mt:Unless> <mt:If name="cont" eq="3"> <mt:SetVar name="cont" value="0"> </ul> </mt:If> <mt:SubCatIsLast></ul></mt:SubCatIsLast> </MTSubCategories>
処理の流れを文章化してみた
カウンターを 0 にセットしておく ==ここからループ開始 == カウンターが 0 であるか ? Yes -> </ul> をつける ↓ No -> カテゴリラベルを表示 ↓ カウンターが 3 ではない ? Yes -> カウンターに1を足す ↓ カウンターは 3 である(== 4番目のリストである ) ? Yes -> カウンターを 0 にもどす </ul> をつける ↓ No ↓ サブカテゴリリストの最後か? No -> ループのはじめに戻る ==> ↓ Yes -> </ul> をつける。ループの終了
一見、子カテゴリがちょうど3の倍数であったときに、リストの閉じタグ(</ul>)が重複するのでないか、という風に見えますけれども、先にいったとおりに、次のグループの先頭に</ul> を入れるというルールにしてあるので、ループが子カテゴリの最後になったときは次のグループが存在しないために</ul>は振られません。その代わりとしてmt:SubCatIsLast
の条件において</ul>を振るということにしてあります。
但し。。
この方法だと、セットした変数にリセットをかけていますから、カウントの変数の値を使ってユニークなIDを振るといった処理には適用できません。
そのような時は、普通にカウントをインクリメントにして剰余を求めたほうがいいです。
Google Blog検索のフィードから関連記事を取り出すテンプレート
Googleのブログ検索の結果のフィードを用いて、Google Ajax feedによる関連記事リスト出力するMTMLテンプレートです。
ここで使ったコードをそのままでのせておきます。自分の環境に応じて改変するなどしてみてください。Google API keyの取得は各自ですませておいてもらえばよいかと思います。
<MTEntryIfTagged> <MTSetVarBlock name="AddFeeds"> <MTEntryTags> feedControl.addFeed("https://www.google.co.jp/search?q=<$MTTagName normalize="1" encode_js="1" encode_url="1"$>&tbm=blg&output=atom", "<$MTTagName normalize="1" encode_js="1"$>"); </MTEntryTags></MTSetVarBlock> <script type="text/javascript" src="http://www.google.com/jsapi"></script> <script type="text/javascript"> google.load("feeds", "1"); function feedInit() { // create a feed control var feedControl = new google.feeds.FeedControl(); <mt:Ignore> feedControl.addFeed("https://www.google.co.jp/search?q=(keywords)&tbm=blg&output=atom" , "keywords"); </mt:Ignore> <$MTGetVar name="AddFeeds"$> feedControl.draw( document.getElementById("feed-ctrl"), { drawMode : google.feeds.FeedControl.DRAW_MODE_TABBED }); } google.setOnLoadCallback(feedInit); // --> </script> <h3>関連するブログ記事</h3> <div id="feed-ctrl">読み込んでいます...</div> </MTEntryIfTagged>
ブログ記事につけられている「タグ」をもとにして、JavaScriptの配列におさめて、それをキーワードにしてaddFeed()の値に渡しています。
その部分のテンプレートはわかりにくい解説が以下の記事にあります。
追記: addFeed()が個別に記述しないといけないようなので、MTEntryTagsのループ内でfeedControl.addFeed()を作成するように変更しました。
MTタグの結果をJavaScriptのArrayオブジェクトに渡すためのテンプレート
あれこれとやっていて頓挫してしまったので、テンプレートの一部分だけを公開してみます。
以下は、エントリーにつけられたタグ名をもとにしてArrayオブジェクトに使用できるようにする出力を得るためのテンプレートです。
<MTEntryIfTagged> <MTSetVarBlock name="TagName"> <MTEntryTags glue=','> <$MTTagName normalize="1" encode_js="1" regex_replace='/(.*)/','"$1"'$> </MTEntryTags> </MTSetVarBlock> </MTEntryIfTagged>
MTMLでglueモディファイアのように任意文字で結合するというのはありますが、任意の文字でラッピングするといったものはグローバルモディファイアを組んだりしないといけないので、regex_replace
で対応することにします。
後は、以下の通りにnew ArrayにgetVarすれば、そのままJavaScriptの配列として扱えるようになりました。
var words = new Array(<$MTGetVar name="TagName"$>);
DisqusコメントにインポートするためのWXRテンプレートサンプル
DISQUSコメントのインポート機能へ送信してインポートするためのWXRファイルのテンプレートです。
注意事項
- 存在する記事すべてを対象としてエクスポートします
- WXRのヴァージョンは1.1で、WordPress 3.3.1で運用しているブログからエクスポートしたファイルを元に構成したものです
- DISQUSのコメントインポート機能のみ動作を確認してありますが、その他の(このテンプレートおよび出力されたファイルの)利用については検証していません
- 具体的な使い方はこのページでは解説しません
- テンプレートタグについての質問なども受け付けていませんので、ある程度MTタグに精通されている方向けです
- 記載のテンプレートのコードは、将来的に変更になる可能性あるいは無効と成る可能性がございます
- このテンプレートの使用によりおこった損害については当方では責任をおわないこととします。
テンプレート
<?xml version="1.0" encoding="<$mt:PublishCharset$>"?> <rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:wp="http://wordpress.org/export/1.1/" > <channel> <title><$mt:BlogName remove_html="1" encode_xml="1"$></title> <link><$mt:BlogURL encode_xml="1"$></link> <description><$mt:BlogDescription remove_html="1" encode_xml="1"$></description> <pubDate><mt:Entries sort_by="modified_on" limit="1"><$mt:EntryModifiedDate utc="1" language="en" format="%a, %d %b %Y %H:%M:%S +0000"$></mt:Entries></pubDate> <language><$mt:BlogLanguage ietf="1"$></language> <wp:wxr_version>1.1</wp:wxr_version> <wp:base_site_url>http://<$mt:blogHost exclude_port="1" encode_xml="1"$>/</wp:base_site_url> <wp:base_blog_url><$mt:BlogURL encode_xml="1"$></wp:base_blog_url> <generator>http://www.sixapart.com/movabletype/"><$mt:ProductName version="1"$></generator> <$mt:BlogEntryCount setvar="count"$> <mt:Entries lastn="$count" sort_order="ascend"> <item> <title><$mt:EntryTitle remove_html="1" encode_xml="1"$></title> <link><$mt:EntryPermalink encode_xml="1"$></link> <pubDate><$mt:EntryDate utc="1" language="en" format="%a, %d %b %Y %H:%M:%S +0000"$></pubDate> <dc:creator><mt:If tag="EntryAuthorDisplayName"><$MTEntryAuthorDisplayName$><mt:Else><$MTEntryAuthorUsername$></mt:If></dc:creator> <guid isPermaLink="false"><$mt:EntryAtomID$></guid> <description><mt:EntryExcerpt remove_html="1" encode_xml="1"></description> <content:encoded><mt:EntryBody encode_xml="1"> <mt:EntryMore encode_xml="1"></content:encoded> <wp:post_id><$MTEntryID$></wp:post_id> <wp:post_date><$mt:EntryDate format="%Y-%m-%d %H:%M:%S"$></wp:post_date> <wp:post_date_gmt><$mt:EntryDate utc="1" format="%Y-%m-%d %H:%M:%S"$></wp:post_date_gmt> <wp:comment_status><MTIfCommentsActive>open<mt:Else>closed</MTIfCommentsActive></wp:comment_status> <wp:ping_status><MTIfPingsActive>open<mt:Else>closed</MTIfPingsActive></wp:ping_status> <wp:post_name><mt:EntryBasename></wp:post_name> <wp:status>publish</wp:status> <wp:post_parent>0</wp:post_parent> <wp:menu_order>0</wp:menu_order> <wp:post_type><mt:If tag="EntryClass" eq="page">page<mt:Else>post</mt:If></wp:post_type> <mt:entryCategories> <category domain="category" nicename="<mt:CategoryBasename>"><mt:CategoryLabel encode_xml="1"></category></mt:entryCategories> <MTEntryIfTagged><mt:EntryTags> <category domain="post_tag" nicename="<$MTTagName encode_url="1"$>"><$MTTagName$></category></mt:EntryTags></MTEntryIfTagged> <mt:Comments> <wp:comment> <wp:comment_id><mt:CommentID></wp:comment_id> <wp:comment_author><mt:CommentAuthor default="匿名" encode_xml="1"></wp:comment_author> <wp:comment_author_email><mt:CommentEmail></wp:comment_author_email> <wp:comment_author_url><mt:CommentURL></wp:comment_author_url> <wp:comment_author_IP><mt:CommentIP></wp:comment_author_IP> <wp:comment_date><$mt:CommentDate format="%Y-%m-%d %H:%M:%S"$></wp:comment_date> <wp:comment_date_gmt><$mt:CommentDate utc="1" format="%Y-%m-%d %H:%M:%S"$></wp:comment_date_gmt> <wp:comment_content><mt:CommentBody encode_xml="1"></wp:comment_content> <wp:comment_approved>1</wp:comment_approved> <wp:comment_type></wp:comment_type> <wp:comment_parent><mt:CommentParentID _default="0"></wp:comment_parent> <mt:if tag="CommenterID"><wp:comment_user_id><mt:CommenterID></wp:comment_user_id></mt:if> </wp:comment> </mt:Comments> </item></mt:Entries> </channel> </rss>
追記:<mt:If tag="EntryClass" eq="page">
の条件式で、日本語環境の場合に<mt:If tag="EntryClass" eq=ウェブページ
とする必要がある場合があるので、注意してください(多くの場合はウェブページで積極的なコメントがつくことは少ないかコメントを閉じるケースもあるので問題とは思っていません)。