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