ウェブデザインとプログラムのナインマイルズ

s9yドキュメント

プラグインのAPI

  1. このセクションのオリジナルドキュメント
  2. ..(テクニカルドキュメント)
  3. 全般的な構造
  4. Serendipityフレームワークの変数
  5. 国際化
  6. 自作プラグインをビルドする
  7. イベントフックの利用と追加

全般的な構造

サイドバーまたはイベントの各プラグインは、一つのサブディレクトリの中にプラグインのサブディレクトリが格納されている必要があります。
サブディレクトリはserendipity_event_mypluginまたはserendipity_plugin_mypluginという名称となります。 OS特有の問題が生じないよう、サブディレクトリに特殊文字列を使ってはいけません。

サブディレクトリ内に空のPHPファイルを作ったら、あなたのディレクトリ名に続けて ".php"をつけたファイル名にします。例:serendipity_event_myplugin.php

構造の特徴を確認するために、既存のプラグインを参考にして、それを変更するようお勧めします。
これ以上説明するのは難しいので、実際にやってみて覚えて下さい。

サイドバープラグインはユーザーのフロントエンドへの出力を生成するだけのプラグインです。それらはフロントエンド領域内で対話的に出力生成するためのシンプルな手続きロジックを受け持つことができます(シャウトボックスプラグインやテンプレートドロップダウンのように)。

イベントプラグイン

イベントプラグインはサイドバープラグインが拡張されたもので、ユーザーのフロントエンドにおいてデータを直接出力するものではありません。
イベントプラグインはs9y内部のパワーに手を加えるための強烈な手段です。 s9yが行う各動作(エントリ表示やエントリの保存、エントリの編集など)の際に、いくつかのフックを置くための特別な箇所がコード内に存在します。 これらの特別な場所で、イベントプラグインはs9yの動作を変更するよう実行され、あなたの必要に合わせてデータを操作することができます。

これらのフックがプラグインを動かしデータ(フックによりますが、何がどのデータを送るのかをコントロールし)を一度ばらし、次にそれから再度普通の処理をし、s9yに結果を返します。

現在利用可能なフックは プラグインAPI - イベントフックに一覧表示されています。

ひとつのイベントプラグインは複数のフックを扱うことができ、各フックはプラグインのプロパティバッグに登録されている必要があります。

s9yのプラグインAPIは下記4つのクラスから成ります:

serendipity_plugin_api

ひとつ目のAPIクラスは serendipity_plugin_api と呼ばれます。
これはs9yとあなたのプラグインの接続をコントロールするクラスです。 新しいプラグインを入れるとか(「アクティベート」と言います)プラグインをはずなど、プラグインの読み込みに使われます。
このクラスはしばしば、それが構成に必要な子クラスのメソッドを呼びます、そして、それは利用可能なプラグインのリストを通した繰り返し処理を担当します。
このAPIクラスはs9y開発陣によってメンテナンスされており、勝手に改造したり拡張したりはできません。 もしもっと詳しく知りたいのなら、各メソッドの振る舞いの詳細について serendipity_plugin_api ファイルを見てください。 ただ、あなたが自分でプラグインを作るのにこれらのメソッドは必要ありません。

serendipity_property_bag

ふたつ目の重要なAPIクラスは serendipity_property_bag です。
これは何れかのプラグインを複数回発動させることができます。 それは、一般的なプラグイン構成と同様にプラグインの構成データや他の値を保持して、データを保存して取り出すには都合のよい方法です。 データはデータベースの serendipity_config テーブルに保存され、プラグインを相関的にメンテナンスしやすいように、プラグイン固有のID番号が接頭辞についています。

以下のメソッドが利用できます:

add($name, $value)

$name で呼び出された設定オプションをプラグインのプロパティに追加します。
$value を含みます。
後ほど使われる $name の共通変数は以下の通りです:

type (context: introspect_config_item)

構成オプションのタイプを設定します。可能な値は以下の通りです:

radio (context: introspect_config_item)

ラジオタイプの設定に使われる変数の連想配列を保持します。
配列のキーはオプション要素の値に使われ、配列の値はユーザーに表示するオプションの名前です。

radio_per_row (context: introspect_config_item)

プラグイン設定管理内で1行にいくつのラジオボタンを表示するかを決定します。
デフォルトは1行に2つです。

select_values (context: introspect_config_item)

セレクトタイプの設定に使われる変数の連想配列を保持します。
配列のキーはオプション要素の値に使われ、配列の値はユーザーに表示するオプションの説明です。

default (context: introspect_config_item)

何もチェックされなかった場合のデフォルト値を保持します。

validate (context: introspect_config_item)

※Serendipty 0.9~alpha5以下で利用可能(※訳者注:古いので内容は略します)

validate_error (context: introspect_config_item)

※Serendipty 0.9~alpha5以下で利用可能(※訳者注:古いので内容は略します)

name (context: introspect_config_item)

設定項目の名前が入ります。 通常、言語の文字列定数で埋まっています。

name (context: introspect)

プラグインの名前が入ります。

description (context: introspect_config_item)

設定項目の説明が入ります。 通常、言語の文字列定数で埋まっています。

description (context: introspect)

プラグインの説明が入ります。

configuration (context: introspect)

設定可能な項目の配列を保持します。
項目が一つしかなくても配列になります。

event_hooks (context: introspect)

登録されているイベントフックの配列を保持します。
イベントプラグインで使用されるプロパティバッグのインスタンスにのみ必要とされます。

groups (context: introspect)

バックエンドで表示される有効なプラグイングループ定数の配列を保持します。
有効なグループは次の通りです:

requirements (context: introspect)

必要とされる最低限のバージョンの依存関係を保持します。
有効な配列のキーは今のところ、「serendipity」「smarty」「php」です。

cachable_events (context: introspect)

キャッシュ可能なイベントフックの名前を保持し、特定のイベントプラグインがキャッシュ可能になります。 もし一つのイベントフックが含まれた場合、プラグインAPIはフックの手段のために $eventData のクエリ問い合わせを行い、配列のキー「is_cached」を確認します。

理解しやすい例を挙げます:
一つのイベントプラグインが「frontend_display」を登録宣言するとします。 そのイベントフックは $entry 配列の中身を持った $eventData 配列を受け取ります。
これでそのプラグインは $entry['is_cached'] のキーを、どこか他のプラグインのフック (例えば「frontend_fetchentries」など) においてもセットできます。
もし今「frontend_display」フックが呼び出されたとすると、プラグインAPIは $eventData['is_cached'] がセットされているかどうかを確認します。 プラグインが既にセットされた後は、イベントフックの実行はされません。

このためCPUの消費をかなり節約します。 このメカニズムは「entryproperties」プラグインに大いに使用されているので、実際のコードの例に関してそこを見てください。

author (context: introspect)

プラグインの作者名を保持します。

version (context: introspect)

プラグインのバージョン番号を保持します。

license (context: introspect)

プラグインのライセンスをセットします。 設定されない場合はGPLが標準となります。

stackable (context: introspect)

プラグインが一度以上インストールできるかどうかを論理値 (TRUE/FALSE) で保持します。
TRUEの場合、プラグインは複数のインストールが可能です。

get($name)

$name で呼び出された設定項目を取得します。

is_set($name)

$name で呼び出された設定項目がプラグインインスタンスにセットされているかどうかを検出します。

serendipity_plugin

サイドバーおよびイベントプラグインのコアクラスです。
自作プラグインの拡張のためにこのクラスを使います。

Properties
$this->title

プラグインのタイトルが入っています。 これはプラグインが自分自身を自動的に確認する (introspection) ためと、スパルタカスプラグインのリポジトリのXMLファイル生成に使われます。

$this->protected (boolean)

プラグインの変更と設定を全てのユーザーに許可するか、そのプラグインをインストールしたs9yのユーザーのみに許可するかを設定します。

$this->wrap_class, $this->$title_class, $this->content_class

サイドバー内に表示されるプラグイン用に使用されるCSSのラッパークラスを設定します。

$this->dependencies

プラグインが依存している他のプラグインの一覧を配列に保持できます。
この変数を設定することによって、一方のパートナープラグインが作られた時に生成される一対のプラグインを作ることができます。 この配列には複数の登録が可能です。

キーには依存しているプラグイン名 (文字列) が必要で、値には「remove」「keep」のどちらかが必要です。
「remove」を設定すると、パートナープラグインが一旦削除されるとそのプラグインも削除されます。
「keep」を設定した場合、パートナープラグインが削除されることによる影響を受けなくなります。

Methods:
introspect(&$propbag)

あなたのレイアウトの特性を定義するためにプロパティバッグを作動させます。
プロパティバッグは、プラグインにおける他の操作ができるように、この関数内で要求を満たされていなければいけません。
あなたは何か一つのカスタムを用いて、このメソッドを必ず多重定義 (オーバーロード) しなければなりません。 それからあなたの設定用の要素をここに配置します。

これはあなたの拡張クラス内で introspect() 関数がどう見えるのかのサンプルコードです:

<?php
class serendipity_plugin_myclass extend serendipity_plugin {
    function introspect(&$propbag) {
        $propbag->add('copyright', 'MIT License');
        $propbag->add('name'     , 'My first Plugin');

        $propbag->add(
          'configuration',
          array(
            'option1',
            'option2'
          )
        );

        return true;
    }
}
?>

return の部分を忘れないようにしてください。 さもないと、正しく初期化されないでしょう。
今、あなたの自作プラグインには、option1 と option2 の二つの設定オプションがあります:

introspect_config_item($name, &$propbag)

この関数は設定項目をどう作動させるのかをs9yに伝えるために必要とされます。 入力データは構成されるべきである設定項目の名前です。 そして次に正しい値で $propbag の参照値 (reference) をセットします。 例を示します:

<?php
    function introspect_config_item($name, &$propbag)
    {
        switch($name) {
            case 'option1':
                $propbag->add('type', 'string');
                $propbag->add('name', 'My option1!');
                $propbag->add('description', 'Please enter something');
                break;

            case 'option2':
                $propbag->add('type', 'string');
                $propbag->add('name', 'My option1!');
                $propbag->add('description', 'Please enter something');
                break;
        }
        
        return true;
    }
?>

「type」の利用可能な値一覧は「property_bag」クラスの説明を見てください。

generate_content(&$title)

$title の値に空の参照値を渡します。
この関数は $title 変数を正しく設定し、次にサイドバーのデータを出力します:

<?php
function generate_content(&$title) {
    $title = 'All your plugins are belong to us!';
    echo 'This is just text, but in the real world here be dragons!';
}
?>
get_config($name, $defaultvalue = null, $empty = true)

s9yのインターフェイスを通して、データベースから設定データを取得します。
プロパティバッグとともに (※訳者注:以下わかりませんでした(-_-;)
Will be used in eye-to-eye work with a property bag.
Should most likely not loverloaded by you.

set_config($name, $value)

s9yのインターフェイスを通して、正しい設定ディレクティブから設定データをセットします。
プロパティバッグとともに (※訳者注:以下わかりませんでした(-_-;)
Will be used in eye-to-eye work with a property bag.
Should most likely not loverloaded by you.

install()

このメソッドはプラグインがインストールされた時に実行されます。
このメソッド内で、可能で必要なデータベースのテーブルをセットアップするために提案されました。

uninstall()

このメソッドはプラグインが削除された時に実行されます。
もし気になるのなら、このメソッドにより既存のデータベースのテーブルを削除できます。

cleanup()

s9yのバックエンドである管理者スイートで設定項目が保存された後に呼ばれる関数です。
例えば、時代遅れの一時的なデータ/ガーベージコレクションを取り除くのにこれを使用できます。

register_dependencies($remove = false, $authorid = '0')

依存関係を登録します。
$this->dependencies を評価します。
(※訳者注:以下わかりませんでした(-_-;)
Should most likely not loverloaded by you.

example()

プラグイン設定欄における出力を表示します。
例えば感情表現プラグインが画像一覧を表示するような感じです。

serendipity_event

このクラスはserendipity_pluginクラスを拡張して、イベントのために必要な特別なメソッドを加えます。

Methods:
event_hook($event, &$bag, &$eventData)

$event (文字列、前述の利用可能なフックの一つ) に対応します。
$bag の特性を操作、評価し、参照された $eventdata を変更できます。 $eventdata は入力値に依存します。

この関数は、自身の動作を繰り返して登録済みプラグインをフックするために、あなたの自作プラグインでオーバーロードされる必要があります。

次のように、全てのマークアップイベントプラグインは、フックと機能に対応するための共通の手段を共有します:

<?php
function event_hook($event, &$bag, &$eventData, $addData = null) {
    global $serendipity;

    $hooks = &$bag->get('event_hooks');

    if (isset($hooks[$event])) {
        switch($event) {
            case 'frontend_display':

                foreach ($this->markup_elements as $temp) {
                    if (serendipity_db_bool($this->get_config($temp['name'], true)) && isset($eventData[$temp['element']])) {
                        $element = $temp['element'];
                        $eventData[$element] = $this->_s9y_markup($eventData[$element]);
                    }
                }
                return true;
                break;

            case 'frontend_comment':
                if (serendipity_db_bool($this->get_config(COMMENT, true))) {
                    echo PLUGIN_EVENT_S9YMARKUP_TRANSFORM . '<br />';
                }
                return true;
                break;

            default:
                return false;
        }
    } else {
        return false;
    }
}
?>

詳細はこういうことです。

見た目よりずっと簡単ですね!

$addData 変数はイベントフックの実行に使用される追加データを含むことができます。
この変数は値によってコピーされることに注意してください。 参照ではありません。
従って自作プラグインの中でこの変数を変更できるようにしないでください。

getFieldReference($fieldname, &$eventData)

このメソッドはエントリ内容を含んでいる $eventData 配列のbody/extended変数 ($fieldname) を返します。 これは、キャッシュする entrypropertiesプラグイン との相互作用を確実にするのに使用されます。 これが後で $entry['body'] キーを $entry['ep_cache_body'] としてリセットするので、あなたの自作プラグインは $entry['body'] キーを直接変更してはいけません。
代わりにキャッシュプラグインの存在を確認してください。
これを簡単に行うには以下を使います:

$body = $this->getFieldReference('body', $eventData);

キーを直接触る代わりに、あなたのプラグインが正しい body/extended配列の値を関数に適用するというマジックを行ってくれます。

Serendipityフレームワークの変数

プラグインは、Serendipityフレームワークの振舞いか出力を変更するのに Serendipity変数 を利用できます。参考のため、定義済みの変数をここに一覧化します。

$serendipity

処理情報やプラグイン情報、動作、そしてその他ほとんどのことを格納するために、Serendipityフレームワークでは、$serendipity というグローバル連想配列を使います。
この配列は次のようにアクセス可能です:

<?php
  global $serendipity;
  $value = $serendipity['key'];
?>

参考に $serendipity 配列のキーを一覧化します。

GET

GET のキーは連想配列です。
フォームを通してGETメソッドを使用することでSerendipityフレームワークに送られた変数に対応しています。 次のキーが利用可能で、フォームの呼び出しを通してアクセスできます: $serendipity['GET']['key']

action

actionキーはユーザがSerendipityからリクエストした内容により異なった値をとります。

actionキーはユーザーが特定のリクエストをしていなければ空です: オーバービューページを表示した時や、存在しないページ (404 not found) からSerendipityがリダイレクトされた時など。

一つだけのカテゴリを見るとき、複数カテゴリを見るとき、一つだけのエントリを見るとき、複数エントリを見るとき、そしてコメントをプレビューする時、コメントを投稿する時、アーカイブのページにアクセスする時に、actionキーが読み取り (read) としてセットされます。

検索がリクエストされた時、actionキーが検索 (search) としてセットされます。 この場合、検索語 (searchTerm) もまた利用できます。

adminAction
id

一つのエントリがリクエストされた時、エントリの識別子として一意の数字がidキーにセットされます。

category

一つのカテゴリも無い時、複数カテゴリ表示がリクエストされた時は、categoryキーは利用できません。 要求されたカテゴリ群がセミコロンで区切られたリストにセットされます。

カテゴリサイドバーでチェックボックスに一つだけしかチェックを入れなくても、それは複数カテゴリのリクエストとなりますので注意してください。 これについては議論 (セミコロンはつけないべきか) にもなっています。

calendarZoom

アーカイブページがリクエストされていない場合、calendarZoomキーは利用できません。 リクエストされたアーカイブの最後の日付 (1970年1月1日以来の秒数) がセットされます。

もっと読みやすい形の情報が $serendipity 変数のuriArgumentsキーに保存されています。

searchTerm

searchTermキーはクイックサーチが使われた時に、検索引数に代入されます。 引数はエスケープされず、クイックサーチ内に平文で入力されます。

lang

デフォルト以外の言語が指定されると、このキーの値に国識別コードが入ります。
例えばデフォルト言語は英語ですが、ドイツ語インターフェイスが指定されるとします。 $serendipity['lang'] はdeとなります。

uriArguments

uriArgumentsキーは、Serendipityがページをどう表示するのか決定するための仮引数の配列です。
当然、状況によってそのキーと値は異なります。

archives

アーカイブページが選択されたとき、値 archivesが uriArguments 内に含まれます。
一連の値はリクエストされたアーカイブページを指定します。 例えば、アーカイブが月別表示指定された場合、次の2つの値 month、year が入ります。

アーカイブの期間を全く指定しないなら、オーバービューページを表示します。

categories

一つまたは複数のカテゴリがリクエストされると、値 categoriesが uriArguments に含まれます。
一つのカテゴリの場合、値はカテゴリIDとなります。 複数カテゴリでは、カテゴリIDに「-multi」と付け足されてセミコロンで区切られたリスト化された値が、リクエストされたカテゴリ明示します。

国際化

可能なら言語のハードコーディングは避けてください。
代わりにあなたのプラグインの先頭に定数を加えるだけにします。 こんな感じです:

<?php
switch ($serendipity['lang']) {
    case 'de':
        @define('PLUGIN_EVENT_TEXTILE_NAME', 'Textformatierung: Textile');
        @define('PLUGIN_EVENT_TEXTILE_DESC', 'Textile-Formatierung durchfuhren');
        @define('PLUGIN_EVENT_TEXTILE_TRANSFORM', 'Textile-Formatierung erlaubt');
        break;

    case 'en':
    default:
        @define('PLUGIN_EVENT_TEXTILE_NAME', 'Markup: Textile');
        @define('PLUGIN_EVENT_TEXTILE_DESC', 'Parse all output through the Textile converter');
        @define('PLUGIN_EVENT_TEXTILE_TRANSFORM', 'Textile-formatting allowed');
        break;
}
?>

複数の言語をサポートするのなら、include_once 'lang/yourlang.inc.php' として別途ファイルを読み込むこともできます。

自作プラグインをビルドする

既に自分のやりたいことがわかっているのでしたら、リポジトリにあるプラグインで似たような機能のものを試してみてください。 それからそのファイルを編集して、上述のメソッドを使いながら自分のニーズに合うよう、煮詰めていってください。

ファイルを (上述の) 正しいディレクトリ構造内に保存したら、s9yの管理スイートの中にそれを導入するのは簡単です。

この例は最も基本的なプラグインサンプルのひとつです:

<?php
switch ($serendipity['lang']) {
    case 'en':
    default:
        @define('PLUGIN_MYPLUGIN_BLAHBLAH', 'Displays random strings');
        @define('PLUGIN_MYPLUGIN_WORDWRAP', 'Wordwrap');
        @define('PLUGIN_MYPLUGIN_WORDWRAP_BLAHBLAH', 'How many words until a wordwrap will occur? (Default: 30)');
        break;
}

class serendipity_plugin_myplugin extends serendipity_plugin
{
    function introspect(&$propbag)
    {
        global $serendipity;

        $propbag->add('name', COMMENTS);
        $propbag->add('description', PLUGIN_MYPLUGIN_BLAHBLAH);

        $propbag->add('configuration', array('wordwrap'));
    }

    function introspect_config_item($name, &$propbag)
    {
        switch($name) {
            case 'wordwrap':
                $propbag->add('type', 'string');
                $propbag->add('name', PLUGIN_MYPLUGIN_WORDWRAP);
                $propbag->add('description', PLUGIN_MYPLUGIN_WORDWRAP_BLAHBLAH);
                break;
            default:
                return false;
        }
        return true;
    }

    function generate_content(&$title)
    {
        global $serendipity;
        $title       = PLUGIN_MYPLUGIN_BLAHBLAH;
        $wordwrap    = $this->get_config('wordwrap');

        if (!is_numeric($wordwrap)) {
            $wordwrap = 30;
        }
        
        $runs = mt_rand(0,900);
        $out  = '';
        for ($i = 0; $i < $runs; $i++) {
            $out .= chr(mt_rand(60,70));
        }
        echo wordwrap(mt_rand(0, 900), $wordwrap, '<br />', 1);
    }
}
?>

イベントフックの利用と追加

イベントフックがs9yからどう呼ばれるかに関する普通の方法はこのような感じになります (例は erendipity_entries.php からの抜粋で、serendipity_event_statistics のような登録済みのプラグインに依存して新しいメニューアイテムを表示するためのもの):

<?php
serendipity_plugin_api::hook_event('backend_sidebar_entries', $serendipity);
?>

見ておわかりの通り、マジックではありません。 s9yのソースコード内のどこにでもこれと同じ行を置くことができますし、 backend_sidebar_entries を別のユニークIDで置き換えれば、このイベントフックを登録するイベントプラグインをまさに作成できます。

Serendipity