Commit | Line | Data |
---|---|---|
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 | ||
271ffe3f | 17 | /** |
d3603157 | 18 | * Defines the editing form for the multi-answer question type. |
271ffe3f | 19 | * |
b04a4319 TH |
20 | * @package qtype |
21 | * @subpackage multianswer | |
22 | * @copyright 2007 Jamie Pratt me@jamiep.org | |
23 | * @license http://www.gnu.org/copyleft/gpl.html GNU Public License | |
271ffe3f | 24 | */ |
25 | ||
d3603157 | 26 | |
a17b297d TH |
27 | defined('MOODLE_INTERNAL') || die(); |
28 | ||
1649a4f7 | 29 | require_once($CFG->dirroot . '/question/type/numerical/questiontype.php'); |
30 | ||
a17b297d | 31 | |
271ffe3f | 32 | /** |
d3603157 | 33 | * Form for editing multi-answer questions. |
b04a4319 TH |
34 | * |
35 | * @copyright 2007 Jamie Pratt me@jamiep.org | |
36 | * @license http://www.gnu.org/copyleft/gpl.html GNU Public License | |
271ffe3f | 37 | */ |
ab50232b | 38 | class qtype_multianswer_edit_form extends question_edit_form { |
271ffe3f | 39 | |
1649a4f7 | 40 | // The variable $questiondisplay will contain the qtype_multianswer_extract_question from |
41 | // the questiontext. | |
59a3fcd3 | 42 | public $questiondisplay; |
1649a4f7 | 43 | // The variable $savedquestiondisplay will contain the qtype_multianswer_extract_question |
44 | // from the questiontext in database. | |
59a3fcd3 TH |
45 | public $savedquestion; |
46 | public $savedquestiondisplay; | |
47 | public $used_in_quiz = false; | |
48 | public $qtype_change = false; | |
49 | public $negative_diff = 0; | |
fe6ce234 DC |
50 | public $nb_of_quiz = 0; |
51 | public $nb_of_attempts = 0; | |
59a3fcd3 TH |
52 | public $confirm = 0; |
53 | public $reload = false; | |
1649a4f7 | 54 | /** @var qtype_numerical_answer_processor used when validating numerical answers. */ |
55 | protected $ap = null; | |
56 | ||
aeb15530 | 57 | |
59a3fcd3 TH |
58 | public function __construct($submiturl, $question, $category, $contexts, $formeditable = true) { |
59 | global $SESSION, $CFG, $DB; | |
347fb175 | 60 | $this->regenerate = true; |
7d087744 | 61 | $this->reload = optional_param('reload', false, PARAM_BOOL); |
59a3fcd3 TH |
62 | |
63 | $this->used_in_quiz = false; | |
64 | ||
65 | if (isset($question->id) && $question->id != 0) { | |
e5c5f52e | 66 | // TODO MDL-43779 should not have quiz-specific code here. |
59a3fcd3 | 67 | $this->savedquestiondisplay = fullclone($question); |
dc4a3ea1 TH |
68 | $this->nb_of_quiz = $DB->count_records('quiz_question_instances', array('questionid' => $question->id)); |
69 | $this->used_in_quiz = $this->nb_of_quiz > 0; | |
70 | $this->nb_of_attempts = $DB->count_records_sql(" | |
71 | SELECT count(1) | |
72 | FROM {quiz_question_instances} qqi | |
73 | JOIN {quiz_attempts} quiza ON quiza.quiz = qqi.quizid | |
74 | WHERE qqi.questionid = ? | |
75 | AND quiza.preview = 0", array($question->id)); | |
d3e3bd6c | 76 | } |
77 | ||
59a3fcd3 | 78 | parent::__construct($submiturl, $question, $category, $contexts, $formeditable); |
d3e3bd6c | 79 | } |
80 | ||
c7df5006 | 81 | protected function definition_inner($mform) { |
347fb175 PP |
82 | $mform->addElement('hidden', 'reload', 1); |
83 | $mform->setType('reload', PARAM_INT); | |
705b5874 | 84 | |
ab50232b TH |
85 | // Remove meaningless defaultmark field. |
86 | $mform->removeElement('defaultmark'); | |
7d087744 | 87 | $this->confirm = optional_param('confirm', false, PARAM_BOOL); |
f34488b2 | 88 | |
1649a4f7 | 89 | // Display the questions from questiontext. |
b8cc71e7 PS |
90 | if ($questiontext = optional_param_array('questiontext', false, PARAM_RAW)) { |
91 | $this->questiondisplay = fullclone(qtype_multianswer_extract_question($questiontext)); | |
f34488b2 | 92 | |
59a3fcd3 TH |
93 | } else { |
94 | if (!$this->reload && !empty($this->savedquestiondisplay->id)) { | |
1649a4f7 | 95 | // Use database data as this is first pass |
96 | // question->id == 0 so no stored datasets. | |
d3e3bd6c | 97 | $this->questiondisplay = fullclone($this->savedquestiondisplay); |
59a3fcd3 TH |
98 | foreach ($this->questiondisplay->options->questions as $subquestion) { |
99 | if (!empty($subquestion)) { | |
fe6ce234 | 100 | $subquestion->answer = array(''); |
59a3fcd3 TH |
101 | foreach ($subquestion->options->answers as $ans) { |
102 | $subquestion->answer[] = $ans->answer; | |
fe6ce234 | 103 | } |
d3e3bd6c | 104 | } |
aeb15530 | 105 | } |
59a3fcd3 | 106 | } else { |
d3e3bd6c | 107 | $this->questiondisplay = ""; |
108 | } | |
f34488b2 | 109 | } |
705b5874 | 110 | |
59a3fcd3 TH |
111 | if (isset($this->savedquestiondisplay->options->questions) && |
112 | is_array($this->savedquestiondisplay->options->questions)) { | |
113 | $countsavedsubquestions = 0; | |
114 | foreach ($this->savedquestiondisplay->options->questions as $subquestion) { | |
115 | if (!empty($subquestion)) { | |
fe6ce234 | 116 | $countsavedsubquestions++; |
df79079f | 117 | } |
f34488b2 | 118 | } |
705b5874 | 119 | } else { |
59a3fcd3 | 120 | $countsavedsubquestions = 0; |
705b5874 | 121 | } |
59a3fcd3 TH |
122 | if ($this->reload) { |
123 | if (isset($this->questiondisplay->options->questions) && | |
124 | is_array($this->questiondisplay->options->questions)) { | |
125 | $countsubquestions = 0; | |
126 | foreach ($this->questiondisplay->options->questions as $subquestion) { | |
127 | if (!empty($subquestion)) { | |
fe6ce234 | 128 | $countsubquestions++; |
d3e3bd6c | 129 | } |
130 | } | |
fe6ce234 | 131 | } else { |
59a3fcd3 | 132 | $countsubquestions = 0; |
705b5874 | 133 | } |
59a3fcd3 TH |
134 | } else { |
135 | $countsubquestions = $countsavedsubquestions; | |
fe6ce234 | 136 | } |
aeb15530 | 137 | |
59a3fcd3 TH |
138 | $mform->addElement('submit', 'analyzequestion', |
139 | get_string('decodeverifyquestiontext', 'qtype_multianswer')); | |
fe6ce234 | 140 | $mform->registerNoSubmitButton('analyzequestion'); |
59a3fcd3 | 141 | if ($this->reload) { |
59a3fcd3 | 142 | for ($sub = 1; $sub <= $countsubquestions; $sub++) { |
aeb15530 | 143 | |
59a3fcd3 | 144 | if (isset($this->questiondisplay->options->questions[$sub]->qtype)) { |
ab50232b | 145 | $this->editas[$sub] = $this->questiondisplay->options->questions[$sub]->qtype; |
7d087744 TH |
146 | } else { |
147 | $this->editas[$sub] = optional_param('sub_'.$sub.'_qtype', 'unknown type', PARAM_PLUGIN); | |
fd97082c | 148 | } |
928e7d2a | 149 | |
d3e3bd6c | 150 | $storemess = ''; |
59a3fcd3 TH |
151 | if (isset($this->savedquestiondisplay->options->questions[$sub]->qtype) && |
152 | $this->savedquestiondisplay->options->questions[$sub]->qtype != | |
153 | $this->questiondisplay->options->questions[$sub]->qtype) { | |
154 | $this->qtype_change = true; | |
635971c7 TH |
155 | $storemess = ' ' . html_writer::tag('span', get_string( |
156 | 'storedqtype', 'qtype_multianswer', question_bank::get_qtype_name( | |
157 | $this->savedquestiondisplay->options->questions[$sub]->qtype)), | |
158 | array('class' => 'error')); | |
59a3fcd3 | 159 | } |
aeb15530 | 160 | |
5e8a85aa | 161 | $mform->addElement('header', 'subhdr'.$sub, get_string('questionno', 'question', |
59a3fcd3 TH |
162 | '{#'.$sub.'}').' '.question_bank::get_qtype_name( |
163 | $this->questiondisplay->options->questions[$sub]->qtype).$storemess); | |
aeb15530 | 164 | |
7d087744 | 165 | $mform->addElement('static', 'sub_'.$sub.'_questiontext', |
928e7d2a | 166 | get_string('questiondefinition', 'qtype_multianswer')); |
aeb15530 | 167 | |
59a3fcd3 | 168 | if (isset ($this->questiondisplay->options->questions[$sub]->questiontext)) { |
7d087744 | 169 | $mform->setDefault('sub_'.$sub.'_questiontext', |
59a3fcd3 | 170 | $this->questiondisplay->options->questions[$sub]->questiontext['text']); |
f34488b2 | 171 | } |
aeb15530 | 172 | |
7d087744 | 173 | $mform->addElement('static', 'sub_'.$sub.'_defaultmark', |
ab50232b | 174 | get_string('defaultmark', 'question')); |
7d087744 | 175 | $mform->setDefault('sub_'.$sub.'_defaultmark', |
ab50232b | 176 | $this->questiondisplay->options->questions[$sub]->defaultmark); |
aeb15530 | 177 | |
59a3fcd3 | 178 | if ($this->questiondisplay->options->questions[$sub]->qtype == 'shortanswer') { |
7d087744 | 179 | $mform->addElement('static', 'sub_'.$sub.'_usecase', |
ab50232b | 180 | get_string('casesensitive', 'qtype_shortanswer')); |
fe6ce234 | 181 | } |
aeb15530 | 182 | |
59a3fcd3 | 183 | if ($this->questiondisplay->options->questions[$sub]->qtype == 'multichoice') { |
7d087744 | 184 | $mform->addElement('static', 'sub_'.$sub.'_layout', |
928e7d2a | 185 | get_string('layout', 'qtype_multianswer')); |
fe6ce234 | 186 | } |
aeb15530 | 187 | |
928e7d2a | 188 | foreach ($this->questiondisplay->options->questions[$sub]->answer as $key => $ans) { |
7d087744 | 189 | $mform->addElement('static', 'sub_'.$sub.'_answer['.$key.']', |
928e7d2a | 190 | get_string('answer', 'question')); |
aeb15530 | 191 | |
59a3fcd3 TH |
192 | if ($this->questiondisplay->options->questions[$sub]->qtype == 'numerical' && |
193 | $key == 0) { | |
7d087744 | 194 | $mform->addElement('static', 'sub_'.$sub.'_tolerance['.$key.']', |
ab50232b | 195 | get_string('acceptederror', 'qtype_numerical')); |
d3e3bd6c | 196 | } |
aeb15530 | 197 | |
7d087744 | 198 | $mform->addElement('static', 'sub_'.$sub.'_fraction['.$key.']', |
59a3fcd3 | 199 | get_string('grade')); |
aeb15530 | 200 | |
7d087744 | 201 | $mform->addElement('static', 'sub_'.$sub.'_feedback['.$key.']', |
59a3fcd3 | 202 | get_string('feedback', 'question')); |
d3e3bd6c | 203 | } |
f34488b2 | 204 | } |
928e7d2a | 205 | |
59a3fcd3 TH |
206 | $this->negative_diff = $countsavedsubquestions - $countsubquestions; |
207 | if (($this->negative_diff > 0) ||$this->qtype_change || | |
208 | ($this->used_in_quiz && $this->negative_diff != 0)) { | |
209 | $mform->addElement('header', 'additemhdr', | |
210 | get_string('warningquestionmodified', 'qtype_multianswer')); | |
fe6ce234 | 211 | } |
59a3fcd3 TH |
212 | if ($this->negative_diff > 0) { |
213 | $mform->addElement('static', 'alert1', "<strong>". | |
214 | get_string('questiondeleted', 'qtype_multianswer')."</strong>", | |
215 | get_string('questionsless', 'qtype_multianswer', $this->negative_diff)); | |
d3e3bd6c | 216 | } |
59a3fcd3 TH |
217 | if ($this->qtype_change) { |
218 | $mform->addElement('static', 'alert1', "<strong>". | |
219 | get_string('questiontypechanged', 'qtype_multianswer')."</strong>", | |
220 | get_string('questiontypechangedcomment', 'qtype_multianswer')); | |
d3e3bd6c | 221 | } |
d3e3bd6c | 222 | } |
59a3fcd3 TH |
223 | if ($this->used_in_quiz) { |
224 | if ($this->negative_diff < 0) { | |
c4979f02 | 225 | $diff = $countsubquestions - $countsavedsubquestions; |
59a3fcd3 TH |
226 | $mform->addElement('static', 'alert1', "<strong>". |
227 | get_string('questionsadded', 'qtype_multianswer')."</strong>", | |
228 | "<strong>".get_string('questionsmore', 'qtype_multianswer', $diff). | |
229 | "</strong>"); | |
c4979f02 | 230 | } |
59a3fcd3 | 231 | $a = new stdClass(); |
c4979f02 PP |
232 | $a->nb_of_quiz = $this->nb_of_quiz; |
233 | $a->nb_of_attempts = $this->nb_of_attempts; | |
59a3fcd3 TH |
234 | $mform->addElement('header', 'additemhdr2', |
235 | get_string('questionusedinquiz', 'qtype_multianswer', $a)); | |
236 | $mform->addElement('static', 'alertas', | |
237 | get_string('youshouldnot', 'qtype_multianswer')); | |
d3e3bd6c | 238 | } |
59a3fcd3 TH |
239 | if (($this->negative_diff > 0 || $this->used_in_quiz && |
240 | ($this->negative_diff > 0 || $this->negative_diff < 0 || $this->qtype_change)) && | |
241 | $this->reload) { | |
242 | $mform->addElement('header', 'additemhdr', | |
243 | get_string('questionsaveasedited', 'qtype_multianswer')); | |
244 | $mform->addElement('checkbox', 'confirm', '', | |
245 | get_string('confirmquestionsaveasedited', 'qtype_multianswer')); | |
d3e3bd6c | 246 | $mform->setDefault('confirm', 0); |
59a3fcd3 TH |
247 | } else { |
248 | $mform->addElement('hidden', 'confirm', 0); | |
e380d57f | 249 | $mform->setType('confirm', PARAM_BOOL); |
705b5874 | 250 | } |
347fb175 | 251 | |
e9af6091 | 252 | $this->add_interactive_settings(true, true); |
120e5cbf | 253 | } |
705b5874 | 254 | |
f34488b2 | 255 | |
59a3fcd3 | 256 | public function set_data($question) { |
f34488b2 | 257 | global $DB; |
59a3fcd3 TH |
258 | $default_values = array(); |
259 | if (isset($question->id) and $question->id and $question->qtype && | |
260 | $question->questiontext) { | |
c6fc9988 | 261 | |
262 | foreach ($question->options->questions as $key => $wrapped) { | |
59a3fcd3 | 263 | if (!empty($wrapped)) { |
fe6ce234 | 264 | // The old way of restoring the definitions is kept to gradually |
1649a4f7 | 265 | // update all multianswer questions. |
fe6ce234 | 266 | if (empty($wrapped->questiontext)) { |
ab50232b | 267 | $parsableanswerdef = '{' . $wrapped->defaultmark . ':'; |
fe6ce234 | 268 | switch ($wrapped->qtype) { |
59a3fcd3 TH |
269 | case 'multichoice': |
270 | $parsableanswerdef .= 'MULTICHOICE:'; | |
271 | break; | |
272 | case 'shortanswer': | |
273 | $parsableanswerdef .= 'SHORTANSWER:'; | |
274 | break; | |
275 | case 'numerical': | |
276 | $parsableanswerdef .= 'NUMERICAL:'; | |
277 | break; | |
278 | default: | |
279 | print_error('unknownquestiontype', 'question', '', | |
280 | $wrapped->qtype); | |
c6fc9988 | 281 | } |
59a3fcd3 | 282 | $separator = ''; |
fe6ce234 DC |
283 | foreach ($wrapped->options->answers as $subanswer) { |
284 | $parsableanswerdef .= $separator | |
285 | . '%' . round(100*$subanswer->fraction) . '%'; | |
c1f15d35 TH |
286 | if (is_array($subanswer->answer)) { |
287 | $parsableanswerdef .= $subanswer->answer['text']; | |
288 | } else { | |
289 | $parsableanswerdef .= $subanswer->answer; | |
290 | } | |
fe6ce234 | 291 | if (!empty($wrapped->options->tolerance)) { |
1649a4f7 | 292 | // Special for numerical answers. |
fe6ce234 DC |
293 | $parsableanswerdef .= ":{$wrapped->options->tolerance}"; |
294 | // We only want tolerance for the first alternative, it will | |
295 | // be applied to all of the alternatives. | |
296 | unset($wrapped->options->tolerance); | |
297 | } | |
298 | if ($subanswer->feedback) { | |
299 | $parsableanswerdef .= "#$subanswer->feedback"; | |
300 | } | |
301 | $separator = '~'; | |
c6fc9988 | 302 | } |
fe6ce234 | 303 | $parsableanswerdef .= '}'; |
1649a4f7 | 304 | // Fix the questiontext fields of old questions. |
59a3fcd3 TH |
305 | $DB->set_field('question', 'questiontext', $parsableanswerdef, |
306 | array('id' => $wrapped->id)); | |
fe6ce234 DC |
307 | } else { |
308 | $parsableanswerdef = str_replace('&#', '&\#', $wrapped->questiontext); | |
c6fc9988 | 309 | } |
59a3fcd3 TH |
310 | $question->questiontext = str_replace("{#$key}", $parsableanswerdef, |
311 | $question->questiontext); | |
c6fc9988 | 312 | } |
c6fc9988 | 313 | } |
314 | } | |
347fb175 | 315 | |
1649a4f7 | 316 | // Set default to $questiondisplay questions elements. |
59a3fcd3 | 317 | if ($this->reload) { |
fe6ce234 | 318 | if (isset($this->questiondisplay->options->questions)) { |
59a3fcd3 | 319 | $subquestions = fullclone($this->questiondisplay->options->questions); |
fe6ce234 | 320 | if (count($subquestions)) { |
59a3fcd3 | 321 | $sub = 1; |
fe6ce234 | 322 | foreach ($subquestions as $subquestion) { |
59a3fcd3 | 323 | $prefix = 'sub_'.$sub.'_'; |
fe6ce234 | 324 | |
1649a4f7 | 325 | // Validate parameters. |
fe6ce234 DC |
326 | $answercount = 0; |
327 | $maxgrade = false; | |
328 | $maxfraction = -1; | |
59a3fcd3 | 329 | if ($subquestion->qtype == 'shortanswer') { |
fe6ce234 | 330 | switch ($subquestion->usecase) { |
59a3fcd3 TH |
331 | case '1': |
332 | $default_values[$prefix.'usecase'] = | |
333 | get_string('caseyes', 'qtype_shortanswer'); | |
334 | break; | |
335 | case '0': | |
336 | default : | |
337 | $default_values[$prefix.'usecase'] = | |
338 | get_string('caseno', 'qtype_shortanswer'); | |
fe6ce234 | 339 | } |
fd97082c | 340 | } |
fd97082c | 341 | |
59a3fcd3 TH |
342 | if ($subquestion->qtype == 'multichoice') { |
343 | $default_values[$prefix.'layout'] = $subquestion->layout; | |
fe6ce234 | 344 | switch ($subquestion->layout) { |
59a3fcd3 TH |
345 | case '0': |
346 | $default_values[$prefix.'layout'] = | |
347 | get_string('layoutselectinline', 'qtype_multianswer'); | |
348 | break; | |
349 | case '1': | |
350 | $default_values[$prefix.'layout'] = | |
351 | get_string('layoutvertical', 'qtype_multianswer'); | |
352 | break; | |
353 | case '2': | |
354 | $default_values[$prefix.'layout'] = | |
355 | get_string('layouthorizontal', 'qtype_multianswer'); | |
356 | break; | |
357 | default: | |
358 | $default_values[$prefix.'layout'] = | |
359 | get_string('layoutundefined', 'qtype_multianswer'); | |
fe6ce234 | 360 | } |
aeb15530 | 361 | } |
59a3fcd3 TH |
362 | foreach ($subquestion->answer as $key => $answer) { |
363 | if ($subquestion->qtype == 'numerical' && $key == 0) { | |
e64e28d7 | 364 | $default_values[$prefix.'tolerance['.$key.']'] = |
59a3fcd3 | 365 | $subquestion->tolerance[0]; |
705b5874 | 366 | } |
c1f15d35 TH |
367 | if (is_array($answer)) { |
368 | $answer = $answer['text']; | |
369 | } | |
fe6ce234 DC |
370 | $trimmedanswer = trim($answer); |
371 | if ($trimmedanswer !== '') { | |
372 | $answercount++; | |
59a3fcd3 | 373 | if ($subquestion->qtype == 'numerical' && |
1649a4f7 | 374 | !($this->is_valid_number($trimmedanswer) || $trimmedanswer == '*')) { |
59a3fcd3 TH |
375 | $this->_form->setElementError($prefix.'answer['.$key.']', |
376 | get_string('answermustbenumberorstar', | |
377 | 'qtype_numerical')); | |
fe6ce234 DC |
378 | } |
379 | if ($subquestion->fraction[$key] == 1) { | |
380 | $maxgrade = true; | |
381 | } | |
382 | if ($subquestion->fraction[$key] > $maxfraction) { | |
59a3fcd3 | 383 | $maxfraction = $subquestion->fraction[$key]; |
fe6ce234 | 384 | } |
f34488b2 | 385 | } |
fe6ce234 | 386 | |
59a3fcd3 TH |
387 | $default_values[$prefix.'answer['.$key.']'] = |
388 | htmlspecialchars($answer); | |
fe6ce234 DC |
389 | } |
390 | if ($answercount == 0) { | |
59a3fcd3 TH |
391 | if ($subquestion->qtype == 'multichoice') { |
392 | $this->_form->setElementError($prefix.'answer[0]', | |
393 | get_string('notenoughanswers', 'qtype_multichoice', 2)); | |
fe6ce234 | 394 | } else { |
59a3fcd3 TH |
395 | $this->_form->setElementError($prefix.'answer[0]', |
396 | get_string('notenoughanswers', 'question', 1)); | |
705b5874 | 397 | } |
398 | } | |
fe6ce234 | 399 | if ($maxgrade == false) { |
59a3fcd3 TH |
400 | $this->_form->setElementError($prefix.'fraction[0]', |
401 | get_string('fractionsnomax', 'question')); | |
705b5874 | 402 | } |
59a3fcd3 | 403 | foreach ($subquestion->feedback as $key => $answer) { |
f34488b2 | 404 | |
59a3fcd3 TH |
405 | $default_values[$prefix.'feedback['.$key.']'] = |
406 | htmlspecialchars ($answer['text']); | |
fe6ce234 | 407 | } |
59a3fcd3 TH |
408 | foreach ($subquestion->fraction as $key => $answer) { |
409 | $default_values[$prefix.'fraction['.$key.']'] = $answer; | |
fe6ce234 | 410 | } |
f34488b2 | 411 | |
fe6ce234 DC |
412 | $sub++; |
413 | } | |
705b5874 | 414 | } |
415 | } | |
416 | } | |
59a3fcd3 TH |
417 | $default_values['alertas']= "<strong>".get_string('questioninquiz', 'qtype_multianswer'). |
418 | "</strong>"; | |
347fb175 | 419 | |
59a3fcd3 | 420 | if ($default_values != "") { |
705b5874 | 421 | $question = (object)((array)$question + $default_values); |
422 | } | |
e9af6091 | 423 | $question = $this->data_preprocessing_hints($question, true, true); |
120e5cbf | 424 | parent::set_data($question); |
425 | } | |
426 | ||
1649a4f7 | 427 | /** |
428 | * Validate that a string is a nubmer formatted correctly for the current locale. | |
429 | * @param string $x a string | |
430 | * @return bool whether $x is a number that the numerical question type can interpret. | |
431 | */ | |
432 | protected function is_valid_number($x) { | |
433 | if (is_null($this->ap)) { | |
434 | $this->ap = new qtype_numerical_answer_processor(array()); | |
435 | } | |
436 | ||
437 | list($value, $unit) = $this->ap->apply_units($x); | |
438 | ||
439 | return !is_null($value) && !$unit; | |
440 | } | |
441 | ||
442 | ||
59a3fcd3 | 443 | public function validation($data, $files) { |
a78890d5 | 444 | $errors = parent::validation($data, $files); |
8e8f9f3d | 445 | |
0bc0a265 | 446 | $questiondisplay = qtype_multianswer_extract_question($data['questiontext']); |
f34488b2 | 447 | |
347fb175 | 448 | if (isset($questiondisplay->options->questions)) { |
59a3fcd3 | 449 | $subquestions = fullclone($questiondisplay->options->questions); |
705b5874 | 450 | if (count($subquestions)) { |
59a3fcd3 | 451 | $sub = 1; |
f34488b2 | 452 | foreach ($subquestions as $subquestion) { |
59a3fcd3 | 453 | $prefix = 'sub_'.$sub.'_'; |
705b5874 | 454 | $answercount = 0; |
455 | $maxgrade = false; | |
456 | $maxfraction = -1; | |
59a3fcd3 TH |
457 | if (isset($this->savedquestiondisplay->options->questions[$sub]->qtype) && |
458 | $this->savedquestiondisplay->options->questions[$sub]->qtype != | |
459 | $questiondisplay->options->questions[$sub]->qtype) { | |
460 | $storemess = " STORED QTYPE ".question_bank::get_qtype_name( | |
461 | $this->savedquestiondisplay->options->questions[$sub]->qtype); | |
462 | } | |
463 | foreach ($subquestion->answer as $key => $answer) { | |
c1f15d35 TH |
464 | if (is_array($answer)) { |
465 | $answer = $answer['text']; | |
466 | } | |
705b5874 | 467 | $trimmedanswer = trim($answer); |
468 | if ($trimmedanswer !== '') { | |
469 | $answercount++; | |
e64e28d7 DP |
470 | if ($subquestion->qtype == 'numerical' && |
471 | !($this->is_valid_number($trimmedanswer) || $trimmedanswer == '*')) { | |
472 | $errors[$prefix.'answer['.$key.']'] = | |
59a3fcd3 | 473 | get_string('answermustbenumberorstar', 'qtype_numerical'); |
fe6ce234 | 474 | } |
705b5874 | 475 | if ($subquestion->fraction[$key] == 1) { |
476 | $maxgrade = true; | |
f34488b2 | 477 | } |
705b5874 | 478 | if ($subquestion->fraction[$key] > $maxfraction) { |
59a3fcd3 | 479 | $maxfraction = $subquestion->fraction[$key]; |
705b5874 | 480 | } |
f34488b2 | 481 | } |
482 | } | |
59a3fcd3 TH |
483 | if ($answercount == 0) { |
484 | if ($subquestion->qtype == 'multichoice') { | |
485 | $errors[$prefix.'answer[0]'] = | |
486 | get_string('notenoughanswers', 'qtype_multichoice', 2); | |
487 | } else { | |
488 | $errors[$prefix.'answer[0]'] = | |
489 | get_string('notenoughanswers', 'question', 1); | |
705b5874 | 490 | } |
491 | } | |
492 | if ($maxgrade == false) { | |
59a3fcd3 TH |
493 | $errors[$prefix.'fraction[0]'] = |
494 | get_string('fractionsnomax', 'question'); | |
f34488b2 | 495 | } |
496 | $sub++; | |
705b5874 | 497 | } |
498 | } else { | |
59a3fcd3 | 499 | $errors['questiontext'] = get_string('questionsmissing', 'qtype_multianswer'); |
705b5874 | 500 | } |
120e5cbf | 501 | } |
705b5874 | 502 | |
59a3fcd3 TH |
503 | if (($this->negative_diff > 0 || $this->used_in_quiz && |
504 | ($this->negative_diff > 0 || $this->negative_diff < 0 || | |
7d087744 | 505 | $this->qtype_change)) && !$this->confirm) { |
59a3fcd3 TH |
506 | $errors['confirm'] = |
507 | get_string('confirmsave', 'qtype_multianswer', $this->negative_diff); | |
508 | } | |
fe6ce234 DC |
509 | |
510 | return $errors; | |
c6fc9988 | 511 | } |
271ffe3f | 512 | |
59a3fcd3 | 513 | public function qtype() { |
271ffe3f | 514 | return 'multianswer'; |
515 | } | |
516 | } |