Differences Between: [Versions 310 and 311] [Versions 311 and 400] [Versions 311 and 401] [Versions 311 and 402] [Versions 311 and 403] [Versions 39 and 311]
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 quiz module ettings form. 19 * 20 * @package mod_quiz 21 * @copyright 2006 Jamie Pratt 22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 */ 24 25 26 defined('MOODLE_INTERNAL') || die(); 27 28 require_once($CFG->dirroot . '/course/moodleform_mod.php'); 29 require_once($CFG->dirroot . '/mod/quiz/locallib.php'); 30 31 32 /** 33 * Settings form for the quiz module. 34 * 35 * @copyright 2006 Jamie Pratt 36 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 37 */ 38 class mod_quiz_mod_form extends moodleform_mod { 39 /** @var array options to be used with date_time_selector fields in the quiz. */ 40 public static $datefieldoptions = array('optional' => true); 41 42 protected $_feedbacks; 43 protected static $reviewfields = array(); // Initialised in the constructor. 44 45 /** @var int the max number of attempts allowed in any user or group override on this quiz. */ 46 protected $maxattemptsanyoverride = null; 47 48 public function __construct($current, $section, $cm, $course) { 49 self::$reviewfields = array( 50 'attempt' => array('theattempt', 'quiz'), 51 'correctness' => array('whethercorrect', 'question'), 52 'marks' => array('marks', 'quiz'), 53 'specificfeedback' => array('specificfeedback', 'question'), 54 'generalfeedback' => array('generalfeedback', 'question'), 55 'rightanswer' => array('rightanswer', 'question'), 56 'overallfeedback' => array('reviewoverallfeedback', 'quiz'), 57 ); 58 parent::__construct($current, $section, $cm, $course); 59 } 60 61 protected function definition() { 62 global $COURSE, $CFG, $DB, $PAGE; 63 $quizconfig = get_config('quiz'); 64 $mform = $this->_form; 65 66 // ------------------------------------------------------------------------------- 67 $mform->addElement('header', 'general', get_string('general', 'form')); 68 69 // Name. 70 $mform->addElement('text', 'name', get_string('name'), array('size'=>'64')); 71 if (!empty($CFG->formatstringstriptags)) { 72 $mform->setType('name', PARAM_TEXT); 73 } else { 74 $mform->setType('name', PARAM_CLEANHTML); 75 } 76 $mform->addRule('name', null, 'required', null, 'client'); 77 $mform->addRule('name', get_string('maximumchars', '', 255), 'maxlength', 255, 'client'); 78 79 // Introduction. 80 $this->standard_intro_elements(get_string('introduction', 'quiz')); 81 82 // ------------------------------------------------------------------------------- 83 $mform->addElement('header', 'timing', get_string('timing', 'quiz')); 84 85 // Open and close dates. 86 $mform->addElement('date_time_selector', 'timeopen', get_string('quizopen', 'quiz'), 87 self::$datefieldoptions); 88 $mform->addHelpButton('timeopen', 'quizopenclose', 'quiz'); 89 90 $mform->addElement('date_time_selector', 'timeclose', get_string('quizclose', 'quiz'), 91 self::$datefieldoptions); 92 93 // Time limit. 94 $mform->addElement('duration', 'timelimit', get_string('timelimit', 'quiz'), 95 array('optional' => true)); 96 $mform->addHelpButton('timelimit', 'timelimit', 'quiz'); 97 98 // What to do with overdue attempts. 99 $mform->addElement('select', 'overduehandling', get_string('overduehandling', 'quiz'), 100 quiz_get_overdue_handling_options()); 101 $mform->addHelpButton('overduehandling', 'overduehandling', 'quiz'); 102 // TODO Formslib does OR logic on disableif, and we need AND logic here. 103 // $mform->disabledIf('overduehandling', 'timelimit', 'eq', 0); 104 // $mform->disabledIf('overduehandling', 'timeclose', 'eq', 0); 105 106 // Grace period time. 107 $mform->addElement('duration', 'graceperiod', get_string('graceperiod', 'quiz'), 108 array('optional' => true)); 109 $mform->addHelpButton('graceperiod', 'graceperiod', 'quiz'); 110 $mform->hideIf('graceperiod', 'overduehandling', 'neq', 'graceperiod'); 111 112 // ------------------------------------------------------------------------------- 113 // Grade settings. 114 $this->standard_grading_coursemodule_elements(); 115 116 $mform->removeElement('grade'); 117 if (property_exists($this->current, 'grade')) { 118 $currentgrade = $this->current->grade; 119 } else { 120 $currentgrade = $quizconfig->maximumgrade; 121 } 122 $mform->addElement('hidden', 'grade', $currentgrade); 123 $mform->setType('grade', PARAM_FLOAT); 124 125 // Number of attempts. 126 $attemptoptions = array('0' => get_string('unlimited')); 127 for ($i = 1; $i <= QUIZ_MAX_ATTEMPT_OPTION; $i++) { 128 $attemptoptions[$i] = $i; 129 } 130 $mform->addElement('select', 'attempts', get_string('attemptsallowed', 'quiz'), 131 $attemptoptions); 132 133 // Grading method. 134 $mform->addElement('select', 'grademethod', get_string('grademethod', 'quiz'), 135 quiz_get_grading_options()); 136 $mform->addHelpButton('grademethod', 'grademethod', 'quiz'); 137 if ($this->get_max_attempts_for_any_override() < 2) { 138 $mform->hideIf('grademethod', 'attempts', 'eq', 1); 139 } 140 141 // ------------------------------------------------------------------------------- 142 $mform->addElement('header', 'layouthdr', get_string('layout', 'quiz')); 143 144 $pagegroup = array(); 145 $pagegroup[] = $mform->createElement('select', 'questionsperpage', 146 get_string('newpage', 'quiz'), quiz_questions_per_page_options(), array('id' => 'id_questionsperpage')); 147 $mform->setDefault('questionsperpage', $quizconfig->questionsperpage); 148 149 if (!empty($this->_cm) && !quiz_has_attempts($this->_cm->instance)) { 150 $pagegroup[] = $mform->createElement('checkbox', 'repaginatenow', '', 151 get_string('repaginatenow', 'quiz'), array('id' => 'id_repaginatenow')); 152 } 153 154 $mform->addGroup($pagegroup, 'questionsperpagegrp', 155 get_string('newpage', 'quiz'), null, false); 156 $mform->addHelpButton('questionsperpagegrp', 'newpage', 'quiz'); 157 $mform->setAdvanced('questionsperpagegrp', $quizconfig->questionsperpage_adv); 158 159 // Navigation method. 160 $mform->addElement('select', 'navmethod', get_string('navmethod', 'quiz'), 161 quiz_get_navigation_options()); 162 $mform->addHelpButton('navmethod', 'navmethod', 'quiz'); 163 164 // ------------------------------------------------------------------------------- 165 $mform->addElement('header', 'interactionhdr', get_string('questionbehaviour', 'quiz')); 166 167 // Shuffle within questions. 168 $mform->addElement('selectyesno', 'shuffleanswers', get_string('shufflewithin', 'quiz')); 169 $mform->addHelpButton('shuffleanswers', 'shufflewithin', 'quiz'); 170 171 // How questions behave (question behaviour). 172 if (!empty($this->current->preferredbehaviour)) { 173 $currentbehaviour = $this->current->preferredbehaviour; 174 } else { 175 $currentbehaviour = ''; 176 } 177 $behaviours = question_engine::get_behaviour_options($currentbehaviour); 178 $mform->addElement('select', 'preferredbehaviour', 179 get_string('howquestionsbehave', 'question'), $behaviours); 180 $mform->addHelpButton('preferredbehaviour', 'howquestionsbehave', 'question'); 181 182 // Can redo completed questions. 183 $redochoices = array(0 => get_string('no'), 1 => get_string('canredoquestionsyes', 'quiz')); 184 $mform->addElement('select', 'canredoquestions', get_string('canredoquestions', 'quiz'), $redochoices); 185 $mform->addHelpButton('canredoquestions', 'canredoquestions', 'quiz'); 186 foreach ($behaviours as $behaviour => $notused) { 187 if (!question_engine::can_questions_finish_during_the_attempt($behaviour)) { 188 $mform->hideIf('canredoquestions', 'preferredbehaviour', 'eq', $behaviour); 189 } 190 } 191 192 // Each attempt builds on last. 193 $mform->addElement('selectyesno', 'attemptonlast', 194 get_string('eachattemptbuildsonthelast', 'quiz')); 195 $mform->addHelpButton('attemptonlast', 'eachattemptbuildsonthelast', 'quiz'); 196 if ($this->get_max_attempts_for_any_override() < 2) { 197 $mform->hideIf('attemptonlast', 'attempts', 'eq', 1); 198 } 199 200 // ------------------------------------------------------------------------------- 201 $mform->addElement('header', 'reviewoptionshdr', 202 get_string('reviewoptionsheading', 'quiz')); 203 $mform->addHelpButton('reviewoptionshdr', 'reviewoptionsheading', 'quiz'); 204 205 // Review options. 206 $this->add_review_options_group($mform, $quizconfig, 'during', 207 mod_quiz_display_options::DURING, true); 208 $this->add_review_options_group($mform, $quizconfig, 'immediately', 209 mod_quiz_display_options::IMMEDIATELY_AFTER); 210 $this->add_review_options_group($mform, $quizconfig, 'open', 211 mod_quiz_display_options::LATER_WHILE_OPEN); 212 $this->add_review_options_group($mform, $quizconfig, 'closed', 213 mod_quiz_display_options::AFTER_CLOSE); 214 215 foreach ($behaviours as $behaviour => $notused) { 216 $unusedoptions = question_engine::get_behaviour_unused_display_options($behaviour); 217 foreach ($unusedoptions as $unusedoption) { 218 $mform->disabledIf($unusedoption . 'during', 'preferredbehaviour', 219 'eq', $behaviour); 220 } 221 } 222 $mform->disabledIf('attemptduring', 'preferredbehaviour', 223 'neq', 'wontmatch'); 224 $mform->disabledIf('overallfeedbackduring', 'preferredbehaviour', 225 'neq', 'wontmatch'); 226 foreach (self::$reviewfields as $field => $notused) { 227 $mform->disabledIf($field . 'closed', 'timeclose[enabled]'); 228 } 229 230 // ------------------------------------------------------------------------------- 231 $mform->addElement('header', 'display', get_string('appearance')); 232 233 // Show user picture. 234 $mform->addElement('select', 'showuserpicture', get_string('showuserpicture', 'quiz'), 235 quiz_get_user_image_options()); 236 $mform->addHelpButton('showuserpicture', 'showuserpicture', 'quiz'); 237 238 // Overall decimal points. 239 $options = array(); 240 for ($i = 0; $i <= QUIZ_MAX_DECIMAL_OPTION; $i++) { 241 $options[$i] = $i; 242 } 243 $mform->addElement('select', 'decimalpoints', get_string('decimalplaces', 'quiz'), 244 $options); 245 $mform->addHelpButton('decimalpoints', 'decimalplaces', 'quiz'); 246 247 // Question decimal points. 248 $options = array(-1 => get_string('sameasoverall', 'quiz')); 249 for ($i = 0; $i <= QUIZ_MAX_Q_DECIMAL_OPTION; $i++) { 250 $options[$i] = $i; 251 } 252 $mform->addElement('select', 'questiondecimalpoints', 253 get_string('decimalplacesquestion', 'quiz'), $options); 254 $mform->addHelpButton('questiondecimalpoints', 'decimalplacesquestion', 'quiz'); 255 256 // Show blocks during quiz attempt. 257 $mform->addElement('selectyesno', 'showblocks', get_string('showblocks', 'quiz')); 258 $mform->addHelpButton('showblocks', 'showblocks', 'quiz'); 259 260 // ------------------------------------------------------------------------------- 261 $mform->addElement('header', 'security', get_string('extraattemptrestrictions', 'quiz')); 262 263 // Require password to begin quiz attempt. 264 $mform->addElement('passwordunmask', 'quizpassword', get_string('requirepassword', 'quiz')); 265 $mform->setType('quizpassword', PARAM_TEXT); 266 $mform->addHelpButton('quizpassword', 'requirepassword', 'quiz'); 267 268 // IP address. 269 $mform->addElement('text', 'subnet', get_string('requiresubnet', 'quiz')); 270 $mform->setType('subnet', PARAM_TEXT); 271 $mform->addHelpButton('subnet', 'requiresubnet', 'quiz'); 272 273 // Enforced time delay between quiz attempts. 274 $mform->addElement('duration', 'delay1', get_string('delay1st2nd', 'quiz'), 275 array('optional' => true)); 276 $mform->addHelpButton('delay1', 'delay1st2nd', 'quiz'); 277 if ($this->get_max_attempts_for_any_override() < 2) { 278 $mform->hideIf('delay1', 'attempts', 'eq', 1); 279 } 280 281 $mform->addElement('duration', 'delay2', get_string('delaylater', 'quiz'), 282 array('optional' => true)); 283 $mform->addHelpButton('delay2', 'delaylater', 'quiz'); 284 if ($this->get_max_attempts_for_any_override() < 3) { 285 $mform->hideIf('delay2', 'attempts', 'eq', 1); 286 $mform->hideIf('delay2', 'attempts', 'eq', 2); 287 } 288 289 // Browser security choices. 290 $mform->addElement('select', 'browsersecurity', get_string('browsersecurity', 'quiz'), 291 quiz_access_manager::get_browser_security_choices()); 292 $mform->addHelpButton('browsersecurity', 'browsersecurity', 'quiz'); 293 294 // Any other rule plugins. 295 quiz_access_manager::add_settings_form_fields($this, $mform); 296 297 // ------------------------------------------------------------------------------- 298 $mform->addElement('header', 'overallfeedbackhdr', get_string('overallfeedback', 'quiz')); 299 $mform->addHelpButton('overallfeedbackhdr', 'overallfeedback', 'quiz'); 300 301 if (isset($this->current->grade)) { 302 $needwarning = $this->current->grade === 0; 303 } else { 304 $needwarning = $quizconfig->maximumgrade == 0; 305 } 306 if ($needwarning) { 307 $mform->addElement('static', 'nogradewarning', '', 308 get_string('nogradewarning', 'quiz')); 309 } 310 311 $mform->addElement('static', 'gradeboundarystatic1', 312 get_string('gradeboundary', 'quiz'), '100%'); 313 314 $repeatarray = array(); 315 $repeatedoptions = array(); 316 $repeatarray[] = $mform->createElement('editor', 'feedbacktext', 317 get_string('feedback', 'quiz'), array('rows' => 3), array('maxfiles' => EDITOR_UNLIMITED_FILES, 318 'noclean' => true, 'context' => $this->context)); 319 $repeatarray[] = $mform->createElement('text', 'feedbackboundaries', 320 get_string('gradeboundary', 'quiz'), array('size' => 10)); 321 $repeatedoptions['feedbacktext']['type'] = PARAM_RAW; 322 $repeatedoptions['feedbackboundaries']['type'] = PARAM_RAW; 323 324 if (!empty($this->_instance)) { 325 $this->_feedbacks = $DB->get_records('quiz_feedback', 326 array('quizid' => $this->_instance), 'mingrade DESC'); 327 $numfeedbacks = count($this->_feedbacks); 328 } else { 329 $this->_feedbacks = array(); 330 $numfeedbacks = $quizconfig->initialnumfeedbacks; 331 } 332 $numfeedbacks = max($numfeedbacks, 1); 333 334 $nextel = $this->repeat_elements($repeatarray, $numfeedbacks - 1, 335 $repeatedoptions, 'boundary_repeats', 'boundary_add_fields', 3, 336 get_string('addmoreoverallfeedbacks', 'quiz'), true); 337 338 // Put some extra elements in before the button. 339 $mform->insertElementBefore($mform->createElement('editor', 340 "feedbacktext[$nextel]", get_string('feedback', 'quiz'), array('rows' => 3), 341 array('maxfiles' => EDITOR_UNLIMITED_FILES, 'noclean' => true, 342 'context' => $this->context)), 343 'boundary_add_fields'); 344 $mform->insertElementBefore($mform->createElement('static', 345 'gradeboundarystatic2', get_string('gradeboundary', 'quiz'), '0%'), 346 'boundary_add_fields'); 347 348 // Add the disabledif rules. We cannot do this using the $repeatoptions parameter to 349 // repeat_elements because we don't want to dissable the first feedbacktext. 350 for ($i = 0; $i < $nextel; $i++) { 351 $mform->disabledIf('feedbackboundaries[' . $i . ']', 'grade', 'eq', 0); 352 $mform->disabledIf('feedbacktext[' . ($i + 1) . ']', 'grade', 'eq', 0); 353 } 354 355 // ------------------------------------------------------------------------------- 356 $this->standard_coursemodule_elements(); 357 358 // Check and act on whether setting outcomes is considered an advanced setting. 359 $mform->setAdvanced('modoutcomes', !empty($quizconfig->outcomes_adv)); 360 361 // The standard_coursemodule_elements method sets this to 100, but the 362 // quiz has its own setting, so use that. 363 $mform->setDefault('grade', $quizconfig->maximumgrade); 364 365 // ------------------------------------------------------------------------------- 366 $this->apply_admin_defaults(); 367 $this->add_action_buttons(); 368 369 $PAGE->requires->yui_module('moodle-mod_quiz-modform', 'M.mod_quiz.modform.init'); 370 } 371 372 protected function add_review_options_group($mform, $quizconfig, $whenname, 373 $when, $withhelp = false) { 374 global $OUTPUT; 375 376 $group = array(); 377 foreach (self::$reviewfields as $field => $string) { 378 list($identifier, $component) = $string; 379 380 $label = get_string($identifier, $component); 381 $group[] = $mform->createElement('html', html_writer::start_div('review_option_item')); 382 $el = $mform->createElement('checkbox', $field . $whenname, '', $label); 383 if ($withhelp) { 384 $el->_helpbutton = $OUTPUT->render(new help_icon($identifier, $component)); 385 } 386 $group[] = $el; 387 $group[] = $mform->createElement('html', html_writer::end_div()); 388 } 389 $mform->addGroup($group, $whenname . 'optionsgrp', 390 get_string('review' . $whenname, 'quiz'), null, false); 391 392 foreach (self::$reviewfields as $field => $notused) { 393 $cfgfield = 'review' . $field; 394 if ($quizconfig->$cfgfield & $when) { 395 $mform->setDefault($field . $whenname, 1); 396 } else { 397 $mform->setDefault($field . $whenname, 0); 398 } 399 } 400 401 if ($whenname != 'during') { 402 $mform->disabledIf('correctness' . $whenname, 'attempt' . $whenname); 403 $mform->disabledIf('specificfeedback' . $whenname, 'attempt' . $whenname); 404 $mform->disabledIf('generalfeedback' . $whenname, 'attempt' . $whenname); 405 $mform->disabledIf('rightanswer' . $whenname, 'attempt' . $whenname); 406 } 407 } 408 409 protected function preprocessing_review_settings(&$toform, $whenname, $when) { 410 foreach (self::$reviewfields as $field => $notused) { 411 $fieldname = 'review' . $field; 412 if (array_key_exists($fieldname, $toform)) { 413 $toform[$field . $whenname] = $toform[$fieldname] & $when; 414 } 415 } 416 } 417 418 public function data_preprocessing(&$toform) { 419 if (isset($toform['grade'])) { 420 // Convert to a real number, so we don't get 0.0000. 421 $toform['grade'] = $toform['grade'] + 0; 422 } 423 424 if (count($this->_feedbacks)) { 425 $key = 0; 426 foreach ($this->_feedbacks as $feedback) { 427 $draftid = file_get_submitted_draft_itemid('feedbacktext['.$key.']'); 428 $toform['feedbacktext['.$key.']']['text'] = file_prepare_draft_area( 429 $draftid, // Draftid. 430 $this->context->id, // Context. 431 'mod_quiz', // Component. 432 'feedback', // Filarea. 433 !empty($feedback->id) ? (int) $feedback->id : null, // Itemid. 434 null, 435 $feedback->feedbacktext // Text. 436 ); 437 $toform['feedbacktext['.$key.']']['format'] = $feedback->feedbacktextformat; 438 $toform['feedbacktext['.$key.']']['itemid'] = $draftid; 439 440 if ($toform['grade'] == 0) { 441 // When a quiz is un-graded, there can only be one lot of 442 // feedback. If the quiz previously had a maximum grade and 443 // several lots of feedback, we must now avoid putting text 444 // into input boxes that are disabled, but which the 445 // validation will insist are blank. 446 break; 447 } 448 449 if ($feedback->mingrade > 0) { 450 $toform['feedbackboundaries['.$key.']'] = 451 round(100.0 * $feedback->mingrade / $toform['grade'], 6) . '%'; 452 } 453 $key++; 454 } 455 } 456 457 if (isset($toform['timelimit'])) { 458 $toform['timelimitenable'] = $toform['timelimit'] > 0; 459 } 460 461 $this->preprocessing_review_settings($toform, 'during', 462 mod_quiz_display_options::DURING); 463 $this->preprocessing_review_settings($toform, 'immediately', 464 mod_quiz_display_options::IMMEDIATELY_AFTER); 465 $this->preprocessing_review_settings($toform, 'open', 466 mod_quiz_display_options::LATER_WHILE_OPEN); 467 $this->preprocessing_review_settings($toform, 'closed', 468 mod_quiz_display_options::AFTER_CLOSE); 469 $toform['attemptduring'] = true; 470 $toform['overallfeedbackduring'] = false; 471 472 // Password field - different in form to stop browsers that remember 473 // passwords from getting confused. 474 if (isset($toform['password'])) { 475 $toform['quizpassword'] = $toform['password']; 476 unset($toform['password']); 477 } 478 479 // Load any settings belonging to the access rules. 480 if (!empty($toform['instance'])) { 481 $accesssettings = quiz_access_manager::load_settings($toform['instance']); 482 foreach ($accesssettings as $name => $value) { 483 $toform[$name] = $value; 484 } 485 } 486 487 if (empty($toform['completionminattempts'])) { 488 $toform['completionminattempts'] = 1; 489 } else { 490 $toform['completionminattemptsenabled'] = $toform['completionminattempts'] > 0; 491 } 492 } 493 494 /** 495 * Allows module to modify the data returned by form get_data(). 496 * This method is also called in the bulk activity completion form. 497 * 498 * Only available on moodleform_mod. 499 * 500 * @param stdClass $data the form data to be modified. 501 */ 502 public function data_postprocessing($data) { 503 parent::data_postprocessing($data); 504 if (!empty($data->completionunlocked)) { 505 // Turn off completion settings if the checkboxes aren't ticked. 506 $autocompletion = !empty($data->completion) && $data->completion == COMPLETION_TRACKING_AUTOMATIC; 507 if (empty($data->completionminattemptsenabled) || !$autocompletion) { 508 $data->completionminattempts = 0; 509 } 510 } 511 } 512 513 public function validation($data, $files) { 514 $errors = parent::validation($data, $files); 515 516 // Check open and close times are consistent. 517 if ($data['timeopen'] != 0 && $data['timeclose'] != 0 && 518 $data['timeclose'] < $data['timeopen']) { 519 $errors['timeclose'] = get_string('closebeforeopen', 'quiz'); 520 } 521 522 // Check that the grace period is not too short. 523 if ($data['overduehandling'] == 'graceperiod') { 524 $graceperiodmin = get_config('quiz', 'graceperiodmin'); 525 if ($data['graceperiod'] <= $graceperiodmin) { 526 $errors['graceperiod'] = get_string('graceperiodtoosmall', 'quiz', format_time($graceperiodmin)); 527 } 528 } 529 530 if (array_key_exists('completion', $data) && $data['completion'] == COMPLETION_TRACKING_AUTOMATIC) { 531 $completionpass = isset($data['completionpass']) ? $data['completionpass'] : $this->current->completionpass; 532 533 // Show an error if require passing grade was selected and the grade to pass was set to 0. 534 if ($completionpass && (empty($data['gradepass']) || grade_floatval($data['gradepass']) == 0)) { 535 if (isset($data['completionpass'])) { 536 $errors['completionpassgroup'] = get_string('gradetopassnotset', 'quiz'); 537 } else { 538 $errors['gradepass'] = get_string('gradetopassmustbeset', 'quiz'); 539 } 540 } 541 } 542 543 if (!empty($data['completionminattempts'])) { 544 if ($data['attempts'] > 0 && $data['completionminattempts'] > $data['attempts']) { 545 $errors['completionminattemptsgroup'] = get_string('completionminattemptserror', 'quiz'); 546 } 547 } 548 549 // Check the boundary value is a number or a percentage, and in range. 550 $i = 0; 551 while (!empty($data['feedbackboundaries'][$i] )) { 552 $boundary = trim($data['feedbackboundaries'][$i]); 553 if (strlen($boundary) > 0) { 554 if ($boundary[strlen($boundary) - 1] == '%') { 555 $boundary = trim(substr($boundary, 0, -1)); 556 if (is_numeric($boundary)) { 557 $boundary = $boundary * $data['grade'] / 100.0; 558 } else { 559 $errors["feedbackboundaries[$i]"] = 560 get_string('feedbackerrorboundaryformat', 'quiz', $i + 1); 561 } 562 } else if (!is_numeric($boundary)) { 563 $errors["feedbackboundaries[$i]"] = 564 get_string('feedbackerrorboundaryformat', 'quiz', $i + 1); 565 } 566 } 567 if (is_numeric($boundary) && $boundary <= 0 || $boundary >= $data['grade'] ) { 568 $errors["feedbackboundaries[$i]"] = 569 get_string('feedbackerrorboundaryoutofrange', 'quiz', $i + 1); 570 } 571 if (is_numeric($boundary) && $i > 0 && 572 $boundary >= $data['feedbackboundaries'][$i - 1]) { 573 $errors["feedbackboundaries[$i]"] = 574 get_string('feedbackerrororder', 'quiz', $i + 1); 575 } 576 $data['feedbackboundaries'][$i] = $boundary; 577 $i += 1; 578 } 579 $numboundaries = $i; 580 581 // Check there is nothing in the remaining unused fields. 582 if (!empty($data['feedbackboundaries'])) { 583 for ($i = $numboundaries; $i < count($data['feedbackboundaries']); $i += 1) { 584 if (!empty($data['feedbackboundaries'][$i] ) && 585 trim($data['feedbackboundaries'][$i] ) != '') { 586 $errors["feedbackboundaries[$i]"] = 587 get_string('feedbackerrorjunkinboundary', 'quiz', $i + 1); 588 } 589 } 590 } 591 for ($i = $numboundaries + 1; $i < count($data['feedbacktext']); $i += 1) { 592 if (!empty($data['feedbacktext'][$i]['text']) && 593 trim($data['feedbacktext'][$i]['text'] ) != '') { 594 $errors["feedbacktext[$i]"] = 595 get_string('feedbackerrorjunkinfeedback', 'quiz', $i + 1); 596 } 597 } 598 599 // If CBM is involved, don't show the warning for grade to pass being larger than the maximum grade. 600 if (($data['preferredbehaviour'] == 'deferredcbm') OR ($data['preferredbehaviour'] == 'immediatecbm')) { 601 unset($errors['gradepass']); 602 } 603 // Any other rule plugins. 604 $errors = quiz_access_manager::validate_settings_form_fields($errors, $data, $files, $this); 605 606 return $errors; 607 } 608 609 /** 610 * Display module-specific activity completion rules. 611 * Part of the API defined by moodleform_mod 612 * @return array Array of string IDs of added items, empty array if none 613 */ 614 public function add_completion_rules() { 615 $mform = $this->_form; 616 $items = array(); 617 618 $group = array(); 619 $group[] = $mform->createElement('advcheckbox', 'completionpass', null, get_string('completionpass', 'quiz'), 620 array('group' => 'cpass')); 621 $mform->disabledIf('completionpass', 'completionusegrade', 'notchecked'); 622 $group[] = $mform->createElement('advcheckbox', 'completionattemptsexhausted', null, 623 get_string('completionattemptsexhausted', 'quiz'), 624 array('group' => 'cattempts')); 625 $mform->disabledIf('completionattemptsexhausted', 'completionpass', 'notchecked'); 626 $mform->addGroup($group, 'completionpassgroup', get_string('completionpass', 'quiz'), ' ', false); 627 $mform->addHelpButton('completionpassgroup', 'completionpass', 'quiz'); 628 $items[] = 'completionpassgroup'; 629 630 $group = array(); 631 $group[] = $mform->createElement('checkbox', 'completionminattemptsenabled', '', 632 get_string('completionminattempts', 'quiz')); 633 $group[] = $mform->createElement('text', 'completionminattempts', '', array('size' => 3)); 634 $mform->setType('completionminattempts', PARAM_INT); 635 $mform->addGroup($group, 'completionminattemptsgroup', get_string('completionminattemptsgroup', 'quiz'), array(' '), false); 636 $mform->disabledIf('completionminattempts', 'completionminattemptsenabled', 'notchecked'); 637 638 $items[] = 'completionminattemptsgroup'; 639 640 return $items; 641 } 642 643 /** 644 * Called during validation. Indicates whether a module-specific completion rule is selected. 645 * 646 * @param array $data Input data (not yet validated) 647 * @return bool True if one or more rules is enabled, false if none are. 648 */ 649 public function completion_rule_enabled($data) { 650 return !empty($data['completionattemptsexhausted']) || 651 !empty($data['completionpass']) || 652 !empty($data['completionminattemptsenabled']); 653 } 654 655 /** 656 * Get the maximum number of attempts that anyone might have due to a user 657 * or group override. Used to decide whether disabledIf rules should be applied. 658 * @return int the number of attempts allowed. For the purpose of this method, 659 * unlimited is returned as 1000, not 0. 660 */ 661 public function get_max_attempts_for_any_override() { 662 global $DB; 663 664 if (empty($this->_instance)) { 665 // Quiz not created yet, so no overrides. 666 return 1; 667 } 668 669 if ($this->maxattemptsanyoverride === null) { 670 $this->maxattemptsanyoverride = $DB->get_field_sql(" 671 SELECT MAX(CASE WHEN attempts = 0 THEN 1000 ELSE attempts END) 672 FROM {quiz_overrides} 673 WHERE quiz = ?", 674 array($this->_instance)); 675 if ($this->maxattemptsanyoverride < 1) { 676 // This happens when no override alters the number of attempts. 677 $this->maxattemptsanyoverride = 1; 678 } 679 } 680 681 return $this->maxattemptsanyoverride; 682 } 683 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body