読者です 読者をやめる 読者になる 読者になる

mixi engineer blog

ミクシィ・グループで、実際に開発に携わっているエンジニア達が執筆している公式ブログです。様々なサービスの開発や運用を行っていく際に得た技術情報から採用情報まで、有益な情報を幅広く取り扱っています。

かんたんCMS「Tokyo Promenade」の便利機能

mixi

新生児が家に来たおかげで生活が一変して激太りしたmikioです。さて、一部のWebマニアには好評をいただいているTokyo Promenadeですが、今回はその追加機能について語ります。

サイト移転

Tokyoシリーズの配布サイトを新規設置したホームページに移しました。そもそも、Tokyo Promenadeはそこで使うためのCMSとして作ったのです。以後、Tokyo CabinetTokyo TyrantTokyo DystopiaTokyo Promenade、そしてまだ見ぬTokyo製品のパッケージとドキュメントはそこで配布します。

TPの最新版を使ったブログもそこで書いていく所存です。使い勝手はそちらか、以前設置したデモサイトで確認していただけます。

Atomフィード

最近人気のあるブラウザはRSSAtomの購読機能をデフォルトでサポートしています。また、Google ReaderLivedoor Readerなどのフィード購読サービスを使って効率的にブラウジングを行う習慣も一般化してきています。そういった背景もあって、イマドキのCMSではRSSなりAtomなりのフィード機能を実装していないとユーザにそっぽ向かれてしまうようになってしまいました。個人的にはXHTMLに標準語彙のメタデータを埋め込めばRSSやAtomなど不要なはずだと思っているのですが、世の流れには逆らえません。

ということで、TPの最新版ではAtomフィード機能が実装されています。TPのAtomフィードには以下の5種類のビューがあり、それぞれ対応するHTMLページにあるオートディスカバリから取得することができます。

  • 作成日時順一覧: サイト全体の記事を作成された日時の降順で提示
  • 最終更新日時順一覧: サイト全体の記事を最後に更新された日時の降順で提示
  • 最終コメント日時順一覧: サイト全体の記事を最後にコメントされた日時の降順で提示
  • 記事毎の本文とコメント一覧: 個々の記事に着目して本文とコメントを各エントリとして提示
  • 検索結果: 検索ページの任意の結果の最新情報を提示

デフォルトでは各フィードは降順(新しい順)で提示されますが、ソート順のパラメータ(order)の値に「_」を接頭させると昇順(古い順)にできるという裏技もあります。例えば、「order=cdate」だと作成日時降順なので、「order=_cdate」にすると作成日時昇順になります。

なお、Atomの各エントリおける時間情報のメタデータは、更新日時(updated)と作成日時(published)しかありません。一方で、TPでは、記事の最終更新時刻と最終コメント時刻は別々の属性として管理しているので、そのままだと最終コメント日時順一覧をうまく表現できません。Atom Threading Extensions(RFC 4685)という語彙が標準化されているのでそれを使うことも試みましたが、それに対応したユーザエージェントがそれほど普及していないので現時点では断念しました。仕方ないので、最終コメント日時順の場合に限って、各記事の最終コメント日時を最終更新時刻(updated)として配信するようにしています。結果として、最終コメント日時順のフィードを常用すると2ちゃんねる的なフローティングが発生するフィードになります。

更新プラグイン

サイトが更新された際にはブログ検索エンジンやフィード購読サービスに対して更新通知(ping)を打ってクローラを呼び寄せるというのがブログ等のCMSでは一般的です。しかし、pingを打つURLやプロトコルは対象のサービスによってまちまちなので、具体的な指示を設定ファイルだけで行うことは現実的ではありません。そこで、TPでは、各記事が更新された際に、その記事の情報を渡して任意のコマンドが呼べるようにしています。そのコマンドとしてシェルスクリプトを指定して、その中でpingを打つなり差分をバックアップするなりの任意の処理を行うことができます。具体的な仕様は以下にになります。

  • 設定ファイルのupdatecmdという変数でコマンド名(パス)を指定する。
  • 記事が更新された際にそのコマンドが呼び出され、以下の7つの引数が渡される。
    1. 操作種別 : new/update/comment/removeのいずれか
    2. 記事ID : 対象記事のID番号
    3. 更新後データ : 更新後の記事のWikiデータを保存したキャッシュファイルのパス
    4. 更新前データ : 更新前の記事のWikiデータを保存したキャッシュファイルのパス
    5. タイムスタンプ : 更新時刻のマイクロ秒
    6. ユーザ名 : 更新操作を行ったユーザの名前
    7. スクリプトのURL : 実行されているCGIスクリプトの絶対URL
  • コマンドの終了コードが真(0)なら何もせず、偽(0以外)ならエラーメッセージを出力する。

更新プラグインのサンプルとして、すべての記事の更新履歴をパッチ(diffデータ)として保存してトレーサビリティを確保するための「promupdiff」というスクリプトが同梱されています。また、GoogleとYahooのクローラに更新通知を送るための「promupping」というスクリプトもあります。なお、promuppingの方は、今話題の「Pubsubhubub」の公開ハブにも更新通知を送っています。この仕組みが普及すれば、CMS側としてはいろんなサービスにpingを打つプラグインを量産しなくて済むようになって嬉しいし、購読サービス側としてはクローラの実装が簡単になってかつリアルタイムで情報が取得できて嬉しいことになりそうなので、うまいこと流行ってほしいところです。

object要素

従来から、「@」の後ろにURLを書く記法によって、記事の中に画像を埋め込むができました。しかし、埋め込みたいのは画像だけではなく、動画や音楽を埋め込みたいこともあるでしょう。ということで、「@!」の後ろにURLを書く記法を追加しました。そうすると、img要素の代わりにobject要素が使われ、src属性の代わりにdata属性が使われます。width属性やheight属性を「|」で区切って指定できることは同様です。その後ろにさらに「|」で区切ったフィールドを追加すると、param要素を子要素として持たせることができます。例えば、以下のような変換ルールになります。

@! http://example.com/foo.mov|400|300
 ↓
<object data="http://example.com/foo.mov" width="400" height="300"
  type="video/quicktime"></object>
@! http://example.com/foo.class|400|300|nick|mikio|sex|male
 ↓
<object data="http://example.com/foo.class" width="400" height="300">
<param name="nick" value="mikio">
<param name="sex" value="male">
</object>

この方法はかなり汎用性が高く、FlashやJavaアプレットを埋め込むこともできますし、MIDIを埋め込んでBGMを鳴らすこともできますし、Google Mapsも埋め込むことができます。

提示されたobject要素をどうレンダリングするかはユーザエージェントに任されることになっており、ブラウザ毎のサポート状況の違いが激しいことになっていますが、画像以外のデータを埋め込むためのstrict標準に準拠した方法はobject要素しかないのが現状です。しかし、近い未来を想定したアクセシビリティを考えると、あくまで「任意の形式のデータを埋め込む」という意味のWiki記法を定義して、それに応じてobject要素を提示するという方針が最善だと思います。applet要素、embed要素、iframe要素など、個々のデータ形式に応じて要素を使い分けないと表示できないブラウザもまだまだ多くあります(例えば上記のGoogle Map埋め込みはIEだと表示できない)。しかし、個々のブラウザへの最適化はonloadイベントを拾ってJavaScriptでDOMに干渉することによって可能なので、その方針で適宜サポート範囲を広げることで、「現在のアクセシビリティ」と「保守性」すなわち想定される近未来のアクセシビリティの両立を図る所存です。

画像配置の拡充

また、「@」記法も「@!」記法も、「@|」や「@!|」などとして「|」を後置させることによって画像などを段組みさせることができるようになりました。デモとして、段組みなどの凝った画像配置をしたページをご覧ください。「|」が1つだと2段組み、2つ並べると3段組み、3つ並べると4段組み、4つ並べると5段組みになります。例えば、TCのロゴを3段組みで表示したい場合には以下のようにします。

@|| http://1978th.net/tokyocabinet/logo.png
@|| http://1978th.net/tokyocabinet/logo.png
@|| http://1978th.net/tokyocabinet/logo.png

なお、段組みはスタイルシート(CSS)のみで実現しているので、テキストブラウザなどのCSS非対応ブラウザで見る場合には段組みでなく上下に並べられて表示されることになります。

検索機能の強化

今年の花火大会をどこに行くか決める時に、去年の花火大会について書いた記事を読み返したくなるようなユースケースを想定してみましょう。そのような場合は記事の作成日時を指定して検索できると便利です。従来から、記事名、著者名、タグ、本文のそれぞれの全文検索とそれらを統合した全文検索機能をサポートしているTPですが、最新バージョンからは日付を指定した検索ができるようになりました。例えば、検索対象を「creatin date」にして検索条件を「2008」にすると、2008年中に執筆した記事の一覧を得ることができます。なお、「2008-07」とすると2008年7月中という条件になり、「2008-07-29」とすると2008年7月29日中という条件になります。

また、検索ページのファーストビューで、全記事の名前の一覧「recent articles」と、記事を執筆日時毎にまとめた検索条件の一覧「archives」を表示するようにしました。前者は最近どんな記事を書いたか手軽に確認するのに便利ですし、後者は上述の日付検索を手軽に行うのに便利です。

サイドバー

ページの右端にサイトの更新情報やナビゲーションを表示する「サイドバー」を実装しました。これを実装するかどうかは正直かなり悩みました。というのも、シンプルさを何よりも大切にするTPのコンセプトにおいて、必ずしも日常的に使うわけではない機能群を常にファーストビュー(最初に目に入る画面)に表示するというサイドバーの存在は違和感があるからです。サイドバーには更新日時や新着記事の一覧や新着コメントの一覧などが表示されるわけですが、同じ情報はタイムラインや検索ページにあるので、サイドバーは冗長な存在とも言えます。そして、そこに張られているリンクを実際にクリックすることはほとんどありません。このブログの読者の皆さんにおいても、右端にあるサイドバーのリンクをクリックしたことのある人の割合はかなり低いのではないでしょうか。

このように、存在に必然性がなく冗長で画面(視線移動)の無駄とも言えるサイドバーですが、にもかかわらずなぜサポートしたかと言うと、TP以外のCMSに慣れたユーザ(=読者)の暗黙的期待に答えるためです。また、検索系やナビゲーション系の機能の価値は利用頻度だけで量るべきではなく、執筆した情報にいつでも到達できるという「安心」を提供できるという利点も考慮しました。ただし、ある程度規模が大きくて更新が頻繁なサイトでないとサイドバーは心理的意味すら持たないので、デフォルトでは無効になっています。設定ファイルで有効にした場合にのみ、サイドバーが表示されます。

なお、このブログ(WordPress)に見られるように、検索のところで述べた「archives」に相当するコラムをサイドバーに表示する実装も多くありますが、TPではそれは回避しました。「recent articles」に乗らないくらい古い記事を日付指定してまで読むというのは、頻度もとても低いですし、上述の花火大会の例のようにかなり能動的な欲求から起こる行動なので、「search」リンクの1クリックを要求されても苦にはならないでしょう。また、「archives」のコラムは過去の情報の増大とともに占有領域を増すことになって見栄えがよくない(安定しない)という理由もあります。さらに、全記事のレコードを走査しないと日付毎の集計ができないので、その演算を全ページでやるとなると応答時間に影響する可能性が否定できないという理由もあります(キャッシュすれば問題ないのですが、それもあんまりやりたくない)。

その他の小技

ここまで述べた以外にも細かい改善をしています。iPhoneで見た場合に画面を効率的に使うようにしているとか、印刷した場合だけ不要なナビゲーションを非表示にするとか、ブラウザの言語設定を見てヘルプ記事を出しわけたりとか、HTMLのmeta要素をリッチにしたりとかです。ブラウザのキャッシュを効率的に利用できるようにHTTPのCache-Control設定も工夫しました。

まとめ

Tokyo Promenadeの付加機能について説明しました。どれも自分でしばらく運用してみて欲しくなった機能ですので、類似のユースケースの人には嬉しい機能だと思います。まだまだ改良の余地はありそうですが、凝り出すと止まらないのがUIの世界です。いくら高機能なCMSを使ったところで内容がショボいのでは意味がないので、ここらで機能追加は自重して、本来の研究開発と情報発信をする仕事に戻りたいと思います。