516cf3eb |
1 | <?php // $Id$ |
2 | |
3 | ///////////// |
4 | /// MATCH /// |
5 | ///////////// |
6 | |
7 | /// QUESTION TYPE CLASS ////////////////// |
1976496e |
8 | /** |
9 | * @package questionbank |
10 | * @subpackage questiontypes |
7375c542 |
11 | */ |
af3830ee |
12 | class question_match_qtype extends default_questiontype { |
516cf3eb |
13 | |
14 | function name() { |
15 | return 'match'; |
16 | } |
17 | |
18 | function get_question_options(&$question) { |
32a189d6 |
19 | $question->options = get_record('question_match', 'question', $question->id); |
f07d1d31 |
20 | $question->options->subquestions = get_records('question_match_sub', 'question', $question->id, 'id ASC'); |
516cf3eb |
21 | return true; |
22 | } |
23 | |
24 | function save_question_options($question) { |
a58ffe3f |
25 | $result = new stdClass; |
069a820a |
26 | |
32a189d6 |
27 | if (!$oldsubquestions = get_records("question_match_sub", "question", $question->id, "id ASC")) { |
516cf3eb |
28 | $oldsubquestions = array(); |
29 | } |
30 | |
516cf3eb |
31 | // $subquestions will be an array with subquestion ids |
32 | $subquestions = array(); |
33 | |
34 | // Insert all the new question+answer pairs |
35 | foreach ($question->subquestions as $key => $questiontext) { |
36 | $answertext = $question->subanswers[$key]; |
ebf83e2c |
37 | if ($questiontext != '' || $answertext != '') { |
516cf3eb |
38 | if ($subquestion = array_shift($oldsubquestions)) { // Existing answer, so reuse it |
39 | $subquestion->questiontext = $questiontext; |
40 | $subquestion->answertext = $answertext; |
32a189d6 |
41 | if (!update_record("question_match_sub", $subquestion)) { |
7518b645 |
42 | $result->error = "Could not insert match subquestion! (id=$subquestion->id)"; |
516cf3eb |
43 | return $result; |
44 | } |
45 | } else { |
a58ffe3f |
46 | $subquestion = new stdClass; |
516cf3eb |
47 | // Determine a unique random code |
48 | $subquestion->code = rand(1,999999999); |
18bd0d68 |
49 | while (record_exists('question_match_sub', 'code', $subquestion->code, 'question', $question->id)) { |
516cf3eb |
50 | $subquestion->code = rand(); |
51 | } |
52 | $subquestion->question = $question->id; |
53 | $subquestion->questiontext = $questiontext; |
54 | $subquestion->answertext = $answertext; |
32a189d6 |
55 | if (!$subquestion->id = insert_record("question_match_sub", $subquestion)) { |
7518b645 |
56 | $result->error = "Could not insert match subquestion!"; |
516cf3eb |
57 | return $result; |
58 | } |
59 | } |
60 | $subquestions[] = $subquestion->id; |
61 | } |
ebf83e2c |
62 | if ($questiontext != '' && $answertext == '') { |
a58ffe3f |
63 | $result->notice = get_string('nomatchinganswer', 'quiz', $questiontext); |
64 | } |
516cf3eb |
65 | } |
66 | |
67 | // delete old subquestions records |
68 | if (!empty($oldsubquestions)) { |
69 | foreach($oldsubquestions as $os) { |
32a189d6 |
70 | delete_records('question_match_sub', 'id', $os->id); |
516cf3eb |
71 | } |
72 | } |
73 | |
32a189d6 |
74 | if ($options = get_record("question_match", "question", $question->id)) { |
516cf3eb |
75 | $options->subquestions = implode(",",$subquestions); |
76 | $options->shuffleanswers = $question->shuffleanswers; |
32a189d6 |
77 | if (!update_record("question_match", $options)) { |
7518b645 |
78 | $result->error = "Could not update match options! (id=$options->id)"; |
516cf3eb |
79 | return $result; |
80 | } |
81 | } else { |
82 | unset($options); |
83 | $options->question = $question->id; |
84 | $options->subquestions = implode(",",$subquestions); |
85 | $options->shuffleanswers = $question->shuffleanswers; |
32a189d6 |
86 | if (!insert_record("question_match", $options)) { |
7518b645 |
87 | $result->error = "Could not insert match options!"; |
516cf3eb |
88 | return $result; |
89 | } |
90 | } |
a58ffe3f |
91 | |
92 | if (!empty($result->notice)) { |
93 | return $result; |
94 | } |
95 | |
96 | if (count($subquestions) < 3) { |
97 | $result->notice = get_string('notenoughanswers', 'quiz', 3); |
98 | return $result; |
99 | } |
100 | |
516cf3eb |
101 | return true; |
102 | } |
103 | |
104 | /** |
105 | * Deletes question from the question-type specific tables |
106 | * |
107 | * @return boolean Success/Failure |
108 | * @param integer $question->id |
109 | */ |
90c3f310 |
110 | function delete_question($questionid) { |
111 | delete_records("question_match", "question", $questionid); |
112 | delete_records("question_match_sub", "question", $questionid); |
516cf3eb |
113 | return true; |
114 | } |
115 | |
116 | function create_session_and_responses(&$question, &$state, $cmoptions, $attempt) { |
f07d1d31 |
117 | if (!$state->options->subquestions = get_records('question_match_sub', 'question', $question->id, 'id ASC')) { |
00c30069 |
118 | notify('Error: Missing subquestions!'); |
119 | return false; |
516cf3eb |
120 | } |
121 | |
122 | foreach ($state->options->subquestions as $key => $subquestion) { |
123 | // This seems rather over complicated, but it is useful for the |
124 | // randomsamatch questiontype, which can then inherit the print |
125 | // and grading functions. This way it is possible to define multiple |
126 | // answers per question, each with different marks and feedback. |
127 | $answer = new stdClass(); |
128 | $answer->id = $subquestion->code; |
129 | $answer->answer = $subquestion->answertext; |
130 | $answer->fraction = 1.0; |
131 | $state->options->subquestions[$key]->options |
00c30069 |
132 | ->answers[$subquestion->code] = clone($answer); |
516cf3eb |
133 | |
134 | $state->responses[$key] = ''; |
135 | } |
136 | |
137 | // Shuffle the answers if required |
138 | if ($cmoptions->shuffleanswers and $question->options->shuffleanswers) { |
139 | $state->options->subquestions = swapshuffle_assoc($state->options->subquestions); |
140 | } |
141 | |
142 | return true; |
143 | } |
144 | |
145 | function restore_session_and_responses(&$question, &$state) { |
146 | // The serialized format for matching questions is a comma separated |
147 | // list of question answer pairs (e.g. 1-1,2-3,3-2), where the ids of |
32a189d6 |
148 | // both refer to the id in the table question_match_sub. |
516cf3eb |
149 | $responses = explode(',', $state->responses['']); |
150 | $responses = array_map(create_function('$val', |
151 | 'return explode("-", $val);'), $responses); |
152 | |
f07d1d31 |
153 | if (!$questions = get_records('question_match_sub', 'question', $question->id, 'id ASC')) { |
516cf3eb |
154 | notify('Error: Missing subquestions!'); |
155 | return false; |
156 | } |
157 | |
158 | // Restore the previous responses and place the questions into the state options |
159 | $state->responses = array(); |
160 | $state->options->subquestions = array(); |
161 | foreach ($responses as $response) { |
162 | $state->responses[$response[0]] = $response[1]; |
163 | $state->options->subquestions[$response[0]] = $questions[$response[0]]; |
164 | } |
165 | |
166 | foreach ($state->options->subquestions as $key => $subquestion) { |
167 | // This seems rather over complicated, but it is useful for the |
168 | // randomsamatch questiontype, which can then inherit the print |
169 | // and grading functions. This way it is possible to define multiple |
170 | // answers per question, each with different marks and feedback. |
171 | $answer = new stdClass(); |
172 | $answer->id = $subquestion->code; |
29e959a5 |
173 | $answer->answer = format_string($subquestion->answertext); |
516cf3eb |
174 | $answer->fraction = 1.0; |
175 | $state->options->subquestions[$key]->options |
176 | ->answers[$subquestion->code] = clone($answer); |
177 | } |
178 | |
179 | return true; |
180 | } |
181 | |
182 | function save_session_and_responses(&$question, &$state) { |
87ee4968 |
183 | $subquestions = &$state->options->subquestions; |
184 | |
185 | // Prepare an array to help when disambiguating equal answers. |
186 | $answertexts = array(); |
187 | foreach ($subquestions as $subquestion) { |
188 | $ans = reset($subquestion->options->answers); |
189 | $answertexts[$ans->id] = $ans->answer; |
190 | } |
069a820a |
191 | |
516cf3eb |
192 | // Serialize responses |
193 | $responses = array(); |
87ee4968 |
194 | foreach ($subquestions as $key => $subquestion) { |
7d6af8ca |
195 | $response = 0; |
0c24ee0f |
196 | if ($subquestion->questiontext) { |
87ee4968 |
197 | if ($state->responses[$key]) { |
198 | $response = $state->responses[$key]; |
199 | if (!array_key_exists($response, $subquestion->options->answers)) { |
200 | // If studen's answer did not match by id, but there may be |
201 | // two answers with the same text, but different ids, |
202 | // so we need to try matching the answer text. |
203 | $expected_answer = reset($subquestion->options->answers); |
204 | if ($answertexts[$response] == $expected_answer->answer) { |
205 | $response = $expected_answer->id; |
206 | $state->responses[$key] = $response; |
207 | } |
208 | } |
87ee4968 |
209 | } |
0c24ee0f |
210 | } |
7d6af8ca |
211 | $responses[] = $key.'-'.$response; |
516cf3eb |
212 | } |
213 | $responses = implode(',', $responses); |
214 | |
215 | // Set the legacy answer field |
0c24ee0f |
216 | if (!set_field('question_states', 'answer', $responses, 'id', $state->id)) { |
516cf3eb |
217 | return false; |
218 | } |
219 | return true; |
220 | } |
221 | |
222 | function get_correct_responses(&$question, &$state) { |
223 | $responses = array(); |
224 | foreach ($state->options->subquestions as $sub) { |
225 | foreach ($sub->options->answers as $answer) { |
ebf83e2c |
226 | if (1 == $answer->fraction && $sub->questiontext != '') { |
516cf3eb |
227 | $responses[$sub->id] = $answer->id; |
228 | } |
229 | } |
230 | } |
231 | return empty($responses) ? null : $responses; |
232 | } |
233 | |
234 | function print_question_formulation_and_controls(&$question, &$state, $cmoptions, $options) { |
37a12367 |
235 | global $CFG; |
516cf3eb |
236 | $subquestions = $state->options->subquestions; |
237 | $correctanswers = $this->get_correct_responses($question, $state); |
238 | $nameprefix = $question->name_prefix; |
239 | $answers = array(); |
0b4ce29d |
240 | $allanswers = array(); |
87ee4968 |
241 | $answerids = array(); |
516cf3eb |
242 | $responses = &$state->responses; |
243 | |
069a820a |
244 | // Prepare a list of answers, removing duplicates. |
516cf3eb |
245 | foreach ($subquestions as $subquestion) { |
246 | foreach ($subquestion->options->answers as $ans) { |
0b4ce29d |
247 | $allanswers[$ans->id] = $ans->answer; |
87ee4968 |
248 | if (!in_array($ans->answer, $answers)) { |
249 | $answers[$ans->id] = $ans->answer; |
250 | $answerids[$ans->answer] = $ans->id; |
87ee4968 |
251 | } |
516cf3eb |
252 | } |
253 | } |
069a820a |
254 | |
0b4ce29d |
255 | // Fix up the ids of any responses that point the the eliminated duplicates. |
256 | foreach ($responses as $subquestionid => $ignored) { |
257 | if ($responses[$subquestionid]) { |
258 | $responses[$subquestionid] = $answerids[$allanswers[$responses[$subquestionid]]]; |
259 | } |
260 | } |
261 | foreach ($correctanswers as $subquestionid => $ignored) { |
262 | $correctanswers[$subquestionid] = $answerids[$allanswers[$correctanswers[$subquestionid]]]; |
263 | } |
516cf3eb |
264 | |
265 | // Shuffle the answers |
266 | $answers = draw_rand_array($answers, count($answers)); |
267 | |
37a12367 |
268 | // Print formulation |
1b8a7434 |
269 | $questiontext = $this->format_text($question->questiontext, |
270 | $question->questiontextformat, $cmoptions); |
9fc3100f |
271 | $image = get_question_image($question); |
516cf3eb |
272 | |
069a820a |
273 | // Print the input controls |
516cf3eb |
274 | foreach ($subquestions as $key => $subquestion) { |
ebf83e2c |
275 | if ($subquestion->questiontext != '') { |
069a820a |
276 | // Subquestion text: |
a58ffe3f |
277 | $a = new stdClass; |
1b8a7434 |
278 | $a->text = $this->format_text($subquestion->questiontext, |
279 | $question->questiontextformat, $cmoptions); |
2b087056 |
280 | |
069a820a |
281 | // Drop-down list: |
a58ffe3f |
282 | $menuname = $nameprefix.$subquestion->id; |
283 | $response = isset($state->responses[$subquestion->id]) |
284 | ? $state->responses[$subquestion->id] : '0'; |
2b087056 |
285 | |
286 | $a->class = ' '; |
287 | $a->feedbackimg = ' '; |
288 | |
069a820a |
289 | if ($options->readonly and $options->correct_responses) { |
290 | if (isset($correctanswers[$subquestion->id]) |
2b087056 |
291 | and ($correctanswers[$subquestion->id] == $response)) { |
069a820a |
292 | $correctresponse = 1; |
293 | } else { |
294 | $correctresponse = 0; |
295 | } |
2b087056 |
296 | |
1b16ecd1 |
297 | if ($options->feedback && $response) { |
069a820a |
298 | $a->class = question_get_feedback_class($correctresponse); |
299 | $a->feedbackimg = question_get_feedback_image($correctresponse); |
300 | } |
2b087056 |
301 | } |
302 | |
303 | $a->control = choose_from_menu($answers, $menuname, $response, 'choose', |
304 | '', 0, true, $options->readonly); |
069a820a |
305 | |
a58ffe3f |
306 | // Neither the editing interface or the database allow to provide |
307 | // fedback for this question type. |
308 | // However (as was pointed out in bug bug 3294) the randomsamatch |
309 | // type which reuses this method can have feedback defined for |
310 | // the wrapped shortanswer questions. |
311 | //if ($options->feedback |
312 | // && !empty($subquestion->options->answers[$responses[$key]]->feedback)) { |
313 | // print_comment($subquestion->options->answers[$responses[$key]]->feedback); |
314 | //} |
2b087056 |
315 | |
a58ffe3f |
316 | $anss[] = $a; |
516cf3eb |
317 | } |
516cf3eb |
318 | } |
aaae75b0 |
319 | include("$CFG->dirroot/question/type/match/display.html"); |
516cf3eb |
320 | } |
321 | |
322 | function grade_responses(&$question, &$state, $cmoptions) { |
323 | $subquestions = &$state->options->subquestions; |
324 | $responses = &$state->responses; |
325 | |
87ee4968 |
326 | // Prepare an array to help when disambiguating equal answers. |
327 | $answertexts = array(); |
328 | foreach ($subquestions as $subquestion) { |
329 | $ans = reset($subquestion->options->answers); |
330 | $answertexts[$ans->id] = $ans->answer; |
331 | } |
069a820a |
332 | |
87ee4968 |
333 | // Add up the grades from each subquestion. |
516cf3eb |
334 | $sumgrade = 0; |
a58ffe3f |
335 | $totalgrade = 0; |
516cf3eb |
336 | foreach ($subquestions as $key => $sub) { |
a58ffe3f |
337 | if ($sub->questiontext) { |
338 | $totalgrade += 1; |
87ee4968 |
339 | $response = $responses[$key]; |
340 | if ($response && !array_key_exists($response, $sub->options->answers)) { |
341 | // If studen's answer did not match by id, but there may be |
342 | // two answers with the same text, but different ids, |
343 | // so we need to try matching the answer text. |
344 | $expected_answer = reset($sub->options->answers); |
345 | if ($answertexts[$response] == $expected_answer->answer) { |
346 | $response = $expected_answer->id; |
347 | } |
348 | } |
349 | if (array_key_exists($response, $sub->options->answers)) { |
350 | $sumgrade += $sub->options->answers[$response]->fraction; |
a58ffe3f |
351 | } |
516cf3eb |
352 | } |
353 | } |
354 | |
a58ffe3f |
355 | $state->raw_grade = $sumgrade/$totalgrade; |
516cf3eb |
356 | if (empty($state->raw_grade)) { |
357 | $state->raw_grade = 0; |
358 | } |
359 | |
360 | // Make sure we don't assign negative or too high marks |
361 | $state->raw_grade = min(max((float) $state->raw_grade, |
362 | 0.0), 1.0) * $question->maxgrade; |
363 | $state->penalty = $question->penalty * $question->maxgrade; |
364 | |
f30bbcaf |
365 | // mark the state as graded |
366 | $state->event = ($state->event == QUESTION_EVENTCLOSE) ? QUESTION_EVENTCLOSEANDGRADE : QUESTION_EVENTGRADE; |
367 | |
516cf3eb |
368 | return true; |
369 | } |
370 | |
b25486fc |
371 | function compare_responses($question, $state, $teststate) { |
2c89cfb5 |
372 | foreach ($state->responses as $i=>$sr) { |
373 | if (empty($teststate->responses[$i])) { |
374 | if (!empty($state->responses[$i])) { |
375 | return false; |
376 | } |
377 | } else if ($state->responses[$i] != $teststate->responses[$i]) { |
b25486fc |
378 | return false; |
379 | } |
380 | } |
381 | return true; |
382 | } |
383 | |
516cf3eb |
384 | // ULPGC ecastro for stats report |
385 | function get_all_responses($question, $state) { |
974383f9 |
386 | $answers = array(); |
516cf3eb |
387 | if (is_array($question->options->subquestions)) { |
645d7610 |
388 | foreach ($question->options->subquestions as $aid => $answer) { |
389 | if ($answer->questiontext) { |
390 | $r = new stdClass; |
391 | $r->answer = $answer->questiontext . ": " . $answer->answertext; |
392 | $r->credit = 1; |
393 | $answers[$aid] = $r; |
394 | } |
516cf3eb |
395 | } |
516cf3eb |
396 | } |
a58ffe3f |
397 | $result = new stdClass; |
516cf3eb |
398 | $result->id = $question->id; |
399 | $result->responses = $answers; |
400 | return $result; |
401 | } |
402 | |
403 | // ULPGC ecastro |
404 | function get_actual_response($question, $state) { |
01bd54e0 |
405 | $subquestions = &$state->options->subquestions; |
406 | $responses = &$state->responses; |
407 | $results=array(); |
408 | foreach ($subquestions as $key => $sub) { |
409 | foreach ($responses as $ind => $code) { |
410 | if (isset($sub->options->answers[$code])) { |
645d7610 |
411 | $results[$ind] = $subquestions[$ind]->questiontext . ": " . $sub->options->answers[$code]->answer; |
01bd54e0 |
412 | } |
413 | } |
414 | } |
415 | return $results; |
416 | } |
0a5b58af |
417 | |
418 | function response_summary($question, $state, $length=80) { |
419 | // This should almost certainly be overridden |
755bddf1 |
420 | return substr(implode(', ', $this->get_actual_response($question, $state)), 0, $length); |
0a5b58af |
421 | } |
069a820a |
422 | |
c5d94c41 |
423 | /// BACKUP FUNCTIONS //////////////////////////// |
424 | |
425 | /* |
426 | * Backup the data in the question |
427 | * |
428 | * This is used in question/backuplib.php |
429 | */ |
430 | function backup($bf,$preferences,$question,$level=6) { |
431 | |
432 | $status = true; |
433 | |
f07d1d31 |
434 | $matchs = get_records('question_match_sub', 'question', $question, 'id ASC'); |
c5d94c41 |
435 | //If there are matchs |
436 | if ($matchs) { |
437 | $status = fwrite ($bf,start_tag("MATCHS",6,true)); |
438 | //Iterate over each match |
439 | foreach ($matchs as $match) { |
440 | $status = fwrite ($bf,start_tag("MATCH",7,true)); |
441 | //Print match contents |
442 | fwrite ($bf,full_tag("ID",8,false,$match->id)); |
443 | fwrite ($bf,full_tag("CODE",8,false,$match->code)); |
444 | fwrite ($bf,full_tag("QUESTIONTEXT",8,false,$match->questiontext)); |
445 | fwrite ($bf,full_tag("ANSWERTEXT",8,false,$match->answertext)); |
446 | $status = fwrite ($bf,end_tag("MATCH",7,true)); |
447 | } |
448 | $status = fwrite ($bf,end_tag("MATCHS",6,true)); |
449 | } |
450 | return $status; |
451 | } |
516cf3eb |
452 | |
315559d3 |
453 | /// RESTORE FUNCTIONS ///////////////// |
454 | |
455 | /* |
456 | * Restores the data in the question |
457 | * |
458 | * This is used in question/restorelib.php |
459 | */ |
460 | function restore($old_question_id,$new_question_id,$info,$restore) { |
9db7dab2 |
461 | global $DB; |
315559d3 |
462 | |
463 | $status = true; |
464 | |
465 | //Get the matchs array |
466 | $matchs = $info['#']['MATCHS']['0']['#']['MATCH']; |
467 | |
468 | //We have to build the subquestions field (a list of match_sub id) |
469 | $subquestions_field = ""; |
470 | $in_first = true; |
471 | |
472 | //Iterate over matchs |
473 | for($i = 0; $i < sizeof($matchs); $i++) { |
474 | $mat_info = $matchs[$i]; |
475 | |
476 | //We'll need this later!! |
477 | $oldid = backup_todb($mat_info['#']['ID']['0']['#']); |
478 | |
479 | //Now, build the question_match_SUB record structure |
a58ffe3f |
480 | $match_sub = new stdClass; |
315559d3 |
481 | $match_sub->question = $new_question_id; |
1f4d6e9a |
482 | $match_sub->code = isset($mat_info['#']['CODE']['0']['#'])?backup_todb($mat_info['#']['CODE']['0']['#']):''; |
315559d3 |
483 | if (!$match_sub->code) { |
484 | $match_sub->code = $oldid; |
485 | } |
486 | $match_sub->questiontext = backup_todb($mat_info['#']['QUESTIONTEXT']['0']['#']); |
487 | $match_sub->answertext = backup_todb($mat_info['#']['ANSWERTEXT']['0']['#']); |
488 | |
489 | //The structure is equal to the db, so insert the question_match_sub |
9db7dab2 |
490 | $newid = $DB->insert_record ("question_match_sub",$match_sub); |
315559d3 |
491 | |
492 | //Do some output |
493 | if (($i+1) % 50 == 0) { |
494 | if (!defined('RESTORE_SILENTLY')) { |
495 | echo "."; |
496 | if (($i+1) % 1000 == 0) { |
497 | echo "<br />"; |
498 | } |
499 | } |
500 | backup_flush(300); |
501 | } |
502 | |
503 | if ($newid) { |
504 | //We have the newid, update backup_ids |
505 | backup_putid($restore->backup_unique_code,"question_match_sub",$oldid, |
506 | $newid); |
507 | //We have a new match_sub, append it to subquestions_field |
508 | if ($in_first) { |
509 | $subquestions_field .= $newid; |
510 | $in_first = false; |
511 | } else { |
512 | $subquestions_field .= ",".$newid; |
513 | } |
514 | } else { |
515 | $status = false; |
516 | } |
517 | } |
518 | |
519 | //We have created every match_sub, now create the match |
87ee4968 |
520 | $match = new stdClass; |
315559d3 |
521 | $match->question = $new_question_id; |
522 | $match->subquestions = $subquestions_field; |
523 | |
524 | //The structure is equal to the db, so insert the question_match_sub |
9db7dab2 |
525 | $newid = $DB->insert_record ("question_match",$match); |
315559d3 |
526 | |
527 | if (!$newid) { |
528 | $status = false; |
529 | } |
530 | |
531 | return $status; |
532 | } |
533 | |
534 | function restore_map($old_question_id,$new_question_id,$info,$restore) { |
535 | |
536 | $status = true; |
537 | |
538 | //Get the matchs array |
539 | $matchs = $info['#']['MATCHS']['0']['#']['MATCH']; |
540 | |
541 | //We have to build the subquestions field (a list of match_sub id) |
542 | $subquestions_field = ""; |
543 | $in_first = true; |
544 | |
545 | //Iterate over matchs |
546 | for($i = 0; $i < sizeof($matchs); $i++) { |
547 | $mat_info = $matchs[$i]; |
548 | |
549 | //We'll need this later!! |
550 | $oldid = backup_todb($mat_info['#']['ID']['0']['#']); |
551 | |
552 | //Now, build the question_match_SUB record structure |
553 | $match_sub->question = $new_question_id; |
554 | $match_sub->questiontext = backup_todb($mat_info['#']['QUESTIONTEXT']['0']['#']); |
555 | $match_sub->answertext = backup_todb($mat_info['#']['ANSWERTEXT']['0']['#']); |
556 | |
557 | //If we are in this method is because the question exists in DB, so its |
558 | //match_sub must exist too. |
559 | //Now, we are going to look for that match_sub in DB and to create the |
560 | //mappings in backup_ids to use them later where restoring states (user level). |
561 | |
562 | //Get the match_sub from DB (by question, questiontext and answertext) |
563 | $db_match_sub = get_record ("question_match_sub","question",$new_question_id, |
564 | "questiontext",$match_sub->questiontext, |
565 | "answertext",$match_sub->answertext); |
566 | //Do some output |
567 | if (($i+1) % 50 == 0) { |
568 | if (!defined('RESTORE_SILENTLY')) { |
569 | echo "."; |
570 | if (($i+1) % 1000 == 0) { |
571 | echo "<br />"; |
572 | } |
573 | } |
574 | backup_flush(300); |
575 | } |
576 | |
577 | //We have the database match_sub, so update backup_ids |
578 | if ($db_match_sub) { |
579 | //We have the newid, update backup_ids |
580 | backup_putid($restore->backup_unique_code,"question_match_sub",$oldid, |
581 | $db_match_sub->id); |
582 | } else { |
583 | $status = false; |
584 | } |
585 | } |
586 | |
587 | return $status; |
588 | } |
589 | |
590 | function restore_recode_answer($state, $restore) { |
591 | |
592 | //The answer is a comma separated list of hypen separated math_subs (for question and answer) |
593 | $answer_field = ""; |
594 | $in_first = true; |
595 | $tok = strtok($state->answer,","); |
596 | while ($tok) { |
597 | //Extract the match_sub for the question and the answer |
598 | $exploded = explode("-",$tok); |
599 | $match_question_id = $exploded[0]; |
3ff8a187 |
600 | $match_answer_id = $exploded[1]; |
315559d3 |
601 | //Get the match_sub from backup_ids (for the question) |
602 | if (!$match_que = backup_getid($restore->backup_unique_code,"question_match_sub",$match_question_id)) { |
3ff8a187 |
603 | echo 'Could not recode question in question_match_sub '.$match_question_id.'<br />'; |
315559d3 |
604 | } |
3ff8a187 |
605 | //Get the match_sub from backup_ids (for the answer) |
606 | if ($match_answer_id) { // only recode answer if not 0, not answered yet |
607 | if (!$match_ans = backup_getid($restore->backup_unique_code,"question_match_sub",$match_answer_id)) { |
608 | echo 'Could not recode answer in question_match_sub '.$match_answer_id.'<br />'; |
609 | } |
610 | } |
611 | |
612 | if ($match_que) { |
613 | //If the question hasn't response, it must be 0 |
614 | if (!$match_ans and $match_answer_id == 0) { |
615 | $match_ans->new_id = 0; |
616 | } |
617 | |
618 | if ($in_first) { |
619 | $answer_field .= $match_que->new_id."-".$match_ans->new_id; |
620 | $in_first = false; |
621 | } else { |
622 | $answer_field .= ",".$match_que->new_id."-".$match_ans->new_id; |
623 | } |
315559d3 |
624 | } |
625 | //check for next |
626 | $tok = strtok(","); |
627 | } |
628 | return $answer_field; |
629 | } |
630 | |
c85607f0 |
631 | /** |
632 | * Decode links in question type specific tables. |
633 | * @return bool success or failure. |
271e6dec |
634 | */ |
c85607f0 |
635 | function decode_content_links_caller($questionids, $restore, &$i) { |
44e1b7d7 |
636 | global $DB; |
637 | |
e3b2eb60 |
638 | $status = true; |
639 | |
c85607f0 |
640 | // Decode links in the question_match_sub table. |
44e1b7d7 |
641 | if ($subquestions = $DB->get_records_list('question_match_sub', 'question', $questionids, '', 'id, questiontext')) { |
c85607f0 |
642 | |
643 | foreach ($subquestions as $subquestion) { |
644 | $questiontext = restore_decode_content_links_worker($subquestion->questiontext, $restore); |
645 | if ($questiontext != $subquestion->questiontext) { |
646 | $subquestion->questiontext = addslashes($questiontext); |
647 | if (!update_record('question_match_sub', $subquestion)) { |
648 | $status = false; |
649 | } |
650 | } |
651 | |
652 | // Do some output. |
653 | if (++$i % 5 == 0 && !defined('RESTORE_SILENTLY')) { |
654 | echo "."; |
655 | if ($i % 100 == 0) { |
656 | echo "<br />"; |
657 | } |
658 | backup_flush(300); |
659 | } |
660 | } |
661 | } |
e3b2eb60 |
662 | |
663 | return $status; |
c85607f0 |
664 | } |
271e6dec |
665 | |
666 | function find_file_links($question, $courseid){ |
667 | // find links in the question_match_sub table. |
668 | $urls = array(); |
6f9ce926 |
669 | if (isset($question->options->subquestions)){ |
670 | foreach ($question->options->subquestions as $subquestion) { |
671 | $urls += question_find_file_links_from_html($subquestion->questiontext, $courseid); |
672 | } |
271e6dec |
673 | |
6f9ce926 |
674 | //set all the values of the array to the question object |
675 | if ($urls){ |
676 | $urls = array_combine(array_keys($urls), array_fill(0, count($urls), array($question->id))); |
677 | } |
271e6dec |
678 | } |
679 | $urls = array_merge_recursive($urls, parent::find_file_links($question, $courseid)); |
6f9ce926 |
680 | |
271e6dec |
681 | return $urls; |
682 | } |
683 | |
684 | function replace_file_links($question, $fromcourseid, $tocourseid, $url, $destination){ |
685 | parent::replace_file_links($question, $fromcourseid, $tocourseid, $url, $destination); |
686 | // replace links in the question_match_sub table. |
687 | if (isset($question->options->subquestions)){ |
688 | foreach ($question->options->subquestions as $subquestion) { |
689 | $subquestionchanged = false; |
690 | $subquestion->questiontext = question_replace_file_links_in_html($subquestion->questiontext, $fromcourseid, $tocourseid, $url, $destination, $subquestionchanged); |
691 | if ($subquestionchanged){//need to update rec in db |
692 | if (!update_record('question_match_sub', addslashes_recursive($subquestion))) { |
5a2a5331 |
693 | print_error('Couldn\'t update \'question_match_sub\' record '.$subquestion->id); |
271e6dec |
694 | } |
695 | |
696 | } |
697 | } |
698 | } |
699 | } |
516cf3eb |
700 | } |
701 | //// END OF CLASS //// |
702 | |
703 | ////////////////////////////////////////////////////////////////////////// |
704 | //// INITIATION - Without this line the question type is not in use... /// |
705 | ////////////////////////////////////////////////////////////////////////// |
a2156789 |
706 | question_register_questiontype(new question_match_qtype()); |
516cf3eb |
707 | ?> |