See Release Notes
Long Term Support Release
Differences Between: [Versions 310 and 401] [Versions 39 and 401]
1 <?php 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 * Defines the editing form for the numerical question type. 19 * 20 * @package qtype 21 * @subpackage numerical 22 * @copyright 2007 Jamie Pratt me@jamiep.org 23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 */ 25 26 27 defined('MOODLE_INTERNAL') || die(); 28 29 require_once($CFG->dirroot . '/question/type/edit_question_form.php'); 30 require_once($CFG->dirroot . '/question/type/numerical/questiontype.php'); 31 32 33 /** 34 * numerical editing form definition. 35 * 36 * @copyright 2007 Jamie Pratt me@jamiep.org 37 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 38 */ 39 class qtype_numerical_edit_form extends question_edit_form { 40 /** @var int we always show at least this many sets of unit fields. */ 41 const UNITS_MIN_REPEATS = 1; 42 const UNITS_TO_ADD = 2; 43 44 protected $ap = null; 45 46 protected function definition_inner($mform) { 47 $this->add_per_answer_fields($mform, get_string('answerno', 'qtype_numerical', '{no}'), 48 question_bank::fraction_options()); 49 50 $this->add_unit_options($mform); 51 $this->add_unit_fields($mform); 52 $this->add_interactive_settings(); 53 } 54 55 protected function get_per_answer_fields($mform, $label, $gradeoptions, 56 &$repeatedoptions, &$answersoption) { 57 $repeated = parent::get_per_answer_fields($mform, $label, $gradeoptions, 58 $repeatedoptions, $answersoption); 59 60 $tolerance = $mform->createElement('float', 'tolerance', 61 get_string('answererror', 'qtype_numerical'), array('size' => 15)); 62 $repeatedoptions['tolerance']['default'] = 0; 63 $elements = $repeated[0]->getElements(); 64 $elements[0]->setSize(15); 65 array_splice($elements, 1, 0, array($tolerance)); 66 $repeated[0]->setElements($elements); 67 68 return $repeated; 69 } 70 71 protected function get_more_choices_string() { 72 return get_string('addmoreanswerblanks', 'qtype_numerical'); 73 } 74 75 /** 76 * Add the unit handling options to the form. 77 * @param object $mform the form being built. 78 */ 79 protected function add_unit_options($mform) { 80 81 $mform->addElement('header', 'unithandling', 82 get_string('unithandling', 'qtype_numerical')); 83 84 $unitoptions = array( 85 qtype_numerical::UNITNONE => get_string('onlynumerical', 'qtype_numerical'), 86 qtype_numerical::UNITOPTIONAL => get_string('manynumerical', 'qtype_numerical'), 87 qtype_numerical::UNITGRADED => get_string('unitgraded', 'qtype_numerical'), 88 ); 89 $mform->addElement('select', 'unitrole', 90 get_string('unithandling', 'qtype_numerical'), $unitoptions); 91 $mform->setDefault('unitrole', $this->get_default_value('unitrole', qtype_numerical::UNITNONE)); 92 93 $penaltygrp = array(); 94 $penaltygrp[] = $mform->createElement('float', 'unitpenalty', 95 get_string('unitpenalty', 'qtype_numerical'), array('size' => 6)); 96 $mform->setDefault('unitpenalty', $this->get_default_value('unitpenalty', 0.1000000)); 97 98 $unitgradingtypes = array( 99 qtype_numerical::UNITGRADEDOUTOFMARK => 100 get_string('decfractionofresponsegrade', 'qtype_numerical'), 101 qtype_numerical::UNITGRADEDOUTOFMAX => 102 get_string('decfractionofquestiongrade', 'qtype_numerical'), 103 ); 104 $penaltygrp[] = $mform->createElement('select', 'unitgradingtypes', '', $unitgradingtypes); 105 $mform->setDefault('unitgradingtypes', 106 $this->get_default_value('unitgradingtypes', qtype_numerical::UNITGRADEDOUTOFMARK)); 107 108 $mform->addGroup($penaltygrp, 'penaltygrp', 109 get_string('unitpenalty', 'qtype_numerical'), ' ', false); 110 $mform->addHelpButton('penaltygrp', 'unitpenalty', 'qtype_numerical'); 111 112 $unitinputoptions = array( 113 qtype_numerical::UNITINPUT => get_string('editableunittext', 'qtype_numerical'), 114 qtype_numerical::UNITRADIO => get_string('unitchoice', 'qtype_numerical'), 115 qtype_numerical::UNITSELECT => get_string('unitselect', 'qtype_numerical'), 116 ); 117 $mform->addElement('select', 'multichoicedisplay', 118 get_string('studentunitanswer', 'qtype_numerical'), $unitinputoptions); 119 $mform->setDefault('multichoicedisplay', 120 $this->get_default_value('multichoicedisplay', qtype_numerical::UNITINPUT)); 121 122 $unitsleftoptions = array( 123 0 => get_string('rightexample', 'qtype_numerical'), 124 1 => get_string('leftexample', 'qtype_numerical') 125 ); 126 $mform->addElement('select', 'unitsleft', 127 get_string('unitposition', 'qtype_numerical'), $unitsleftoptions); 128 $mform->setDefault('unitsleft', $this->get_default_value('unitsleft', 0)); 129 130 $mform->disabledIf('penaltygrp', 'unitrole', 'eq', qtype_numerical::UNITNONE); 131 $mform->disabledIf('penaltygrp', 'unitrole', 'eq', qtype_numerical::UNITOPTIONAL); 132 133 $mform->disabledIf('unitsleft', 'unitrole', 'eq', qtype_numerical::UNITNONE); 134 135 $mform->disabledIf('multichoicedisplay', 'unitrole', 'eq', qtype_numerical::UNITNONE); 136 $mform->disabledIf('multichoicedisplay', 'unitrole', 'eq', qtype_numerical::UNITOPTIONAL); 137 } 138 139 /** 140 * Add the input areas for each unit. 141 * @param object $mform the form being built. 142 */ 143 protected function add_unit_fields($mform) { 144 $mform->addElement('header', 'unithdr', 145 get_string('units', 'qtype_numerical'), ''); 146 147 $unitfields = array($mform->createElement('group', 'units', 148 get_string('unitx', 'qtype_numerical'), $this->unit_group($mform), null, false)); 149 150 $repeatedoptions['unit']['disabledif'] = array('unitrole', 'eq', qtype_numerical::UNITNONE); 151 $repeatedoptions['unit']['type'] = PARAM_NOTAGS; 152 $repeatedoptions['multiplier']['disabledif'] = array('unitrole', 'eq', qtype_numerical::UNITNONE); 153 154 $mform->disabledIf('addunits', 'unitrole', 'eq', qtype_numerical::UNITNONE); 155 156 if (isset($this->question->options->units)) { 157 $repeatsatstart = max(count($this->question->options->units), self::UNITS_MIN_REPEATS); 158 } else { 159 $repeatsatstart = self::UNITS_MIN_REPEATS; 160 } 161 162 $this->repeat_elements($unitfields, $repeatsatstart, $repeatedoptions, 'nounits', 163 'addunits', self::UNITS_TO_ADD, get_string('addmoreunitblanks', 'qtype_numerical', '{no}'), true); 164 165 // The following strange-looking if statement is to do with when the 166 // form is used to move questions between categories. See MDL-15159. 167 if ($mform->elementExists('units[0]')) { 168 $firstunit = $mform->getElement('units[0]'); 169 $elements = $firstunit->getElements(); 170 foreach ($elements as $element) { 171 if ($element->getName() != 'multiplier[0]') { 172 continue; 173 } 174 $element->freeze(); 175 $element->setValue('1.0'); 176 $element->setPersistantFreeze(true); 177 } 178 $mform->addHelpButton('units[0]', 'numericalmultiplier', 'qtype_numerical'); 179 } 180 } 181 182 /** 183 * Get the form fields needed to edit one unit. 184 * @param MoodleQuickForm $mform the form being built. 185 * @return array of form fields. 186 */ 187 protected function unit_group($mform) { 188 $grouparray = array(); 189 $grouparray[] = $mform->createElement('text', 'unit', get_string('unit', 'qtype_numerical'), array('size'=>10)); 190 $grouparray[] = $mform->createElement('float', 'multiplier', 191 get_string('multiplier', 'qtype_numerical'), array('size'=>10)); 192 193 return $grouparray; 194 } 195 196 protected function data_preprocessing($question) { 197 $question = parent::data_preprocessing($question); 198 $question = $this->data_preprocessing_answers($question); 199 $question = $this->data_preprocessing_hints($question); 200 $question = $this->data_preprocessing_units($question); 201 $question = $this->data_preprocessing_unit_options($question); 202 return $question; 203 } 204 205 protected function data_preprocessing_answers($question, $withanswerfiles = false) { 206 $question = parent::data_preprocessing_answers($question, $withanswerfiles); 207 if (empty($question->options->answers)) { 208 return $question; 209 } 210 211 $key = 0; 212 foreach ($question->options->answers as $answer) { 213 // See comment in the parent method about this hack. 214 unset($this->_form->_defaultValues["tolerance[{$key}]"]); 215 216 $question->tolerance[$key] = $answer->tolerance; 217 218 if (is_numeric($question->answer[$key])) { 219 $question->answer[$key] = format_float($question->answer[$key], -1); 220 } 221 222 $key++; 223 } 224 225 return $question; 226 } 227 228 /** 229 * Perform the necessary preprocessing for the fields added by 230 * {@link add_unit_fields()}. 231 * @param object $question the data being passed to the form. 232 * @return object $question the modified data. 233 */ 234 protected function data_preprocessing_units($question) { 235 if (empty($question->options->units)) { 236 return $question; 237 } 238 239 foreach ($question->options->units as $key => $unit) { 240 $question->unit[$key] = $unit->unit; 241 $question->multiplier[$key] = $unit->multiplier; 242 } 243 244 return $question; 245 } 246 247 /** 248 * Perform the necessary preprocessing for the fields added by 249 * {@link add_unit_options()}. 250 * @param object $question the data being passed to the form. 251 * @return object $question the modified data. 252 */ 253 protected function data_preprocessing_unit_options($question) { 254 if (empty($question->options)) { 255 return $question; 256 } 257 258 $question->unitpenalty = $question->options->unitpenalty; 259 $question->unitsleft = $question->options->unitsleft; 260 261 if ($question->options->unitgradingtype) { 262 $question->unitgradingtypes = $question->options->unitgradingtype; 263 $question->multichoicedisplay = $question->options->showunits; 264 $question->unitrole = qtype_numerical::UNITGRADED; 265 } else { 266 $question->unitrole = $question->options->showunits; 267 } 268 269 return $question; 270 } 271 272 public function validation($data, $files) { 273 $errors = parent::validation($data, $files); 274 $errors = $this->validate_answers($data, $errors); 275 $errors = $this->validate_numerical_options($data, $errors); 276 return $errors; 277 } 278 279 /** 280 * Validate the answers. 281 * @param array $data the submitted data. 282 * @param array $errors the errors array to add to. 283 * @return array the updated errors array. 284 */ 285 protected function validate_answers($data, $errors) { 286 // Check the answers. 287 $answercount = 0; 288 $maxgrade = false; 289 $answers = $data['answer']; 290 foreach ($answers as $key => $answer) { 291 $trimmedanswer = trim($answer); 292 if ($trimmedanswer != '') { 293 $answercount++; 294 if (!$this->is_valid_answer($trimmedanswer, $data)) { 295 $errors['answeroptions[' . $key . ']'] = $this->valid_answer_message($trimmedanswer); 296 } 297 if ($data['fraction'][$key] == 1) { 298 $maxgrade = true; 299 } 300 if ($answer !== '*' && $data['tolerance'][$key] === false) { 301 $errors['answeroptions['.$key.']'] = 302 get_string('xmustbenumeric', 'qtype_numerical', 303 get_string('acceptederror', 'qtype_numerical')); 304 } 305 } else if ($data['fraction'][$key] != 0 || 306 !html_is_blank($data['feedback'][$key]['text'])) { 307 $errors['answeroptions[' . $key . ']'] = $this->valid_answer_message($trimmedanswer); 308 $answercount++; 309 } 310 } 311 if ($answercount == 0) { 312 $errors['answeroptions[0]'] = get_string('notenoughanswers', 'qtype_numerical'); 313 } 314 if ($maxgrade == false) { 315 $errors['answeroptions[0]'] = get_string('fractionsnomax', 'question'); 316 } 317 318 return $errors; 319 } 320 321 /** 322 * Validate a particular answer. 323 * @param string $answer an answer to validate. Known to be non-blank and already trimmed. 324 * @param array $data the submitted data. 325 * @return bool whether this is a valid answer. 326 */ 327 protected function is_valid_answer($answer, $data) { 328 return $answer == '*' || qtype_numerical::is_valid_number($answer); 329 } 330 331 /** 332 * @return string erre describing what an answer should be. 333 */ 334 protected function valid_answer_message($answer) { 335 return get_string('answermustbenumberorstar', 'qtype_numerical'); 336 } 337 338 /** 339 * Validate the answers. 340 * @param array $data the submitted data. 341 * @param array $errors the errors array to add to. 342 * @return array the updated errors array. 343 */ 344 protected function validate_numerical_options($data, $errors) { 345 if ($data['unitrole'] != qtype_numerical::UNITNONE && trim($data['unit'][0]) == '') { 346 $errors['units[0]'] = get_string('unitonerequired', 'qtype_numerical'); 347 } 348 349 if (empty($data['unit']) || $data['unitrole'] == qtype_numerical::UNITNONE) { 350 return $errors; 351 } 352 353 // Basic unit validation. 354 foreach ($data['unit'] as $key => $unit) { 355 if (is_numeric($unit)) { 356 $errors['units[' . $key . ']'] = 357 get_string('xmustnotbenumeric', 'qtype_numerical', 358 get_string('unit', 'qtype_numerical')); 359 } 360 361 $trimmedunit = trim($unit); 362 if (empty($trimmedunit)) { 363 continue; 364 } 365 366 $trimmedmultiplier = trim($data['multiplier'][$key]); 367 if (empty($trimmedmultiplier)) { 368 $errors['units[' . $key . ']'] = 369 get_string('youmustenteramultiplierhere', 'qtype_numerical'); 370 } else if ($trimmedmultiplier === false) { 371 $errors['units[' . $key . ']'] = 372 get_string('xmustbenumeric', 'qtype_numerical', 373 get_string('multiplier', 'qtype_numerical')); 374 } 375 } 376 377 // Check for repeated units. 378 $alreadyseenunits = array(); 379 foreach ($data['unit'] as $key => $unit) { 380 $trimmedunit = trim($unit); 381 if ($trimmedunit == '') { 382 continue; 383 } 384 385 if (in_array($trimmedunit, $alreadyseenunits)) { 386 $errors['units[' . $key . ']'] = 387 get_string('errorrepeatedunit', 'qtype_numerical'); 388 } else { 389 $alreadyseenunits[] = $trimmedunit; 390 } 391 } 392 393 return $errors; 394 } 395 396 public function qtype() { 397 return 'numerical'; 398 } 399 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body