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