See Release Notes
Long Term Support Release
Differences Between: [Versions 39 and 311] [Versions 39 and 400] [Versions 39 and 401] [Versions 39 and 402] [Versions 39 and 403]
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 calculated multiple-choice questions. 19 * 20 * @package qtype 21 * @subpackage calculatedmulti 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 30 /** 31 * Calculated multiple-choice question editing form. 32 * 33 * @copyright 2007 Jamie Pratt me@jamiep.org 34 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 35 */ 36 class qtype_calculatedmulti_edit_form extends question_edit_form { 37 /** 38 * Handle to the question type for this question. 39 * 40 * @var question_calculatedmulti_qtype 41 */ 42 public $qtypeobj; 43 public $questiondisplay; 44 public $initialname = ''; 45 public $reload = false; 46 47 public function __construct($submiturl, $question, $category, 48 $contexts, $formeditable = true) { 49 $this->question = $question; 50 $this->qtypeobj = question_bank::get_qtype('calculatedmulti'); 51 $this->reload = optional_param('reload', false, PARAM_BOOL); 52 if (!$this->reload) { 53 // Use database data as this is first pass. 54 if (isset($this->question->id)) { 55 // Remove prefix #{..}# if exists. 56 $this->initialname = $question->name; 57 $question->name = question_bank::get_qtype('calculated') 58 ->clean_technical_prefix_from_question_name($question->name); 59 } 60 } 61 parent::__construct($submiturl, $question, $category, $contexts, $formeditable); 62 } 63 64 protected function can_preview() { 65 return false; // Generally not possible for calculated multi-choice questions on this page. 66 } 67 68 public function get_per_answer_fields($mform, $label, $gradeoptions, 69 &$repeatedoptions, &$answersoption) { 70 $repeated = array(); 71 $answeroptions = array(); 72 $answeroptions[] = $mform->createElement('text', 'answer', 73 $label, array('size' => 50)); 74 $answeroptions[] = $mform->createElement('select', 'fraction', 75 get_string('grade'), $gradeoptions); 76 $repeated[] = $mform->createElement('group', 'answeroptions', 77 $label, $answeroptions, null, false); 78 79 // Added answeroptions help button in definition_inner() after called to add_per_answer_fields. 80 81 $repeatedoptions['answer']['type'] = PARAM_RAW; 82 $repeatedoptions['fraction']['default'] = 0; 83 $answersoption = 'answers'; 84 85 $mform->setType('answer', PARAM_NOTAGS); 86 87 $repeated[] = $mform->createElement('hidden', 'tolerance'); 88 $repeated[] = $mform->createElement('hidden', 'tolerancetype', 1); 89 $repeatedoptions['tolerance']['type'] = PARAM_FLOAT; 90 $repeatedoptions['tolerance']['default'] = 0.01; 91 $repeatedoptions['tolerancetype']['type'] = PARAM_INT; 92 93 // Create display group. 94 $answerdisplay = array(); 95 $answerdisplay[] = $mform->createElement('select', 'correctanswerlength', 96 get_string('answerdisplay', 'qtype_calculated'), range(0, 9)); 97 $repeatedoptions['correctanswerlength']['default'] = 2; 98 99 $answerlengthformats = array( 100 '1' => get_string('decimalformat', 'qtype_numerical'), 101 '2' => get_string('significantfiguresformat', 'qtype_calculated') 102 ); 103 $answerdisplay[] = $mform->createElement('select', 'correctanswerformat', 104 get_string('correctanswershowsformat', 'qtype_calculated'), $answerlengthformats); 105 $repeated[] = $mform->createElement('group', 'answerdisplay', 106 get_string('answerdisplay', 'qtype_calculated'), $answerdisplay, null, false); 107 108 // Add feedback. 109 $repeated[] = $mform->createElement('editor', 'feedback', 110 get_string('feedback', 'question'), null, $this->editoroptions); 111 112 return $repeated; 113 } 114 115 protected function definition_inner($mform) { 116 117 $label = get_string('sharedwildcards', 'qtype_calculated'); 118 $mform->addElement('hidden', 'initialcategory', 1); 119 $mform->addElement('hidden', 'reload', 1); 120 $mform->setType('initialcategory', PARAM_INT); 121 $mform->setType('reload', PARAM_BOOL); 122 123 $html2 = ''; 124 $mform->insertElementBefore( 125 $mform->createElement('static', 'listcategory', $label, $html2), 'name'); 126 if (isset($this->question->id)) { 127 $mform->insertElementBefore($mform->createElement('static', 'initialname', 128 get_string('questionstoredname', 'qtype_calculated'), 129 format_string($this->initialname)), 'name'); 130 }; 131 $addfieldsname = 'updatecategory'; 132 $addstring = get_string('updatecategory', 'qtype_calculated'); 133 $mform->registerNoSubmitButton($addfieldsname); 134 $this->editasmultichoice = 1; 135 136 $mform->insertElementBefore( 137 $mform->createElement('submit', $addfieldsname, $addstring), 'listcategory'); 138 $mform->registerNoSubmitButton('createoptionbutton'); 139 $mform->addElement('hidden', 'multichoice', $this->editasmultichoice); 140 $mform->setType('multichoice', PARAM_INT); 141 142 $menu = array(get_string('answersingleno', 'qtype_multichoice'), 143 get_string('answersingleyes', 'qtype_multichoice')); 144 $mform->addElement('select', 'single', 145 get_string('answerhowmany', 'qtype_multichoice'), $menu); 146 $mform->setDefault('single', 1); 147 148 $mform->addElement('advcheckbox', 'shuffleanswers', 149 get_string('shuffleanswers', 'qtype_multichoice'), null, null, array(0, 1)); 150 $mform->addHelpButton('shuffleanswers', 'shuffleanswers', 'qtype_multichoice'); 151 $mform->setDefault('shuffleanswers', 1); 152 153 $numberingoptions = question_bank::get_qtype('multichoice')->get_numbering_styles(); 154 $mform->addElement('select', 'answernumbering', 155 get_string('answernumbering', 'qtype_multichoice'), $numberingoptions); 156 $mform->setDefault('answernumbering', 'abc'); 157 158 $this->add_per_answer_fields($mform, get_string('choiceno', 'qtype_multichoice', '{no}'), 159 question_bank::fraction_options_full(), max(5, QUESTION_NUMANS_START)); 160 $mform->addHelpButton('answeroptions[0]', 'answeroptions', 'qtype_calculatedmulti'); 161 162 $repeated = array(); 163 $nounits = optional_param('nounits', 1, PARAM_INT); 164 $mform->addElement('hidden', 'nounits', $nounits); 165 $mform->setType('nounits', PARAM_INT); 166 $mform->setConstants(array('nounits'=>$nounits)); 167 for ($i = 0; $i < $nounits; $i++) { 168 $mform->addElement('hidden', 'unit'."[{$i}]", 169 optional_param("unit[{$i}]", '', PARAM_NOTAGS)); 170 $mform->setType('unit'."[{$i}]", PARAM_NOTAGS); 171 $mform->addElement('hidden', 'multiplier'."[{$i}]", 172 optional_param("multiplier[{$i}]", '', PARAM_FLOAT)); 173 $mform->setType("multiplier[{$i}]", PARAM_FLOAT); 174 } 175 176 $this->add_combined_feedback_fields(true); 177 $mform->disabledIf('shownumcorrect', 'single', 'eq', 1); 178 179 $this->add_interactive_settings(true, true); 180 181 // Hidden elements. 182 $mform->addElement('hidden', 'synchronize', ''); 183 $mform->setType('synchronize', PARAM_INT); 184 if (isset($this->question->options) && isset($this->question->options->synchronize)) { 185 $mform->setDefault('synchronize', $this->question->options->synchronize); 186 } else { 187 $mform->setDefault('synchronize', 0); 188 } 189 $mform->addElement('hidden', 'wizard', 'datasetdefinitions'); 190 $mform->setType('wizard', PARAM_ALPHA); 191 } 192 193 public function data_preprocessing($question) { 194 $question = parent::data_preprocessing($question); 195 $question = $this->data_preprocessing_answers($question, false); 196 $question = $this->data_preprocessing_combined_feedback($question, true); 197 $question = $this->data_preprocessing_hints($question, true, true); 198 199 if (isset($question->options)) { 200 $question->synchronize = $question->options->synchronize; 201 $question->single = $question->options->single; 202 $question->answernumbering = $question->options->answernumbering; 203 $question->shuffleanswers = $question->options->shuffleanswers; 204 } 205 206 return $question; 207 } 208 209 protected function data_preprocessing_answers($question, $withanswerfiles = false) { 210 $question = parent::data_preprocessing_answers($question, $withanswerfiles); 211 if (empty($question->options->answers)) { 212 return $question; 213 } 214 215 $key = 0; 216 foreach ($question->options->answers as $answer) { 217 // See comment in the parent method about this hack. 218 unset($this->_form->_defaultValues["tolerance[{$key}]"]); 219 unset($this->_form->_defaultValues["tolerancetype[{$key}]"]); 220 unset($this->_form->_defaultValues["correctanswerlength[{$key}]"]); 221 unset($this->_form->_defaultValues["correctanswerformat[{$key}]"]); 222 223 $question->tolerance[$key] = $answer->tolerance; 224 $question->tolerancetype[$key] = $answer->tolerancetype; 225 $question->correctanswerlength[$key] = $answer->correctanswerlength; 226 $question->correctanswerformat[$key] = $answer->correctanswerformat; 227 $key++; 228 } 229 230 return $question; 231 } 232 233 /** 234 * Validate the equations in the some question content. 235 * @param array $errors where errors are being accumulated. 236 * @param string $field the field being validated. 237 * @param string $text the content of that field. 238 * @return array the updated $errors array. 239 */ 240 protected function validate_text($errors, $field, $text) { 241 $problems = qtype_calculated_find_formula_errors_in_text($text); 242 if ($problems) { 243 $errors[$field] = $problems; 244 } 245 return $errors; 246 } 247 248 public function validation($data, $files) { 249 $errors = parent::validation($data, $files); 250 251 // Verifying for errors in {=...} in question text. 252 $errors = $this->validate_text($errors, 'questiontext', $data['questiontext']['text']); 253 $errors = $this->validate_text($errors, 'generalfeedback', $data['generalfeedback']['text']); 254 $errors = $this->validate_text($errors, 'correctfeedback', $data['correctfeedback']['text']); 255 $errors = $this->validate_text($errors, 'partiallycorrectfeedback', $data['partiallycorrectfeedback']['text']); 256 $errors = $this->validate_text($errors, 'incorrectfeedback', $data['incorrectfeedback']['text']); 257 258 $answers = $data['answer']; 259 $answercount = 0; 260 $maxgrade = false; 261 $possibledatasets = $this->qtypeobj->find_dataset_names($data['questiontext']['text']); 262 $mandatorydatasets = array(); 263 foreach ($answers as $key => $answer) { 264 $mandatorydatasets += $this->qtypeobj->find_dataset_names($answer); 265 } 266 if (count($mandatorydatasets) == 0) { 267 foreach ($answers as $key => $answer) { 268 $errors['answeroptions['.$key.']'] = 269 get_string('atleastonewildcard', 'qtype_calculated'); 270 } 271 } 272 273 $totalfraction = 0; 274 $maxfraction = -1; 275 foreach ($answers as $key => $answer) { 276 $trimmedanswer = trim($answer); 277 $fraction = (float) $data['fraction'][$key]; 278 if (empty($trimmedanswer) && $trimmedanswer != '0' && empty($fraction)) { 279 continue; 280 } 281 if (empty($trimmedanswer)) { 282 $errors['answeroptions['.$key.']'] = get_string('errgradesetanswerblank', 'qtype_multichoice'); 283 } 284 if ($trimmedanswer != '' || $answercount == 0) { 285 // Verifying for errors in {=...} in answer text. 286 $errors = $this->validate_text($errors, 'answeroptions[' . $key . ']', $answer); 287 $errors = $this->validate_text($errors, 'feedback[' . $key . ']', 288 $data['feedback'][$key]['text']); 289 } 290 291 if ($trimmedanswer != '') { 292 if ('2' == $data['correctanswerformat'][$key] && 293 '0' == $data['correctanswerlength'][$key]) { 294 $errors['correctanswerlength['.$key.']'] = 295 get_string('zerosignificantfiguresnotallowed', 'qtype_calculated'); 296 } 297 if (!is_numeric($data['tolerance'][$key])) { 298 $errors['tolerance['.$key.']'] = 299 get_string('xmustbenumeric', 'qtype_numerical', 300 get_string('acceptederror', 'qtype_numerical')); 301 } 302 if ($data['fraction'][$key] > 0) { 303 $totalfraction += $data['fraction'][$key]; 304 } 305 if ($data['fraction'][$key] > $maxfraction) { 306 $maxfraction = $data['fraction'][$key]; 307 } 308 309 $answercount++; 310 } 311 } 312 if ($answercount == 0) { 313 $errors['answeroptions[0]'] = get_string('notenoughanswers', 'qtype_multichoice', 2); 314 $errors['answeroptions[1]'] = get_string('notenoughanswers', 'qtype_multichoice', 2); 315 } else if ($answercount == 1) { 316 $errors['answeroptions[1]'] = get_string('notenoughanswers', 'qtype_multichoice', 2); 317 318 } 319 // Perform sanity checks on fractional grades. 320 if ($data['single']== 1 ) { 321 if ($maxfraction != 1) { 322 $errors['answeroptions[0]'] = get_string('errfractionsnomax', 'qtype_multichoice', 323 $maxfraction * 100); 324 } 325 } else { 326 $totalfraction = round($totalfraction, 2); 327 if ($totalfraction != 1) { 328 $totalfraction = $totalfraction * 100; 329 $errors['answeroptions[0]'] = 330 get_string('errfractionsaddwrong', 'qtype_multichoice', $totalfraction); 331 } 332 } 333 return $errors; 334 } 335 336 public function qtype() { 337 return 'calculatedmulti'; 338 } 339 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body