開発:ブロック

提供:MoodleDocs
2009年8月17日 (月) 19:41時点におけるMitsuhiro Yoshida (トーク | 投稿記録)による版 (→‎認証ユーザのみ)
移動先:案内検索

作成中です - Mitsuhiro Yoshida 2009年5月28日 (木) 15:39 (UTC)

ブロックの作成に関するステップバイステップガイド

原作者: Jon Papaioannou (pj@moodle.org)

このドキュメントでは、Moodleで使用する独自のブロックを開発したい開発者のためのガイドを提供します。ブロックサブシステムは、1.5リリースのため書き換えられ、拡張されたので、このドキュメントは、1.5開発バージョンのMoodle (およびすべての新しいバージョン) のみに適用されます。しかし、あなたがMoodle 1.3および1.4のために書かれたブロックを修正したい場合、最新のバージョンに関するドキュメントを有用だと思うでしょう (付録Bをご覧ください)。

このガイドは、任意のHTMLを表示できる、設定可能な多目的ブロックの開発を目標としたインタラクティブコースのために執筆されています。また、Moodleまたは一般的なプログラミングに関する多少の経験のある人を対象として、Moodleのブロックを作成することがいかに簡単であるか示すことを目標とします。しかし、少しのPHPプログラミングの知識を必要とします。

メインガイドでは、ドキュメントに関する純粋な情報に焦点が置かれていないため、経験豊富な開発者および参考文献のみ閲覧したい人は、 付録Aをご覧ください。

基本コンセプト

このガイドを通じて、私たちは、ほとんどのブロックの特徴を自由に実演するため、HTMLブロックをスクラッチから作成してみます。私たちのブロックには、「SimpleHTML」というブロック名をつけます。このブロック名は、私たちのブロックが実際に保存されるサーバの実ディレクトリ名に関して、同じディレクトリ名を強制することはありません。しかし、一貫性を保持するため、「simplehtml」を使用することで、Moodleの開発において、どのような場合でも要求される小文字の名前付けに従います。

「simplehtml」を含んだ名称のファイルまたはディレクトリを参照する場合は、常に私たちが変更する部分は「simplehtml」のみだと記憶に留めることが重要です。残りは、Moodleが正常に動作するため必要な標準化された部分です。

このガイドでファイルパスを参照する場合、常にスラッシュ (/) から始まります。これは、Moodleホームディレクトリを参照します。また、すべてのファイルおよびディレクトリは、Moodleホームディレクトリに対して参照されます。

レディ、セット、ゴー!

最も基本的な場合、Moodleで「ブロック」を定義するためには、1つのソースコードファイルのみ必要です。ディレクトリ「/blocks/simplehtml」を作成して、その中に私たちのコードを保持する「block_simplehtml.php」というファイルを作成します。それから、私たちは、ブロックのコーディングを始めます:

<?php class block_simplehtml extends block_base {

 function init() {
   $this->title   = get_string('simplehtml', 'block_simplehtml');
   $this->version = 2004111200;
 }
 // 次のセクションで、もうひとつの関数が定義されるので、PHPタグおよびクラス定義の丸括弧が閉じられています。

最初の1行は、ブロッククラス定義です。これは、表示されているのと同じ様式で記述する必要があります。もう一度、「simplehtml」の部分のみ変更することができます (また、そうすべきです)。他のすべては標準化されています。

そして、私たちのクラスは、小さなメソッドを与えられます: init()。これは、すべてのブロックに必要なメソッドであり、内部に2つのクラスメンバ変数を設定することが目的です。しかし、これらの変数の本当の意味は何でしょう? 詳細は、下記をご覧ください。

$this->titleは、私たちのブロックヘッダに表示されるタイトルです。私たちは、どのうなタイトルでも設定することができます。この場合、恐らくブロックと共に提供される言語ファイルから実際のタイトルが取得されます。あなたがブロックタイトルに何も「表示したくない」場合、ここに説明的な値を入力してください (しかし、空のストリングにはしないでください)。では、先に進みましょう。タイトルを表示しない方法に関して、後ほどタイトル表示を無効にする方法を参照します。

$this->versionは、私たちのブロックのバージョンです。実際には、あなたのブロックがデータベースの特別テーブルに独自のデータを保持したい場合のみ重要です (例 非常に複雑なブロック)。その場合、バージョンナンバーは、活動で使用されるのと全く同じように使用されます。「古い」バージョンのブロックデータを最新のデータに増分アップグレードするため、バージョンナンバーはアップグレードスクリプトが使用します。ブロックは比較的シンプルで自データを保持しない傾向にありますので、私たちは、この処理に関する概要を後で説明します。

私たちの例では、正に前述の例に該当しますので、 $this->versionYYYYMMDD00をセットして、後は忘れることにします。

アップデート:
バージョン 1.5以前では、それぞれのブロッククラスの基本構造は、若干異なっていました。古いブロックを新しい標準に準拠させるための変更に関して、詳細は 付録Bをご覧ください。

私には雑音 (Static) が聴こえるだけ

ブロックが画面で何かを表示するために、私たちは、さらに1つのメソッドを (右中括弧が閉じる前に) クラスに追加する必要があります。新しいコードは以下のとおりです:

 function get_content() {
   if ($this->content !== NULL) {
     return $this->content;
   }
   $this->content         =  new stdClass;
   $this->content->text   = 'The content of our SimpleHTML block!';
   $this->content->footer = 'Footer here...';

   return $this->content;
 }

} // Here's the closing curly bracket for the class definition

   // and here's the closing PHP tag from the section above.

?>

これよりシンプルになりません、でしょ? 何が起きるか、このメソッドを解剖してみましょう ...

まず第一に、値がNULLではない場合、 $this->contentの現在値を返すチェックがあります。そうでない場合、私たちはこの値を計算し始めます。計算には、潜在的に多大な時間を要して、それぞれのブロックで複数回コールされるため (そのようにMoodleが内部で動かします)、私たちは予防措置として、この時間節約の手段を含むようにします。コンテンツが計算されていない場合 (NULLの場合)、私たちは最初から定義します。そこでは、コード自体が語りますので、それほど語ることはありません。必要であれば、私たちは、HTMLをテキストおよびフッタで使用できるということに留意してください。

この時点で、私たちのブロックは、Moodleに自動的にインストールされ、コースに追加することができます。管理ページに移動して、ブロックのインストール (サイト管理ブロック内の「通知」をクリック) を確認した後、このチュートリアル (ドキュメント) に戻ってください。

設定しよう

私たちのブロックの現在のバージョンでは、それほど多くのことはできません。非常に有用だとは言えない固定メッセージを表示するのみです。私たちが本当にやりたいことは、教師がブロック内に入れるものをカスタマイズできるようにすることです。このブロックとの会話は、「インスタンス設定」と呼ばれます。それでは、私たちのブロックにインスタンス設定を与えてみましょう ... まず第一に、私たちはMoodleに対して、私たちのブロックにインスタンス固有の設定装備を提供したいと伝える必要があります。 これは、私たちのブロッククラスに、もうひとつのメソッドを追加するのと同じくらい簡単です:

function instance_allow_config() {

 return true;

}

すべてのコースにおいて、「編集モードの開始」ボタンをクリックした場合、私たちのブロックのヘッダに「編集 ...」アイコンを表示するには、少しの修正のみで十分です。しかし、あなたがそのアイコンをクリックした場合、ブロック設定が正しく実装されていない旨、警告が表示されてしまいます。挑戦してみましょう、安全です。Moodleの苦情は当然です。私たちは設定を必要だと伝えたのに、「どのような」設定が欲しいのか伝えていませんし、また何を表示したいかも伝えていません。そのためには、さらに1つのファイルを作成する必要があります: /blocks/simplehtml/config_instance.html (ファイル名は、正確にこのファイル名にしてください)。差し当たり、以下をコピーしてファイルに貼り付けた後、保存してください:

      <?php print_string('configcontent', 'block_simplehtml'); ?>:
      <?php print_textarea(true, 10, 50, 0, 0, 'text', $this->config->text); ?>
     <input type="submit" value="<?php print_string('savechanges') ?>" />

<?php use_html_editor(); ?>

上記コードを読む限り、WYSIWYGエディタが有効にされたtextareaで私たちのブロックの必要なコンテンツを記述して、保存ボタンをクリックすることは難しいことではありません。しかし ... $this->config->text とは何でしょう? そうですね ... Moodleでは、ブロック開発者がブロックを簡単に開発できるようにしています。実際には、textareaが「text」と名前付けされていることに気付きましたか? 送信ボタンがクリックされた場合、Moodleはconfig_instance.htmlファイルで探すことのできる、すべてのフィールドを設定データのインスタンスとして保存します。

そして、私たちは$this->config->variablenameとしてデータにアクセスすることができます。ここで「variablename」は、私たちのフィールドに使用する実フィールド名です。この場合、"text"となります。ですから、基本的に上記のフォームでは、現在のブロックコンテンツ (そのまま) と共にテキストエリアをプレ実装して、私たちがコンテンツを変更できるようにします。

あなたは、送信ボタンの存在して、<form>エレメントが存在しないことにも驚いていることでしょう。しかし、私たちは全く心配する必要がないことが真実です。長期間に渡り、Moodleは開発者が簡単に開発できるよう、発展してきました! 私たちに必要なことは、どのようなフォーマットであれ、送信ボタンを含んだ必要な設定オプションを表示することです。Moodleは、残りのすべてを自ら処理します。インスタンス設定変数は、 init()以外、自由にすべてのクラスメソッドからアクセスすることができます。

イベントのデフォルトの動作に満足できない場合、私たちはオーバーライドすることができます。しかし、オーバーライドするためには、私たちのブロックに関して、高度な修正が必要であり、ここでは取り上げられていません。詳細は、 付録Aをご覧ください。 $this->configを通して、このインスタンス設定データを参照できるようになります。最後の工夫は、私たちのブロックに対して、設定データに保存された内容を「表示すること」を実際に伝えることです。伝えるには、/blocks/simplehtml/block_simplehtml.phpの中で、このスニペットを見つけてください:

$this->content = new stdClass;
$this->content->text   = 'The content of our SimpleHTML block!';
$this->content->footer = 'Footer here...';

そして、以下のように変更します:

$this->content = new stdClass;
$this->content->text   = $this->config->text;
$this->content->footer = 'Footer here...';

おや、この時点でフッタは実際に存在していません。役立つことはありませんので、私たちのブロックからフッタを取り除きます。私たちは、上記と同じように簡単にフッタ設定を決定することができます。ですから、私たちの最新のコード、スニペットは以下のようになります:

$this->content = new stdClass;
$this->content->text   = $this->config->text;
$this->content->footer = ;

このディスカッションの後、私たちのブロックはゴールデンタイムに入ります! 本当に、あなたが、SimpleHTMLブロックを設置したコースに移動すると、コンテンツの修正は簡単だということを目の当たりにすることでしょう。

トップページに戻る

スペシャリスト

ブロックコンテンツのインスタンス設定の実装は、私たちの食欲を満たすには十分でした。しかし、誰がそこで止めたいですか? なぜ、ブロックタイトルもカスタマイズしないのですか?

本当に、なぜ。さて、これに到達するための、私たちの最初の試みは、極めて普通です。/blocks/simplehtml/config_instance.htmlに別のフィールドを追加してみましょう。さあ、始めましょう:

<?php print_string('configtitle', 'block_simplehtml'); ?>:

   <input type="text" name="title" size="30" value="<?php echo $this->config->title; ?>" />

私たちは、編集したファイルを保存して、コースに移動して、ブロックのタイトルを編集して、そして ... 何も起きません! インスタンス設定は、正しく保存され、(念のため再度編集して) すべて問題ありませんが、何も表示されません。単に「SimpleHTML」タイトルが表示されるだけです。 少しだけ戻って考えてみれば、これは、とても奇妙だということではりません。私たちが$this->titleで設定したinit() メソッドを覚えていますか? 実際、それから私たちは、その設定値を変更していません。また、$this->titleは、当然$this->config->title (少なくともMoodle) と同じではありません。私たちに必要なことは、$this->titleのインスタンス設定の値を更新することです。しかし、少し前に述べたように、私たちは、init()を除くすべてのメソッドで、 $this->configを使用することができます! では、私たちには何ができるのでしょうか? 私たちの袖から別のエースを取り出してみましょう。そして、この小さなメソッドを私たちのブロッククラスに追加してみましょう: function specialization() { if(!empty($this->config->title)){ $this->title = $this->config->title; }else{ $this->config->title = 'Some title ...'; } if(empty($this->config->text)){ $this->config->text = 'Some text ...'; } } なるほど、私たちが最初からずっとやりたかったことですね! しかし、 specialization() メソッドで何が起こるのでしょう? 「魔法の」メソッドは、実際に極めて素晴らしい特性を持っています。私たちのインスタンス設定がロードされ、利用されるようになるとすぐに (つまり、init()がコールされるとすぐに)、Moodleから自動的にコールされることが「保証」されています。これは、ブロックコンテンツが最初に計算されること、そして本当にブロックに関して、何らかの処理が実行される前を意味します。従って、この場合、「可能な限り速やか」に実行される、すべてのデータ設定に関して specialization() メソッドを提供することは、自然な選択と言えます。

あなたには私が見える、そして、あなたには私が見えなくなる (Now You See Me, Now You Don't)

さて、ブロックで使用することのできる、別の素晴らしいテクニック、そして、かなり頻繁に役立つテクニックに関して、話すのに良い時間になりました。特に、時々私たちのブロックは興味深いものを表示します。しかし、有益ではないものを表示する場合もあります (例えば、「最近の活動」ブロックで実際に表示するものがない場合です)。

しかし、この場合、ブロックは前述の活動の欠如に関して、あなたに明確に伝えることを選んだわけで、これはほぼ間違いなく有用ではあります。そして、表示する必要のないブロックを「非表示にする」ことは、素晴らしいことです。

これは、実際に可能です。その方法は、 get_content() メソッドがコールされた後、ブロックの中身が完全にないようにすることです。具体的に「中身がない」とは、$this->content->textおよび$this->content->footerThisの両方が、それぞれ空のストリング ('') になっていることです。Moodleは、 is_empty() メソッドをコールすることにより、この状態を確認します。そして、ブロックが実際に空の場合、実際に全く何も表示されません。

ブロックタイトルの実際の値、 hide_header() メソッドの存在の有無は、この動作に影響を及ぼしませんので留意してください。コンテンツがない場合、他の内容に関わらず、ブロックは空であると判断します。

私たちは軍団

現在、私たちのブロックはタイトルおよびコンテンツに関して、完全に設定可能な状態です。実際のところ、私たちは非常に殆どのことを実現することができます。1つのコースに同一ブロックを複数追加することができることは大変素晴らしいことです。そして、あなたが推測するように、このようにすることは、私たちのブロックに別の小さなメソッドを追加するくらい非常に簡単です:

function instance_allow_multiple() {

 return true;

}

これにより、すべてのコースにおいて、MoodleはSimpleHTMLブロックのインスタンスをいくらでも設置することができます。私たちのファイルの変更を保存した後、さらなる騒動なしで、即座に複数のブロックをMoodleで追加することができるようになります!

ここで言及すべき、いくつかの興味深い点があります。まず第一にブログ自体で同一ページに複数インスタンスを許可したとしても、管理者には、そのような挙動を許可しないオプションがあります。この設定は、それぞれのブロックから分離され、「サイト管理 > プラグイン > ブロック > ブロックの管理」にて変更することができます。

そして最後に素晴らしい内容です。私たちが instance_allow_multiple() メソッドを定義するとすぐに、すでに定義されている instance_allow_config() メソッドは、廃止されてしまいます。

ブロックに関して、複数インスタンスが許可された場合、「ブロックは設定したいはず」とMoodleは考えます (ブロックが同じ種類の場合、同じページに複数ブロックインスタンスを設置する時のポイントは何でしょう?)。従って、Moodleは自動的に「編集」アイコンを提供します。ですから、私たちは、すべての instance_allow_config() メソッドを実害なしに取り除くことができるのです。ブロックの複数インスタンスが許可されていない場合のみ、私たちは当該メソッドが必要です。

トップページに戻る

グローバリゼーションの効果

それぞれのブロックインスタンスを独自のパーソナルデータで設定することは、十分に素晴らしいのですが、時々管理者は、特定のブロックに関して、同時にすべてのインスタンスを「触る」方法を必要とするものです。私たちのSimpleHTMLの場合、少しの設定をすべてのインスタンスに適用することは道理にかなっていますし、考え付くことが困難であるというわけでもありません。

例えば、私たちは、それぞれのブロックのコンテンツを文字のみに制限したり、純粋なテキストを使用するため、ブロックコンテンツからHTMLをフィルタすると良いでしょう。確かに、そのような機能は私たちのブロックのネーミング「SimpleHTML」に何ももたらしません。しかし、悩んだ管理者たちは、どこかに実際に有益な点を見つけることでしょう。

このような設定は「グローバル設定」と呼ばれ、特定のブロックタイプにのみ提供されます (しかし、ブロックタイプのすべてのインスタンスが影響を受けます)。そのような設定を私たちのブロックに実装することは、インスタンス設定の実装に極めて似ています。では、私たちは2番目の例の実装方法を見てみましょう。ここでは、ブロックコンテンツにHTMLではなく、テキストのみ許可するよう設定します。 まず第一に、これは驚くことなのですが、ブロッククラスに小さなメソッドを追加することで、私たちのブロックにグローバル設定を提供したい旨、Moodleに伝える必要があります::

function has_config() {

 return true;

}

それから、実際に設定画面を表示するため、私たちはHTMLファイルを作成する必要があります。私たちのケースでは、「コンテンツにHTMLを許可しない」チェックボックスおよび「送信」ボタンを表示するのみです。それでは、/blocks/simplehtml/config_global.htmlという名称のファイルを作成して、下記の内容をコピー&ペースとしましょう:

<input type="hidden" name="block_simplehtml_strict" value="0" />
<input type="checkbox" name="block_simplehtml_strict" value="1"
  <?php if(!empty($CFG->block_simplehtml_strict)) 
            echo 'checked="checked"'; ?> />
  <?php print_string('donotallowhtml', 'block_simplehtml'); ?>

<input type="submit" value="<?php print_string('savechanges'); ?>" />

私たちのブロック名に忠実なように、十分にシンプルです。これは、「block_simplehtml_strict」という名称のチェックボックスを表示して、Moodle設定変数に同じ名称の値が設定されて (例 $CFG->block_simplehtml_strict) 空白ではない場合、(空白、ゼロまたはブール値のFALSEではない) チェック済みの (現在のステータスを反映した) チェックボックスを表示します。

なぜ、同じ名称の設定をチェックするのですか? グローバル設定のコードを保存するデフォルトでは、私たちのフォームの中にある、すべての変数を取得して、Moodle設定オプションとして同一名で保存するためです。従って、説明的な名称を使うこと、別の設定の名称と衝突する可能性のない名称を使うことは、優れた実践と言えます。

「block_simplehtml_strict」という名称は、明らかに両者の要件を満たします。

目先の利く読者は、実際には「block_simplehtml_strict」という名称の2つの入力フィールドが、私たちの設定ファイルにあることに気付いたことでしょう。1つは隠しフィールドで、その値は常にゼロ、別のフィールドはチェックボックスで、その値は1です。何が起こるのでしょう? なぜ、両方のフィールドがそこにあるのでしょう?

実際のところ、これは私たちの仕事を可能な限り簡単にするための、小さなトリックです。HTMLフォーマットは、次のように動作します: フォーム内のチェックボックスがチェックされていない場合、フォーム送信時、PHPに渡される変数の中にチェックボックスの名称は全く出現しません。これは事実上、私たちがチェックボックスのチェックを外して送信ボタンをクリックした場合、PHPに変数が全く渡されないことを意味します。従って、その値をPHPが「0」に更新する方法を知らないわけで、私たちが最初に「厳密型 (strict)」を有効にした場合、それが無効にされることはありません。もちろん、無効にされることは私たちが望む挙動でもありません。

しかしながら、フォームから受け取った変数をPHPが処理する場合、フォームで表示される順で変数が処理されます。すでに処理されている変数と同一名の変数を受け取った場合、古い値を新しい値がオーバーライトします。これを利用して、私たちのロジックは、次のようになります: 最初、「block_simplehtml_strict」の値には、無条件にゼロが設定されます。それから、チェックボックスがチェックされた場合、前述のように前の値をオーバーライトして、値には「1」が設定されます。最終結果として、設定値は、私たちの希望通りに設定されます。

私たちのトリックのバグに対応するには、「デフォルトでチェックボックスがチェックされているか?」をテストするため、「if(!empty($CFG->block_simplehtml_strict))」が使用されている点に注目してください。このスクリプトが最初に実行された場合、変数$CFG->block_simplehtml_strictは、全く存在していません。そして、最初にその変数が設定された場合、値は「0」または「1」となり得ます。文字列「1」が評価されないのに対して、「設定なし」文字列「0」は両者とも空として評価されます。変数が全く設定されていないというPHPの警告を避けるため、そして可能性のある値 (0または1) に関して、人間に可読可能な素晴らしい表現を持つため、私たちはこの処理を追加しています。

config_save()

私たちは、HTMLの少しの行の中に相当量のトリックを何とか詰め込みました。私たちが念頭に置いている特定の設定に対して、トリックが十分でない場合のことを考えて、代わりの方法を議論した方がよさそうです。メソッド config_save()でデータが保存されているため、デフォルトの実装は、以下のようになります:

function config_save($data) {

 // Default behavior: save all variables as $CFG properties
 foreach ($data as $name => $value) {
   set_config($name, $value);
 }
 return true;

}

コードから明らかなように、Moodleはこのメソッドで、私たちの設定画面から取得できるすべての変数を連想配列「$data」に渡しています。私たちが「同一名の変数を隠す」というトリックなしで処理を実行したい場合、1つの方法は、このメソッドを下記のメソッドでオーバーライドすることです:

function config_save($data) {

 if(isset($data['block_simplehtml_strict'])) {
   set_config('block_simplehtml_strict', '1');
 }else {
   set_config('block_simplehtml_strict', '0');
 }
 return true;

}

極めて直接的: 変数「block_simplehtml_strict」が私たちに渡された場合、これはユーザがチェックしたことだけ意味するため、同じ名称の設定変数に「1」をセットします。そうでない場合、「0」をセットします。もちろん、私たちがさらに設定オプションを追加する場合、デフォルトでは反応しないため、このバージョンを更新する必要があります。さらに、私たちの必要性に合致しない場合、デフォルトの実装をどのようにオーバーライドできるか知ることは有用です (例えば、私たちが変数をMoodle設定の一部として保存せず、その変数で他の処理を実行したい場合)。

そして、私たちは、ブロックがそのコンテンツにHTMLタグを許可べきかどうか知ることのできるポイントまで到着しました。実際、どのようにすれば、ブロックがその設定を留意するようにできるのでしょうか?

私たちは、次のうち1つを実行することができます: インスタンス設定に保存される前、ブロックがHTMLをクリーニングして、現状のまま表示する (「熱心」なアプローチ) または、データをそのまま保存して、表示される前、毎回クリーニングする (「怠惰」なアプローチ)。「熱心」なアプローチでは、設定保存時、1回のみ処理を必要とします。「怠惰」なアプローチでは、ブロックが表示されるたびに処理が必要となります。ですから、パフォーマンスが悪くなってしまいます。そのため、私たちは「熱心」なアプローチに取り組むべきです。

トップページに戻る

instance_config_save()

私たちは、 config_save()をオーバーライドする直前に実行しますが、ここで必要なことは、インスタンスを処理するメソッド、 instance_config_save()をオーバーライドすることです。デフォルトの実装は、以下のようになります:

function instance_config_save($data) {

 $data = stripslashes_recursive($data);
 $this->config = $data;
 return set_field('block_instance', 
                  'configdata',
                   base64_encode(serialize($data)),
                  'id', 
                  $this->instance->id);

}

最初、(stripslashes_recursive()、base64_encode()、serialize()が影響して?) 脅威に感じるかもしれませんが、どうか絶望しないでださい。私たちは、これらのどれも触る必要はありません。私たちは、いくつかのコードを最初の部分に追加して、加えて実際にデータを保存するため、Moodleにこのデフォルトの実装をコールするよう指示します。具体的に言えば、下記のようなメソッドを私たちのクラスに追加します:

function instance_config_save($data) {

 // Clean the data if we have to
 global $CFG;
 if(!empty($CFG->block_simplehtml_strict)) {
   $data->text = strip_tags($data->text);
 }
 // And now forward to the default implementation defined in the parent class
 return parent::instance_config_save($data);

}

とうとう最後です! 現在、管理者は、私たちの「SimpleHTML」ブロックにどのようなタイプのコンテンツを許可するかどうかに関して、絶対的な生死の権限を持つに至りました! 絶対的な? そうですね ... 厳密に言えば違います。実際のところ、暫くの間、私たちがそのことを考えた場合、ある時点でHTMLが許可されて、HTMLを含んだ状態でブロックが保存されます。そして、その後、管理者が設定を「off」にすることにより、その後に続く、HTMLが含まれたコンテンツの変更のみ抑制します。すでにコンテンツにHTMLを含んだブロックでは、表示を継続します!

一連の考えを巡らせながら、しばらく前に「怠惰」なアプローチを選択したのであれば、この問題を経験しないことに気付くことが次の停止位置です。なぜならば、私たちは、それぞれのブロックコンテンツが表示される直前に「サニタイズ」するからです。

私たちが「熱心」なアプローチでやれる唯一のことは、管理設定が「HTML off」に変更された直後に、SimpleHTMLインスタンスすべてのコンテンツからタグすべてを取り除くことです。しかし、その時でさえ、私たちが取り除いたタグは、「HTML on」にしても戻すことはできません。他方、「怠惰」なアプローチは「熱心」なアプローチよりも遅いですが、多目的に使用できます。私たちは、コンテンツを表示する前、HTMLを取り除くか、保持するか選択することができます。そして、管理者が再度設定をONまたはOFFにしたとしても、HTMLを失うことはありません。開発者の人生が、シンプルで素晴らしいものになったと思いませんか?

エクササイズ

私たちは、チュートリアルのこの部分を、読者の強制的なエクササイズで終幕にしましょう: SimpleHTMLブロックを「正常」に動作させるには、「熱心」なアプローチが、私たちのブロックの既存のインスタンス設定すべてのタグを取り除くことを強化する方法を探す、または、戻って、代わりに「怠惰」なアプローチを実装することです。 (ヒント: get_content()メソッドで実行します。)

更新:

Moodle 1.5以前のバージョンでは、ファイルconfig_global.htmlは、単純にconfig.htmlというファイル名が付けられていました。また、メソッド config_saveおよび config_printは、各々handle_configおよびprint_configと名前付けられていました。ブロックがMoodle 1.5でも動作するようにするには、こられの名前付けを含めたアップグレードが必要となります。詳細は、 Appendix Bをご覧ください。

目の保養 (Eye Candy)

私たちのブロックは、ほとんど完璧に動作するようになりました。それでは、さらに何箇所か便利にできるよう、私たちが使用できるトリックを見てみましょう。

私たちのブロックの視覚的側面を調整できる、いくつかの方法があります。まず第一に、ヘッダ (タイトル) を全く表示しないブロックを作成できると便利でしょう。あなたは、この公開をMoodleに実装されている「コース説明」ブロックで見ることができます。この動作は、あなたの想像どおり、私たちのブロッククラスに、さらに1つのメソッドを追加することで実現できます:

function hide_header() {

 return true;

}

ここでもう一つ: 私たちは、空のタイトルをブロックの init()メソッドに設定することはできません。すべてのインストール済みブロックで、Moodleが異なるタイトルを使用できるよう、 init()がコールされた後、それぞれのブロックがユニークなタイトルを持つ必要があります。

私たちが実施したい、もう一つの調整は、私たちのブロックに画面上での表示幅を教えることです。Moodleは、これを2つのプロセスで処理します: まず最初に、それぞれのブロックに推奨幅を問い合わせて、最大値を望ましい値として記憶します。それから、表示されるページが、この値を使用できるようにするか、こちらが最もあり得るのですが、推奨幅がない場合、特定の範囲の値を取得します。これは、表示幅の設定が最善努力型 (best-effort) の合意であることを意味します。あなたのブロックは、特定の表示幅を「リクエスト」することができます。Moodleは、「リクエスト」された表示幅を提供できるよう「試みます」が、結果に関して、一切保障はありません。具体的な例として、すべての標準的なMoodleコースフォーマットでは、リクエストされた表示幅を、180から210ピクセルの間で提供します。

私たちの推奨幅をブロックに教えるには、さらに1つのメソッドをブロッククラスに追加する必要があります:

function preferred_width() {

 // The preferred value is in pixels
 return 200;

}

これにより、私たちのブロック (およびページの同じ側に表示される、すべてのブロック) は、標準よりも少しだけ幅広くなります。

最後に、私たちのブロック表示に使用される、実HTMLのプロパティに影響を及ぼすことができます。それぞれのブロックは、すべて<table>エレメント内に含まれ、その中で、すべてのHTMLが表示されます。そのコンテナに特定の値を持ったHTML属性を追加するよう、私たちは、Moodleに指示することができます。これは、a) 終了結果に直接影響を及ぼす (例えば、bgcolor="black" を割り当てます)、または、b) CSSを使って、終了結果をカスタマイズする自由を私たちに与える CSS (実際のところ、これは下記のようにデフォルトで実施されます)、いずれかの方法を取ることができます。

私たちの場合、この機能のデフォルトの動作は、ブロックのコンテナに属性「sideblock block_simplehtml」(接頭辞 "block_" の次に私たちの小文字ブロック名が続きます) を持ったHTMLクラスを割り当てることです。そして、このブロックの視覚的スタイルを変更するため、そのクラスを使用して、私たちのテーマ内にCSSセレクタを作成することができます (例えば、".sideblock.block_simplehtml { border: 1px black solid}")。

デフォルトの動作を変更するには、私たちは、属性名および値の連想配列を返すメソッドを定義する必要があります。例えば、このバージョンでは、

function html_attributes() {

 return array(
   'class'       => 'sideblock block_'. $this->name(),
   'onmouseover' => "alert('Mouseover on our block!');"
 );

}

Javaスクリプトを使用したマウスオーバーイベントが、HTML内で「onmouseover="alert(...)"」と記述するのと同じように、私たちのブロックに追加されます。ここで留意すべき点は、私たちがクラス属性をセットする部分を複製することです (私たちは、この属性を保持したいと思います。また、デフォルトの動作をオーバーライドするため、必要であればエミュレートすることが、私たちの責任です)。

そして、最後にエレガントな手法は、ハードコードされた値「block_simplehtml」にクラスを設定せず、私たちのブロック名に動的に合致させるため、代わりに name()メソッドを使用することです。

認証ユーザのみ

ある状況において、ブロックが非常に有用であることを想像することは、難しいことではありません。しかし、他のユーザにとって意味あるブロックを作成することは、非常に難しいことです。この例として、ソーシャルフォーマットのコースにとって、非常に使いやすい「社会活動」ブロックがあります。しかし、このブロックは、ウィークリーフォーマットに対しては、全く有用なことは実現できません。そのようなブロックは、本当に意味がある場合のみ、使用を許可するという考え方があるでしょう。そして、ブロックの使用に意味がない場合、ユーザを混乱させるべきではありません。

Moodleでは、どのコースフォーマットでブロックを表示できるか、宣言することができます。そして、ブロック開発者から設定された、この制限を常に強制することができます。この情報は、ページフォーマットに対応するキーおよび、そのページフォーマットでブロックが表示可能かどうか宣言するブール値 (true/false) の定義を持った連想配列として、Moodleに提供されます。

上の段落では、意図的に用語「コース」の代わりに「ページ」を使っていることに注意してください。この理由は、Moodle 1.5以降、ブロックは、サポートされているどのページにも表示できるからです。そのようなページの最良の例は、コースページですが、私たちは、コースページに限定するわけではありません。例えば、小テスト表示ページ (私たちが小テスト名をクリックした後、表示される最初のページ) でも、ブロックをサポートします。

私たちがページに使用できるフォーマット名は、実際にページを表示するために使用されるスクリプト名に由来します。例えば、コースでは、そのスクリプトは、/course/view.php (これは、ブラウザのアドレス行で判断できます) となります。従って、そのページのフォーマット名は、course-viewとなります。小テストビューページのフォーマット名が、mod-quiz-viewであると簡単に導くことができます。しかし、サムネイルに関するルールには、少しだけ例外があります:

  1. Moodleフロントページのフォーマット名は、site-indexです。
  2. コースのフォーマット名は、実際には、course-viewだけでなく、course-view-weekscourse-view-topics等です。
  3. そのようなページがないとしても、フォーマット名allを包括的なオプションとして使用することができます。

適用可能なフォーマット定義中で、私たちが必要なだけ、フォーマット名を含むことができます。それぞれのフォーマットを許可、不許可にすることができます。また、「このブロックは、このページで許可されますか?」という問題を解決を手助けするため、さらに3つのルールがあります:

  1. フォーマット名の接頭辞は、そのフォーマット名と合致します。例えば、modは、活動モジュールと合致します。course-viewは、コースフォーマットに関わらず、すべてのコースに合致します。そして、最後に、site は、フロントページと合致します (フルフォーマット名は、site-indexであることを記憶してください)。
  2. The more specialized a format name that matches our page is, the higher precedence it has when deciding if the block will be allowed. For example, mod, mod-quiz and mod-quiz-view all match the quiz view page. But if all three are present, mod-quiz-view will take precedence over the other two because it is a better match.
  3. The character * can be used in place of any word. For example, mod and mod-* are equivalent. At the time of this document's writing, there is no actual reason to utilize this "wildcard matching" feature, but it exists for future usage.
  4. The order that the format names appear does not make any difference.

All of the above are enough to make the situation sound complex, so let's look at some specific examples. First of all, to have our block appear only in the site front page, we would use:

function applicable_formats() {

 return array('site' => true);

}

Since all is missing, the block is disallowed from appearing in any course format; but then site is set to TRUE, so it's explicitly allowed to appear in the site front page (remember that site matches site-index because it's a prefix).

For another example, if we wanted to allow the block to appear in all course formats except social, and also to not be allowed anywhere but in courses, we would use:

function applicable_formats() {

 return array(
          'course-view' => true, 
   'course-view-social' => false);

}

This time, we first allow the block to appear in all courses and then we explicitly disallow the social format. For our final, most complicated example, suppose that a block can be displayed in the site front page, in courses (but not social courses) and also when we are viewing any activity module, except quiz. This would be:

function applicable_formats() {

 return array(
          'site-index' => true,
         'course-view' => true, 
  'course-view-social' => false,
                 'mod' => true, 
            'mod-quiz' => false
 );

}

It is not difficult to realize that the above accomplishes the objective if we remember that there is a "best match" policy to determine the end result.

UPDATING:
Prior to version 1.5, blocks were only allowed in courses (and in Moodle 1.4, in the site front page). Also, the keywords used to describe the valid course formats at the time were slightly different and had to be changed in order to allow for a more open architecture. Refer to Appendix B for more information on the changes that old blocks have to make to conform to the new standard.

Lists and Icons

In this final part of the guide we will briefly discuss an additional capability of Moodle's block system, namely the ability to very easily create blocks that display a list of choices to the user. This list is displayed with one item per line, and an optional image (icon) next to the item. An example of such a list block is the standard Moodle "admin" block, which illustrates all the points discussed in this section.

As we have seen so far, blocks use two properties of $this->content: "text" and "footer". The text is displayed as-is as the block content, and the footer is displayed below the content in a smaller font size. List blocks use $this->content->footer in the exact same way, but they ignore $this->content->text.

Instead, Moodle expects such blocks to set two other properties when the get_content() method is called: $this->content->items and $this->content->icons. $this->content->items should be a numerically indexed array containing elements that represent the HTML for each item in the list that is going to be displayed. Usually these items will be HTML anchor tags which provide links to some page. $this->content->icons should also be a numerically indexed array, with exactly as many items as $this->content->items has. Each of these items should be a fully qualified HTML <img> tag, with "src", "height", "width" and "alt" attributes. Obviously, it makes sense to keep the images small and of a uniform size.

In order to tell Moodle that we want to have a list block instead of the standard text block, we need to make a small change to our block class declaration. Instead of extending class block_base, our block will extend class block_list. For example:

class block_my_menu extends block_list {
    // The init() method does not need to change at all
}

In addition to making this change, we must of course also modify the get_content() method to construct the $this->content variable as discussed above:

function get_content() {

 if ($this->content !== null) {
   return $this->content;
 }

 $this->content         = new stdClass;
 $this->content->items  = array();
 $this->content->icons  = array();
 $this->content->footer = 'Footer here...';

 $this->content->items[] = '<a href="some_file.php">Menu Option 1</a>';
 $this->content->icons[] = '<img src="images/icons/1.gif" class="icon" alt="" />';

 // Add more list items here

 return $this->content;

}

To summarize, if we want to create a list block instead of a text block, we just need to change the block class declaration and the get_content() method. Adding the mandatory init() method as discussed earlier will then give us our first list block in no time!

トップページに戻る

付録

The appendices have been moved to separate pages: