Movable Type の記事を iCal で管理する

Movable Type は、テキスト形式であればどんな種類のファイルでも自由自在に生成できます。これは、Movable Type の大きな魅力の一つです。この点については、2009年2月に行われた『実験!Movable Typeラボラトリー』で再認識することができました。

そこで今回は、Movable Type のブログ記事を、Mac の「iCal」(アップル - Mac OS X Leopard - 新機能 - 300を越える新機能)で管理できるようにしてみました。

ical_blogcalendar_001.png

iCal で管理することのメリット

今回のテンプレートは、知り合いに「 MT はカスタマイズすればスケジュール管理だってできちゃうんだぜ」なんてことを見せてあげようと思って作りました。

しかし、いざやってみると「お、意外と便利かも」と思ったので、実用化するときのメリットをあげてみます。(先にテンプレートを見たい方はこちら

頻繁に立ち上げるアプリでブログ記事を管理できる

僕のブログは、「自分で得た知識等をブログでアウトプットすることで知識を定着させる」という面があるので、自分で書いた記事を見直すシーンが時々あります。

そんなとき、iCal でブログ記事が検索できれば、起動も速いので、Firefox を開いていないときでもすぐに記事を検索できるのですごく便利です。

iCalのインクリメンタルサーチを使って記事を検索できる

僕はGoogle カレンダーと iCal を同期させてスケジュールを管理しています。そのとき、iCal をメインに使う最大の理由は、「 iCal ではインクリメンタルサーチができるから」といっても過言ではないかもしれません。

その機能をブログ記事の検索時にも使えるのはすごく便利です。

ブログの更新状況を把握できる

自分のブログの更新状況を確認できます。更新間隔が開いてしまうと焦ってくるので、いい刺激になります。

コメントへの返信をタスクにできる

ブログ記事に寄せられたコメントを「To Do 項目」に追加することで、訪問者さまからのコメントに対する返信をタスクとして管理できます。

今回のテンプレートでは、コメントが投稿された日から3日後をタスクの締め切りとして設定しています。つまり、コメントが投稿されてから3日以内に返信せよ、と自分に課すことができます。

ブログ記事の管理画面へダイレクトに飛べる

テンプレートにブログ記事の管理画面のURLを入れておけば、個々のブログ記事の編集画面へダイレクトに飛べます。

パッと思いつくだけでも上記のようなメリットがありました。

テンプレート

今回作成したテンプレートは以下の通りです。

<mt:SetVarBlock name="ical_blog_id"><mt:BlogID /></mt:SetVarBlock>
<mt:SetVarBlock name="ical_blog_host"><mt:BlogHost regex_replace="/[\/\.:-_]/g","" /><mt:BlogHost regex_replace="/[\/\.:-_]/g","" /><mt:BlogHost regex_replace="/[\/\.:-_]/g","" /></mt:SetVarBlock>
<mt:SetVarBlock name="ical_blog_id_4"><mt:GetVar name="ical_blog_id" op="*" value="3456" trim_to="4" /></mt:SetVarBlock>
<mt:SetVarBlock name="ical_blog_id_8"><mt:GetVar name="ical_blog_id" op="*" value="13579246" trim_to="8" /></mt:SetVarBlock>
<mt:SetVarBlock name="ical_blog_host_12"><mt:GetVar name="ical_blog_host" trim_to="12" /></mt:SetVarBlock>
BEGIN:VCALENDAR
METHOD:PUBLISH
X-WR-TIMEZONE:Asia/Tokyo
PRODID:-//Movable Type <mt:Version />//iCal Blog Template//EN
CALSCALE:GREGORIAN
X-WR-CALNAME:<mt:BlogName />
X-WR-CALDESC:<mt:BlogDescription />
VERSION:2.0
X-WR-RELCALID:<mt:GetVar name="ical_blog_id_8" />-<mt:GetVar name="ical_blog_id_8" regex_replace="/(.{4})(.{4})/","$1-$2" />-<mt:GetVar name="ical_blog_id_4" />-<mt:GetVar name="ical_blog_host_12" />
X-APPLE-CALENDAR-COLOR:#0a50a1
BEGIN:VTIMEZONE
TZID:Asia/Tokyo
BEGIN:DAYLIGHT
TZOFFSETFROM:+0900
TZOFFSETTO:+1000
DTSTART:19500507T020000
RRULE:FREQ=YEARLY;UNTIL=19510505T170000Z;BYMONTH=5;BYDAY=1SU
TZNAME:JDT
END:DAYLIGHT
BEGIN:STANDARD
TZOFFSETFROM:+0900
TZOFFSETTO:+0900
DTSTART:20010101T090000
RDATE:20010101T090000
TZNAME:JST
END:STANDARD
END:VTIMEZONE
<mt:Entries lastn="0">
<mt:SetVarBlock name="dt_start"><mt:EntryDate format="%Y%m%d" /></mt:SetVarBlock>
<mt:SetVarBlock name="dt_Modified"><mt:EntryModifiedDate format="%Y%m%d" /></mt:SetVarBlock>
<mt:SetVarBlock name="ical_entry_id"><mt:EntryID /></mt:SetVarBlock>
<mt:SetVarBlock name="ical_entry_id_4"><mt:GetVar name="ical_blog_id" op="*" value="3456" trim_to="4" /></mt:SetVarBlock>
<mt:SetVarBlock name="ical_entry_id_8"><mt:GetVar name="ical_blog_id" op="*" value="13579246" trim_to="8" /></mt:SetVarBlock>
<mt:SetVarBlock name="ical_entry_id_12"><mt:EntryID pad="1"/><mt:EntryID pad="1"/></mt:SetVarBlock>
BEGIN:VEVENT
SEQUENCE:<mt:GetVar name="dt_Modified" op="-" value="$dt_start" />
DESCRIPTION:<mt:EntryExcerpt remove_html="1" regex_replace="/\n/g","\\n" />\n\n[管理画面]\n/blog/管理画面へのパス/mt.cgi?__mode=view&_type=entry&id=<mt:EntryID />&blog_id=<mt:BlogID />\n
UID:<mt:GetVar name="ical_entry_id_8" />-<mt:GetVar name="ical_entry_id_8" regex_replace="/(.{4})(.{4})/","$1-$2" />-<mt:GetVar name="ical_entry_id_4" />-<mt:GetVar name="ical_entry_id_12" />
TRANSP:TRANSPARENT
URL;VALUE=URI:<mt:EntryPermalink />
DTSTART;VALUE=DATE:<mt:GetVar name="dt_start" />
DTSTAMP:<mt:EntryModifiedDate utc="1" format_name="iso8601" regex_replace="/-|:/g","" />
SUMMARY:<mt:EntryTitle remove_html="1" />
CREATED:<mt:EntryCreatedDate utc="1" format_name="iso8601" regex_replace="/-|:/g","" />
DTEND;VALUE=DATE:<mt:SetVarBlock name="dt_end"><mt:GetVar name="dt_start" op="+" value="1" /></mt:SetVarBlock><mt:GetVar name="dt_end" />
END:VEVENT
</mt:Entries>
<mt:Comments lastn="5">
<mt:SetVarBlock name="ical_comment_id"><mt:CommentBlogID /><mt:CommentEntryID /><mt:CommentID /></mt:SetVarBlock>
<mt:SetVarBlock name="ical_comment_id_4"><mt:GetVar name="ical_comment_id" op="*" value="3456" trim_to="4" /></mt:SetVarBlock>
<mt:SetVarBlock name="ical_comment_id_8"><mt:GetVar name="ical_comment_id" op="*" value="13579246" trim_to="8" /></mt:SetVarBlock>
<mt:SetVarBlock name="ical_comment_id_12"><mt:CommentEntryID pad="1" /><mt:CommentID pad="1" /></mt:SetVarBlock>
BEGIN:VTODO
PRIORITY:0
SEQUENCE:0
DESCRIPTION:<mt:CommentBody remove_html="1" regex_replace="/\n/g","\\n" >\n\n[管理画面]\n/blog/管理画面へのパス/mt.cgi?__mode=view&_type=comment&id=<mt:CommentID />&blog_id=<mt:CommentBlogID />\n
UID:<mt:GetVar name="ical_comment_id_8" />-<mt:GetVar name="ical_comment_id_8" regex_replace="/(.{4})(.{4})/","$1-$2" />-<mt:GetVar name="ical_comment_id_4" />-<mt:GetVar name="ical_comment_id_12" />
DTSTART;TZID=Asia/Tokyo:<mt:CommentDate format="%Y%m%d" />T120000
URL;VALUE=URI:<mt:CommentLink />
DTSTAMP:<mt:CommentDate utc="1" format_name="iso8601" regex_replace="/-|:/g","" />
SUMMARY:<mt:CommentEntry><mt:EntryTitle remove_html="1" /></mt:CommentEntry>へのコメント
CLASS:PUBLIC
CREATED:<mt:CommentDate utc="1" format_name="iso8601" regex_replace="/-|:/g","" />
X-APPLE-SORT-ORDER:2147483647
DUE;VALUE=DATE:<mt:SetVarBlock name="ical_comment_date"><mt:CommentDate format="%Y%m%d" /></mt:SetVarBlock><mt:GetVar name="ical_comment_date" op="+" value="3" />
END:VTODO
</mt:Comments>
END:VCALENDAR

ちなみに、僕は iCal ファイルの仕様に関しては全くの素人なので間違っているところもあるかも知れません。興味のある方はさらに改良してお使いいただければと思います。iCal のファイルの仕様は次のページが参考になります。

正式な仕様は、RFC2445で定められているようです。

テンプレートの解説

iCal のファイル(拡張子 .ics)は、「BEGIN:VCALENDAR」から始まり「END:VCALENDAR」で終わる、ひとつのiCalendarオブジェクトとなっています。この中に「VEVENT」や「VTODO」といったコンポーネントを、「BEGIN:〜」「END:〜」といった形で入れていきます。

さて、では簡単に、ポイントを絞って上記のテンプレートを解説していきます。

カレンダーの情報

テンプレートの7行目〜15行目で、このカレンダーに関する基本情報を設定します。各項目の上に、コメントアウトして解説しています(解説になっていない部分もありますが。。)。

<!-- このままで良いと思う -->
METHOD:PUBLISH

<!-- カレンダーのタイムゾーン -->
X-WR-TIMEZONE:Asia/Tokyo

<!-- このファイルを生成したツールについて書く。何でも良い。 -->
PRODID:-//Movable Type <mt:Version />//iCal Blog Template//EN

<!-- このままで良いと思う -->
CALSCALE:GREGORIAN

<!-- カレンダーの名前。ブログ名で良いと思う -->
X-WR-CALNAME:<mt:BlogName />

<!-- カレンダーの概要。ブログの概要で良いと思う。 -->
X-WR-CALDESC:<mt:BlogDescription />

<!-- RFC2445では2.0と定められている。 -->
VERSION:2.0

<!-- 良くわからないけどカレンダーのユニークID。8桁-4桁-4桁-4桁-12桁からなる。
 このテンプレートの最初の5行で、ブログのIDとホスト名を基にして、4桁、8桁、12桁の数字をかなり適当に生成している。
 -->
X-WR-RELCALID:<mt:GetVar name="ical_blog_id_8" />-<mt:GetVar name="ical_blog_id_8" regex_replace="/(.{4})(.{4})/","$1-$2" />-<mt:GetVar name="ical_blog_id_4" />-<mt:GetVar name="ical_blog_host_12" />

<!-- iCalに表示されるカレンダーの色。なんでも良い。
 ブログ情報にカスタムフィールドで「ブログの色」なんてフィールドを追加しても良いと思う。
 -->
X-APPLE-CALENDAR-COLOR:#0a50a1

タイムゾーンの情報

テンプレートの16行目から32行目は、カレンダーのタイムゾーンに関する情報です。今回は日本のタイムゾーンが設定されています。ここは「おまじない」と思ってこのままで良いでしょう。

イベントの情報

テンプレートの33行目から52行目で、ブログ記事をカレンダーの各イベントとして設定しています。

<!-- 全てのブログ記事をリストアップする。今回は「ページ」は入れていません。 -->
<mt:Entries lastn="0">

<!-- ブログ記事の作成日時を変数dt_startに入れておく。 -->
<mt:SetVarBlock name="dt_start"><mt:EntryDate format="%Y%m%d" /></mt:SetVarBlock>

<!-- ブログ記事の更新日時を変数dt_Modifiedに入れておく。 -->
<mt:SetVarBlock name="dt_Modified"><mt:EntryModifiedDate format="%Y%m%d" /></mt:SetVarBlock>

<!-- 以下の4行で「UID」のための4桁、8桁、12桁の数字を、エントリーIDを基にしてかなり適当に生成している。 -->
<mt:SetVarBlock name="ical_entry_id"><mt:EntryID /></mt:SetVarBlock>
<mt:SetVarBlock name="ical_entry_id_4"><mt:GetVar name="ical_blog_id" op="*" value="3456" trim_to="4" /></mt:SetVarBlock>
<mt:SetVarBlock name="ical_entry_id_8"><mt:GetVar name="ical_blog_id" op="*" value="13579246" trim_to="8" /></mt:SetVarBlock>
<mt:SetVarBlock name="ical_entry_id_12"><mt:EntryID pad="1"/><mt:EntryID pad="1"/></mt:SetVarBlock>

<!-- ここからイベント情報 -->
BEGIN:VEVENT

<!-- リビジョンのシーケンス番号だそうだ。
 初期値は0で、更新されるたびに増えていく。
 今回のテンプレートでは、ブログの更新日時(dt_Modified)からブログの作成日時(dt_start)を引くことで、
 ブログ記事を更新したときにシーケンス番号が増えるようにした。
 この数字が増えていかないとiCalが更新情報を取得しないようだ。
 -->
SEQUENCE:<mt:GetVar name="dt_Modified" op="-" value="$dt_start" />

<!-- イベントの概要。ブログ記事の概要と、ブログ記事の管理画面へのリンクを記載している。 -->
DESCRIPTION:<mt:EntryExcerpt remove_html="1" regex_replace="/\n/g","\\n" />\n\n[管理画面]\n/blog/管理画面へのパス/mt.cgi?__mode=view&_type=entry&id=<mt:EntryID />&blog_id=<mt:BlogID />\n

<!-- イベントのユニークID。8桁-4桁-4桁-4桁-12桁からなる。
 先ほど生成した数字を入れている。
 -->
UID:<mt:GetVar name="ical_entry_id_8" />-<mt:GetVar name="ical_entry_id_8" regex_replace="/(.{4})(.{4})/","$1-$2" />-<mt:GetVar name="ical_entry_id_4" />-<mt:GetVar name="ical_entry_id_12" />

<!-- このままで良いと思う。 -->
TRANSP:TRANSPARENT

<!-- ブログ記事のパーマリンク -->
URL;VALUE=URI:<mt:EntryPermalink />

<!-- イベントの開始日。このテンプレートでは、1日イベントとして扱っている。 -->
DTSTART;VALUE=DATE:<mt:GetVar name="dt_start" />

<!-- イベントの更新日時?ブログ記事の更新日時を入れている。
 フォーマットはiso8601の標準時刻を入れ、ハイフンやコロンは不要となる。
 -->
DTSTAMP:<mt:EntryModifiedDate utc="1" format_name="iso8601" regex_replace="/-|:/g","" />

<!-- イベントのタイトル -->
SUMMARY:<mt:EntryTitle remove_html="1" />

<!-- イベントの作成日時?ブログ記事の作成日時を入れている。 -->
CREATED:<mt:EntryCreatedDate utc="1" format_name="iso8601" regex_replace="/-|:/g","" />

<!-- イベントの終了日。このテンプレートでは、1日イベントとして扱っている。
 1日イベントとする場合は、終了日を開始日の翌日にするようだ。
 dt_startに1を加算して算出している。
 -->
DTEND;VALUE=DATE:<mt:SetVarBlock name="dt_end"><mt:GetVar name="dt_start" op="+" value="1" /></mt:SetVarBlock><mt:GetVar name="dt_end" />

<!-- イベント情報はここまで -->
END:VEVENT

<!-- これをMTEntriesでループする。 -->
</mt:Entries>

To Do リストの情報

テンプレートの53行目以降で、To Do リストに関する情報を設定しています。

今回のテンプレートでは、ブログに投稿されたコメントを最新5件出力しています。また、To Do(以下「タスク」といいます)の締め切りを、コメント投稿日の3日後に設定しているので、コメントが投稿されてから3日以内に返信するよう自分にプレッシャーを与えるようにしています。

<!-- コメントの最新5件をループする。 -->
<mt:Comments lastn="5">

<!-- 以下の4行で「UID」のための4桁、8桁、12桁の数字を、コメントIDを基にしてかなり適当に生成している。 -->
<mt:SetVarBlock name="ical_comment_id"><mt:CommentBlogID /><mt:CommentEntryID /><mt:CommentID /></mt:SetVarBlock>
<mt:SetVarBlock name="ical_comment_id_4"><mt:GetVar name="ical_comment_id" op="*" value="3456" trim_to="4" /></mt:SetVarBlock>
<mt:SetVarBlock name="ical_comment_id_8"><mt:GetVar name="ical_comment_id" op="*" value="13579246" trim_to="8" /></mt:SetVarBlock>
<mt:SetVarBlock name="ical_comment_id_12"><mt:CommentEntryID pad="1" /><mt:CommentID pad="1" /></mt:SetVarBlock>

<!-- ここからタスク情報 -->
BEGIN:VTODO

<!-- タスクの優先度。とりあえず最も低くしている。 -->
PRIORITY:0

<!-- リビジョンのシーケンス番号。コメントを編集することはあまりないのでこのままで良いと思う。 -->
SEQUENCE:0

<!-- タスクの内容。コメント本文とコメントの管理画面へのリンクを記載している。 -->
DESCRIPTION:<mt:CommentBody remove_html="1" regex_replace="/\n/g","\\n" >\n\n[管理画面]\n/blog/管理画面へのパス/mt.cgi?__mode=view&_type=comment&id=<mt:CommentID />&blog_id=<mt:CommentBlogID />\n

<!-- タスクのユニークID。8桁-4桁-4桁-4桁-12桁からなる。
 先ほど生成した数字を入れている。
 -->
UID:<mt:GetVar name="ical_comment_id_8" />-<mt:GetVar name="ical_comment_id_8" regex_replace="/(.{4})(.{4})/","$1-$2" />-<mt:GetVar name="ical_comment_id_4" />-<mt:GetVar name="ical_comment_id_12" />

<!-- タスクの開始日。コメントの投稿日を入れている。なぜか12時。 -->
DTSTART;TZID=Asia/Tokyo:<mt:CommentDate format="%Y%m%d" />T120000

<!-- コメントへのパーマリンク。 -->
URL;VALUE=URI:<mt:CommentLink />

<!-- タスクの更新日?コメントの投稿日を入れている。
 フォーマットはiso8601の標準時刻を入れ、ハイフンやコロンは不要となる。
 -->
DTSTAMP:<mt:CommentDate utc="1" format_name="iso8601" regex_replace="/-|:/g","" />

<!-- タスクのタイトル。「コメントがあったブログ記事へのコメント」としている。 -->
SUMMARY:<mt:CommentEntry><mt:EntryTitle remove_html="1" /></mt:CommentEntry>へのコメント

<!-- 公開するか否か。初期値は公開。 -->
CLASS:PUBLIC

<!-- タスクの作成日?コメントの投稿日を入れている。
 フォーマットはiso8601の標準時刻を入れ、ハイフンやコロンは不要となる。
 -->
CREATED:<mt:CommentDate utc="1" format_name="iso8601" regex_replace="/-|:/g","" />

<!-- 不明。とりあえず、自分のiCalでカレンダーを書き出したときの数値を入れておけば良いでしょう。 -->
X-APPLE-SORT-ORDER:2147483647

<!-- タスクの終了日。コメント投稿日の3日後に設定している。 -->
DUE;VALUE=DATE:<mt:SetVarBlock name="ical_comment_date"><mt:CommentDate format="%Y%m%d" /></mt:SetVarBlock><mt:GetVar name="ical_comment_date" op="+" value="3" />

<!-- タスク情報ここまで -->
END:VTODO

<!-- これをMTCommentsでループして出力する。 -->
</mt:Comments>

<!-- カレンダー情報、おしまい。 -->
END:VCALENDAR

インデックステンプレートを作成

上記のテンプレートを、インデックステンプレートとして作成し再構築します。ここでは 出力ファイルを「blog_calendar.ics」とします。拡張子は「.ics」です。

iCal でカレンダーを読み込む

最後に、作成した「blog_calendar.ics」を iCal で読み込みます。

「カレンダー > 照会...」をクリック

ical_blogcalendar_002.png

再構築して生成したファイルのURLを入力して「照会」をクリック

ical_blogcalendar_003.png

カレンダーの情報設定画面が表示されるので、正しくインポートされていることを確認します。

ical_blogcalendar_005.png

今回のテンプレートのように「To Do 項目」もインポートする場合は、「削除:」欄の「To Do 項目」のチェックを外します。また「自動更新」を「毎日」等に設定しておくと良いでしょう。

これで「照会」欄にカレンダーが表示されます。

ical_blogcalendar_004.png

ただ、なぜか最初の「照会」だけでは「To Do 項目」がインポートされないので、カレンダーの上で右クリックして「更新」をクリックします。1回やってダメだったときは、2、3回やればうまく行きました。

ical_blogcalendar_006.png

ical_blogcalendar_007.png

以上です。意外と便利なのでお試しください。