プラグインの仕様

原則的にPukiWikiの規格を拡張したものになります。ほとんどの文章は、本家(dev:プラグイン/開発者向け)からの流用です。

Adv.対応のプラグインを作るときは、最低限error_reportingの値をE_ALLにして開発するようお願いします。


プラグインディレクトリについて

PukiWiki のページをHTML 形式へコンバートする時と、プラグイン機能からの値を受け取った時に処理を行うプラグインを設置することができます。

デフォルトでは plugin です。

ファイル名

<プラグイン名>.inc.php

Wikiからのプラグインの呼び出し

ブロック型、インライン型の違いについては、../Block and Inlineを参照。

ブロック型
プラグインディレクトリ/<プラグイン名>.inc.php 内の関数 「plugin_<プラグイン名>_convert()」 が呼び出されます
#プラグイン名
#プラグイン名(arg[1],arg[2]...arg[n-1]){arg[n]}
  • 行頭にスペースは含めることはできない
  • 引数内に括弧()を使用することができる。ただし ) 単体は不可
  • 引数を指定しなくても呼び出せる
インライン型
プラグインディレクトリ/<プラグイン名>.inc.php 内の関数 「plugin_<プラグイン名>_inline()」 が呼び出されます
&プラグイン名(引数リスト);
&プラグイン名(引数リスト){[文字列]};
URL指定でのプラグインの呼び出し
プラグインディレクトリ/<プラグイン名>.inc.php 内の関数 「plugin_<プラグイン名>_action()」 が呼び出されます
index.php?cmd=プラグイン名

関数

function plugin_<プラグイン名>_convert()

  • 行頭に「#プラグイン名」形式を書くことによって呼び出されます
  • HTMLへのコンバート時に呼び出されます
  • 引数は func_get_args() で配列へ格納できます([0]~[n])
    • 複数行引数を使ったかどうかの確認は、n 番目(最後)の配列に改行("\r")が含まれているかで判断する必要があります。
  • func_num_args() によって、渡された引数の数を求めることができます
  • 返値がそのままHTML ソースとして使用されるので、クロスサイトスクリプティングなどのセキュリティホールを作らないよう、注意が必要です。
  • 返値をFALSE とすると、代わりにエラーメッセージを表示します。
    • ただし、本体以外から呼び出された場合は、その呼び出し元に依存します。

function plugin_<プラグイン名>_inline()

  • 「&プラグイン名;」形式で呼び出されます
    • 1.4 系で追加
  • HTMLへのコンバート時に呼び出されます
  • 引数は func_get_args() で配列へ格納できます([0]~[n])
  • func_num_args() によって、渡された引数の数を求めることができます
    • 但し、
      &hoge(引数リスト);
      形式と
      &hoge(引数リスト){[文字列]};
      形式の互換のため、function plugin_プラグイン名_inline()では、前者は[文字列]部分が省略されている、という扱いになり、func_get_args() で格納した配列の最後が空文字列('')になり、引数の数も()内に記述した引数の数+1になります
      • 関連:BugTrack/404?
    • 文字列はPukiWiki 書式であるとして、HTML 形式へと変換済みの状態でプラグインへと渡されます。ただし、ユーザ定義ルールやWikiName などの設定によっては、同じ文字列であっても得られる値が変化するので、取り扱いには注意が必要です。
      • 関連:BugTrack2/65?
  • 返値がそのままHTML ソースとして使用されるので、クロスサイトスクリプティングなどのセキュリティホールを作らないよう、注意が必要です。
  • 返値をFALSE とすると、代わりにエラーメッセージを表示します。
    • ただし、本体以外から呼び出された場合は、その呼び出し元に依存します。

function plugin_<プラグイン名>_action()

  • GET・POSTメソッドでpluginを指定されたときに呼び出されます。
  • 返値は array('msg' => 'title', 'body' => 'contents')
    • キー'body' の要素にはスキンのbody 部分に表示させたいコンテンツを指定します。
      • 省略した場合には、$vars['refer'] で指定されているページを表示します。
      • ただし、ページを表示する前にアクセス制御を確認しないので、注意が必要です。
    • キー'msg' の要素には表示させたいタイトルを指定します。
      • キー'body' を指定した場合は省略できません。
      • キー'msg' の要素に$1 がある場合は、$1 を$vars['refer'] で指定されているページ名に置き換えします。(1.4 系でrefer を省略した場合、$defaultpage が使われます)
        ちなみに、cmd でプラグインを呼び出した場合は、refer の代わりにpage が置き換えに使われます。
  • 1.4 系では、返値にFALSE を指定可能です。
    • この場合、何も表示せずに終了(exit() を実行)します。
      • 本体以外から呼び出された場合は、その呼び出し元に依存します。
    • エラーメッセージを表示させたい場合は、プラグイン内でその処理を完結させてから終了させて下さい。
  • PHP関数:die またはPHP関数:exit を実行することにより、スキンを適用させない事も可能です。
    • pcomment プラグインなどの様にPHP関数:header で別ページに飛ばしたり、attach プラグインやrss プラグインなどの様にファイルをダウンロードさせたり、したい時に便利です。
    • この場合に限り、全てのheader を出力した後にPHP関数:echo やそれに類する関数を利用する事ができます。(これ以外で利用すると、「PHP エラー: headers already sent」に引っかかります)
  • 返値がそのままHTML ソースとして使用されるので、クロスサイトスクリプティングなどのセキュリティホールを作らないよう、注意が必要です。
  • 利用者(管理している人)に知らせたい情報や、デバッグ、エラーではあるが動作を停止するほどのものではない場合は、$info[]変数を利用してください。デバッグモード、もしくは警報表示モードの場合、画面上部にテキストが表示されます。

function plugin_<プラグイン名>_init()

初期化関数。プラグインが呼び出されるとまず最初に実行されます。プラグイン実行毎ではなく、最初の呼び出し時にだけ実行されます。一般的にはプラグインの言語設定、初期設定、JavaScriptやCSSの読み込み指定などで使用します。

一般的に、言語設定は、$msgハッシュ配列を設定し、set_plugin_messages($msg); に渡して使います。。

function plugin_<プラグイン名>_init(){
	// メッセージ設定
	// この場合、$_<プラグイン名>_messagesにメッセージが入る
	$messages['_<プラグイン名>_messages'] = array(
		'msg_title' => T_('message:%s'),
		'err' => T_('error:%s');
	set_plugin_messages($messages);
	// 呼び出すJavaScript設定(ページにスクリプトを埋め込む場合)
	global $js_blocks;
	$js_blocks[] = <<<JAVASCRIPT
google.load('maps',1);
$(document).ready(function(){
	$('#hoge').click(function(){
		alert('クリックされました');
	});
});
JAVASCRIPT;
}

この例では、$_<プラグイン名>_messages という名前の global 変数ができます。値は $messages['_<プラグイン名>_messages'] で設定したものです。

plugin_<プラグイン名>_convert() などの関数中では

global $_<プラグイン名>_messages;

のように宣言して使用します。 $_<プラグイン名>_messages['msg_title'] のようにしてアクセスできることでしょう。

i18n

なお、多言語化する場合は、gettextで行ないます。英語と日本語の文法の違いを良く考えて指定しましょう。例えば確認のメッセージを入れる場合、日本語では、「○○を××してもよろしいですか?」となりますが、英語だと、「May I ×× ○○?」のようになります。(この例は的確な表現ではない)

たとえば、○○に「テキスト」、××に「保存」という単語が入ると、日本語では、「テキストを保存してもよろしいですか?」になりますが、英語の場合、「May I save Text?」になります。

これを防ぐためには、以下のようにsprintf関数を使うといいでしょう。

$message['confirm'] = sprintf(_('May I %1s %2s?'),$message['action'],$message['text']);

英語:

May I %1s %2s?

日本語:

%2sを%1sしてもよろしいですか?

この他に、複数形の取り扱いに気をつけましょう。(面倒な場合は、May I save Text(s)?でもいいかもしれないけど・・・。)

ファイル内容

ファイル内にPHP関数:echoPHP関数:var-dump など、出力を開始してしまう関数やコードを置かないでください。
PHP エラー: headers already sent」が出る原因になります。(例外は、PHP関数:die またはPHP関数:exit を実行して、スクリプトを終了させる場合)

ユーザに設定させる初期値などについては、initディレクトリ内の<plugin-name>.ini.phpで指定するようにしてください。

他の定義名とバッティングしないように、PLUGIN_<plugin-name>_<subject> という形式を推奨します。

コンバート時のGET・POSTの出力内容に必要なものは refer と plugin という値で、

refer : そのページの名前($vars['page'])
plugin : プラグイン名

とします。

以下の値を global でグローバル変数にすることによって値を取得できます。

$script : スクリプトのurl ※get_script_uri() の使用を推奨します
$get : GETメソッドによるHTTPからの引数 ※$varsの使用を推奨します
$post : POSTメソッドによるHTTPからの引数 ※$varsの使用を推奨します
$vars : GET・POST両方のメソッドによるHTTPからの引数
$vars['page'] : 開いているページ名
  • グローバル変数 $scriptは参照せず、get_script_uri() を使用することを推奨
  • 意図的にGETメソッド、またはPOSTメソッドを禁止するのでなければ、$vars を利用してください。
    • 特に、他からも参照されるpage やreferer の値を書き換える必要のある場合は、$get や、$post だけでなく、$vars も書き換える必要があります。(PukiWiki のシステムが$vars['page'] 、$vars['referer'] と参照しているため)

GET, POST メソッドの入出力パラメータ名に関する制限

cmd, plugin
プラグイン名を渡すために、予約されています。また、どちらか一方しか利用できません。フォームを使ってプラグインを呼び出す場合や、URL から直接呼び出す場合に使われます。Adv.では、cmdを使うことが推奨されます。
page
'cmd' でプラグイン名を渡す場合のページ名。閲覧時などでページ名を得る場合に、使用することになる。
refer
'plugin' でプラグイン名を渡す場合のページ名。
'encode_hint
フォーム利用時に、文字化け防止用文字を自動付加するために予約されています。
word
本文中の特定の単語を色分けするために、予約されています。検索結果を表示する時などで使われています。$search_word_color の設定によっては色分け処理が省略されます。
p
keitai.skin.php で本文をを分割する時に、利用されています。携帯向け利用を考慮するのなら、このパラメータを使わないようにしてください。
digest
更新の衝突を検知するためのmd5 ハッシュを格納するために、利用されています。他の目的でこのパラメータを使うのは、避けたほうが無難です。(特に、他のプラグインにデータを引き継ぐ場合)
ajax Adv.
ページをスキンを通さず、JSONもしくはXMLで出力します。ajax=jsonのときはJSONになります。JSONの出力はだいたい以下のとおりです。よりパフォーマンスを出すときはプラグイン側で実装し、描画などはJavaScriptで行うような設計にしましょう。関連
{
 title:ページのタイトル,
 body:$bodyの内容
};
  • これ以外のパラメータ名は基本的に自由に使えます。しかし、他のプラグインを呼び出したりする場合は、同じ名前のパラメータが違う目的で使われている事によるトラブルを防ぐために、調整をする必要があります。

内部で予約しているグローバル変数など

代表的なものをいくつか。この他にも、pukiwiki.ini.php などの設定ファイルで使われている変数があります。

なお、アドレスを生成する場合は、get_cmd_uri()関数を使ってください。

$script
スクリプトへのurl
※get_script_uri() の使用を推奨します
$get, $post, $vars
GETメソッド、POSTメソッド、GET・POST両方のメソッド、によるHTTPからの引数
$pkwk_dtd($html_transitional)
出力するファイルの種類の宣言(DTD)を指定するフラグ。
$x_ua_compatible Adv.
IEのレンダリングモードを指定
$link_tags Adv.
Linkタグを登録(スタイルシートなど)
$js_tags[] Adv.
scriptタグ。
$js_blocks[] Adv.
JavaScriptを入力
$style_blocks Adv.
スタイルシートを入力。
$digest
convert_html() をする時に、$vars['page'] のページソースのmd5ハッシュ値が格納されます。
対象としたいページとは別のものが入っている事があるため、自力で得たほうが無難です。
$do_update_diff_table
更新の衝突時に表示される、差分テーブルを格納するために予約。
$google_loader Adv.
読み込ませたいGoogleAPI
$info[] Adv.
利用者に知らせたい内容やデバッグにご利用ください。PKWK_WARNIGが1のときに、ページ上部に表示されます。

非推奨(関連):

$head_tags[]
<head>タグ内に要素を追加します。この変数は非推奨です。なるべく、$link_tags、$js_tags、$js_blocks、$css_blocksを使ってください。
$foot_tags[] plus!
</body>直前に要素を追加します。この変数は非推奨です。なるべく、$link_tags、$js_tags、$js_blocks、$css_blocksを使ってください。

PukiWiki本体の重要な定義など

これらの定義に応じて、プラグインの動作ポリシーを変更するようにしてください。(例: PKWK_READONLYが有効の場合は、コメント用のフォームを表示しない、投稿を受け付けない)

PKWK_READONLY
変更を不可とする閲覧専用モードにするための設定。1.4.5 以降対応。 同時に各種認証(管理者パス、ユーザー認証)が無効(正解でも拒否する状態)になるので、プラグイン設計時に注意する事。
PKWK_SAFE_MODE
もはや利用されていない機能、常時有効にする必要がない機能、その他安全ではないと思われる機能など、公開Web環境では使わない方がよい機能を、まとめて無効にするための設定。1.4.5 以降対応。
PKWK_OPTIMISE
きちんと動作確認を済ませたのであれば必要のない処理(ダブルチェックやデバッグ用メッセージなど)、有用ではあるがスパムやボットなどに大量にアクセス(実行)されるとパフォーマンスに影響がでる動作の重い機能、などを、まとめて無効にするための設定。
PKWK_QUERY_STRING_MAX
PukiWikiが受け付けるQUERY_STRING(URIの?以降)の長さを制限するための設定。 長いアドレスを生成する場合には、受付拒否されないようにチェックが必要。

追加分:

PKWK_WARNING
運営上取るに足らないエラーやプラグインのデバッグ情報などを表示します。設置直後や、プラグインを追加した直後は、これを1にしてエラーを確認してください。ここの値を0にすると、エラーの詳細も表示されなくなります。
DEBUG
デバッグモード。スクリプトはすべて結合前のものが読み込まれます。

クロスサイトスクリプティング(XSS)

func_get_args()でプラグインに渡される引数はサニタイズされていませんので、引数の値を出力するプラグインでは、プラグインの中でサニタイズしてから出力しないとクロスサイトスクリプティング(XSS)の脆弱性が発生します1

ただし、インライン型プラグインの場合、{ }内の文字列はサニタイズ済みなので、

&plugin(foo){bar};

形式の bar を得るには、

$args = func_get_args();
$bar = array_pop($args); // サニタイズ済み。そのままHTMLに出力できる

のようにします。 barが省略された

&plugin(foo);

の場合でも、$barには空の文字列が入ります(上記参照)。

また、入力段階でもテキストをチェックすることを推奨します。

プラグイン作成時の注意

PukiWikiにはアクセス制御が組み込まれているため、プラグインからページを直接読み込んでいる場合はアクセス権のチェックをしないとセキュリティホールになります。

プラグインで、JavaScriptのソースを入れる場合は、$js_blocksに入れます。この設定はplugin_[プラグイン名]_init();関数内で実行すると便利です。 例:

function plugin_hoge_init(){
global  $js_blocks;
$js_blocks[] = <<<JS
$(document).ready(function(){
	$(".block").toggle(function() {
		$(this).animate({ backgroundColor: "black" }, 1000);
	},function() {
		$(this).animate({ backgroundColor: "#68BFEF" }, 500);
	});
});
JS;
 ...

ページにスクリプト処理を入れることは好ましくありません。外部ファイルにするのが無難です。外部ファイルは、skin/js/plugin内に入れるようにしてください。

また、JavaScriptを使用するプログラムの場合は、JavaScriptが無効のブラウザからどういうふうに見えるのかをよく考慮してください。例えば、

<button onclick="alert('ボタンが押されました');">Button</button>
<a href="javascript:"alert('リンクがクリックされました');">リンク</a>

というような出力をするプラグインの場合、JavaScriptが無効時は両方とも機能しません。つまり、わざわざPHPに書かせるコードではないということです。

もし、このような実装をしたい場合は、以下のようにしましょう。

<div class="attach_point">JavaScriptを有効にしてください</div>
global $js_blocks;
$js_blocks = <<< JS
$(document).ready(function(){
$('.attach_point').html('<button onclick="alert(\'ボタンが押されました\');">Button</button><a href="javascript:"alert(\'リンクがクリックされました\');">リンク</a>');
});

これで、JavaScript無効時は「JavaScriptを有効にしてください」というテキストが表示されます。なお、この例は流しこむHTMLに直接イベントを割り当てていますが、正しい例ではありません。実際使う場合は、ボタンやリンクに識別子(idやclassなど)を入れて$(...).click(function(){...});でイベントを割り当ててください。

デバッグ用関数および変数

これらの関数は、PKWK_WARNIGなどの設定値に関係なく出力されるので開発用途以外には使用しないでください

pr(変数名)
変数をダンプします。<pre><xmp>var_dump(変数)</xmp></pre>のエイリアスです。
print_backtrace(変数名)
変数のバックトレースを行ないます。

スケルトンのプラグイン

PukiWiki Adv.ではより開発を容易にするためプラグインのスケルトンファイルを用意しました。本家のプラグインでも使用可能なのでぜひ使ってください。

<?php
// PukiWiki - Yet another WikiWikiWeb clone
// $Id: [プラグイン名].inc.php,v [バージョンX.Y.Z] [更新日時YYYY/MM/DD] [更新時刻HH/MM/SS] [作者名] Exp $
// Copyright (C) [著作権表記(普通は自分の名前)]
// License: [ライセンス(通常はGPL2)]
//
// [プラグインの動作内容]

// プラグインの初期設定
function plugin_[プラグイン名]_init(){
	$messages = array(
		'_[プラグイン名]_msg' => array(
			'変数名' => '内容', // Plus! i18n / Adv.の場合は_()でくくることで多言語化できます。
			'usage' => 'プラグインの使い方',
			...
		),
	);
	set_plugin_messages($messages);
}

// アクション時の動作
// $vars配列には、QueryStringが入る(例:pcmd=uploadの場合、$vars['pcmd'] = uploadとなる)
// index.php?cmd=[プラグイン名]で実行
function plugin_[プラグイン名]_action(){
	global $vars, $_[プラグイン名]_msg;
	
	$action = isset($vars['action']) ? $vars['action'] : '';
	switch($action) {
		case 'view' :
		...
		break;
		...
	}
	
	return array(
		'msg' => 'スキンのタイトル部分に表示されるテキスト',
		'body' => 'スキンの$body部分に表示される文章'
	}
}

// ブロック要素
// #[プラグイン名](変数[1]...変数[n-1]){変数[n]};
function plugin_[プラグイン名]_convert(){
	global $vars, $_[プラグイン名]_msg;
 
	$value = func_get_args();
	$count = func_num_args();
 
	// #[プラグイン名](変数がない場合)
	if ($count < 1 || strlen($value[$count-1]) == 0) {
		return $_[プラグイン]_msg['usage'];
	}

 
	if ($count == 1){
		// #[プラグイン名](){内容}({~}のみ入っている場合
		// サニタイズ(必ず入れること)
		$data = htmlsc($value[$count-1], ENT_QUOTES, SOURCE_ENCODING);

		// 任意の処理
		...

		return $data; // 出力
	}else{
		// #[プラグイン名](オプション1,オプション2,...){内容}の場合
		$data = htmlsc(array_pop($value)); // 内容の部分の配列、すなわち末尾)を削除してその部分を$dataに入れる。

		$ret = array();
		// パラメーターを解析
		for ($i = 0; $i <= count($value); $i++) {
			$cond = htmlsc($value[$i]);
			switch($cond){
				case 'hoge' :
				...
				break;
				...
			}
 			...
		}
		// 任意の処理
		...
		return $output;
	}
}

/* End of file [プラグイン名].inc.php */
/* Location: ./wiki-common/extend/plugin/[プラグイン名].inc.php */

i18n

  1. まず、パソコンにPoeditをインストールしてください。
  2. 次に、Poeditへのパスを通します。Windowsの場合、コンピューターを右クリックし、システムの詳細設定をクリックしたあと、詳細設定から環境変数を開き、pathに、Poeditのインストール場所(C:\Program Files (x86)\Poedit\bin)を追記きます。
  3. 次に、locale/potディレクトリに移動し、Shiftキーを押しながら右クリックし、コマンドプロンプトを開いてください。
  4. コマンドプロンプトでは以下のように入力します。これで、自動的にpotファイルが生成されます。
    poedit [プラグイン名]
  5. Potファイルが生成されたら、そのpotファイルをPoeditで開いてください。
  6. あとは翻訳するだけです。 多言語化にはgettextを使用します。まず、すべてのメッセージを_(...)でくくります。次に、プラグインのpotファイルを生成します。