Differences Between: [Versions 310 and 403] [Versions 311 and 403] [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 the calculated question data set items. 19 * 20 * @package qtype 21 * @subpackage calculated 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 31 32 /** 33 * Calculated question data set items editing form definition. 34 * 35 * @copyright 2007 Jamie Pratt me@jamiep.org 36 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 37 */ 38 class question_dataset_dependent_items_form extends question_wizard_form { 39 /** 40 * Question object with options and answers already loaded by get_question_options 41 * Be careful how you use this it is needed sometimes to set up the structure of the 42 * form in definition_inner but data is always loaded into the form with set_defaults. 43 * 44 * @var object 45 */ 46 public $question; 47 /** 48 * Reference to question type object 49 * 50 * @var question_dataset_dependent_questiontype 51 */ 52 public $qtypeobj; 53 54 /** @var stdClass the question category. */ 55 protected $category; 56 57 /** @var context the context of the question category. */ 58 protected $categorycontext; 59 60 public $datasetdefs; 61 62 public $maxnumber = -1; 63 64 public $regenerate; 65 66 public $noofitems; 67 68 public $outsidelimit = false; 69 70 public $commentanswers = array(); 71 72 /** 73 * Add question-type specific form fields. 74 * 75 * @param MoodleQuickForm $mform the form being built. 76 */ 77 public function __construct($submiturl, $question, $regenerate) { 78 global $SESSION; 79 80 // Validate the question category. 81 if (!isset($question->categoryobject)) { 82 throw new moodle_exception('categorydoesnotexist', 'question'); 83 } 84 $question->category = $question->categoryobject->id; 85 $this->category = $question->categoryobject; 86 $this->categorycontext = context::instance_by_id($this->category->contextid); 87 $this->regenerate = $regenerate; 88 $this->question = $question; 89 $this->qtypeobj = question_bank::get_qtype($this->question->qtype); 90 // Get the dataset defintions for this question. 91 if (empty($question->id)) { 92 $this->datasetdefs = $this->qtypeobj->get_dataset_definitions( 93 $question->id, $SESSION->calculated->definitionform->dataset); 94 } else { 95 if (empty($question->options)) { 96 $this->get_question_options($question); 97 } 98 $this->datasetdefs = $this->qtypeobj->get_dataset_definitions( 99 $question->id, array()); 100 } 101 102 foreach ($this->datasetdefs as $datasetdef) { 103 // Get maxnumber. 104 if ($this->maxnumber == -1 || $datasetdef->itemcount < $this->maxnumber) { 105 $this->maxnumber = $datasetdef->itemcount; 106 } 107 } 108 foreach ($this->datasetdefs as $defid => $datasetdef) { 109 if (isset($datasetdef->id)) { 110 $this->datasetdefs[$defid]->items = 111 $this->qtypeobj->get_database_dataset_items($datasetdef->id); 112 } 113 } 114 parent::__construct($submiturl); 115 } 116 117 protected function definition() { 118 global $PAGE; 119 120 $labelsharedwildcard = get_string("sharedwildcard", "qtype_calculated"); 121 $mform = $this->_form; 122 $mform->setDisableShortforms(); 123 124 $strquestionlabel = $this->qtypeobj->comment_header($this->question); 125 if ($this->maxnumber != -1 ) { 126 $this->noofitems = $this->maxnumber; 127 } else { 128 $this->noofitems = 0; 129 } 130 $label = get_string("sharedwildcards", "qtype_calculated"); 131 132 $html2 = $this->qtypeobj->print_dataset_definitions_category_shared( 133 $this->question, $this->datasetdefs); 134 $mform->addElement('static', 'listcategory', $label, $html2); 135 // ...----------------------------------------------------------------------. 136 $mform->addElement('submit', 'updatedatasets', 137 get_string('updatedatasetparam', 'qtype_calculated')); 138 $mform->registerNoSubmitButton('updatedatasets'); 139 $mform->addElement('header', 'additemhdr', 140 get_string('itemtoadd', 'qtype_calculated')); 141 $idx = 1; 142 $data = array(); 143 $j = (($this->noofitems) * count($this->datasetdefs))+1; 144 foreach ($this->datasetdefs as $defkey => $datasetdef) { 145 if ($datasetdef->category |= 0 ) { 146 $name = get_string('sharedwildcard', 'qtype_calculated', $datasetdef->name); 147 } else { 148 $name = get_string('wildcard', 'qtype_calculated', $datasetdef->name); 149 } 150 $mform->addElement('float', "number[{$j}]", $name); 151 $this->qtypeobj->custom_generator_tools_part($mform, $idx, $j); 152 $idx++; 153 $mform->addElement('hidden', "definition[{$j}]"); 154 $mform->setType("definition[{$j}]", PARAM_RAW); 155 $mform->addElement('hidden', "itemid[{$j}]"); 156 $mform->setType("itemid[{$j}]", PARAM_RAW); 157 $mform->addElement('static', "divider[{$j}]", '', '<hr />'); 158 $mform->setType("divider[{$j}]", PARAM_RAW); 159 $j++; 160 } 161 162 $mform->addElement('header', 'updateanswershdr', 163 get_string('answerstoleranceparam', 'qtype_calculated')); 164 $mform->addElement('submit', 'updateanswers', 165 get_string('updatetolerancesparam', 'qtype_calculated')); 166 $mform->setAdvanced('updateanswers', true); 167 $mform->registerNoSubmitButton('updateanswers'); 168 169 $answers = fullclone($this->question->options->answers); 170 $key1 =1; 171 foreach ($answers as $key => $answer) { 172 $ans = shorten_text($answer->answer, 17, true); 173 if ($ans === '*') { 174 $mform->addElement('static', 175 'answercomment[' . ($this->noofitems+$key1) . ']', $ans); 176 $mform->addElement('hidden', 'tolerance['.$key.']', ''); 177 $mform->setType('tolerance['.$key.']', PARAM_FLOAT); // No need to worry about localisation, as the value of this field will not be shown to users anymore. 178 $mform->setAdvanced('tolerance['.$key.']', true); 179 $mform->addElement('hidden', 'tolerancetype['.$key.']', ''); 180 $mform->setType('tolerancetype['.$key.']', PARAM_RAW); 181 $mform->setAdvanced('tolerancetype['.$key.']', true); 182 $mform->addElement('hidden', 'correctanswerlength['.$key.']', ''); 183 $mform->setType('correctanswerlength['.$key.']', PARAM_RAW); 184 $mform->setAdvanced('correctanswerlength['.$key.']', true); 185 $mform->addElement('hidden', 'correctanswerformat['.$key.']', ''); 186 $mform->setType('correctanswerformat['.$key.']', PARAM_RAW); 187 $mform->setAdvanced('correctanswerformat['.$key.']', true); 188 } else if ( $ans !== '' ) { 189 $mform->addElement('static', 'answercomment[' . ($this->noofitems+$key1) . ']', 190 $ans); 191 $mform->addElement('float', 'tolerance['.$key.']', 192 get_string('tolerance', 'qtype_calculated')); 193 $mform->setAdvanced('tolerance['.$key.']', true); 194 $mform->addElement('select', 'tolerancetype['.$key.']', 195 get_string('tolerancetype', 'qtype_numerical'), 196 $this->qtypeobj->tolerance_types()); 197 $mform->setAdvanced('tolerancetype['.$key.']', true); 198 199 $mform->addElement('select', 'correctanswerlength['.$key.']', 200 get_string('correctanswershows', 'qtype_calculated'), range(0, 9)); 201 $mform->setAdvanced('correctanswerlength['.$key.']', true); 202 203 $answerlengthformats = array( 204 '1' => get_string('decimalformat', 'qtype_numerical'), 205 '2' => get_string('significantfiguresformat', 'qtype_calculated') 206 ); 207 $mform->addElement('select', 'correctanswerformat['.$key.']', 208 get_string('correctanswershowsformat', 'qtype_calculated'), 209 $answerlengthformats); 210 $mform->setAdvanced('correctanswerformat['.$key.']', true); 211 $mform->addElement('static', 'dividertolerance', '', '<hr />'); 212 $mform->setAdvanced('dividertolerance', true); 213 } 214 $key1++; 215 } 216 217 $addremoveoptions = array(); 218 $addremoveoptions['1']='1'; 219 for ($i=10; $i<=100; $i+=10) { 220 $addremoveoptions["{$i}"] = "{$i}"; 221 } 222 $showoptions = Array(); 223 $showoptions['1']='1'; 224 $showoptions['2']='2'; 225 $showoptions['5']='5'; 226 for ($i=10; $i<=100; $i+=10) { 227 $showoptions["{$i}"] = "{$i}"; 228 } 229 $mform->addElement('header', 'addhdr', get_string('add', 'moodle')); 230 $mform->closeHeaderBefore('addhdr'); 231 232 if ($this->qtypeobj->supports_dataset_item_generation()) { 233 $radiogrp = array(); 234 $radiogrp[] =& $mform->createElement('radio', 'nextpageparam[forceregeneration]', 235 null, get_string('reuseifpossible', 'qtype_calculated'), 0); 236 $radiogrp[] =& $mform->createElement('radio', 'nextpageparam[forceregeneration]', 237 null, get_string('forceregenerationshared', 'qtype_calculated'), 1); 238 $radiogrp[] =& $mform->createElement('radio', 'nextpageparam[forceregeneration]', 239 null, get_string('forceregenerationall', 'qtype_calculated'), 2); 240 $mform->addGroup($radiogrp, 'forceregenerationgrp', 241 get_string('nextitemtoadd', 'qtype_calculated'), "<br/>", false); 242 } 243 244 $mform->addElement('submit', 'getnextbutton', get_string('getnextnow', 'qtype_calculated')); 245 $mform->addElement('static', "dividera", '', '<hr />'); 246 $addgrp = array(); 247 $addgrp[] =& $mform->createElement('submit', 'addbutton', get_string('add', 'moodle')); 248 $addgrp[] =& $mform->createElement('select', "selectadd", 249 get_string('additem', 'qtype_calculated'), $addremoveoptions); 250 $addgrp[] = & $mform->createElement('static', "stat", "Items", 251 get_string('newsetwildcardvalues', 'qtype_calculatedsimple')); 252 $mform->addGroup($addgrp, 'addgrp', get_string('additem', 'qtype_calculated'), ' ', false); 253 $mform->addElement('static', "divideradd", '', ''); 254 if ($this->noofitems > 0) { 255 $mform->addElement('header', 'deleteitemhdr', get_string('delete', 'moodle')); 256 $deletegrp = array(); 257 $deletegrp[] = $mform->createElement('submit', 'deletebutton', 258 get_string('delete', 'moodle')); 259 $deletegrp[] = $mform->createElement('select', 'selectdelete', 260 get_string('deleteitem', 'qtype_calculated')."1", $addremoveoptions); 261 $deletegrp[] = $mform->createElement('static', "stat", "Items", 262 get_string('setwildcardvalues', 'qtype_calculatedsimple')); 263 $mform->addGroup($deletegrp, 'deletegrp', '', ' ', false); 264 } else { 265 $mform->addElement('static', 'warning', '', '<span class="error">' . 266 get_string('youmustaddatleastoneitem', 'qtype_calculated').'</span>'); 267 } 268 269 $addgrp1 = array(); 270 $addgrp1[] = $mform->createElement('submit', 'showbutton', 271 get_string('showitems', 'qtype_calculated')); 272 $addgrp1[] = $mform->createElement('select', "selectshow", '' , $showoptions); 273 $addgrp1[] = $mform->createElement('static', "stat", '', 274 get_string('setwildcardvalues', 'qtype_calculated')); 275 $mform->addGroup($addgrp1, 'addgrp1', '', ' ', false); 276 $mform->registerNoSubmitButton('showbutton'); 277 $mform->closeHeaderBefore('addgrp1'); 278 // ...----------------------------------------------------------------------. 279 $j = $this->noofitems * count($this->datasetdefs); 280 $k = optional_param('selectshow', 1, PARAM_INT); 281 for ($i = $this->noofitems; $i >= 1; $i--) { 282 if ($k > 0) { 283 $mform->addElement('header', 'setnoheader' . $i, "<b>" . 284 get_string('setno', 'qtype_calculated', $i)."</b> "); 285 } 286 foreach ($this->datasetdefs as $defkey => $datasetdef) { 287 if ($k > 0) { 288 if ($datasetdef->category == 0 ) { 289 $mform->addElement('float', "number[{$j}]", 290 get_string('wildcard', 'qtype_calculated', $datasetdef->name)); 291 } else { 292 $mform->addElement('float', "number[{$j}]", get_string( 293 'sharedwildcard', 'qtype_calculated', $datasetdef->name)); 294 } 295 296 } else { 297 $mform->addElement('hidden', "number[{$j}]" , ''); 298 $mform->setType("number[{$j}]", PARAM_LOCALISEDFLOAT); // Localisation handling has to be done manually. 299 } 300 $mform->addElement('hidden', "itemid[{$j}]"); 301 $mform->setType("itemid[{$j}]", PARAM_INT); 302 303 $mform->addElement('hidden', "definition[{$j}]"); 304 $mform->setType("definition[{$j}]", PARAM_NOTAGS); 305 $data[$datasetdef->name] =$datasetdef->items[$i]->value; 306 307 $j--; 308 } 309 if ('' != $strquestionlabel && ($k > 0 )) { 310 // ... $this->outsidelimit || !empty($this->numbererrors ). 311 $repeated[] = $mform->addElement('static', "answercomment[{$i}]", $strquestionlabel); 312 // Decode equations in question text. 313 $qtext = $this->qtypeobj->substitute_variables( 314 $this->question->questiontext, $data); 315 $textequations = $this->qtypeobj->find_formulas($qtext); 316 if ($textequations != '' && count($textequations) > 0 ) { 317 $mform->addElement('static', "divider1[{$j}]", '', 318 'Formulas {=..} in question text'); 319 foreach ($textequations as $key => $equation) { 320 if ($formulaerrors = qtype_calculated_find_formula_errors($equation)) { 321 $str = $formulaerrors; 322 } else { 323 eval('$str = '.$equation.';'); 324 } 325 $equation = shorten_text($equation, 17, true); 326 $mform->addElement('static', "textequation", "{={$equation}}", "=".$str); 327 } 328 } 329 330 } 331 $k--; 332 333 } 334 $mform->addElement('static', 'outsidelimit', '', ''); 335 336 // Submit buttons. 337 if ($this->noofitems > 0) { 338 $buttonarray = []; 339 $buttonarray[] = $mform->createElement( 340 'submit', 'savechanges', get_string('savechanges')); 341 if (\core\plugininfo\qbank::is_plugin_enabled('qbank_previewquestion')) { 342 $previewlink = $PAGE->get_renderer('qbank_previewquestion')->question_preview_link($this->question->id, 343 $this->categorycontext, true); 344 } 345 $buttonarray[] = $mform->createElement('static', 'previewlink', '', $previewlink); 346 347 $mform->addGroup($buttonarray, 'buttonar', '', array(' '), false); 348 $mform->closeHeaderBefore('buttonar'); 349 } 350 351 $this->add_hidden_fields(); 352 353 $mform->addElement('hidden', 'category'); 354 $mform->setType('category', PARAM_SEQUENCE); 355 356 $mform->addElement('hidden', 'wizard', 'datasetitems'); 357 $mform->setType('wizard', PARAM_ALPHA); 358 } 359 360 public function set_data($question) { 361 $formdata = array(); 362 $fromform = new stdClass(); 363 if (isset($question->options)) { 364 $answers = $question->options->answers; 365 if (count($answers)) { 366 if (optional_param('updateanswers', false, PARAM_BOOL) || 367 optional_param('updatedatasets', false, PARAM_BOOL)) { 368 foreach ($answers as $key => $answer) { 369 $fromform->tolerance[$key]= $this->_form->getElementValue( 370 'tolerance['.$key.']'); 371 $answer->tolerance = $fromform->tolerance[$key]; 372 $fromform->tolerancetype[$key]= $this->_form->getElementValue( 373 'tolerancetype['.$key.']'); 374 if (is_array($fromform->tolerancetype[$key])) { 375 $fromform->tolerancetype[$key]= $fromform->tolerancetype[$key][0]; 376 } 377 $answer->tolerancetype = $fromform->tolerancetype[$key]; 378 $fromform->correctanswerlength[$key]= $this->_form->getElementValue( 379 'correctanswerlength['.$key.']'); 380 if (is_array($fromform->correctanswerlength[$key])) { 381 $fromform->correctanswerlength[$key] = 382 $fromform->correctanswerlength[$key][0]; 383 } 384 $answer->correctanswerlength = $fromform->correctanswerlength[$key]; 385 $fromform->correctanswerformat[$key] = $this->_form->getElementValue( 386 'correctanswerformat['.$key.']'); 387 if (is_array($fromform->correctanswerformat[$key])) { 388 $fromform->correctanswerformat[$key] = 389 $fromform->correctanswerformat[$key][0]; 390 } 391 $answer->correctanswerformat = $fromform->correctanswerformat[$key]; 392 } 393 $this->qtypeobj->save_question_calculated($question, $fromform); 394 395 } else { 396 foreach ($answers as $key => $answer) { 397 $formdata['tolerance['.$key.']'] = $answer->tolerance; 398 $formdata['tolerancetype['.$key.']'] = $answer->tolerancetype; 399 $formdata['correctanswerlength['.$key.']'] = $answer->correctanswerlength; 400 $formdata['correctanswerformat['.$key.']'] = $answer->correctanswerformat; 401 } 402 } 403 } 404 } 405 // Fill out all data sets and also the fields for the next item to add. 406 $j = $this->noofitems * count($this->datasetdefs); 407 for ($itemnumber = $this->noofitems; $itemnumber >= 1; $itemnumber--) { 408 $data = array(); 409 foreach ($this->datasetdefs as $defid => $datasetdef) { 410 if (isset($datasetdef->items[$itemnumber])) { 411 $value = $datasetdef->items[$itemnumber]->value; 412 if ($this->_form->getElementType("number[{$j}]") == 'hidden') { 413 // Some of the number elements are from the float type and some are from the hidden type. 414 // We need to manually handle localised floats for hidden elements. 415 $value = format_float($value, -1); 416 } 417 $formdata["number[{$j}]"] = $value; 418 $formdata["definition[{$j}]"] = $defid; 419 $formdata["itemid[{$j}]"] = $datasetdef->items[$itemnumber]->id; 420 $data[$datasetdef->name] = $datasetdef->items[$itemnumber]->value; 421 } 422 $j--; 423 } 424 $comment = $this->qtypeobj->comment_on_datasetitems($this->qtypeobj, $question->id, 425 $question->questiontext, $answers, $data, $itemnumber); 426 if ($comment->outsidelimit) { 427 $this->outsidelimit=$comment->outsidelimit; 428 } 429 $totalcomment=''; 430 foreach ($question->options->answers as $key => $answer) { 431 $totalcomment .= $comment->stranswers[$key].'<br/>'; 432 } 433 $formdata['answercomment['.$itemnumber.']'] = $totalcomment; 434 } 435 436 $formdata['nextpageparam[forceregeneration]'] = $this->regenerate; 437 $formdata['selectdelete'] = '1'; 438 $formdata['selectadd'] = '1'; 439 $j = $this->noofitems * count($this->datasetdefs)+1; 440 $data = array(); // Data for comment_on_datasetitems later. 441 // Dataset generation defaults. 442 if ($this->qtypeobj->supports_dataset_item_generation()) { 443 $itemnumber = $this->noofitems+1; 444 foreach ($this->datasetdefs as $defid => $datasetdef) { 445 if (!optional_param('updatedatasets', false, PARAM_BOOL) && 446 !optional_param('updateanswers', false, PARAM_BOOL)) { 447 $value = $this->qtypeobj->generate_dataset_item($datasetdef->options); 448 } else { 449 $value = $this->_form->getElementValue("number[{$j}]"); 450 } 451 if ($this->_form->getElementType("number[{$j}]") == 'hidden') { 452 // Some of the number elements are from the float type and some are from the hidden type. 453 // We need to manually handle localised floats for hidden elements. 454 $value = format_float($value, -1); 455 } 456 $formdata["number[{$j}]"] = $value; 457 $formdata["definition[{$j}]"] = $defid; 458 $formdata["itemid[{$j}]"] = isset($datasetdef->items[$itemnumber]) ? 459 $datasetdef->items[$itemnumber]->id : 0; 460 $data[$datasetdef->name] = $formdata["number[{$j}]"]; 461 $j++; 462 } 463 } 464 465 // Existing records override generated data depending on radio element. 466 $j = $this->noofitems * count($this->datasetdefs) + 1; 467 if (!$this->regenerate && !optional_param('updatedatasets', false, PARAM_BOOL) && 468 !optional_param('updateanswers', false, PARAM_BOOL)) { 469 $itemnumber = $this->noofitems + 1; 470 foreach ($this->datasetdefs as $defid => $datasetdef) { 471 if (isset($datasetdef->items[$itemnumber])) { 472 $formdata["number[{$j}]"] = $datasetdef->items[$itemnumber]->value; 473 $formdata["definition[{$j}]"] = $defid; 474 $formdata["itemid[{$j}]"] = $datasetdef->items[$itemnumber]->id; 475 $data[$datasetdef->name] = $datasetdef->items[$itemnumber]->value; 476 } 477 $j++; 478 } 479 } 480 481 $comment = $this->qtypeobj->comment_on_datasetitems($this->qtypeobj, $question->id, 482 $question->questiontext, $answers, $data, ($this->noofitems + 1)); 483 if (isset($comment->outsidelimit) && $comment->outsidelimit) { 484 $this->outsidelimit=$comment->outsidelimit; 485 } 486 $key1 = 1; 487 foreach ($question->options->answers as $key => $answer) { 488 $formdata['answercomment['.($this->noofitems+$key1).']'] = $comment->stranswers[$key]; 489 $key1++; 490 } 491 492 if ($this->outsidelimit) { 493 $formdata['outsidelimit']= '<span class="error">' . 494 get_string('oneanswertrueansweroutsidelimits', 'qtype_calculated') . '</span>'; 495 } 496 $formdata = $this->qtypeobj->custom_generator_set_data($this->datasetdefs, $formdata); 497 498 parent::set_data((object)($formdata + (array)$question)); 499 } 500 501 public function validation($data, $files) { 502 $errors = array(); 503 if (isset($data['savechanges']) && ($this->noofitems==0) ) { 504 $errors['warning'] = get_string('warning', 'mnet'); 505 } 506 if ($this->outsidelimit) { 507 $errors['outsidelimits'] = 508 get_string('oneanswertrueansweroutsidelimits', 'qtype_calculated'); 509 } 510 $numbers = $data['number']; 511 foreach ($numbers as $key => $number) { 512 if (! is_numeric($number)) { 513 if (stristr($number, ',')) { 514 $errors['number['.$key.']'] = get_string('nocommaallowed', 'qtype_calculated'); 515 } else { 516 $errors['number['.$key.']'] = get_string('notvalidnumber', 'qtype_calculated'); 517 } 518 } else if (stristr($number, 'x')) { 519 $a = new stdClass(); 520 $a->name = ''; 521 $a->value = $number; 522 $errors['number['.$key.']'] = get_string('hexanotallowed', 'qtype_calculated', $a); 523 } else if (is_nan($number)) { 524 $errors['number['.$key.']'] = get_string('notvalidnumber', 'qtype_calculated'); 525 } 526 } 527 return $errors; 528 } 529 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body