public function load_choices() { global $CFG; if (is_array($this->choices)) { return true; } require_once $CFG->dirroot . '/mod/quiz/locallib.php'; $this->choices = quiz_access_manager::get_browser_security_choices(); return true; }
/** * Used by {create()} and {create_from_usage_id()}. * @param array $conditions passed to $DB->get_record('quiz_attempts', $conditions). */ protected static function create_helper($conditions) { global $DB; $attempt = $DB->get_record('quiz_attempts', $conditions, '*', MUST_EXIST); $quiz = quiz_access_manager::load_quiz_and_settings($attempt->quiz); $course = $DB->get_record('course', array('id' => $quiz->course), '*', MUST_EXIST); $cm = get_coursemodule_from_instance('quiz', $quiz->id, $course->id, false, MUST_EXIST); // Update quiz with override information. $quiz = quiz_update_effective_access($quiz, $attempt->userid); return new quiz_attempt($attempt, $quiz, $cm, $course); }
} // Check login and get context. require_login($course, false, $cm); $context = get_context_instance(CONTEXT_MODULE, $cm->id); require_capability('mod/quiz:view', $context); // Cache some other capabilities we use several times. $canattempt = has_capability('mod/quiz:attempt', $context); $canreviewmine = has_capability('mod/quiz:reviewmyattempts', $context); $canpreview = has_capability('mod/quiz:preview', $context); // Create an object to manage all the other (non-roles) access rules. $timenow = time(); $quizobj = quiz::create($cm->instance, $USER->id); $accessmanager = new quiz_access_manager($quizobj, $timenow, has_capability('mod/quiz:ignoretimelimits', $context, null, false)); $quiz = $quizobj->get_quiz(); // Log this request. add_to_log($course->id, 'quiz', 'view', 'view.php?id=' . $cm->id, $quiz->id, $cm->id); $completion = new completion_info($course); $completion->set_module_viewed($cm); // Initialize $PAGE, compute blocks $PAGE->set_url('/mod/quiz/view.php', array('id' => $cm->id)); // Get this user's attempts. $attempts = quiz_get_user_attempts($quiz->id, $USER->id, 'finished', true); $lastfinishedattempt = end($attempts); $unfinished = false;
/** * Return access information for a given attempt in a quiz. * * @param int $quizid quiz instance id * @param int $attemptid attempt id, 0 for the user last attempt if exists * @return array of warnings and the access information * @since Moodle 3.1 * @throws moodle_quiz_exception */ public static function get_attempt_access_information($quizid, $attemptid = 0) { global $DB, $USER; $warnings = array(); $params = array('quizid' => $quizid, 'attemptid' => $attemptid); $params = self::validate_parameters(self::get_attempt_access_information_parameters(), $params); list($quiz, $course, $cm, $context) = self::validate_quiz($params['quizid']); $attempttocheck = 0; if (!empty($params['attemptid'])) { $attemptobj = quiz_attempt::create($params['attemptid']); if ($attemptobj->get_userid() != $USER->id) { throw new moodle_quiz_exception($attemptobj->get_quizobj(), 'notyourattempt'); } $attempttocheck = $attemptobj->get_attempt(); } // Access manager now. $quizobj = quiz::create($cm->instance, $USER->id); $ignoretimelimits = has_capability('mod/quiz:ignoretimelimits', $context, null, false); $timenow = time(); $accessmanager = new quiz_access_manager($quizobj, $timenow, $ignoretimelimits); $attempts = quiz_get_user_attempts($quiz->id, $USER->id, 'finished', true); $lastfinishedattempt = end($attempts); if ($unfinishedattempt = quiz_get_user_attempt_unfinished($quiz->id, $USER->id)) { $attempts[] = $unfinishedattempt; // Check if the attempt is now overdue. In that case the state will change. $quizobj->create_attempt_object($unfinishedattempt)->handle_if_time_expired(time(), false); if ($unfinishedattempt->state != quiz_attempt::IN_PROGRESS and $unfinishedattempt->state != quiz_attempt::OVERDUE) { $lastfinishedattempt = $unfinishedattempt; } } $numattempts = count($attempts); if (!$attempttocheck) { $attempttocheck = $unfinishedattempt ? $unfinishedattempt : $lastfinishedattempt; } $result = array(); $result['isfinished'] = $accessmanager->is_finished($numattempts, $lastfinishedattempt); $result['preventnewattemptreasons'] = $accessmanager->prevent_new_attempt($numattempts, $lastfinishedattempt); if ($attempttocheck) { $endtime = $accessmanager->get_end_time($attempttocheck); $result['endtime'] = $endtime === false ? 0 : $endtime; $attemptid = $unfinishedattempt ? $unfinishedattempt->id : null; $result['ispreflightcheckrequired'] = $accessmanager->is_preflight_check_required($attemptid); } $result['warnings'] = $warnings; return $result; }
if (!($cm = get_coursemodule_from_instance("quiz", $quiz->id, $course->id))) { print_error('invalidcoursemodule'); } } // Check login and get context. require_login($course, false, $cm); $context = context_module::instance($cm->id); require_capability('mod/quiz:view', $context); // Cache some other capabilities we use several times. $canattempt = has_capability('mod/quiz:attempt', $context); $canreviewmine = has_capability('mod/quiz:reviewmyattempts', $context); $canpreview = has_capability('mod/quiz:preview', $context); // Create an object to manage all the other (non-roles) access rules. $timenow = time(); $quizobj = quiz::create($cm->instance, $USER->id); $accessmanager = new quiz_access_manager($quizobj, $timenow, has_capability('mod/quiz:ignoretimelimits', $context, null, false)); $quiz = $quizobj->get_quiz(); // Trigger course_module_viewed event and completion. quiz_view($quiz, $course, $cm, $context); // Initialize $PAGE, compute blocks. $PAGE->set_url('/mod/quiz/view.php', array('id' => $cm->id)); // Create view object which collects all the information the renderer will need. $viewobj = new mod_quiz_view_object(); $viewobj->accessmanager = $accessmanager; $viewobj->canreviewmine = $canreviewmine; // Get this user's attempts. $attempts = quiz_get_user_attempts($quiz->id, $USER->id, 'finished', true); $lastfinishedattempt = end($attempts); $unfinished = false; $unfinishedattemptid = null; if ($unfinishedattempt = quiz_get_user_attempt_unfinished($quiz->id, $USER->id)) {
public function validation($data, $files) { $errors = parent::validation($data, $files); // Check open and close times are consistent. if ($data['timeopen'] != 0 && $data['timeclose'] != 0 && $data['timeclose'] < $data['timeopen']) { $errors['timeclose'] = get_string('closebeforeopen', 'quiz'); } // Check that the grace period is not too short. if ($data['overduehandling'] == 'graceperiod') { $graceperiodmin = get_config('quiz', 'graceperiodmin'); if ($data['graceperiod'] <= $graceperiodmin) { $errors['graceperiod'] = get_string('graceperiodtoosmall', 'quiz', format_time($graceperiodmin)); } } // Check the boundary value is a number or a percentage, and in range. $i = 0; while (!empty($data['feedbackboundaries'][$i])) { $boundary = trim($data['feedbackboundaries'][$i]); if (strlen($boundary) > 0) { if ($boundary[strlen($boundary) - 1] == '%') { $boundary = trim(substr($boundary, 0, -1)); if (is_numeric($boundary)) { $boundary = $boundary * $data['grade'] / 100.0; } else { $errors["feedbackboundaries[{$i}]"] = get_string('feedbackerrorboundaryformat', 'quiz', $i + 1); } } else { if (!is_numeric($boundary)) { $errors["feedbackboundaries[{$i}]"] = get_string('feedbackerrorboundaryformat', 'quiz', $i + 1); } } } if (is_numeric($boundary) && $boundary <= 0 || $boundary >= $data['grade']) { $errors["feedbackboundaries[{$i}]"] = get_string('feedbackerrorboundaryoutofrange', 'quiz', $i + 1); } if (is_numeric($boundary) && $i > 0 && $boundary >= $data['feedbackboundaries'][$i - 1]) { $errors["feedbackboundaries[{$i}]"] = get_string('feedbackerrororder', 'quiz', $i + 1); } $data['feedbackboundaries'][$i] = $boundary; $i += 1; } $numboundaries = $i; // Check there is nothing in the remaining unused fields. if (!empty($data['feedbackboundaries'])) { for ($i = $numboundaries; $i < count($data['feedbackboundaries']); $i += 1) { if (!empty($data['feedbackboundaries'][$i]) && trim($data['feedbackboundaries'][$i]) != '') { $errors["feedbackboundaries[{$i}]"] = get_string('feedbackerrorjunkinboundary', 'quiz', $i + 1); } } } for ($i = $numboundaries + 1; $i < count($data['feedbacktext']); $i += 1) { if (!empty($data['feedbacktext'][$i]['text']) && trim($data['feedbacktext'][$i]['text']) != '') { $errors["feedbacktext[{$i}]"] = get_string('feedbackerrorjunkinfeedback', 'quiz', $i + 1); } } // Any other rule plugins. $errors = quiz_access_manager::validate_settings_form_fields($errors, $data, $files, $this); return $errors; }
} if (!($cm = get_coursemodule_from_instance("quiz", $quiz->id, $course->id))) { print_error('invalidcoursemodule'); } } /// Check login and get context. require_login($course->id, false, $cm); $context = get_context_instance(CONTEXT_MODULE, $cm->id); require_capability('mod/quiz:view', $context); /// Cache some other capabilites we use several times. $canattempt = has_capability('mod/quiz:attempt', $context); $canreviewmine = has_capability('mod/quiz:reviewmyattempts', $context); $canpreview = has_capability('mod/quiz:preview', $context); /// Create an object to manage all the other (non-roles) access rules. $timenow = time(); $accessmanager = new quiz_access_manager(new quiz($quiz, $cm, $course), $timenow, has_capability('mod/quiz:ignoretimelimits', $context, NULL, false)); /// If no questions have been set up yet redirect to edit.php if (!$quiz->questions && has_capability('mod/quiz:manage', $context)) { redirect($CFG->wwwroot . '/mod/quiz/edit.php?cmid=' . $cm->id); } /// Log this request. add_to_log($course->id, "quiz", "view", "view.php?id={$cm->id}", $quiz->id, $cm->id); /// Initialize $PAGE, compute blocks $PAGE->set_url('mod/quiz/view.php', array('id' => $cm->id)); $edit = optional_param('edit', -1, PARAM_BOOL); if ($edit != -1 && $PAGE->user_allowed_editing()) { $USER->editing = $edit; } /// Print the page header $bodytags = ''; if ($accessmanager->securewindow_required($canpreview)) {
public function data_preprocessing(&$toform) { if (isset($toform['grade'])) { // Convert to a real number, so we don't get 0.0000. $toform['grade'] = $toform['grade'] + 0; } if (count($this->_feedbacks)) { $key = 0; foreach ($this->_feedbacks as $feedback) { $draftid = file_get_submitted_draft_itemid('feedbacktext[' . $key . ']'); $toform['feedbacktext[' . $key . ']']['text'] = file_prepare_draft_area($draftid, $this->context->id, 'mod_quiz', 'feedback', !empty($feedback->id) ? (int) $feedback->id : null, null, $feedback->feedbacktext); $toform['feedbacktext[' . $key . ']']['format'] = $feedback->feedbacktextformat; $toform['feedbacktext[' . $key . ']']['itemid'] = $draftid; if ($toform['grade'] == 0) { // When a quiz is un-graded, there can only be one lot of // feedback. If the quiz previously had a maximum grade and // several lots of feedback, we must now avoid putting text // into input boxes that are disabled, but which the // validation will insist are blank. break; } if ($feedback->mingrade > 0) { $toform['feedbackboundaries[' . $key . ']'] = 100.0 * $feedback->mingrade / $toform['grade'] . '%'; } $key++; } } if (isset($toform['timelimit'])) { $toform['timelimitenable'] = $toform['timelimit'] > 0; } $this->preprocessing_review_settings($toform, 'during', mod_quiz_display_options::DURING); $this->preprocessing_review_settings($toform, 'immediately', mod_quiz_display_options::IMMEDIATELY_AFTER); $this->preprocessing_review_settings($toform, 'open', mod_quiz_display_options::LATER_WHILE_OPEN); $this->preprocessing_review_settings($toform, 'closed', mod_quiz_display_options::AFTER_CLOSE); $toform['attemptduring'] = true; $toform['overallfeedbackduring'] = false; // Password field - different in form to stop browsers that remember // passwords from getting confused. if (isset($toform['password'])) { $toform['quizpassword'] = $toform['password']; unset($toform['password']); } // Load any settings belonging to the access rules. if (!empty($toform['instance'])) { $accesssettings = quiz_access_manager::load_settings($toform['instance']); foreach ($accesssettings as $name => $value) { $toform[$name] = $value; } } }
/** * This function is called at the end of quiz_add_instance * and quiz_update_instance, to do the common processing. * * @param object $quiz the quiz object. */ function quiz_after_add_or_update($quiz) { global $DB; $cmid = $quiz->coursemodule; // We need to use context now, so we need to make sure all needed info is already in db. $DB->set_field('course_modules', 'instance', $quiz->id, array('id'=>$cmid)); $context = context_module::instance($cmid); // Save the feedback. $DB->delete_records('quiz_feedback', array('quizid' => $quiz->id)); for ($i = 0; $i <= $quiz->feedbackboundarycount; $i++) { $feedback = new stdClass(); $feedback->quizid = $quiz->id; $feedback->feedbacktext = $quiz->feedbacktext[$i]['text']; $feedback->feedbacktextformat = $quiz->feedbacktext[$i]['format']; $feedback->mingrade = $quiz->feedbackboundaries[$i]; $feedback->maxgrade = $quiz->feedbackboundaries[$i - 1]; $feedback->id = $DB->insert_record('quiz_feedback', $feedback); $feedbacktext = file_save_draft_area_files((int)$quiz->feedbacktext[$i]['itemid'], $context->id, 'mod_quiz', 'feedback', $feedback->id, array('subdirs' => false, 'maxfiles' => -1, 'maxbytes' => 0), $quiz->feedbacktext[$i]['text']); $DB->set_field('quiz_feedback', 'feedbacktext', $feedbacktext, array('id' => $feedback->id)); } // Store any settings belonging to the access rules. quiz_access_manager::save_settings($quiz); // Update the events relating to this quiz. quiz_update_events($quiz); // Update related grade item. quiz_grade_item_update($quiz); }
/** * Returns a list of quizzes in a provided list of courses, * if no list is provided all quizzes that the user can view will be returned. * * @param array $courseids Array of course ids * @return array of quizzes details * @since Moodle 3.1 */ public static function get_quizzes_by_courses($courseids = array()) { global $USER; $warnings = array(); $returnedquizzes = array(); $params = array('courseids' => $courseids); $params = self::validate_parameters(self::get_quizzes_by_courses_parameters(), $params); $mycourses = array(); if (empty($params['courseids'])) { $mycourses = enrol_get_my_courses(); $params['courseids'] = array_keys($mycourses); } // Ensure there are courseids to loop through. if (!empty($params['courseids'])) { list($courses, $warnings) = external_util::validate_courses($params['courseids'], $mycourses); // Get the quizzes in this course, this function checks users visibility permissions. // We can avoid then additional validate_context calls. $quizzes = get_all_instances_in_courses("quiz", $courses); foreach ($quizzes as $quiz) { $context = context_module::instance($quiz->coursemodule); // Update quiz with override information. $quiz = quiz_update_effective_access($quiz, $USER->id); // Entry to return. $quizdetails = array(); // First, we return information that any user can see in the web interface. $quizdetails['id'] = $quiz->id; $quizdetails['coursemodule'] = $quiz->coursemodule; $quizdetails['course'] = $quiz->course; $quizdetails['name'] = external_format_string($quiz->name, $context->id); if (has_capability('mod/quiz:view', $context)) { // Format intro. list($quizdetails['intro'], $quizdetails['introformat']) = external_format_text($quiz->intro, $quiz->introformat, $context->id, 'mod_quiz', 'intro', null); $viewablefields = array('timeopen', 'timeclose', 'grademethod', 'section', 'visible', 'groupmode', 'groupingid'); $timenow = time(); $quizobj = quiz::create($quiz->id, $USER->id); $accessmanager = new quiz_access_manager($quizobj, $timenow, has_capability('mod/quiz:ignoretimelimits', $context, null, false)); // Fields the user could see if have access to the quiz. if (!$accessmanager->prevent_access()) { // Some times this function returns just empty. $hasfeedback = quiz_has_feedback($quiz); $quizdetails['hasfeedback'] = !empty($hasfeedback) ? 1 : 0; $quizdetails['hasquestions'] = (int) $quizobj->has_questions(); $quizdetails['autosaveperiod'] = get_config('quiz', 'autosaveperiod'); $additionalfields = array('timelimit', 'attempts', 'attemptonlast', 'grademethod', 'decimalpoints', 'questiondecimalpoints', 'reviewattempt', 'reviewcorrectness', 'reviewmarks', 'reviewspecificfeedback', 'reviewgeneralfeedback', 'reviewrightanswer', 'reviewoverallfeedback', 'questionsperpage', 'navmethod', 'sumgrades', 'grade', 'browsersecurity', 'delay1', 'delay2', 'showuserpicture', 'showblocks', 'completionattemptsexhausted', 'completionpass', 'overduehandling', 'graceperiod', 'preferredbehaviour', 'canredoquestions'); $viewablefields = array_merge($viewablefields, $additionalfields); } // Fields only for managers. if (has_capability('moodle/course:manageactivities', $context)) { $additionalfields = array('shuffleanswers', 'timecreated', 'timemodified', 'password', 'subnet'); $viewablefields = array_merge($viewablefields, $additionalfields); } foreach ($viewablefields as $field) { $quizdetails[$field] = $quiz->{$field}; } } $returnedquizzes[] = $quizdetails; } } $result = array(); $result['quizzes'] = $returnedquizzes; $result['warnings'] = $warnings; return $result; }
/** * Obtains the automatic completion state for this quiz on any conditions * in quiz settings, such as if all attempts are used or a certain grade is achieved. * * @param object $course Course * @param object $cm Course-module * @param int $userid User ID * @param bool $type Type of comparison (or/and; can be used as return value if no conditions) * @return bool True if completed, false if not. (If no conditions, then return * value depends on comparison type) */ function quiz_get_completion_state($course, $cm, $userid, $type) { global $DB; global $CFG; $quiz = $DB->get_record('quiz', array('id' => $cm->instance), '*', MUST_EXIST); if (!$quiz->completionattemptsexhausted && !$quiz->completionpass) { return $type; } // Check if the user has used up all attempts. if ($quiz->completionattemptsexhausted) { $attempts = quiz_get_user_attempts($quiz->id, $userid, 'finished', true); if ($attempts) { $lastfinishedattempt = end($attempts); $context = context_module::instance($cm->id); $quizobj = quiz::create($quiz->id, $userid); $accessmanager = new quiz_access_manager($quizobj, time(), has_capability('mod/quiz:ignoretimelimits', $context, $userid, false)); if ($accessmanager->is_finished(count($attempts), $lastfinishedattempt)) { return true; } } } // Check for passing grade. if ($quiz->completionpass) { require_once $CFG->libdir . '/gradelib.php'; $item = grade_item::fetch(array('courseid' => $course->id, 'itemtype' => 'mod', 'itemmodule' => 'quiz', 'iteminstance' => $cm->instance, 'outcomeid' => null)); if ($item) { $grades = grade_grade::fetch_users_grades($item, array($userid), false); if (!empty($grades[$userid])) { return $grades[$userid]->is_passed($item); } } } return false; }
function prepareQuizAttempt($q) { global $DB; if ($id) { if (!($cm = get_coursemodule_from_id('quiz', $id))) { print_error('invalidcoursemodule'); } if (!($course = $DB->get_record('course', array('id' => $cm->course)))) { print_error('coursemisconf'); } if (!($quiz = $DB->get_record('quiz', array('id' => $cm->instance)))) { print_error('invalidcoursemodule'); } } else { if (!($quiz = $DB->get_record('quiz', array('id' => $q)))) { print_error('invalidquizid', 'quiz'); } if (!($course = $DB->get_record('course', array('id' => $quiz->course)))) { print_error('invalidcourseid'); } if (!($cm = get_coursemodule_from_instance("quiz", $quiz->id, $course->id))) { print_error('invalidcoursemodule'); } } /// Check login and get context. require_login($course->id, false, $cm); $context = get_context_instance(CONTEXT_MODULE, $cm->id); require_capability('mod/quiz:view', $context); /// Cache some other capabilites we use several times. $canattempt = has_capability('mod/quiz:attempt', $context); $canreviewmine = has_capability('mod/quiz:reviewmyattempts', $context); $canpreview = has_capability('mod/quiz:preview', $context); /// Create an object to manage all the other (non-roles) access rules. $timenow = time(); $accessmanager = new quiz_access_manager(new quiz($quiz, $cm, $course), $timenow, has_capability('mod/quiz:ignoretimelimits', $context, NULL, false)); /// Print information about the student's best score for this quiz if possible. $moreattempts = $unfinished || !$accessmanager->is_finished($numattempts, $lastfinishedattempt); /// Determine if we should be showing a start/continue attempt button, /// or a button to go back to the course page. print_box_start('quizattempt'); $buttontext = ''; // This will be set something if as start/continue attempt button should appear. if (!$quiz->questions) { print_heading(get_string("noquestions", "quiz")); } else { if ($unfinished) { if ($canattempt) { $buttontext = get_string('continueattemptquiz', 'quiz'); } else { if ($canpreview) { $buttontext = get_string('continuepreview', 'quiz'); } } } else { if ($canattempt) { $messages = $accessmanager->prevent_new_attempt($numattempts, $lastfinishedattempt); if ($messages) { $accessmanager->print_messages($messages); } else { if ($numattempts == 0) { $buttontext = get_string('attemptquiznow', 'quiz'); } else { $buttontext = get_string('reattemptquiz', 'quiz'); } } } else { if ($canpreview) { $buttontext = get_string('previewquiznow', 'quiz'); } } } // If, so far, we think a button should be printed, so check if they will be allowed to access it. if ($buttontext) { if (!$moreattempts) { $buttontext = ''; } else { if ($canattempt && ($messages = $accessmanager->prevent_access())) { $accessmanager->print_messages($messages); $buttontext = ''; } } } } /// Now actually print the appropriate button. if ($buttontext) { $accessmanager->print_start_attempt_button($canpreview, $buttontext, $unfinished); } else { print_continue($CFG->wwwroot . '/course/view.php?id=' . $course->id); } print_box_end(); }
public function test_cannot_review_message() { $quiz = new stdClass(); $quiz->reviewattempt = 0x10010; $quiz->timeclose = 0; $quiz->attempts = 0; $quiz->questions = '1,2,0,3,4,0'; $cm = new stdClass(); $cm->id = 123; $quizobj = new quiz($quiz, $cm, new stdClass(), false); $am = new quiz_access_manager($quizobj, time(), false); $this->assertEqual('', $am->cannot_review_message(mod_quiz_display_options::DURING)); $this->assertEqual('', $am->cannot_review_message(mod_quiz_display_options::IMMEDIATELY_AFTER)); $this->assertEqual(get_string('noreview', 'quiz'), $am->cannot_review_message(mod_quiz_display_options::LATER_WHILE_OPEN)); $this->assertEqual(get_string('noreview', 'quiz'), $am->cannot_review_message(mod_quiz_display_options::AFTER_CLOSE)); $closetime = time() + 10000; $quiz->timeclose = $closetime; $quizobj = new quiz($quiz, $cm, new stdClass(), false); $am = new quiz_access_manager($quizobj, time(), false); $this->assertEqual(get_string('noreviewuntil', 'quiz', userdate($closetime)), $am->cannot_review_message(mod_quiz_display_options::LATER_WHILE_OPEN)); }
public function validation($data, $files) { $errors = parent::validation($data, $files); // Check open and close times are consistent. if ($data['timeopen'] != 0 && $data['timeclose'] != 0 && $data['timeclose'] < $data['timeopen']) { $errors['timeclose'] = get_string('closebeforeopen', 'quiz'); } // Check that the grace period is not too short. if ($data['overduehandling'] == 'graceperiod') { $graceperiodmin = get_config('quiz', 'graceperiodmin'); if ($data['graceperiod'] <= $graceperiodmin) { $errors['graceperiod'] = get_string('graceperiodtoosmall', 'quiz', format_time($graceperiodmin)); } } if (array_key_exists('completion', $data) && $data['completion'] == COMPLETION_TRACKING_AUTOMATIC) { $completionpass = isset($data['completionpass']) ? $data['completionpass'] : $this->current->completionpass; // Show an error if require passing grade was selected and the grade to pass was set to 0. if ($completionpass && (empty($data['gradepass']) || grade_floatval($data['gradepass']) == 0)) { if (isset($data['completionpass'])) { $errors['completionpassgroup'] = get_string('gradetopassnotset', 'quiz'); } else { $errors['gradepass'] = get_string('gradetopassmustbeset', 'quiz'); } } } // Check the boundary value is a number or a percentage, and in range. $i = 0; while (!empty($data['feedbackboundaries'][$i])) { $boundary = trim($data['feedbackboundaries'][$i]); if (strlen($boundary) > 0) { if ($boundary[strlen($boundary) - 1] == '%') { $boundary = trim(substr($boundary, 0, -1)); if (is_numeric($boundary)) { $boundary = $boundary * $data['grade'] / 100.0; } else { $errors["feedbackboundaries[{$i}]"] = get_string('feedbackerrorboundaryformat', 'quiz', $i + 1); } } else { if (!is_numeric($boundary)) { $errors["feedbackboundaries[{$i}]"] = get_string('feedbackerrorboundaryformat', 'quiz', $i + 1); } } } if (is_numeric($boundary) && $boundary <= 0 || $boundary >= $data['grade']) { $errors["feedbackboundaries[{$i}]"] = get_string('feedbackerrorboundaryoutofrange', 'quiz', $i + 1); } if (is_numeric($boundary) && $i > 0 && $boundary >= $data['feedbackboundaries'][$i - 1]) { $errors["feedbackboundaries[{$i}]"] = get_string('feedbackerrororder', 'quiz', $i + 1); } $data['feedbackboundaries'][$i] = $boundary; $i += 1; } $numboundaries = $i; // Check there is nothing in the remaining unused fields. if (!empty($data['feedbackboundaries'])) { for ($i = $numboundaries; $i < count($data['feedbackboundaries']); $i += 1) { if (!empty($data['feedbackboundaries'][$i]) && trim($data['feedbackboundaries'][$i]) != '') { $errors["feedbackboundaries[{$i}]"] = get_string('feedbackerrorjunkinboundary', 'quiz', $i + 1); } } } for ($i = $numboundaries + 1; $i < count($data['feedbacktext']); $i += 1) { if (!empty($data['feedbacktext'][$i]['text']) && trim($data['feedbacktext'][$i]['text']) != '') { $errors["feedbacktext[{$i}]"] = get_string('feedbackerrorjunkinfeedback', 'quiz', $i + 1); } } // Any other rule plugins. $errors = quiz_access_manager::validate_settings_form_fields($errors, $data, $files, $this); return $errors; }
/** * Validate permissions for creating a new attempt and start a new preview attempt if required. * * @param quiz $quizobj quiz object * @param quiz_access_manager $accessmanager quiz access manager * @param bool $forcenew whether was required to start a new preview attempt * @param int $page page to jump to in the attempt * @param bool $redirect whether to redirect or throw exceptions (for web or ws usage) * @return array an array containing the attempt information, access error messages and the page to jump to in the attempt * @throws moodle_quiz_exception * @since Moodle 3.1 */ function quiz_validate_new_attempt(quiz $quizobj, quiz_access_manager $accessmanager, $forcenew, $page, $redirect) { global $DB, $USER; $timenow = time(); if ($quizobj->is_preview_user() && $forcenew) { $accessmanager->current_attempt_finished(); } // Check capabilities. if (!$quizobj->is_preview_user()) { $quizobj->require_capability('mod/quiz:attempt'); } // Check to see if a new preview was requested. if ($quizobj->is_preview_user() && $forcenew) { // To force the creation of a new preview, we mark the current attempt (if any) // as finished. It will then automatically be deleted below. $DB->set_field('quiz_attempts', 'state', quiz_attempt::FINISHED, array('quiz' => $quizobj->get_quizid(), 'userid' => $USER->id)); } // Look for an existing attempt. $attempts = quiz_get_user_attempts($quizobj->get_quizid(), $USER->id, 'all', true); $lastattempt = end($attempts); $attemptnumber = null; // If an in-progress attempt exists, check password then redirect to it. if ($lastattempt && ($lastattempt->state == quiz_attempt::IN_PROGRESS || $lastattempt->state == quiz_attempt::OVERDUE)) { $currentattemptid = $lastattempt->id; $messages = $accessmanager->prevent_access(); // If the attempt is now overdue, deal with that. $quizobj->create_attempt_object($lastattempt)->handle_if_time_expired($timenow, true); // And, if the attempt is now no longer in progress, redirect to the appropriate place. if ($lastattempt->state == quiz_attempt::ABANDONED || $lastattempt->state == quiz_attempt::FINISHED) { if ($redirect) { redirect($quizobj->review_url($lastattempt->id)); } else { throw new moodle_quiz_exception($quizobj, 'attemptalreadyclosed'); } } // If the page number was not explicitly in the URL, go to the current page. if ($page == -1) { $page = $lastattempt->currentpage; } } else { while ($lastattempt && $lastattempt->preview) { $lastattempt = array_pop($attempts); } // Get number for the next or unfinished attempt. if ($lastattempt) { $attemptnumber = $lastattempt->attempt + 1; } else { $lastattempt = false; $attemptnumber = 1; } $currentattemptid = null; $messages = $accessmanager->prevent_access() + $accessmanager->prevent_new_attempt(count($attempts), $lastattempt); if ($page == -1) { $page = 0; } } return array($currentattemptid, $attemptnumber, $lastattempt, $messages, $page); }
} if (!($cm = get_coursemodule_from_instance("quiz", $quiz->id, $course->id))) { print_error('invalidcoursemodule'); } } // Check login and get context. require_login($course->id, false, $cm); $context = get_context_instance(CONTEXT_MODULE, $cm->id); require_capability('mod/quiz:view', $context); // Cache some other capabilities we use several times. $canattempt = has_capability('mod/quiz:attempt', $context); $canreviewmine = has_capability('mod/quiz:reviewmyattempts', $context); $canpreview = has_capability('mod/quiz:preview', $context); // Create an object to manage all the other (non-roles) access rules. $timenow = time(); $accessmanager = new quiz_access_manager(quiz::create($quiz->id, $USER->id), $timenow, has_capability('mod/quiz:ignoretimelimits', $context, null, false)); // Log this request. add_to_log($course->id, 'quiz', 'view', 'view.php?id=' . $cm->id, $quiz->id, $cm->id); $completion = new completion_info($course); $completion->set_module_viewed($cm); // Initialize $PAGE, compute blocks $PAGE->set_url('/mod/quiz/view.php', array('id' => $cm->id)); $edit = optional_param('edit', -1, PARAM_BOOL); if ($edit != -1 && $PAGE->user_allowed_editing()) { $USER->editing = $edit; } // Update the quiz with overrides for the current user $quiz = quiz_update_effective_access($quiz, $USER->id); // Get this user's attempts. $attempts = quiz_get_user_attempts($quiz->id, $USER->id, 'finished', true); $lastfinishedattempt = end($attempts);