One more class to align all the glossary inline explanations.
[moodle.git] / question / type / random / questiontype.php
CommitLineData
516cf3eb 1<?php // $Id$
2
3//////////////
4/// RANDOM ///
5//////////////
6
7/// QUESTION TYPE CLASS //////////////////
7518b645 8class random_qtype extends default_questiontype {
516cf3eb 9
516cf3eb 10 // Carries questions available as randoms sorted by category
11 // This array is used when needed only
12 var $catrandoms = array();
13
14 function name() {
15 return 'random';
16 }
17
a2156789 18 function menu_name() {
19 return false;
20 }
21
22 function is_usable_by_random() {
23 return false;
24 }
25
516cf3eb 26 function get_question_options(&$question) {
27 // Don't do anything here, because the random question has no options.
28 // Everything is handled by the create- or restore_session_and_responses
29 // functions.
30 return true;
31 }
32
33 function save_question_options($question) {
34 // No options, but we use the parent field to hide random questions.
35 // To avoid problems we set the parent field to the question id.
4f48fb42 36 return (set_field('question', 'parent', $question->id, 'id',
516cf3eb 37 $question->id) ? true : false);
38 }
39
40 function create_session_and_responses(&$question, &$state, $cmoptions, $attempt) {
a2156789 41 global $QTYPE_EXCLUDE_FROM_RANDOM;
516cf3eb 42 // Choose a random question from the category:
43 // We need to make sure that no question is used more than once in the
44 // quiz. Therfore the following need to be excluded:
45 // 1. All questions that are explicitly assigned to the quiz
46 // 2. All random questions
47 // 3. All questions that are already chosen by an other random question
5dba6590 48 // 4. Deleted questions
516cf3eb 49 if (!isset($cmoptions->questionsinuse)) {
50 $cmoptions->questionsinuse = $attempt->layout;
51 }
52
53 if (!isset($this->catrandoms[$question->category][$question->questiontext])) {
54 // Need to fetch random questions from category $question->category"
55 // (Note: $this refers to the questiontype, not the question.)
56 global $CFG;
516cf3eb 57 if ($question->questiontext == "1") {
58 // recurse into subcategories
dc1f00de 59 $categorylist = question_categorylist($question->category);
516cf3eb 60 } else {
61 $categorylist = $question->category;
62 }
5dba6590 63 if ($catrandoms = get_records_select('question',
64 "category IN ($categorylist)
516cf3eb 65 AND parent = '0'
5dba6590 66 AND hidden = '0'
516cf3eb 67 AND id NOT IN ($cmoptions->questionsinuse)
5dba6590 68 AND qtype NOT IN ($QTYPE_EXCLUDE_FROM_RANDOM)", '', 'id')) {
516cf3eb 69 $this->catrandoms[$question->category][$question->questiontext] =
5dba6590 70 draw_rand_array($catrandoms, count($catrandoms));
516cf3eb 71 } else {
72 $this->catrandoms[$question->category][$question->questiontext] = array();
73 }
74 }
75
76 while ($wrappedquestion =
77 array_pop($this->catrandoms[$question->category][$question->questiontext])) {
78 if (!ereg("(^|,)$wrappedquestion->id(,|$)", $cmoptions->questionsinuse)) {
79 /// $randomquestion is not in use and will therefore be used
80 /// as the randomquestion here...
4f48fb42 81 $wrappedquestion = get_record('question', 'id', $wrappedquestion->id);
f02c6f01 82 global $QTYPES;
83 $QTYPES[$wrappedquestion->qtype]
516cf3eb 84 ->get_question_options($wrappedquestion);
f02c6f01 85 $QTYPES[$wrappedquestion->qtype]
516cf3eb 86 ->create_session_and_responses($wrappedquestion,
87 $state, $cmoptions, $attempt);
88 $wrappedquestion->name_prefix = $question->name_prefix;
89 $wrappedquestion->maxgrade = $question->maxgrade;
90 $cmoptions->questionsinuse .= ",$wrappedquestion->id";
91 $state->options->question = &$wrappedquestion;
92 return true;
93 }
94 }
95 $question->questiontext = '<span class="notifyproblem">'.
96 get_string('toomanyrandom', 'quiz'). '</span>';
dfa47f96 97 $question->qtype = 'description';
516cf3eb 98 $state->responses = array('' => '');
99 return true;
100 }
101
102 function restore_session_and_responses(&$question, &$state) {
103 /// The raw response records for random questions come in two flavours:
104 /// ---- 1 ----
105 /// For responses stored by Moodle version 1.5 and later the answer
106 /// field has the pattern random#-* where the # part is the numeric
107 /// question id of the actual question shown in the quiz attempt
108 /// and * represents the student response to that actual question.
109 /// ---- 2 ----
110 /// For responses stored by older Moodle versions - the answer field is
111 /// simply the question id of the actual question. The student response
112 /// to the actual question is stored in a separate response record.
113 /// -----------------------
114 /// This means that prior to Moodle version 1.5, random questions needed
115 /// two response records for storing the response to a single question.
116 /// From version 1.5 and later the question type random works like all
117 /// the other question types in that it now only needs one response
118 /// record per question.
f02c6f01 119 global $QTYPES;
516cf3eb 120 if (!ereg('^random([0-9]+)-(.*)$', $state->responses[''], $answerregs)) {
121 if (empty($state->responses[''])) {
122 // This is the case if there weren't enough questions available in the category.
123 $question->questiontext = '<span class="notifyproblem">'.
124 get_string('toomanyrandom', 'quiz'). '</span>';
dfa47f96 125 $question->qtype = 'description';
516cf3eb 126 return true;
127 }
128 // this must be an old-style state which stores only the id for the wrapped question
4f48fb42 129 if (!$wrappedquestion = get_record('question', 'id', $state->responses[''])) {
516cf3eb 130 notify("Can not find wrapped question {$state->responses['']}");
131 }
132 // In the old model the actual response was stored in a separate entry in
133 // the state table and fortunately there was only a single state per question
4f48fb42 134 if (!$state->responses[''] = get_field('question_states', 'answer', 'attempt', $state->attempt, 'question', $wrappedquestion->id)) {
516cf3eb 135 notify("Wrapped state missing");
136 }
137 } else {
4f48fb42 138 if (!$wrappedquestion = get_record('question', 'id', $answerregs[1])) {
4572d78f 139 // The teacher must have deleted this question by mistake
140 // Convert it into a description type question with an explanation to the student
141 $wrappedquestion = clone($question);
142 $wrappedquestion->id = $answerregs[1];
143 $wrappedquestion->questiontext = get_string('questiondeleted', 'quiz');
f57c6242 144 $wrappedquestion->qtype = 'missingtype';
516cf3eb 145 }
146 $state->responses[''] = (false === $answerregs[2]) ? '' : $answerregs[2];
147 }
148
f02c6f01 149 if (!$QTYPES[$wrappedquestion->qtype]
516cf3eb 150 ->get_question_options($wrappedquestion)) {
151 return false;
152 }
153
f02c6f01 154 if (!$QTYPES[$wrappedquestion->qtype]
516cf3eb 155 ->restore_session_and_responses($wrappedquestion, $state)) {
156 return false;
157 }
158 $wrappedquestion->name_prefix = $question->name_prefix;
159 $wrappedquestion->maxgrade = $question->maxgrade;
160 $state->options->question = &$wrappedquestion;
161 return true;
162 }
163
164 function save_session_and_responses(&$question, &$state) {
f02c6f01 165 global $QTYPES;
516cf3eb 166 $wrappedquestion = &$state->options->question;
167
168 // Trick the wrapped question into pretending to be the random one.
169 $realqid = $wrappedquestion->id;
170 $wrappedquestion->id = $question->id;
f02c6f01 171 $QTYPES[$wrappedquestion->qtype]
516cf3eb 172 ->save_session_and_responses($wrappedquestion, $state);
173
174 // Read what the wrapped question has just set the answer field to
175 // (if anything)
4f48fb42 176 $response = get_field('question_states', 'answer', 'id', $state->id);
516cf3eb 177 if(false === $response) {
178 return false;
179 }
180
181 // Prefix the answer field...
182 $response = "random$realqid-$response";
183
184 // ... and save it again.
4f48fb42 185 if (!set_field('question_states', 'answer', addslashes($response), 'id', $state->id)) {
516cf3eb 186 return false;
187 }
188
189 // Restore the real id
190 $wrappedquestion->id = $realqid;
191 return true;
192 }
193
194 function get_correct_responses(&$question, &$state) {
f02c6f01 195 global $QTYPES;
516cf3eb 196 $wrappedquestion = &$state->options->question;
f02c6f01 197 return $QTYPES[$wrappedquestion->qtype]
516cf3eb 198 ->get_correct_responses($wrappedquestion, $state);
199 }
200
201 // ULPGC ecastro
202 function get_all_responses(&$question, &$state){
f02c6f01 203 global $QTYPES;
516cf3eb 204 $wrappedquestion = &$state->options->question;
f02c6f01 205 return $QTYPES[$wrappedquestion->qtype]
516cf3eb 206 ->get_all_responses($wrappedquestion, $state);
207 }
208
209 // ULPGC ecastro
210 function get_actual_response(&$question, &$state){
f02c6f01 211 global $QTYPES;
516cf3eb 212 $wrappedquestion = &$state->options->question;
f02c6f01 213 return $QTYPES[$wrappedquestion->qtype]
516cf3eb 214 ->get_actual_response($wrappedquestion, $state);
215 }
216
217
218 function print_question(&$question, &$state, &$number, $cmoptions, $options) {
f02c6f01 219 global $QTYPES;
516cf3eb 220 $wrappedquestion = &$state->options->question;
f02c6f01 221 $QTYPES[$wrappedquestion->qtype]
516cf3eb 222 ->print_question($wrappedquestion, $state, $number, $cmoptions, $options);
223 }
224
225 function grade_responses(&$question, &$state, $cmoptions) {
f02c6f01 226 global $QTYPES;
516cf3eb 227 $wrappedquestion = &$state->options->question;
f02c6f01 228 return $QTYPES[$wrappedquestion->qtype]
516cf3eb 229 ->grade_responses($wrappedquestion, $state, $cmoptions);
230 }
231
232 function get_texsource(&$question, &$state, $cmoptions, $type) {
f02c6f01 233 global $QTYPES;
516cf3eb 234 $wrappedquestion = &$state->options->question;
f02c6f01 235 return $QTYPES[$wrappedquestion->qtype]
516cf3eb 236 ->get_texsource($wrappedquestion, $state, $cmoptions, $type);
237 }
238
239 function compare_responses(&$question, $state, $teststate) {
f02c6f01 240 global $QTYPES;
bb080d20 241 $wrappedquestion = &$teststate->options->question;
f02c6f01 242 return $QTYPES[$wrappedquestion->qtype]
516cf3eb 243 ->compare_responses($wrappedquestion, $state, $teststate);
244 }
245
246}
247//// END OF CLASS ////
248
249//////////////////////////////////////////////////////////////////////////
250//// INITIATION - Without this line the question type is not in use... ///
251//////////////////////////////////////////////////////////////////////////
a2156789 252question_register_questiontype(new random_qtype());
516cf3eb 253
254?>