MDL-5482 - Backup and restore problems for match, random and truefalse question types.
[moodle.git] / question / type / truefalse / questiontype.php
blob88ce5f889d31fb14eb1a6a823d6c0d4f9a9d4501
1 <?php // $Id$
3 /////////////////
4 /// TRUEFALSE ///
5 /////////////////
7 /// QUESTION TYPE CLASS //////////////////
8 class question_truefalse_qtype extends default_questiontype {
10 function name() {
11 return 'truefalse';
14 function save_question_options($question) {
15 $result = new stdClass;
17 // fetch old answer ids so that we can reuse them
18 if (!$oldanswers = get_records("question_answers", "question", $question->id, "id ASC")) {
19 $oldanswers = array();
22 // Save answer 'True'
23 if ($true = array_shift($oldanswers)) { // Existing answer, so reuse it
24 $true->answer = get_string("true", "quiz");
25 $true->fraction = $question->answer;
26 $true->feedback = $question->feedbacktrue;
27 if (!update_record("question_answers", $true)) {
28 $result->error = "Could not update quiz answer \"true\")!";
29 return $result;
31 } else {
32 unset($true);
33 $true->answer = get_string("true", "quiz");
34 $true->question = $question->id;
35 $true->fraction = $question->answer;
36 $true->feedback = $question->feedbacktrue;
37 if (!$true->id = insert_record("question_answers", $true)) {
38 $result->error = "Could not insert quiz answer \"true\")!";
39 return $result;
43 // Save answer 'False'
44 if ($false = array_shift($oldanswers)) { // Existing answer, so reuse it
45 $false->answer = get_string("false", "quiz");
46 $false->fraction = 1 - (int)$question->answer;
47 $false->feedback = $question->feedbackfalse;
48 if (!update_record("question_answers", $false)) {
49 $result->error = "Could not insert quiz answer \"false\")!";
50 return $result;
52 } else {
53 unset($false);
54 $false->answer = get_string("false", "quiz");
55 $false->question = $question->id;
56 $false->fraction = 1 - (int)$question->answer;
57 $false->feedback = $question->feedbackfalse;
58 if (!$false->id = insert_record("question_answers", $false)) {
59 $result->error = "Could not insert quiz answer \"false\")!";
60 return $result;
64 // delete any leftover old answer records (there couldn't really be any, but who knows)
65 if (!empty($oldanswers)) {
66 foreach($oldanswers as $oa) {
67 delete_records('question_answers', 'id', $oa->id);
71 // Save question options in question_truefalse table
72 if ($options = get_record("question_truefalse", "question", $question->id)) {
73 // No need to do anything, since the answer IDs won't have changed
74 // But we'll do it anyway, just for robustness
75 $options->trueanswer = $true->id;
76 $options->falseanswer = $false->id;
77 if (!update_record("question_truefalse", $options)) {
78 $result->error = "Could not update quiz truefalse options! (id=$options->id)";
79 return $result;
81 } else {
82 unset($options);
83 $options->question = $question->id;
84 $options->trueanswer = $true->id;
85 $options->falseanswer = $false->id;
86 if (!insert_record("question_truefalse", $options)) {
87 $result->error = "Could not insert quiz truefalse options!";
88 return $result;
91 return true;
94 /**
95 * Loads the question type specific options for the question.
97 function get_question_options(&$question) {
98 // Get additional information from database
99 // and attach it to the question object
100 if (!$question->options = get_record('question_truefalse', 'question', $question->id)) {
101 notify('Error: Missing question options!');
102 return false;
104 // Load the answers
105 if (!$question->options->answers = get_records('question_answers', 'question', $question->id)) {
106 notify('Error: Missing question answers!');
107 return false;
110 return true;
114 * Deletes question from the question-type specific tables
116 * @return boolean Success/Failure
117 * @param object $question The question being deleted
119 function delete_question($questionid) {
120 delete_records("question_truefalse", "question", $questionid);
121 return true;
124 function get_correct_responses(&$question, &$state) {
125 // The correct answer is the one which gives full marks
126 foreach ($question->options->answers as $answer) {
127 if (((int) $answer->fraction) === 1) {
128 return array('' => $answer->id);
131 return null;
135 * Prints the main content of the question including any interactions
137 function print_question_formulation_and_controls(&$question, &$state,
138 $cmoptions, $options) {
139 global $CFG;
141 $readonly = $options->readonly ? ' disabled="disabled"' : '';
143 $formatoptions = new stdClass;
144 $formatoptions->noclean = true;
145 $formatoptions->para = false;
147 // Print question formulation
148 $questiontext = format_text($question->questiontext,
149 $question->questiontextformat,
150 $formatoptions, $cmoptions->course);
151 $image = get_question_image($question, $cmoptions->course);
153 $answers = &$question->options->answers;
154 $trueanswer = &$answers[$question->options->trueanswer];
155 $falseanswer = &$answers[$question->options->falseanswer];
156 $correctanswer = ($trueanswer->fraction == 1) ? $trueanswer : $falseanswer;
158 // Work out which radio button to select (if any)
159 $truechecked = ($state->responses[''] == $trueanswer->id) ? ' checked="checked"' : '';
160 $falsechecked = ($state->responses[''] == $falseanswer->id) ? ' checked="checked"' : '';
162 // Work out which answer is correct if we need to highlight it
163 if ($options->correct_responses) {
164 $trueclass = ($trueanswer->fraction) ? ' class="highlight"' : '';
165 $falseclass = ($falseanswer->fraction) ? ' class="highlight"' : '';
166 } else {
167 $trueclass = '';
168 $falseclass = '';
171 $inputname = ' name="'.$question->name_prefix.'" ';
172 $trueid = $question->name_prefix.'true';
173 $falseid = $question->name_prefix.'false';
175 $radiotrue = '<input type="radio"' . $truechecked . $readonly . $inputname
176 . 'id="'.$trueid . '" value="' . $trueanswer->id . '" alt="'
177 . s($trueanswer->answer) . '" /><label for="'.$trueid . '">'
178 . s($trueanswer->answer) . '</label>';
179 $radiofalse = '<input type="radio"' . $falsechecked . $readonly . $inputname
180 . 'id="'.$falseid . '" value="' . $falseanswer->id . '" alt="'
181 . s($falseanswer->answer) . '" /><label for="'.$falseid . '">'
182 . s($falseanswer->answer) . '</label>';
184 $feedback = '';
185 if ($options->feedback and isset($answers[$state->responses['']])) {
186 $chosenanswer = $answers[$state->responses['']];
187 $feedback = format_text($chosenanswer->feedback, true, $formatoptions, $cmoptions->course);
190 include("$CFG->dirroot/question/type/truefalse/display.html");
193 function grade_responses(&$question, &$state, $cmoptions) {
194 if (isset($question->options->answers[$state->responses['']])) {
195 $state->raw_grade = $question->options->answers[$state->responses['']]->fraction * $question->maxgrade;
196 } else {
197 $state->raw_grade = 0;
199 // Only allow one attempt at the question
200 $state->penalty = 1 * $question->maxgrade;
202 // mark the state as graded
203 $state->event = ($state->event == QUESTION_EVENTCLOSE) ? QUESTION_EVENTCLOSEANDGRADE : QUESTION_EVENTGRADE;
205 return true;
208 function response_summary($question, $state, $length=80) {
209 if (isset($question->options->answers[$state->answer])) {
210 $responses = $question->options->answers[$state->answer]->answer;
211 } else {
212 $responses = '';
214 return $responses;
217 function get_actual_response($question, $state) {
218 if (isset($question->options->answers[$state->responses['']])) {
219 $responses[] = $question->options->answers[$state->responses['']]->answer;
220 } else {
221 $responses[] = '';
223 return $responses;
226 /// BACKUP FUNCTIONS ////////////////////////////
229 * Backup the data in a truefalse question
231 * This is used in question/backuplib.php
233 function backup($bf,$preferences,$question,$level=6) {
235 $status = true;
237 $truefalses = get_records("question_truefalse","question",$question,"id");
238 //If there are truefalses
239 if ($truefalses) {
240 //Iterate over each truefalse
241 foreach ($truefalses as $truefalse) {
242 $status = fwrite ($bf,start_tag("TRUEFALSE",$level,true));
243 //Print truefalse contents
244 fwrite ($bf,full_tag("TRUEANSWER",$level+1,false,$truefalse->trueanswer));
245 fwrite ($bf,full_tag("FALSEANSWER",$level+1,false,$truefalse->falseanswer));
246 $status = fwrite ($bf,end_tag("TRUEFALSE",$level,true));
248 //Now print question_answers
249 $status = question_backup_answers($bf,$preferences,$question);
251 return $status;
254 /// RESTORE FUNCTIONS /////////////////
257 * Restores the data in the question
259 * This is used in question/restorelib.php
261 function restore($old_question_id,$new_question_id,$info,$restore) {
263 $status = true;
265 //Get the truefalse array
266 $truefalses = $info['#']['TRUEFALSE'];
268 //Iterate over truefalse
269 for($i = 0; $i < sizeof($truefalses); $i++) {
270 $tru_info = $truefalses[$i];
272 //Now, build the question_truefalse record structure
273 $truefalse = new stdClass;
274 $truefalse->question = $new_question_id;
275 $truefalse->trueanswer = backup_todb($tru_info['#']['TRUEANSWER']['0']['#']);
276 $truefalse->falseanswer = backup_todb($tru_info['#']['FALSEANSWER']['0']['#']);
278 ////We have to recode the trueanswer field
279 $answer = backup_getid($restore->backup_unique_code,"question_answers",$truefalse->trueanswer);
280 if ($answer) {
281 $truefalse->trueanswer = $answer->new_id;
284 ////We have to recode the falseanswer field
285 $answer = backup_getid($restore->backup_unique_code,"question_answers",$truefalse->falseanswer);
286 if ($answer) {
287 $truefalse->falseanswer = $answer->new_id;
290 //The structure is equal to the db, so insert the question_truefalse
291 $newid = insert_record ("question_truefalse", $truefalse);
293 //Do some output
294 if (($i+1) % 50 == 0) {
295 if (!defined('RESTORE_SILENTLY')) {
296 echo ".";
297 if (($i+1) % 1000 == 0) {
298 echo "<br />";
301 backup_flush(300);
304 if (!$newid) {
305 $status = false;
309 return $status;
312 function restore_recode_answer($state, $restore) {
313 //answer may be empty
314 if ($state->answer) {
315 $answer = backup_getid($restore->backup_unique_code,"question_answers",$state->answer);
316 if ($answer) {
317 return $answer->new_id;
318 } else {
319 echo 'Could not recode truefalse answer id '.$state->answer.' for state '.$state->oldid.'<br />';
325 //// END OF CLASS ////
327 //////////////////////////////////////////////////////////////////////////
328 //// INITIATION - Without this line the question type is not in use... ///
329 //////////////////////////////////////////////////////////////////////////
330 question_register_questiontype(new question_truefalse_qtype());