weekly release 3.2dev
[moodle.git] / question / type / multianswer / questiontype.php
CommitLineData
aeb15530 1<?php
d3603157
TH
2// This file is part of Moodle - http://moodle.org/
3//
4// Moodle is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// Moodle is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
16
17/**
18 * Question type class for the multi-answer question type.
19 *
20 * @package qtype
21 * @subpackage multianswer
22 * @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com}
23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24 */
25
26
a17b297d
TH
27defined('MOODLE_INTERNAL') || die();
28
97562c4d 29require_once($CFG->dirroot . '/question/type/questiontypebase.php');
dcedbb0e
TH
30require_once($CFG->dirroot . '/question/type/multichoice/question.php');
31
1976496e 32/**
d3603157
TH
33 * The multi-answer question type class.
34 *
35 * @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com}
36 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
7375c542 37 */
ab50232b 38class qtype_multianswer extends question_type {
516cf3eb 39
ab50232b
TH
40 public function can_analyse_responses() {
41 return false;
42 }
43
59a3fcd3
TH
44 public function get_question_options($question) {
45 global $DB, $OUTPUT;
516cf3eb 46
3d9645ae 47 // Get relevant data indexed by positionkey from the multianswers table.
ab50232b
TH
48 $sequence = $DB->get_field('question_multianswer', 'sequence',
49 array('question' => $question->id), '*', MUST_EXIST);
516cf3eb 50
ab50232b
TH
51 $wrappedquestions = $DB->get_records_list('question', 'id',
52 explode(',', $sequence), 'id ASC');
516cf3eb 53
3d9645ae 54 // We want an array with question ids as index and the positions as values.
516cf3eb 55 $sequence = array_flip(explode(',', $sequence));
56 array_walk($sequence, create_function('&$val', '$val++;'));
ab50232b
TH
57
58 // If a question is lost, the corresponding index is null
aeb15530 59 // so this null convention is used to test $question->options->questions
857caf3b 60 // before using the values.
3d9645ae 61 // First all possible questions from sequence are nulled
62 // then filled with the data if available in $wrappedquestions.
59a3fcd3
TH
63 foreach ($sequence as $seq) {
64 $question->options->questions[$seq] = '';
df79079f 65 }
ab50232b
TH
66
67 foreach ($wrappedquestions as $wrapped) {
68 question_bank::get_qtype($wrapped->qtype)->get_question_options($wrapped);
3d9645ae 69 // For wrapped questions the maxgrade is always equal to the defaultmark,
70 // there is no entry in the question_instances table for them.
ab50232b
TH
71 $wrapped->maxmark = $wrapped->defaultmark;
72 $question->options->questions[$sequence[$wrapped->id]] = $wrapped;
857caf3b 73 }
a4499532
TH
74 $question->hints = $DB->get_records('question_hints',
75 array('questionid' => $question->id), 'id ASC');
76
516cf3eb 77 return true;
78 }
79
59a3fcd3
TH
80 public function save_question_options($question) {
81 global $DB;
0ff4bd08 82 $result = new stdClass();
9fc3100f 83
516cf3eb 84 // This function needs to be able to handle the case where the existing set of wrapped
85 // questions does not match the new set of wrapped questions so that some need to be
3d9645ae 86 // created, some modified and some deleted.
516cf3eb 87 // Unfortunately the code currently simply overwrites existing ones in sequence. This
9fc3100f 88 // will make re-marking after a re-ordering of wrapped questions impossible and
516cf3eb 89 // will also create difficulties if questiontype specific tables reference the id.
9fc3100f 90
3d9645ae 91 // First we get all the existing wrapped questions.
ab50232b
TH
92 if (!$oldwrappedids = $DB->get_field('question_multianswer', 'sequence',
93 array('question' => $question->id))) {
857caf3b 94 $oldwrappedquestions = array();
0a5b58af 95 } else {
12c6e008
TH
96 $oldwrappedquestions = $DB->get_records_list('question', 'id',
97 explode(',', $oldwrappedids), 'id ASC');
516cf3eb 98 }
ab50232b 99
516cf3eb 100 $sequence = array();
59a3fcd3
TH
101 foreach ($question->options->questions as $wrapped) {
102 if (!empty($wrapped)) {
3d9645ae 103 // If we still have some old wrapped question ids, reuse the next of them.
f34488b2 104
12c6e008
TH
105 if (is_array($oldwrappedquestions) &&
106 $oldwrappedquestion = array_shift($oldwrappedquestions)) {
857caf3b 107 $wrapped->id = $oldwrappedquestion->id;
59a3fcd3 108 if ($oldwrappedquestion->qtype != $wrapped->qtype) {
857caf3b 109 switch ($oldwrappedquestion->qtype) {
59a3fcd3 110 case 'multichoice':
0aa04a7f
TH
111 $DB->delete_records('qtype_multichoice_options',
112 array('questionid' => $oldwrappedquestion->id));
59a3fcd3
TH
113 break;
114 case 'shortanswer':
b4cb0957
TH
115 $DB->delete_records('qtype_shortanswer_options',
116 array('questionid' => $oldwrappedquestion->id));
59a3fcd3
TH
117 break;
118 case 'numerical':
ab50232b
TH
119 $DB->delete_records('question_numerical',
120 array('question' => $oldwrappedquestion->id));
59a3fcd3
TH
121 break;
122 default:
ab50232b
TH
123 throw new moodle_exception('qtypenotrecognized',
124 'qtype_multianswer', '', $oldwrappedquestion->qtype);
59a3fcd3 125 $wrapped->id = 0;
df79079f 126 }
e9028ffc 127 }
59a3fcd3
TH
128 } else {
129 $wrapped->id = 0;
e9028ffc 130 }
516cf3eb 131 }
77fa3a0d 132 $wrapped->name = $question->name;
133 $wrapped->parent = $question->id;
59a3fcd3 134 $previousid = $wrapped->id;
3d9645ae 135 // Save_question strips this extra bit off the category again.
ab50232b
TH
136 $wrapped->category = $question->category . ',1';
137 $wrapped = question_bank::get_qtype($wrapped->qtype)->save_question(
138 $wrapped, clone($wrapped));
516cf3eb 139 $sequence[] = $wrapped->id;
59a3fcd3 140 if ($previousid != 0 && $previousid != $wrapped->id) {
3d9645ae 141 // For some reasons a new question has been created
142 // so delete the old one.
3908a523 143 question_delete_question($previousid);
26053641 144 }
516cf3eb 145 }
146
3d9645ae 147 // Delete redundant wrapped questions.
59a3fcd3 148 if (is_array($oldwrappedquestions) && count($oldwrappedquestions)) {
26053641 149 foreach ($oldwrappedquestions as $oldwrappedquestion) {
3908a523 150 question_delete_question($oldwrappedquestion->id);
e9028ffc 151 }
4bc4ca50 152 }
516cf3eb 153
154 if (!empty($sequence)) {
0ff4bd08 155 $multianswer = new stdClass();
516cf3eb 156 $multianswer->question = $question->id;
157 $multianswer->sequence = implode(',', $sequence);
12c6e008
TH
158 if ($oldid = $DB->get_field('question_multianswer', 'id',
159 array('question' => $question->id))) {
516cf3eb 160 $multianswer->id = $oldid;
ab50232b 161 $DB->update_record('question_multianswer', $multianswer);
516cf3eb 162 } else {
ab50232b 163 $DB->insert_record('question_multianswer', $multianswer);
516cf3eb 164 }
165 }
a4499532 166
e9af6091 167 $this->save_hints($question, true);
516cf3eb 168 }
169
59a3fcd3 170 public function save_question($authorizedquestion, $form) {
e51efd7e 171 $question = qtype_multianswer_extract_question($form->questiontext);
516cf3eb 172 if (isset($authorizedquestion->id)) {
173 $question->id = $authorizedquestion->id;
516cf3eb 174 }
175
516cf3eb 176 $question->category = $authorizedquestion->category;
ab50232b 177 $form->defaultmark = $question->defaultmark;
516cf3eb 178 $form->questiontext = $question->questiontext;
179 $form->questiontextformat = 0;
77fa3a0d 180 $form->options = clone($question->options);
516cf3eb 181 unset($question->options);
94dbfb3a 182 return parent::save_question($question, $form);
516cf3eb 183 }
184
e9af6091
JMV
185 protected function make_hint($hint) {
186 return question_hint_with_parts::load_from_record($hint);
187 }
188
59a3fcd3 189 public function delete_question($questionid, $contextid) {
f34488b2 190 global $DB;
ab50232b 191 $DB->delete_records('question_multianswer', array('question' => $questionid));
9203b705
TH
192
193 parent::delete_question($questionid, $contextid);
516cf3eb 194 }
195
072db71c 196 protected function initialise_question_instance(question_definition $question, $questiondata) {
ab50232b 197 parent::initialise_question_instance($question, $questiondata);
42a5b055
TH
198
199 $bits = preg_split('/\{#(\d+)\}/', $question->questiontext,
200 null, PREG_SPLIT_DELIM_CAPTURE);
201 $question->textfragments[0] = array_shift($bits);
202 $i = 1;
203 while (!empty($bits)) {
204 $question->places[$i] = array_shift($bits);
205 $question->textfragments[$i] = array_shift($bits);
206 $i += 1;
207 }
ab50232b
TH
208 foreach ($questiondata->options->questions as $key => $subqdata) {
209 $subqdata->contextid = $questiondata->contextid;
bd156853
PP
210 if ($subqdata->qtype == 'multichoice') {
211 $answerregs = array();
212 if ($subqdata->options->shuffleanswers == 1 && isset($questiondata->options->shuffleanswers)
213 && $questiondata->options->shuffleanswers == 0 ) {
214 $subqdata->options->shuffleanswers = 0;
215 }
216 }
ab50232b 217 $question->subquestions[$key] = question_bank::make_question($subqdata);
fa6c8620 218 $question->subquestions[$key]->maxmark = $subqdata->defaultmark;
7ac7977c
TH
219 if (isset($subqdata->options->layout)) {
220 $question->subquestions[$key]->layout = $subqdata->options->layout;
221 }
869309b8 222 }
869309b8 223 }
869309b8 224
ab50232b
TH
225 public function get_random_guess_score($questiondata) {
226 $fractionsum = 0;
227 $fractionmax = 0;
228 foreach ($questiondata->options->questions as $key => $subqdata) {
fa6c8620 229 $fractionmax += $subqdata->defaultmark;
ab50232b
TH
230 $fractionsum += question_bank::get_qtype(
231 $subqdata->qtype)->get_random_guess_score($subqdata);
516cf3eb 232 }
ab50232b 233 return $fractionsum / $fractionmax;
516cf3eb 234 }
d44480f6
TH
235
236 public function move_files($questionid, $oldcontextid, $newcontextid) {
237 parent::move_files($questionid, $oldcontextid, $newcontextid);
238 $this->move_files_in_hints($questionid, $oldcontextid, $newcontextid);
239 }
240
241 protected function delete_files($questionid, $contextid) {
242 parent::delete_files($questionid, $contextid);
243 $this->delete_files_in_hints($questionid, $contextid);
244 }
516cf3eb 245}
516cf3eb 246
ab50232b 247
3d9645ae 248// ANSWER_ALTERNATIVE regexes.
ab50232b 249define('ANSWER_ALTERNATIVE_FRACTION_REGEX',
0b346164 250 '=|%(-?[0-9]+)%');
3d9645ae 251// For the syntax '(?<!' see http://www.perl.com/doc/manual/html/pod/perlre.html#item_C.
ab50232b 252define('ANSWER_ALTERNATIVE_ANSWER_REGEX',
e51efd7e 253 '.+?(?<!\\\\|&|&amp;)(?=[~#}]|$)');
ab50232b 254define('ANSWER_ALTERNATIVE_FEEDBACK_REGEX',
0b346164 255 '.*?(?<!\\\\)(?=[~}]|$)');
ab50232b 256define('ANSWER_ALTERNATIVE_REGEX',
e51efd7e 257 '(' . ANSWER_ALTERNATIVE_FRACTION_REGEX .')?' .
258 '(' . ANSWER_ALTERNATIVE_ANSWER_REGEX . ')' .
259 '(#(' . ANSWER_ALTERNATIVE_FEEDBACK_REGEX .'))?');
0b346164 260
3d9645ae 261// Parenthesis positions for ANSWER_ALTERNATIVE_REGEX.
ab50232b
TH
262define('ANSWER_ALTERNATIVE_REGEX_PERCENTILE_FRACTION', 2);
263define('ANSWER_ALTERNATIVE_REGEX_FRACTION', 1);
264define('ANSWER_ALTERNATIVE_REGEX_ANSWER', 3);
265define('ANSWER_ALTERNATIVE_REGEX_FEEDBACK', 5);
0b346164 266
267// NUMBER_FORMATED_ALTERNATIVE_ANSWER_REGEX is used
3d9645ae 268// for identifying numerical answers in ANSWER_ALTERNATIVE_REGEX_ANSWER.
ab50232b 269define('NUMBER_REGEX',
e64e28d7 270 '-?(([0-9]+[.,]?[0-9]*|[.,][0-9]+)([eE][-+]?[0-9]+)?)');
ab50232b 271define('NUMERICAL_ALTERNATIVE_REGEX',
e64e28d7 272 '^(' . NUMBER_REGEX . ')(:' . NUMBER_REGEX . ')?$');
0b346164 273
3d9645ae 274// Parenthesis positions for NUMERICAL_FORMATED_ALTERNATIVE_ANSWER_REGEX.
ab50232b
TH
275define('NUMERICAL_CORRECT_ANSWER', 1);
276define('NUMERICAL_ABS_ERROR_MARGIN', 6);
0b346164 277
3d9645ae 278// Remaining ANSWER regexes.
ab50232b 279define('ANSWER_TYPE_DEF_REGEX',
12c6e008 280 '(NUMERICAL|NM)|(MULTICHOICE|MC)|(MULTICHOICE_V|MCV)|(MULTICHOICE_H|MCH)|' .
bd156853
PP
281 '(SHORTANSWER|SA|MW)|(SHORTANSWER_C|SAC|MWC)|' .
282 '(MULTICHOICE_S|MCS)|(MULTICHOICE_VS|MCVS)|(MULTICHOICE_HS|MCHS)');
ab50232b 283define('ANSWER_START_REGEX',
0b346164 284 '\{([0-9]*):(' . ANSWER_TYPE_DEF_REGEX . '):');
285
ab50232b 286define('ANSWER_REGEX',
0b346164 287 ANSWER_START_REGEX
288 . '(' . ANSWER_ALTERNATIVE_REGEX
289 . '(~'
290 . ANSWER_ALTERNATIVE_REGEX
59a3fcd3 291 . ')*)\}');
0b346164 292
3d9645ae 293// Parenthesis positions for singulars in ANSWER_REGEX.
ab50232b
TH
294define('ANSWER_REGEX_NORM', 1);
295define('ANSWER_REGEX_ANSWER_TYPE_NUMERICAL', 3);
296define('ANSWER_REGEX_ANSWER_TYPE_MULTICHOICE', 4);
297define('ANSWER_REGEX_ANSWER_TYPE_MULTICHOICE_REGULAR', 5);
298define('ANSWER_REGEX_ANSWER_TYPE_MULTICHOICE_HORIZONTAL', 6);
299define('ANSWER_REGEX_ANSWER_TYPE_SHORTANSWER', 7);
300define('ANSWER_REGEX_ANSWER_TYPE_SHORTANSWER_C', 8);
bd156853
PP
301define('ANSWER_REGEX_ANSWER_TYPE_MULTICHOICE_SHUFFLED', 9);
302define('ANSWER_REGEX_ANSWER_TYPE_MULTICHOICE_REGULAR_SHUFFLED', 10);
303define('ANSWER_REGEX_ANSWER_TYPE_MULTICHOICE_HORIZONTAL_SHUFFLED', 11);
304define('ANSWER_REGEX_ALTERNATIVES', 12);
305
306/**
307 * Initialise subquestion fields that are constant across all MULTICHOICE
308 * types.
309 *
310 * @param objet $wrapped The subquestion to initialise
311 *
312 */
313function qtype_multianswer_initialise_multichoice_subquestion($wrapped) {
314 $wrapped->qtype = 'multichoice';
315 $wrapped->single = 1;
316 $wrapped->answernumbering = 0;
317 $wrapped->correctfeedback['text'] = '';
318 $wrapped->correctfeedback['format'] = FORMAT_HTML;
319 $wrapped->correctfeedback['itemid'] = '';
320 $wrapped->partiallycorrectfeedback['text'] = '';
321 $wrapped->partiallycorrectfeedback['format'] = FORMAT_HTML;
322 $wrapped->partiallycorrectfeedback['itemid'] = '';
323 $wrapped->incorrectfeedback['text'] = '';
324 $wrapped->incorrectfeedback['format'] = FORMAT_HTML;
325 $wrapped->incorrectfeedback['itemid'] = '';
326}
516cf3eb 327
7518b645 328function qtype_multianswer_extract_question($text) {
3d9645ae 329 // Variable $text is an array [text][format][itemid].
0ff4bd08 330 $question = new stdClass();
dfa47f96 331 $question->qtype = 'multianswer';
516cf3eb 332 $question->questiontext = $text;
61dfe97e 333 $question->generalfeedback['text'] = '';
c1f15d35 334 $question->generalfeedback['format'] = FORMAT_HTML;
61dfe97e 335 $question->generalfeedback['itemid'] = '';
59a3fcd3 336
caee6e6c 337 $question->options = new stdClass();
59a3fcd3 338 $question->options->questions = array();
3d9645ae 339 $question->defaultmark = 0; // Will be increased for each answer norm.
516cf3eb 340
ab50232b 341 for ($positionkey = 1;
aa9d6e43 342 preg_match('/'.ANSWER_REGEX.'/s', $question->questiontext['text'], $answerregs);
ab50232b 343 ++$positionkey) {
0ff4bd08 344 $wrapped = new stdClass();
61dfe97e 345 $wrapped->generalfeedback['text'] = '';
c1f15d35 346 $wrapped->generalfeedback['format'] = FORMAT_HTML;
61dfe97e 347 $wrapped->generalfeedback['itemid'] = '';
48b5b28f 348 if (isset($answerregs[ANSWER_REGEX_NORM]) && $answerregs[ANSWER_REGEX_NORM] !== '') {
ab50232b 349 $wrapped->defaultmark = $answerregs[ANSWER_REGEX_NORM];
8795a5ae 350 } else {
ab50232b 351 $wrapped->defaultmark = '1';
8795a5ae 352 }
516cf3eb 353 if (!empty($answerregs[ANSWER_REGEX_ANSWER_TYPE_NUMERICAL])) {
dfa47f96 354 $wrapped->qtype = 'numerical';
516cf3eb 355 $wrapped->multiplier = array();
356 $wrapped->units = array();
61dfe97e 357 $wrapped->instructions['text'] = '';
c1f15d35 358 $wrapped->instructions['format'] = FORMAT_HTML;
61dfe97e 359 $wrapped->instructions['itemid'] = '';
59a3fcd3 360 } else if (!empty($answerregs[ANSWER_REGEX_ANSWER_TYPE_SHORTANSWER])) {
dfa47f96 361 $wrapped->qtype = 'shortanswer';
516cf3eb 362 $wrapped->usecase = 0;
59a3fcd3 363 } else if (!empty($answerregs[ANSWER_REGEX_ANSWER_TYPE_SHORTANSWER_C])) {
fd97082c 364 $wrapped->qtype = 'shortanswer';
365 $wrapped->usecase = 1;
59a3fcd3 366 } else if (!empty($answerregs[ANSWER_REGEX_ANSWER_TYPE_MULTICHOICE])) {
bd156853
PP
367 qtype_multianswer_initialise_multichoice_subquestion($wrapped);
368 $wrapped->shuffleanswers = 0;
369 $wrapped->layout = qtype_multichoice_base::LAYOUT_DROPDOWN;
370 } else if (!empty($answerregs[ANSWER_REGEX_ANSWER_TYPE_MULTICHOICE_SHUFFLED])) {
371 qtype_multianswer_initialise_multichoice_subquestion($wrapped);
1cacb059 372 $wrapped->shuffleanswers = 1;
7ac7977c 373 $wrapped->layout = qtype_multichoice_base::LAYOUT_DROPDOWN;
59a3fcd3 374 } else if (!empty($answerregs[ANSWER_REGEX_ANSWER_TYPE_MULTICHOICE_REGULAR])) {
bd156853 375 qtype_multianswer_initialise_multichoice_subquestion($wrapped);
1cacb059 376 $wrapped->shuffleanswers = 0;
bd156853
PP
377 $wrapped->layout = qtype_multichoice_base::LAYOUT_VERTICAL;
378 } else if (!empty($answerregs[ANSWER_REGEX_ANSWER_TYPE_MULTICHOICE_REGULAR_SHUFFLED])) {
379 qtype_multianswer_initialise_multichoice_subquestion($wrapped);
380 $wrapped->shuffleanswers = 1;
7ac7977c 381 $wrapped->layout = qtype_multichoice_base::LAYOUT_VERTICAL;
59a3fcd3 382 } else if (!empty($answerregs[ANSWER_REGEX_ANSWER_TYPE_MULTICHOICE_HORIZONTAL])) {
bd156853 383 qtype_multianswer_initialise_multichoice_subquestion($wrapped);
1cacb059 384 $wrapped->shuffleanswers = 0;
bd156853
PP
385 $wrapped->layout = qtype_multichoice_base::LAYOUT_HORIZONTAL;
386 } else if (!empty($answerregs[ANSWER_REGEX_ANSWER_TYPE_MULTICHOICE_HORIZONTAL_SHUFFLED])) {
387 qtype_multianswer_initialise_multichoice_subquestion($wrapped);
388 $wrapped->shuffleanswers = 1;
7ac7977c 389 $wrapped->layout = qtype_multichoice_base::LAYOUT_HORIZONTAL;
516cf3eb 390 } else {
2471ef86 391 print_error('unknownquestiontype', 'question', '', $answerregs[2]);
516cf3eb 392 return false;
393 }
394
395 // Each $wrapped simulates a $form that can be processed by the
396 // respective save_question and save_question_options methods of the
3d9645ae 397 // wrapped questiontypes.
516cf3eb 398 $wrapped->answer = array();
399 $wrapped->fraction = array();
400 $wrapped->feedback = array();
61dfe97e 401 $wrapped->questiontext['text'] = $answerregs[0];
c1f15d35 402 $wrapped->questiontext['format'] = FORMAT_HTML;
59a3fcd3
TH
403 $wrapped->questiontext['itemid'] = '';
404 $answerindex = 0;
516cf3eb 405
406 $remainingalts = $answerregs[ANSWER_REGEX_ALTERNATIVES];
aa9d6e43 407 while (preg_match('/~?'.ANSWER_ALTERNATIVE_REGEX.'/s', $remainingalts, $altregs)) {
516cf3eb 408 if ('=' == $altregs[ANSWER_ALTERNATIVE_REGEX_FRACTION]) {
f4fe3968 409 $wrapped->fraction["{$answerindex}"] = '1';
59a3fcd3 410 } else if ($percentile = $altregs[ANSWER_ALTERNATIVE_REGEX_PERCENTILE_FRACTION]) {
f4fe3968 411 $wrapped->fraction["{$answerindex}"] = .01 * $percentile;
516cf3eb 412 } else {
f4fe3968 413 $wrapped->fraction["{$answerindex}"] = '0';
516cf3eb 414 }
e51efd7e 415 if (isset($altregs[ANSWER_ALTERNATIVE_REGEX_FEEDBACK])) {
ab50232b
TH
416 $feedback = html_entity_decode(
417 $altregs[ANSWER_ALTERNATIVE_REGEX_FEEDBACK], ENT_QUOTES, 'UTF-8');
095b599a 418 $feedback = str_replace('\}', '}', $feedback);
f4fe3968
TH
419 $wrapped->feedback["{$answerindex}"]['text'] = str_replace('\#', '#', $feedback);
420 $wrapped->feedback["{$answerindex}"]['format'] = FORMAT_HTML;
421 $wrapped->feedback["{$answerindex}"]['itemid'] = '';
e51efd7e 422 } else {
f4fe3968
TH
423 $wrapped->feedback["{$answerindex}"]['text'] = '';
424 $wrapped->feedback["{$answerindex}"]['format'] = FORMAT_HTML;
425 $wrapped->feedback["{$answerindex}"]['itemid'] = '';
61dfe97e 426
e51efd7e 427 }
516cf3eb 428 if (!empty($answerregs[ANSWER_REGEX_ANSWER_TYPE_NUMERICAL])
aa9d6e43 429 && preg_match('~'.NUMERICAL_ALTERNATIVE_REGEX.'~s',
ab50232b 430 $altregs[ANSWER_ALTERNATIVE_REGEX_ANSWER], $numregs)) {
e51efd7e 431 $wrapped->answer[] = $numregs[NUMERICAL_CORRECT_ANSWER];
c83ed025 432 if (array_key_exists(NUMERICAL_ABS_ERROR_MARGIN, $numregs)) {
f4fe3968 433 $wrapped->tolerance["{$answerindex}"] =
e51efd7e 434 $numregs[NUMERICAL_ABS_ERROR_MARGIN];
516cf3eb 435 } else {
f4fe3968 436 $wrapped->tolerance["{$answerindex}"] = 0;
516cf3eb 437 }
3d9645ae 438 } else { // Tolerance can stay undefined for non numerical questions.
1f8db780 439 // Undo quoting done by the HTML editor.
ab50232b
TH
440 $answer = html_entity_decode(
441 $altregs[ANSWER_ALTERNATIVE_REGEX_ANSWER], ENT_QUOTES, 'UTF-8');
095b599a 442 $answer = str_replace('\}', '}', $answer);
f4fe3968 443 $wrapped->answer["{$answerindex}"] = str_replace('\#', '#', $answer);
c1f15d35 444 if ($wrapped->qtype == 'multichoice') {
f4fe3968
TH
445 $wrapped->answer["{$answerindex}"] = array(
446 'text' => $wrapped->answer["{$answerindex}"],
c1f15d35
TH
447 'format' => FORMAT_HTML,
448 'itemid' => '');
449 }
516cf3eb 450 }
451 $tmp = explode($altregs[0], $remainingalts, 2);
452 $remainingalts = $tmp[1];
59a3fcd3 453 $answerindex++;
516cf3eb 454 }
455
ab50232b 456 $question->defaultmark += $wrapped->defaultmark;
516cf3eb 457 $question->options->questions[$positionkey] = clone($wrapped);
61dfe97e
PP
458 $question->questiontext['text'] = implode("{#$positionkey}",
459 explode($answerregs[0], $question->questiontext['text'], 2));
516cf3eb 460 }
516cf3eb 461 return $question;
462}