Manual:Tag extensions/ja

From MediaWiki.org

Jump to: navigation, search
Crystal Clear app tutorials.png タグの拡張機能 パーサー関数 フック 特別ページ スキン マジックワード

個別のプロジェクトにおいてシンプルな文字列の処理、本格的な情報の取得といった付加的な機能を持つ組み込みのwikiマークアップを拡張することが便利だと思うことはよくあります。タグ拡張機能によって新しいカスタムタグを作ることができます。例えば、シンプルな新しい寄付フォームをページに挿入する<donation />タグを導入するタグ拡張機能を使うことを考えます。パーサー関数フックを持つ拡張機能はMediaWikiの機能を変更もしくは強化するためにもっとも効果的な手段です。他のMediaWikiユーザーによって作成された既存の拡張機能に関しては、拡張機能の一覧表をご覧下さい。拡張機能を作成する前に誰かが既に作成していないかどうか常に一覧表を確認すべきです。

シンプルなタグ拡張機能はコールバック(callback)関数で構成されます。パーサーが実行されるとき、実際のHTMLをレンダーするために対応するコールバック関数を呼び出すことで、パーサーはすべての特別なタグのインスタンスを見つけて置き換えるために、コールバック関数はパーサーにフック(hook)されます。

Contents

[edit]

<?php
$wgExtensionFunctions[] = 'efSampleSetup';
 
function efSampleSetup() {
    global $wgParser;
    $wgParser->setHook( 'sample', 'efSampleRender' );
}
 
function efSampleRender( $input, $args, $parser ) {
    // 何も存在しない場合、ユーザ提供の入力を
    // エスケープして再び返す
    return htmlspecialchars( $input );
}

この例では、<sample>タグに対するコールバック関数を登録します。次のようにユーザーはこのタグをページに追加できます:

<sample arg1="xxx" arg2="xxx">...input...</sample>

パーサーはefSampleRender()を呼び出し、3つの引数に渡します:

$input 
<sample></sample>タグの間、もしくはnull、タグが"閉じている"場合では、<sample />となります
$args 
タグの引数で、HTMLのタグ属性のように入力されます; これは属性名でインデックス化された連想配列です
$parser 
親パーサーです; もっと高度な拡張機能が文脈上のTitleを取得したり、wikiテキストを解析したり、中括弧を拡張したり、リンク関係や依存性を登録したりするためにこれを使用します。

[edit] 属性

別の例を見てみましょう:

$wgExtensionFunctions[] = 'efSampleSetup';
 
function efSampleSetup() {
    global $wgParser;
    $wgParser->setHook( 'sample', 'efSampleRender' );
}
 
function efSampleRender( $input, $args, $parser ) {
    $attr = array();    
    // 今回、属性と値のリストを作成し、
    // ユーザーの入力と一緒にそれらをダンプします
    foreach( $args as $name => $value )
        $attr[] = '<strong>' . htmlspecialchars( $name ) . '</strong> = ' . htmlspecialchars( $value );
    return implode( '<br />', $attr ) . "\n\n" . htmlspecialchars( $input );
}

この例では、値と一緒にタグに渡された属性をダンプします。これによって新しい、カスタムタグの柔軟な仕様を作成できるようになります。例えば、<emailform to="User" email="user@foo.com" />のようなものを使用して、ユーザーがユーザーページにコンタクトフォームを差し込むを可能にするタグ拡張機能を定義するとします。

MediaWikiで利用可能なタグ拡張機能は非常に豊富です。その中のいくつかはこのサイトに登録されており; 他のものはウェブ検索を通して見つけることが出来ます。

[edit] 規約

一つの拡張機能を単独のファイルで構成できますが、それぞれの拡張機能に対して、extensionsディレクトリの中にextension_nameサブディレクトリを作成してその中に以下の3つのファイルを設置することをお勧めします:

  • 小さなセットアップファイル、extension_name.setup.php
  • 国際化ファイル、extension_name.i18n.php
  • コード本体を含むファイル、extension_name.body.php

セットアップファイルによって1つの要素が$wgAutoloadClasses配列に追加されます。この配列はどのファイルがロードされるのかを指定します:

$wgAutoloadClasses[''extension_name''] = dirname(__FILE__) . '/''extension_name''.body.php';

[edit] 拡張機能を公開する

  1. Extension:<extension_name>という名前の新しいページを作成します。そのページではどのようにインストールするのか、それを使っている様子を表すスクリーンショットなどの情報を掲載します。これらの情報の掲載を支援するためにTemplate:Extensionという便利なテンプレートがあります。詳細についてはテンプレートページをご覧下さい。ページの本体にできるだけ多くの情報を追加し、talkページに書き込まれるユーザの質問への回答を定期的に行った方がよいでしょう。また、Category:Extensionsカテゴリにあなたのページが表示されているかどうかを確認して下さい。

    ノート: meta.wikimedia.org上に拡張機能のページが存在する場合、管理者によって完全な履歴と一緒に移転されます - それまでの間は、metaで編集を続けて下さい

  2. 拡張機能コードの範囲内で新しくManual:Hooks/jaを作成する拡張機能に関してはExtension hook registryページに登録をお願いします。新しく特別ページを作成する拡張機能に関してはExtension hook registryページで登録をお願いします。
  3. mediawiki-lメーリングリストに通知する。

[edit] FAQ

[edit] セキュリティ関連

上記の例において、入力値を返す前に、htmlspecialchars()を利用して入力値をエスケープしていることにおきづきになると思います。任意のHTMLインジェクションを回避するためにすべてのユーザー入力をechoしてクライアントに返す前にこの方法で取り扱うことは大切です。HTMLインジェクションはクロスサイトスクリプティング(XSS)の脆弱性を導く可能性があるからです。

[edit] タイミングと拡張機能

拡張機能に対してコードを変更する場合、拡張機能を利用するすべてのページが、理論的には、即座に新しいコードの結果を反映します。技術的には、拡張機能を含むページがレンダーされるたびにコードが実行されることを意味します。

実際には、ページキャッシングのおかげで、これはあまり当てはまりません。ページキャッシングはMediaWikiソフトウェア、ブラウザ、もしくは中間のプロクシ、ファイヤーウォールによって行われます。

MediaWikiのパーサーキャッシュを回避して、新しいバージョンのページが生成されることを保証するためには、ブラウザのアドレスバーに表示されている記事のURLの一番後ろに"?action=purge"を追加してEnterキーを押してページを再読込します。これによってページとそれを参照するすべてのテンプレートはすべてのキャッシュデータを無視して再生成されます。メインページ自身が修正されない場合はパージアクションが必要ですが、レンダーされなけばならない方法法は変更されます(拡張機能が修正された、もしくは修正されたテンプレートを参照しました)。

ページの新しいコピーを取得するためにこの方法が十分ではない場合、通常は上記のURLの末端に'&rand=somerandomtext'を追加することで中間状態のキャッシュを回避できます。'somerandomtext'が毎回異なることを確認して下さい。

[edit] 拡張機能を利用してキャッシングを無効にする方法は?

MediaWiki 1.5以降では、パーサーは3番目のパラメータとして拡張機能に渡されます。このパーサは次のようなキャッシュを無効にするために使われます:

 function wfSomeHookFunction( $text, $params, &$parser ) {
     $parser->disableCache();
     ...
 }

サイト上でのすべてのサーバーサイドキャッシングを無効にするために、次のコードをLocalSettings.phpに追加して下さい:

/** ページのクライアントサイドキャッシングを許可する */
$wgCachePages       = false;
 
/**
 * すべての先行するキャッシュページを無効にするために現在の時刻を設定します。
 * クライアントとサーバサイドキャッシングの両方に影響を与えます。
 * 次のコマンドを使うことでサーバ上の現在の日付を取得することが出来ます:
 *  date +%Y%m%d%H%M%S
 */
$wgCacheEpoch = 'date +%Y%m%d%H%M%S';

[edit] 拡張機能でwikitextをレンダーする方法は?

[edit] バージョン1.8以降

パーサーフック関数parserオブジェクトに参照として渡され、wikitextを解析するためにこれを使います。

function efWonderfulHook( $text, $args, &$parser ) {
   $output = $parser->recursiveTagParse( $text );
   return '<div class="wonderful">' . $output . '</div>';
}

Parser::recursiveTagParse()がバージョン1.8前後で導入されました。これを使う利点は簡潔であること(一つの引数だけを取得して一つの文字列を返します)と$textで拡張機能タグを解析するので拡張機能タグをネストできることです。

[edit] バージョン1.8以前

バージョン1.8以前では、次のようなあまりきれいではない方法を使わなければなりません:

function efWonderfulHook( $text, $args, &$parser ) {
   $output = $parser->parse( $text, $parser->mTitle, $parser->mOptions, true, false );
   return '<div class="wonderful">' . $output->getText() . '</div>';
}

Parser::parse()への4番目と5番目のパラメータは$lineStart$clearStateです; これらはパーサーの振るまい方を指示します。 $lineStartは、trueの場合、wikiテキストが新しい行で始まるものとして取り扱うように指示します(ブロック、もしきはリストなどを取り扱う場合に使います)。$clearState、はtrueの場合、内部状態の情報をクリアします; 多くの場合、これはfalseになります。

上記の例において、Parser::parse()は生のHTMLではなく、ParserOutputオブジェクトを返すことに注意して下さい。

これが動作しない場合(実行の順番、ネスト、別のフックとの混合などがよくある原因)、parserオブジェクトのクローンコピーを使って下さい:

function efWonderfulHook( $text, $args, &$parser ) {
   $lparse = clone $parser;
   $output = $lparse->parse( $text, $parser->mTitle, $parser->mOptions, true, false );
   return '<div class="wonderful">' . $output->getText() . '</div>';
}

[edit] 拡張機能とテンプレート

テンプレートパラメータは拡張機能タグ内部で使用される場合(例えば、<myext>{{{1}}}</myext>)、"{{{1}}}"は拡張機能に直接渡され、テンプレートが呼び出されたときに指定された正しい値に変換されません。パーサーのテンプレートの前に拡張機能が解析されるからです。もっと詳細な情報と利用可能なパッチについてはbug 2257をご覧下さい。

この問題の次善策はテンプレート変数を拡張するいパーサー関数を作成し拡張機能コードを作成することです。 テンプレートパラメータで<myext>を呼び出すためには、次のような新しいパーサー関数を作成します:

<?php
$wgExtensionFunctions[] = 'wfExtensionWrapperParserFunction_Setup';
$wgHooks['LanguageGetMagic'][]       = 'wfExtensionWrapperParserFunction_Magic';
 
function wfExtensionWrapperParserFunction_Setup() {
    global $wgParser;
    $wgParser->setFunctionHook( 'myext', 'wfExtensionWrapperParserFunction_Render' );
}
 
function wfExtensionWrapperParserFunction_Magic( &$magicWords, $langCode ) {
    $magicWords['myext'] = array( 0, 'myext' );
    return true;
}
 
function wfExtensionWrapperParserFunction_Render( &$parser, $param1 = '' ) {
    # 入力パラメータは拡張されたテンプレートを伴うwikitextです。出力もwikitextです
    return "<myext>{$param1}</myext>";
}

次のようなページ上で関数を呼び出します: {{#myext: param1}}

[edit] XMLスタイルのパラメータを拡張機能タグに渡すには?

[edit] バージョン1.5以降

MediaWiki 1.5以降では、XMLスタイルのパラメータ(タグ属性)がサポートされています。パラメータは2番目のパラメータとしてフック機能に連想配列として渡されます。文字列の値は既にデコードされたHTML文字エンティティを持つので、それらをHTMLに戻す場合、HTMLインジェクションのリスクを避けるために、htmlspecialchars()を使用することを忘れないで下さい。

[edit] バージョン1.5以前

MediaWiki 1.5はパラメータと拡張機能タグの間にパラメータを渡さなければなりません。すなわち、オプションのために特別な構文と、それらのためのフック入力テキストを分析しなければなりません。可能な構文の一つは次の通りです:

<mytag>
@option1=foo
@option2=bar

...content to be processed by the extension....
</mytag>

拡張機能機能に置いて、通常の処理を実行する前に@option1=fooのような行を探します。他の構文を選択することももちろん自由です。

[edit] 拡張機能のHTML出力の修正を避ける方法は?

現在の拡張機能コードではパーサーフック拡張機能がインラインのマテリアルを生成しそれらが最後のブロックレベルに挿入されることを前提としています(Bugilla:8997参照)。

これに取りかかる方法は実際の結果の代わりにマーカーを出力する拡張機能のパーサーフック関数を作成し、そのマーカーをParserAfterTidyフックのために登楼されたハンドラ関数の実際の結果で置き換えることです。

$wgHooks['ParserAfterTidy'][] = 'myextParserAfterTidy';
$wgExtensionFunctions[] = "myextSetup";
 
function myextSetup {
    global $wgParser;
    $wgParser->setHook( "mytag", "myextParserHook" );
}
 
$markerList = array();
function myextParserHook($input, $argv, &$parser) {
    global $markerList;
    $output = "result that you do not want to be modified by the parser";
    $marker = "xx-marker".count($markerList)."-xx";
    $markerList[] = $output;
    return $marker;
}
 
function myextParserAfterTidy(&$parser, &$text) {
    // $textでマーカーを見つけ
    // マーカーを実際の出力で置き換える
    global $markerList;
    for ($i=0;$i<count($markerList);$i++)
      $text = preg_replace('/xx-marker'.$i.'-xx/',$markerList[$i],$text);
    return true;
}

別の方法としては、マーカーの出力の代わりに、武装されたフォーム(例えば、Base64でエンコードされたもの)に結果を出力することと、ParserAfterTidyハンドラでそれを武装解除(デコード)することも可能です。

[edit] Special:Version上で拡張機能を表示するには?

拡張機能をMediaWikiのSpecial:Versionページで表示するためには、PHPコードの範囲内で拡張機能クレジットを割り当てなければなりません。

これを行うために、フック行もしくは関数の定義の前に$wgExtensionCredits変数を最初の実行可能な行のコードとして追加します

拡張機能クレジットの例は次の通りです:

<?php
/**
 * Example.php
 * This Extension does .......
 * written by John Doe
 * http://www.johndoe.com
 * To activate the functionality of this extension include the following in your
 * LocalSettings.php file:
 * require_once('$IP/extensions/Example.php');
 */
if(! defined( 'MEDIAWIKI' ) ) {
   echo( "This is an extension to the MediaWiki package and cannot be run standalone.\n" );
   die( -1 );
} else {
 
   $wgExtensionCredits['validextensionclass'][] = array(
       'name' => 'Example',
       'author' =>'John Doe', 
       'url' => 'http://www.mediawiki.org/wiki/User:JDoe', www.JohnDoe.com',
       'description' => 'This Extension is an example and performs no discernable function'
       );
 
   .....
 
   function fnExample(){
 
      .....
 
   }
}
?>

'validextensionclass'を次のうちのどれか一つで置き換えて下さい:

  • 'specialpage' -- MediaWikiの特別ページへの追加するために確保されます;
  • 'parserhook' -- 拡張機能が修正され、補完、もしくはMediaWikiでパーサ関数に置き換える場合に使用されます;
  • 'variable' -- MediaWikiに複数の機能を追加する拡張機能です;
  • 'other' -- すべての他の拡張機能です。

[edit] 関連項目