開発:問題エンジン
Moodleは、問題タイプのプラグインを可能にするためのモジュール構造を持つ強力な問題エンジンを持っています。問題エンジンは、問題のレンダリングと学生の解答の処理を担当します。これは小テストモジュールによって使用されており、将来的にはレッスンや他のモジュールによって使用される予定です。
歴史的に問題エンジンは小テストモジュールの一部として始まりました。 Moodle 1.6以降、Moodleの別のコアコンポーネントで他のMoodleコンポーネントまたはモジュールで使用できるようになりました。この再構築中に、コードはmod / quiz /からquestion /に移動され、テーブルと関数の名前が変更されました。古いテーブルまたは関数名に 'quiz_'が含まれている場合は、新しいテーブル名または関数名に 'question_'が含まれます。
内容
用語
問題エンジンについて話すとき、それらが異なる意味で使われることができるので混乱を引き起こすことがある特定の用語があります。 Moodleでは、以下で説明する特定の用語を採用しました。
問題
問題は、再利用可能な評価項目を構成する一連の定義(問題名、問題テキスト、可能な解答、採点規則、フィードバックなど)です。それで、それは日常の言語で問題を作成するものよりはるかに多くを含みます。 QTI仕様の用語では、「問題」はより適切に評価項目と呼ばれるか、または単に「項目」と略されます。
多項選択式の問題や数値式の問題など、さまざまな種類の問題があります。これらはMoodleでは問題タイプと呼ばれています。
バージョン1.5以降、MoodleはQTIで「アダプティブアイテム」とも呼ばれる、いわゆるアダプティブ問題を処理することができます。これらは、学生の解答に応じていくつかの州を通過することによって学生と対話する問題です。例えば、間違って答えられたが一般的な間違いのために間違っている可能性がある複雑な数学的問題は、この間違いに対するヒントをユーザに提供し、ペナルティを課し、そしてこの問題に対して2回目の試みを許すことができる。小テストは '適応モード'で実行することができます。その場合、Moodleは各問題を個別にマークするためのボタンを提供します。
答え
Moodleでは、 ' 解答'という用語は、 教師が定義した問題の解答に対してのみ使用されます 。これらの教師が定義した答えと、学生が実際に出す答えとを混同するのは簡単です。そこで私たちは、学生が提供した解答を「解答」と呼び、教師が定義した解答に適用するために「解答」という用語を予約するという規約を採用しました。教師提供の解答に頼る問題タイプでは、これらは学生の解答と比較することによって評定プロセスで使用されます。もちろん、すべての問題タイプが教師定義の解答を使用するわけではありませんが、学生の解答を処理するためのより知的な方法を使用しています。
おそらく、「正解」という意味で「答え」が常に使用されるわけではないことも強調しておくべきでしょう。例えば、多肢選択問題のあらゆる選択肢は答えと呼ばれます。他のシステムは間違った答えのために 'ディストラクタ'という用語を使用します。
Moodleでは常に問題に対する学生の解答を指すために ' 解答'という用語を使用しています。この用語は常に複数形で使用されますが、問題タイプによっては可能な解答が1つしかありません。
残念ながら、歴史的な理由から、上記の規則の1つの例外があります。question_statesテーブルには、実際には学生の解答を保持することを目的とした「解答」というフィールドがあります。
試み
Moodleでは、「 試み」という用語は「小テストを試みる」(または問題を含む別の活動)という意味で使用されています。小テストの設定によっては、学生は小テストを何回か試みることができます。学生が受験ページの対応するボタンをクリックすると、受験は終了します。学生は1回の訪問で試みを完了する必要はありません。彼らは小テストページから離れてナビゲートして後で同じ試みを続けることができます。
問題エンジンを使用する各モジュールは、それ自身のテーブルに受験のための独自のデータを保持する必要があります。モジュールが問題エンジン機能を呼び出すとき、それはしばしば試みオブジェクトを渡すことが期待される。
1つの同じ小テストの受験内で、少なくともquestiontypeがそれを許可し、小テストがアダプティブモードで設定されている場合、学生は特定の問題に答えることを何回か試みることができます。これらは常に「 問題セッション」と呼ばれることもあれば、「受験」と呼ばれることはありません。
セッション、州、イベント
新しい試みが開始されると、問題ごとに新しいセッションが開始されます。したがって、ある意味でのセッションは、小テスト全体に対する受験とは何かという問題です。問題セッションは1回の受験よりも長く続きません。また、各問題に対して、1回の受験内に1回のセッションでしか参加できません。
Moodleは学生が1つのセッション内で繰り返し問題と対話することを可能にし、そのような各対話は新しい状態につながります。最初の状態は、セッションが作成されたときに作成されます。学生が解答を保存、検証、または送信すると、新しい状態が作成されます。学生の解答、および適切な場合は、解答処理(採点)の結果は、作成された新しい状態に格納されます。
特定の状態の作成につながったイベントの種類は、状態と共に保存されます。現在使用されているイベントの種類は次のとおりです。
- 開いた
- 新しいセッションが作成されたばかりで、これが開始状態です。通常、それはまだ学生の解答を保持していません( 'tryonlast'オプションが設定されているために小テストの受験が前回の受験に基づいている場合を除く)。
- 保存する
- 学生は保存ボタンをクリックしました。
- 検証
- 学生は自分の解答を検証するよう求めました。これは、それらが有効な解答であることが確認されることを意味します。何らかの線形形式での数式の入力を必要とする数学的な問題の場合、問題タイプは検証された結果をタイプセット形式で学生に表示したい場合があります。同様のことが他の主題固有の問題タイプにも当てはまるかもしれません。学生の解答が無効であることが判明した場合、学生にはその旨が伝えられますが、ペナルティは適用されません。無効な解答は状態とともに格納されます。
- 評定
- 学生は送信ボタンを押しました。評定が計算され、受験とともに保存されます。
- 重複評定
- 学生は送信ボタンを押しましたが、この問題に対する解答は実際には変更されていません。これは、学生が1つの問題に対してのみ解答を変更した可能性がある1ページに複数の問題がある小テストでよく起こります。この種のイベントによって作成された状態はデータベースに格納されないと思います。
- 閉じる
- 現在Clozeされているセッションの最後の状態。現在のところ、セッションはセッションが終了したときにのみ終了します。これは、学生が要求したか、または時間制限が経過したためです。
他のイベントタイプを導入する予定です
- 提出する
- 学生は評定に対する解答を送信しましたが、評定はまだ行われていません。これは、例えば作文問題のように、教師がマークした問題タイプによって使用されます。
コード文書
コードはPHPドキュメントの規約に従って文書化されています。このWikiの説明は、これを補足するものです。
インラインコメントはコード内で自由に使用する必要があります。以下の規約は、特別な意味を持つコメントの検索を容易にします。
- やるべきことについてのコメントにTODOを使う
- つかいます ???コードに関する問題であるコメントに
コードはパッケージにまとめられています
- パッケージquestionbank - 問題バンクシステムに関連するコード
- サブパッケージquestiontypes - 問題タイプ基本クラスを含む問題タイプに関連するコード。
- サブパッケージimportexport - 問題のインポートとエクスポートに関連するコード。
一般に、基本クラスdefault_questiontypeとqformat_defaultには、ほとんどのドキュメントタイプが含まれています。これは、すべての問題タイプが同じインタフェースに従うためです。さまざまなサブクラスすべてに同じ情報を繰り返しても意味がありません。サブクラスは、基本クラスから派生していない新しいメソッドの記述に集中する必要があります。
良いコメントを書くためのより多くのアドバイスについてはPHPdocumentorマニュアルを見てください。
MoodleのPHPドキュメントは、 http ://phpdocs.moodle.org/にあります。
API
ライブラリlib / questionlib.phpには、問題を使用したいモジュールで利用できる必要があるすべての関数が含まれています(これはMoodle 1.6の新機能です。Moodle1.5では、これはmod / quiz / locallib.phpの一部でした)。このライブラリをロードすると、questiontype.phpファイルをロードすることによってすべてのquestiontypeクラスがインスタンス化されます。
APIの説明はまだ書かれている必要があります。また、lib / questionlib.phpもAPI関数とヘルパー関数を区別するために少し整理する必要があります。
組織
デフォルトの問題タイプクラスはquestion / type / questiontype.phpで定義されています(Moodle 1.5ではこれはまだmod / quiz / locallib.phpにありました)。個々の問題タイプはそれ自身のquestiontype.phpファイルでこのクラスを拡張します。問題タイプクラスのドキュメントについては、デフォルトの問題タイプのドキュメントをよく見てください。デフォルトクラスのドキュメントの多くは他の問題タイプクラスでは繰り返されていないためです。
問題タイプはクラスとして実現されていますが、問題エンジンは本当にオブジェクト指向の方法で書かれていません。その代わりに、データベースレコードを保持するための配列の代わりとしてのみオブジェクトを使用するというMoodleモデルに従います。そのため、モジュール内で中心的なロールを果たす問題、受験、および状態オブジェクトにはメソッドがありません。 questiontypeオブジェクトだけがメソッドを持ちます。奇妙なことに、小テストモジュールは最初に各questiontypeクラスの1つのオブジェクトをインスタンス化して、それから異なる問題にそれらのメソッドを再利用します。 Moodleのプログラミング方法に慣れている場合は、これで十分に対処できます。
オブジェクトとデータ構造
問題エンジンがどのように機能するかを理解するための鍵は、さまざまな種類のオブジェクトがどのように連携して機能するかを理解することです。最も重要なものは以下のとおりです。
- 問題
- 試み
- 州
問題は教師によって作成されたデータです。受験回数と状態は、小テストと対話するときに学生によって作成されたデータです。
Moodle小テストモジュールを使用すると、学生は小テストで複数回試すことができます。このような受験に関するデータは、受験オブジェクトに格納されています。 Moodle問題エンジンはそのような受験に対処するように最適化されており、したがって問題エンジンを使用したいすべてのモジュールは受験オブジェクトを実装し、それを多くの問題エンジン機能に渡す必要があります。試みオブジェクトは、例えば、この受験に対してどのように小テストが無作為化されたか、および問題と解答の順序付けに関する情報を保持します。
Moodleでは、学生は単一の問題で繰り返し対話することができます。したがって、たとえば、学生は最初に答えを保存し、後でそれをマークし、それから正しくないとマークされた場合はそれを修正することができます。学生が特定の受験内で最初に問題を見ると、 問題セッションと最初の問題状態が作成されます。学生が問題と対話するたびに、新しい問題状態が作成されます。そのため、状態はユーザーID、受験ID、および問題IDによって索引付けされます。
データベース構造
これらすべてのデータはMoodleのデータベースに保存する必要があります。これがどのように達成されるかは、役に立つ図表も含まれている小テストデータベース構造についての別のページで説明されています。
Moodleで慣習的であるように、ほとんどのランタイムオブジェクトは単に特定のデータベースレコードからのデータを表します。したがって、たとえば$ quizオブジェクトには、 小テストテーブルのすべてのフィールドに対応するフィールドがあります 。場合によっては、オブジェクトには実行時に追加される追加のフィールドがいくつかあります。これは特に$ questionおよび$ stateオブジェクトに当てはまります。これらの追加フィールドは、 小テストデータベース構造に関するページにも記載されています。これらのオブジェクトを処理するために使用される多くの機能は追加のフィールドを利用するため、これらのオブジェクトを作成するために正しい機能を使用する必要があります。
ランタイムオブジェクト
小テストモジュールによって使用されるいくつかのオブジェクトは純粋にランタイムオブジェクトであり、データベーステーブルに対応しません。これらのオブジェクトの構造は、 Quizランタイムオブジェクトに関する別のページで詳細に説明されています 。
小テストモジュールの主なスクリプトはattempts.phpで、これらすべてのオブジェクトを処理する必要があります。 attempts.phpの説明を勉強することは、したがって、小テストモジュールのコードを勉強し始めるための良い方法です。
解答ストレージ
問題に対する学生の解答はに保存されます。$ state->レスポンス。問題タイプは、彼らが望む方法で彼らの解答(そして他の状態情報)の保存メカニズムを完全に自由に実装することができます。それでも、標準的な問題タイプはすべて同様のモデルに従います。デフォルトの格納モデルと問題タイプ特有のバリエーションは以下で説明されます。問題タイプが自由に解答記憶メカニズムを選択し、記憶モデルから実行時モデルに変換する柔軟性は、実行時の初期化を可能にする3つの関数のセットによって提供されます。
$ state->レスポンスランタイムからストレージモデルへの変換、およびその逆の変換を行うフィールド。
create_session_and_responses()
- $ stateオブジェクトを初期化します。特に
$ state->レスポンス
フィールド
restore_session_and_responses()
- 問題タイプ固有のセッションデータをデータベースから
$ state
オブジェクト、特に指定されたオブジェクト用に保存されたレスポンスをロードします。$ state
に$ state->レスポンス
フィールド。
save_session_and_responses()
- 問題タイプ固有のセッションデータを$ stateオブジェクトからデータベースに保存します。特に、ほとんどの問題タイプでは、からの解答が保存されます。
$ state->レスポンス
データベースに。
quiz_statesテーブル内の解答フィールドに追加し 、このフィールドの内容を自動的に$ state-> responds ['']
。これは、解答として単一の値しか期待しない問題タイプは、上記の3つのメソッドの実装をスキップできることを意味します。多値解答を持つすべての問題タイプは、これらのメソッドを実装する必要があります。デフォルトの問題タイプは、quiz_statesテーブルの解答フィールドへの/からの解答をシリアライズ/デシリアライズすることによってこの問題を処理します。ただし、問題タイプ固有のテーブルでquiz_statesテーブルを拡張する、つまり問題タイプ固有のテーブルの外部キーとしてquiz_statesレコードのIDを使用することも可能です(そしてより良い方法かもしれません)。の値が$ state-> responds ['']
解答フィールドの値に設定され、解答を直列化する問題タイプは上書きする必要があります。$ state-> responds ['']
save_session_and_responses())一般的なコードがシリアル化した値でこのフィールドを設定する値(通常は単純なset_fieldで達成される)。方法では
restore_session_and_responses()シリアル化された値はから読み取ることができます
これは、quiz_statesテーブルのanswerフィールドからの値が移動された場所であるためです。この配列値が設定解除されるか、配列全体が上書きされるように注意する必要があります。これにより、配列に誤って空の文字列インデックスを持つ値が含まれることはありません。$ state-> responds ['']
解答処理
レスポンスの実行時モデルは$ state->レスポンス配列の構造を決定します。フォームの要素の名前から始めて、このセクションは関連する処理ステップを通り抜けます、そしてそれ故になぜ$ state->レスポンス配列のキーが異なる問題タイプのために異なることができるかを明確にすることを試みます。さらに、配列キーの選択方法と設定方法についても説明しています。
最初はフォームフィールドの命名規則から始めるのは奇妙に思えるかもしれませんが、その理由は後で明らかになります。問題のコントロール(すなわちフォームフィールド)はメソッドによって出力されます。print_question_formulation_and_controls()。規則では、制御要素の名前はの値で始まっていなければならないと規定されているだけです。
$ question-> name_prefix。の
$ question-> name_prefix"resp"で始まり、その後に問題IDとアンダースコアが続く文字列です。
resp56_。デフォルトの場合、コントロール要素が1つだけの場合(これには、同じ名前のラジオボタンのリストの場合も含まれます)、名前の接頭辞には後置記号は追加されません。複数のフォーム要素を許可または要求する問題タイプの場合、名前プレフィックスに任意の文字列を追加してこれらのフォーム要素の名前を形成できます。接尾辞には、リレーショナルデータ(つまり、quiz_answersテーブル内のレコードのID)を含めないでください。これは、バージョン付き問題の評定変更に関する問題を引き起こす可能性があるためです。問題が印刷された後、サーバーは問題が送信されたときにのみ再度表示されます。そのため、送信されたデータには、で始まる文字列でインデックス付けされたいくつかの値が含まれます。
respXX_。提出時に、機能
quiz_process_responses()これは、投稿キー(つまり、アンダースコアの後の部分)を配列キーとして使用して、投稿された解答をid XXの問題の状態に割り当てます。コントロール要素が1つしかないデフォルトの場合、名前は名前の接頭辞のみで構成されます。これはなぜデフォルトのインデックスが
$ state->レスポンスarrayは空の文字列です。各配列要素の値は、明らかにフォームによって送信された値、基本的には生の解答です。関数
quiz_process_responses()次に問題タイプ固有のメソッドを呼び出します。
grade_responses()提出された解答に評点を割り当てる
compare_responses()解答が前回の提出と同一であったかどうかを判断し、同じ解答を繰り返し評定変更しないようにします。これらの問題タイプ特有の機能は、の予想されるキーを知っている必要があります。
$ state->レスポンスアレイ。最後に、メソッド
restore_session_and_responses()そして
save_session_and_responses()また、の問題タイプ固有のレイアウトを知る必要があります。
$ state->レスポンス配列そして、例えばデータ表現からまたはデータ表現へ変換することによって、情報をリストアまたは保存する。
問題の種類
Template:Questiontype developer docs小テストモジュールはそれ自体がモジュール式で、問題タイプのプラグインを可能にします。それぞれの問題タイプについて、右側のメニューからアクセスできるページがあるはずです。そこには、少なくとも以下についての詳細が記載されています。
- データベーステーブル
- 解答ストレージ
- 問題オプションオブジェクト
- 状態オプションオブジェクト
今後、Moodlersが多くのノンコア問題タイプに貢献することが望まれます。そのためには、 How to the write typeプラグインを始めるのがよいでしょう。
評定
評定の取り扱いは少し複雑です。なぜなら、周りにはさまざまな評定があり、さまざまな方法で評価尺度変更および結合されるからです。このセクションでは、これがどのように行われるのか、そしてその理由を要約する必要があります。
次の評定項目が使用されています。
- $ question-> defaultgrade
- これは、この問題の最大評点のデフォルト値です。教師が問題を作成し、それがquiz_questionsテーブルのint(10)フィールドに格納されると、これが設定されます。しかし、問題が実際に特定の小テストで使用されている場合、教師はこのデフォルトを無効にすることができ、これは次の場所に保存されます。
- $ question-> maxgrade
- これは、現在の小テストとの関連で教師がこの問題に課題た最高評点です。これはデフォルトで$ questions-> defaultgradeと同じですが、教師は小テストを編集するときにこれを変更できます。データベースでは、それはquiz_question_instancesテーブルの int(10)フィールドに格納されます 。
- $ question->ペナルティ
- $ state-> raw_grade
- $ state-> grade
- $ state->ペナルティ
- $ state-> sumpenalty
- $ try-> sumgrades
教師が設定した最大評点、$ question-> defaultgradeおよび$ question-> maxgradeは整数です。学生が取得するすべての学年は原則として浮動小数点数です。歴史的な理由から、それらはvarchar(10)フィールドとしてデータベースに格納されています。データベースへの書き込み時には、すべての評定が正しく丸められて10文字以下の文字列になるように注意する必要があります。そうしないと、データベースへの書き込みが失敗します。バグ4220を参照してください。
特定の小テストでのユーザーの評点の計算の最終結果は、 quiz_gradesテーブルの 'grade'フィールドに格納されています 。このフィールドのタイプはdoubleです。
ペナルティメカニズム
それは何のためにあるのか
小テストがアダプティブモードで実行されると、学生は繰り返し問題と対話することができます。そのため、特に学生は間違った答えを受け取ったときにもう一度試すことができます。明らかに問題の最後の印は、学生がもともとそれを正しく得なかったという事実を反映していなければなりません。したがって、最終マークからペナルティが差し引かれます。
ペナルティの決定方法
まず第一に、小テストが適応モードで実行されている場合にのみ関連します。この場合のみ、学生は2回目の受験を行うことができます。したがって、このモードでのみペナルティを差し引くことができます。
アダプティブモードでも、ペナルティメカニズムは小テストオプションで選択されている場合にのみ使用されます。 「ペナルティの適用」が「いいえ」に設定されている場合、問題の最後のマークは最後の採点された解答のマークです。
各問題には、0から1までの数値である「ペナルティ」フィールド(実際には「ペナルティファクタ」と呼ぶ必要があります)があります。誤った解答に対するペナルティは、積($ quiz->ペナルティ* $ quiz-> grade)として計算されます。つまり、問題に対して達成可能な最大評点を持つペナルティ要素の積として。この商品は$ state->ペナルティで保存されています。そのため、$ quiz->ペナルティは、それぞれの誤った解答に対するペナルティとして差し引かれる最大評点の割合です。
データベースとmod / quiz / defaults.phpの両方で、$ quiz->ペナルティフィールドのデフォルト値は0.1です。もちろん、このデフォルトは小テスト設定ページで管理者によって上書きされる可能性があります。この管理者が選択したデフォルトは(通常の管理者のデフォルトと同様に)$ CFG-> quiz_penaltyに格納されています。教師は、問題を追加または編集するときに、問題ごとに異なるペナルティ要素を選択できます。 (Moodle 1.8では、デフォルト値0.1が実際にはmoodle / question / type / edit_question_form.phpの87行目にハードコードされています)
学生が間違った試みを繰り返す(または部分的に正しい試みをする)と、これらすべての受験に対するペナルティは$ state-> sumpenaltiesに加算されます。問題のマークは、最後の採点された解答のマークからペナルティの合計を引いたものとして計算されます。
$ state-> sumpenaltiesに関する奇妙な事実の1つは、効率上の理由から、それはquiz_statesテーブルには格納されず、代わりにquiz_newest_statesテーブルの 'sumpenalty'フィールドに格納されることです。そうすれば、解答ごとに1回ではなく、受験ごとに1回だけ格納する必要があります。
コードのどこで行われているか
関数quiz_apply_penalty_and_timelimit()は、$ state-> raw_gradeの生の評定から$ state-> sumpenaltyのペナルティを引いて、解答の$ state-> gradeを取得します。しかしながら、その問題に対する新しい試みの評定が以前に達成された評定を決して下回らないことが保証されています。この関数は$ state-> sumpenaltyを$ state->ペナルティの量だけ増やします。仮定は、$ state->ペナルティがこの関数を呼び出すコードによって適切に設定されたことです(例:quiz_process_responses)。
問題に属するファイル
これは、Moodle 2.0ファイルAPIに関連した状況について説明しています。
ファイル領域
各問題にはさまざまなファイル領域があります。
たとえば、「question」コアコンポーネントに属する「questiontext」というファイルがあります。このコンポーネントには、問題テキストで使用される画像やその他の埋め込みファイルが格納されています。 'generalfeedback'と 'question_answer'ファイル領域もあります。
各問題タイプに固有のいくつかのファイル領域もあります。たとえば、 'qtype_multichoice'コンポーネントには、(とりわけ) 'correctfeedback'ファイル領域があります。
これらのファイル領域はすべて$ question-> categoryのコンテキストであるコンテキストに属しています。
ファイルの提供とアクセスチェック
ファイルは問題システムまたは特定の問題タイプに属していますが、ユーザーが特定のファイル(一般的なフィードバックの画像など)を見ることができるかどうかは、Moodleのどの部分によっても影響を受けるためです。問題を使用している(たとえば、小テストモジュールや問題のプレビュー)。
説明する最も簡単な方法は、おそらく特定の画像が表示されているかどうかを判断するためのコールスタックを表示することです。その後、コードを読みに行くことができます。
小テストの受験で問題のquestiontext内の画像のための要求... / pluginfile.php / 123 / question / questiontext / 234/345 / image.png。
(ここで、123はコンテクストIDであり、トリプチド(question_attempts.idに対応)の場合は234、そしてクエスチョンIDは345です。)
- pluginfile.php
- lib / questionlib.phpの question_pluginfile()
- mod / quiz / lib.phpの quiz_question_pluginfile()
- mod / quiz / attemptslib.php内の quiz_attempt-> check_file_access()
- lib / questionlib.phpの question-> check_file_access()
- qtype_ どんな - > check_file_access() 問題/タイプ/何/ quetsiontype.phpで
その代わりに、問題が問題プレビューポップアップウィンドウに表示されていた場合、コールスタックは次のようになります。
- pluginfile.php
- lib / questionlib.phpの question_pluginfile()
- question / previewlib.phpの question_preview_question_pluginfile()
- lib / questionlib.phpの question-> check_file_access()
- qtype_ どんな - > check_file_access() 問題/タイプ/何/ quetsiontype.phpで
(異なるビットは太字で強調表示されています)。
代わりに、画像のURLが... / pluginfile.php / 123 / qtype_multichoice / correctfeedback / 234/345 / image.pngの場合、コールスタックは次のようになります。
- pluginfile.php
- 問題のqtype_multichoice_pluginfile() / type / multichoice / lib.php
- lib / questionlib.phpの question_pluginfile()
- mod / quiz / lib.phpの quiz_question_pluginfile()
- mod / quiz / attemptslib.php内の quiz_attempt-> check_file_access()
- lib / questionlib.phpの question-> check_file_access()
- 問題のqtype_multichoice-> check_file_access() / type / multichoice / quetsiontype.php
大まかに言って、qtype_whatever-> check_file_access()はprint_question_formulation_and_controlsと同じ$ question、$ stateおよび$ optionsオブジェクトをとるので、たとえば、フィードバックでファイルにアクセスしようとしたときと同じパーミッションチェックを行うことができます。フィードバックを表示するかどうか。
quiz_question_pluginfile関数またはquestion_preview_question_pluginfile関数は、URL内の$ tryptidおよび$ questionidに基づいて正しい$ question、$ state、$ optionsを取得します。彼らはまたいくつかの基本的なアクセスチェックを行います。
他のすべての機能は、実際には処理するのに適切な場所に要求をディスパッチすることだけです。