やるぞ、といいつつ全然着手していなかったmod_blosxomの最新版ですが、このあいだの日曜と祝日をつかって「それとなく」実装してみました。個々の実装要素については悩む事は無いのですが、やっぱりPlugin APIのあたりで悩んでみたり。ご意見キボンです。
概要
「ビルドして動かす事ができる」というだけで、安定性や処理速度や消費リソースに関しては無視して実装しています。ソースコードはとりあえず転がしておきますが、ライセンスに関してはまだ考えていません。再設計し不足機能を補完した後は、GPL と 修正後にソースコードの公開等が不要なコマーシャルライセンス のデュアルライセンスにしよう、などと要らん事を検討中です。不足はありますが、Blosxomの基本の基本な機能は実装 or 実装可能な状態にしているつもりです。また動的共有オブジェクトによるPlugin機構と、httpd.confや.htaccessでバーチャルホストやディレクトリ毎(もちろんユーザごと)に異なる設定を施せるようにしています。
使用するプラグインをエンドユーザに選択させる場合に、管理者が管理できなくなる問題があるのですが、
管理者がインストールしたPluginを、エンドユーザが選択し利用できるという運用ができるようにするつもりです。エンドユーザにチョー重い処理をするプラグインや、Apacheをクラッシュさせるプラグインを入れられても困るので。
ソースコードの構造は次のとおり:
mod_blosxom2-pre1/Makefile mod_blosxom2-pre1/include/blosxom.h mod_blosxom2-pre1/mod_blosxom.c mod_blosxom2-pre1/context.c mod_blosxom2-pre1/util.c mod_blosxom2-pre1/core_blosxom_plugin.c mod_blosxom2-pre1/revsort_blosxom_plugin.c本体は言うまでも無くmod_blosxom.c。
core_blosxom_plugin.cはBlosxomにおけるファイルの検索やテンプレートの管理やテンプレートの置換など、基本機能をまとめたPluginです。core_blosxom_plugin.cはApacheのhttp_core.cモジュールと同様に、mod_blosxomにスタティックにリンクされるので取り外す事はできません。やろうと思えばできますけど。
Pluginの例として、通常とは逆順にソートするrevsort_blosxom_plugin.cを添付しています。このソースコードから単体の共有ライブラリをビルドし、mod_blosxomから実行時にリンクし、定義した機能を利用します。
利用方法
configureスクリプトはつけていないので、Makefileに記述してあるapxsのパスを修正し、% make # make installしてインストールします。設定はhttpd.confで次のように記述します。
<Location /blosxom> SetHandler blosxom BlosxomDataDir /path/to/blosxom/data/dir </Location>この設定は個別のLocationやDirectoryで行う事ができます。例えばタイトルを設定する場合は
<Location /blog> SetHandler blosxom BlosxomDataDir /path/to/blosxom/data/dir BlosxomTitle "mod_blosxom sample blog" </Location>等と設定します。
プラグインを使用する場合は、ビルド済みのプラグインを設置したディレクトリと、有効にするプラグイン名を列挙します。
<Location /plugih-blog> SetHandler blosxom BlosxomDataDir /path/to/blosxom/data/dir BlosxomPluginDir /path/to/blosxom/plugins BlosxomAddPlugin revsort wikish </Location>この場合、/path/to/blosxom/pluginsディレクトリに設置されたプラグインのうちrevsortとwikish(未開発)プラグインを使用する設定です。
プラグインの定義
プラグインは "NAME_blosxom_plugin.c"というファイル名で定義します。ソースコード内ではApacheモジュールのmodule構造体と同様に、blosxom_plugin構造体を定義して、追加したい機能を登録します。core_blosxom_pluginの場合、次のような定義になります。blosxom_plugin core_blosxom_plugin = { BLOSXOM_PLUGIN_STUFF, /* mod_blosxom plugin stuff (NOT EDIT) */ NULL, /* start function */ mb_core_template, /* template load function */ mb_core_entries, /* find weblog entry function */ NULL, /* filter entry function */ NULL, /* end function */ NULL, /* skip entry function */ mb_core_interpolate, /* interpolate function */ NULL, /* head function */ mb_core_sort, /* sort entry function */ NULL, /* date format function */ NULL, /* story function */ NULL, /* foot function */ NULL /* last function */ };今のところすべてのフックが利用できるわけではありませんし、mod_blosxom的には不要なフックも多いので、後々整理します。
プラグインで実装する各関数では、mod_blosxom本体から引き渡されたコンテキスト(blosxom_context_rec *ctx)の操作に終始します。基本的な流れは
- コンテキストから情報を参照し
- 必要な処理を行い
- 結果をコンテキストに格納する。
コンテキスト
mod_blosxom2の開発はプラグインの実装と追加が主なタスクになると思います。その作業をスマートに無理なく行うために、mod_blosxom本体とプラグイン間で情報を共有するために使用するのがコンテキストです。(んー微妙な表現。。。)Apacheモジュールで言うところのrequest_rec構造体が同じ位置付けである、と考えるとイメージしやすいと思います。コンテキストを表すblosxom_context_rec構造体は、リクエストの処理開始と共に生成され、プラグインの各関数へ引数として引き渡されます。
プラグインが直接コンテンツを出力する事は通常ありません。プラグインはコンテキストを操作するだけです。
例えば、ソート処理を拡張するプラグインを考える場合、その仕事は
- blosxom_context_recから記事のリスト(array_header)を取り出す。
- ソートする。
- ソートしたリスト(array_header)をblosxom_context_recにセットする。
プラグインからの機能呼び出し
Win32など、プラットフォームによってはダイナミックリンクされる共有ライブラリ(プラグイン)からは、親プログラムの持つ関数を実行できないなどの制約があります。そーいうプラットフォームでの利用は当面考えていませんが、一応プラグイン側から親の機能をキレイに利用するために、blosxom_context_recには主な関数のポインタを登録しています。そのためプラグイン側からblosxom_plugin *plugin = ctx->get_top_plugin();といった具合に親の機能を利用できます。
また、プラグインとして組み込まれた各関数のポインタもblosxom_context_recに登録するので、
const char *template = ctx->get_template(ctx, "story"); const char *new_content = ctx->interpolate(ctx, template);といった具合に、現在有効なプラグインの機能を利用する事ができます。
API的にこれらの関数ポインタをblosxom_context_recに含めるか、blosxom_funcs_recフィールドを新設してそちらにまとめるかは、現在検討中です。
拡張性?
Blosxomは1ファイル1記事、という前提の設計です。mod_blosxomも基本的には同様ですが、RDBMSなどファイル以外のストレージから記事を取り出すプラグインも実装可能にしています。誰が喜ぶのかしりませんが「ファイル」に縛る必要も無いと思われるので、内部的な記事の保持にはtypedef struct { void *source; time_t mtime; } blosxom_entry;という構造体の配列で管理します。ファイルの場合は
entry->source = file_name;という風に使いますし、RDBMSの場合は
entry->source = primary_key;という感じに、記事を特定するための識別子と更新時刻を保持します。特定用途向けのCMSのベースとして使う等の応用ができれば良いのかなと。
ご意見よろしくです
大雑把な説明でアレですが、現在こんなかんじで構想・実装してみています。とりあえずBlosxomとして不足している機能の補完と、プラグインの充実を計った後に、評価 & リファクタリングしてリリースしよと考えています。トーゼン時期未定ですが次にやりたいネタも控えていますので、そんなに遠い話では無いと思います。というわけでコードに関しては、パフォーマンスや効率などを無視している要素が満載ですが、大枠の考え方と実装について、ご意見やご希望や、「おぃおぃ、この実装じゃ〜できねーじゃん!」といったツッコミなどなど、お気軽にお申し付け下さいませ。