mixi engineer blog

*** 引っ越しました。最新の情報はこちら → https://medium.com/mixi-developers *** ミクシィ・グループで、実際に開発に携わっているエンジニア達が執筆している公式ブログです。様々なサービスの開発や運用を行っていく際に得た技術情報から採用情報まで、有益な情報を幅広く取り扱っています。

かんたんCMS 「Tokyo Promenade」を使おう

先日、待望の長女が誕生したmikioです。あまりにかわいいから育児ブログでもつけようという魂胆ではありませんが、今回は自作のCMSであるTokyo Promenadeについて語ります。

tp-help-screen

Tokyo Promenadeとは

以前の記事で、Tokyo Cabinet(TC)を使ったCMSを作ることを予告しましたが、Tokyo Promenade(TP)がまさにそれです。TCのテーブルデータベースを使って記事を管理する軽量なコンテンツ管理システム(CMS)の実装です。例によってC言語のみで記述され、libc以外の全実装が "made by mikio" な製品です。

読み方は「東京プロムナード」です。プロムナードとは散歩道のことですが、東京メトロの広告に出てくる宮崎あおい的なキャラが写真付きブログを書いちゃうようなユースケースをイメージして名づけました。まあ実装はそんな洒落た感じとはほど遠いですし、実際に私が使う際には小賢しい技術ネタを書きなぐることになるでしょうけども...。

まずはデモサイトにアクセスしてみてください。ヘルプに基本的な使い方は書いてあります。デモサイトなので、SandBoxの記事を追加したり編集したり削除したりしていただいて構いません。

掲示板なの? Wikiなの?

この質問はFAQでありつつ、回答に困ってしまうものの代表です。TCのようなスキーマレスのデータベースを使っている場合、掲示板(またはブログ)とWikiのデータ構造は全く同じであるとみなすことができます。双方とも、各記事は内部IDで識別され、名前(タイトル)やタイムスタンプや著者名などの属性を持つレコードとして記事を記録し、その属性によって表示対象と並び順を定義したビューを構成できて、ユーザからの入力をもとにビューの決定と記事の操作を行うシステムです(まあそのレベルで抽象化したらほぼ全てのDBアプリはそういうアーキテクチャに収まるとも言えますが)。

掲示板とWikiの主な違いは、トップページで提示するビューが「時系列リスト」なのか、人間の編集者がおすすめする「フロントページ」なのかということです。TPのデフォルト設定では掲示板として時系列リストを提示する掲示板仕様になっていますが、設定ファイルで任意の記事をフロントページを指定するとWiki仕様になります。なお、作成日順の時系列リストを提示すると普通の掲示板として使えますが、更新日順の時系列リストを提示すると「2ちゃんねる」のようなフローティングスレッド掲示板風に使うこともできます。

ということで、質問への回答としては「掲示板とWikiはビューの違いに過ぎないので、あなたがどう使うかでどちらであるかが決まるのです」ということになります。個人的には名前とか分類とかはどうでもよくって、「HTMLとかHTTPとか難しいことなんて知らなくても誰でも簡単にお洒落なWebサイトが作れる」というCMSの醍醐味が味わえればそれでいいかなと。

TPの素敵ポイント1:シンプルでロジカルなUI

私はw3mというテキストブラウザが結構好きで、実はTPのユーザインターフェイスはw3mで最も見やすいように調整されています。しかし市場のブラウザシェアを考えればIEやFirefoxやSafariでそこそこ綺麗に表示できないと話になりませんので、CSSを駆使してそれらでもできるだけ読みやすくなるように努力しています。華美な装飾を施すのではなく、各記事の著者がWiki記法で記述した論理性をいかに直感的に読者に伝えるかということを追求しています。

テキストブラウザで読みやすくするためには論理構造のみをHTMLに記述するという選択が自然になされるため、結果的にロジック(HTML)とスタイル(CSS)の分離が徹底されることになります。また、ロジックの部分をできるだけ論理的に記述してもらうために、Wiki記法では論理的に必要だと思われる表現のみを厳選しています。そういった仕様策定のヒントにしたのは、研究者が論文などを書くときによく用いられるLaTeXというシステムです。LaTeXの文書でよく使う見出しやリストや図表といった構造のみがWiki記法で選択できるようにしています。CSSによるスタイルも、LaTeXっぽい見栄えになるように調整しています。

TPの素敵ポイント2:アクセシビリティ

ロジックとスタイルを分離した結果として、いわゆるフルブラウザはもちろん、テキストブラウザでも、視覚障害者用の音声ブラウザでも、モバイル端末でも、プリンタでも、それぞれの環境なりに最善を尽くした表現ができるようになっていると思います。このように様々な環境および様々な境遇の人に対して情報の授受ができる程度を指してアクセシビリティと言うことがありますが、世に数多あるCMSの中でTPはかなりマシな方だという自負があります。

ただ、デフォルトの状態では多様な環境で読みやすいようにしている分、個々の環境に最適化できているわけではありません。最適化の要求があるユースケースでは、出力用テンプレートをカスタマイズすることで対処できるし、またそうすべきだと考えています。音声ブラウザを使っていない私が音声ブラウザに最適な表現を定義するのは無理ですし、iPhoneを持っていない私がiPhoneに最適化された表現を定義するのも辛いものがありますので、個々のユースケースへの最適化はユーザの皆さんに任せるのが現実的です。出力用テンプレートはTCのテンプレート直列化機能をそのまま使って実装されています。テンプレートファイルはHTMLの中に [% ... %] という形式のディレクティブを記述した単純な構造なので、プログラマでなくても簡単にHTMLレベルの構造を変更することができます。

TP以外の多くのCMSでは記事にHTMLを直接記述できる機能があります。そうすると備え付けのWiki記法では不可能な表現方法を自由に使うことができるからです。しかし、それをやってしまうと論理構造がめちゃくちゃになってしまうし、validなHTMLを書けるユーザの割合は著しく低いので、TPでは禁止しています。そのおかげで、TPが出力するHTMLはXHTML 1.0に完全準拠することが保証されます。したがって、JavaScriptやXSLTで加工するのも容易ですし、スクレイピングをして外部アプリケーションを作るのも容易です。

基本事項ではありますが、コンテンツを表示する際には以下の点に心がけるようにしています。

  • ブックマークの名前として識別しやすいtitle要素をつける。
    • 複数の記事をリスト表示する際にはサイト名と機能名をつなげた文字列
    • 記事単体を表示する際にはサイト名と記事名をつなげた文字列
  • 各ページで最も強い見出しをh1要素にする。そしてh1要素は必ず0個か1個にして複数は置かない。
    • 複数の記事をリスト表示する際にはサイト名をh1にし、各記事の名前はh2、見出し1以降はh3以降
    • 単体の記事を表示する際には記事の名前をh1にし、見出し1以降はh2以降
  • 見出しには各記事のIDおよび見出しの階層を反映させたid属性を付与してリンクされやすくする。
  • データベース内の日付はカレンダー時間で保持するが、表示時にはローカル時間に変換する。

各記事のタイトルや見出しは特定のHTML要素に単純に変換させるわけではなく、ビューに基づく相対的な順位で要素が決められるというあたりが私なりのこだわりです。とはいえCSSでスタイルを割り当てる際の利便性を考えて、記事毎の絶対的な見出し順位もきちんとclass属性として指示しています。

TPの素敵ポイント3:快適な画像挿入

テキストブラウザな人は画像とかあんまり興味ないでしょうが、世の一般的なブログなどではほぼ全ての記事に写真やイラストを載せるのがトレンドだというのも否めません。私が育児ブログをつけるとしてもおそらくそうすることでしょう。そのようなユースケースでは、画像をいかに美しく挿入できるかがCMSとしての良し悪しを左右することになります。

画像はさすがにWiki記法では記述できない(Base64で貼り付けるなどの方法は不可能ではないが現実的ではない)ので、ファイルアップロード機能を実装しました。また、アップロードした画像に任意の名前(デフォルトはローカルでのファイル名)がつけられますが、タイムスタンプで識別されるので同じ名前のファイルが複数個あっても問題ありません。

画像を記事に挿入する際には、「@」の後ろにURLを書くだけです。「http://」で始まるURLでWeb上にある任意の画像データを参照できるのはもちろん、「upfile:」で始まる内部識別子でTP内にアップロードした画像を参照することもできます。URLでなく内部識別子を使った方がサイトを移設した際のリンク切れを回避できるので有利です。

画像を挿入するような記事の著者はおそらくタブブラウザを使っているでしょう。記事を執筆すべく編集画面を開きながら、画像が挿入したくなった段階で別タブでアップロードファイルの管理ページを開き、検索機能やサムネイルを駆使して対象の画像を特定し、そこに張られた内部識別子を右クリックでコピーしてクリップボードに入れます。それから編集画面に戻って任意の位置にペーストすれば作業完了です。原始的なようですが、JavaScriptでURLを挿入するタイプの挙動だと元の編集画面が勝手にスクロールしてしまってイラつくことが多いので、敢えてこの素朴な方法を推奨しています。

挿入した画像の表示位置も簡単に制御できます。「@ hoge」と書けばその位置に画像がそのまま表示されますが、「@< hoge」と書けば左寄せ、「@> hoge」と書けば右寄せで表示されます。左寄せなら本文は右に回りこみ、右寄せならば左に回り込むようになります。結果として、雑誌の記事のような見栄えで写真を紹介できるようになります。

TPの素敵ポイント4:Wikipedia大好き

著者には既知だが読者に未知であるかもしれない一般的な概念を記事の中でわざわざ説明するのはだるいものです。一般的な概念であればだいたいのことはWikipediaで説明されているので、該当の記事にリンクを張れば十分です。ということで、リンク先に「wpja:」で始まる識別子を書くと、自動的にWikipedia日本語版のその名前の記事へのURLに直す機能があります。「wpen:」はWikipedia英語版へのURLになります。いわゆるInterWikiですね。小ネタのような機能ですがかなり時間の節約になります。

実装の苦労

TCのテーブルDBとテンプレート直列化機能とオブジェクトシステムがめちゃくちゃ強力なので、Wiki記法だのユーザ管理機能だのファイルアップロード機能だのを満載している割には、TPの全ソースコードは3000行未満に収まっていて非常にコンパクトです。なので、実装について語ることはあまりありません。ご興味のある方はパッケージ内の promenade.c を読めばだいたいの流れが理解できると思います。

敢えて苦労した点を挙げるなら、拡張子とMIMEタイプの対応表を手でハードコーディングしたりとか、時刻表現は10進数とW3CDTFとRFC1123形式を全てサポートしたりとか、クッキーを暗号化するためにRC4を実装したりとか、ファイルアップロードのmultipart/form-data形式のパーザを自分で書いたりとか、1バイトたりともメモリリークしないようにvalgrindでほぼ全てのコードを調べたりとか、全画面がXHTML 1.0でvalidかどうか確かめるためにxmllintとhtmllintで出力を調べたりとかがありました。それより何より、一番面倒だったのはWiki記法を解析してHTMLに直すコンバータを書くことでした。得にリストがネストする構造が厄介で、途中でやめようかと何度も思ったものです。

とはいえ、全ての課題はクリアされ、現時点で私の欲しい機能が全て実装されているCMSが完成しました。スクリプト言語の処理系やデータベースサーバをインストールせずに使えるし、貧弱なマシンでも軽快に動くし、それでいてテンプレートをいじって簡単にカスタマイズできるし、シンプルで飽きの来ないデフォルトのデザインも付いてくるし、我ながら結構イイ感じです。

インストールしてみましょう

ここまで読んでみてTPを使いたくなってくれた人も少数ながらいるかと思いますので、インストール方法について超要約で説明します。WebサーバとTCが予めインストールされていることを前提とします。

# TPをダウンロードする
wget http://tokyocabinet.sf.net/promenadepkg/tokyopromenade-0.9.1.tar.gz

# TPをインストールする
tar zxvf tokyopromenade-0.9.1.tar.gz
cd tokyopromenade-0.9.1
./configure
make
sudo make install

# CGIスクリプトなどを置くベースディレクトリを作る
mkdir /home/mikio/public_html/cms
cd /home/mikio/public_html/cms

# TPの各種ファイルをベースディレクトリにコピーする。
cp /usr/local/libexec/promenade.cgi .
cp /usr/local/share/tokyopromenade/promenade.* .
cp /usr/local/share/tokyopromenade/passwd.txt .

# データベースファイルとアップロードファイル用ディレクトリを作る
prommgr create promenade.tct
mkdir upload

# CGIスクリプトがデータベースやディレクトリを更新できるように適宜設定する
chmod 666 promenade.tct*
chmod 777 upload

# 気が向いたら、ヘルプファイルをインポートする
prommgr import promenade.tct /usr/local/share/tokyopromenade/misc/help-ja.tpw

あとは、設置したCGIスクリプト「promenade.cgi」にWebブラウザでアクセスすれば使い始めることができます。デフォルトで管理者ユーザのアカウントが名前「admin」およびパスワード「nimda」として定義されていますので、それでログインして記事を書いたりユーザを作ったりファイルをアップロードしたりしてみてください。

promenate.tmpl」というファイルがテンプレートファイルになります。これをいじることで、出力されるHTMLのほぼ全てをカスタマイズすることができます。デフォルトでは掲示板スタイルの時系列表示がトップページに設定されていますが、テンプレートの冒頭にある「[% CONF ... %]」のディレクティブをいじってフロントページを設定するとWikiとして使うことができます。

まとめ

Tokyo Promenadeの概要について述べました。設定によって掲示板(ブログ)風にもWiki風にも使えるシンプルなCMSです。C言語だけでも、DBMだけでも、GNOMEなんたらやApacheなんたらを使わなくても、そこそこ実用的なシステムを作れることが伝われば幸いです。

シンプルっていいですよね。複雑なシステムってだいたいすぐに嫌気が差してしまいますし、特定のユーザのユースケースに特化した機能を操作の選択肢として全員に提示するのは、全体のユーザビリティを下げることにつながります。大多数のユーザが必要最低限の機能を迷わず使えるという大前提を確保した上で、慣れたユーザはその学習曲線に応じて個々のユースケースに最適化された使い方ができるようにするのが理想です。その理想に照らすとTPはちょっとシンプルさが行き過ぎた感もありますが、TPが想定するユーザ層である「ワープロでなくテキストエディタを使う人達」にとってはこれくらいがバランスポイントだと思っています。

TPの今後ですが、RSS配信機能やメールによる更新機能をつけたりといった今のトレンドに合った機能追加を行う予定です。あと、そもそもの開発の動機として英語ブログを立ち上げてTokyoシリーズについて語りまくるというのがあるので、近日中にやりたいと思っています。レンタルサーバとドメイン取得で最もコスト(手間含め)が低いオススメの方法があれば教えてください >識者。