mirror of
https://github.com/moodle/moodle.git
synced 2025-04-14 04:52:36 +02:00
MDL-13806 - Refactor all the code that implements the rules for whether students can attempt the quiz now into some classes.
Resource page now done as well. That should be everything.
This commit is contained in:
parent
111ded5895
commit
c52c62d1b1
@ -6,6 +6,7 @@ $string['2hours'] = '2 Hours';
|
||||
$string['30minutes'] = '30 Minutes';
|
||||
$string['6hours'] = '6 Hours';
|
||||
$string['acceptederror'] = 'Accepted error';
|
||||
$string['accessnoticesheader'] = 'You can preview this quiz, but if this were a real attempt, you would be blocked because:';
|
||||
$string['action'] = 'Action';
|
||||
$string['adaptive'] = 'Adaptive mode';
|
||||
$string['addcategory'] = 'Add category';
|
||||
@ -57,6 +58,7 @@ $string['attemptlast'] = 'Last attempt';
|
||||
$string['attemptquiznow'] = 'Attempt quiz now';
|
||||
$string['attempts'] = 'Attempts';
|
||||
$string['attemptsallowed'] = 'Attempts allowed';
|
||||
$string['attemptsallowedn'] = 'Attempts allowed: $a';
|
||||
$string['attemptsdeleted'] = 'Quiz attempts deleted';
|
||||
$string['attemptselection'] = 'Select which attempts to analyze per user:';
|
||||
$string['attemptsexist'] = 'You can no longer add or remove questions.';
|
||||
@ -77,6 +79,7 @@ $string['calculated'] = 'Calculated';
|
||||
$string['calculatedquestion'] = 'Calculated Question not supported at line $a. The question will be ignored';
|
||||
$string['cannotcreatepath'] = 'Path cannot be created ($a)';
|
||||
$string['cannoteditafterattempts'] = 'You cannot add or remove questions because there are attempts.';
|
||||
$string['cannotfindprevattempt'] = 'Cannot find previous attempt to build on.';
|
||||
$string['cannotinsert'] = 'Cannot insert question';
|
||||
$string['cannotopen'] = 'Cannot open export file ($a)';
|
||||
$string['cannotread'] = 'Cannot read import file (or file is empty)';
|
||||
@ -267,6 +270,7 @@ $string['gradeboundary'] = 'Grade boundary';
|
||||
$string['gradeessays'] = 'Grade Essays';
|
||||
$string['gradehighest'] = 'Highest grade';
|
||||
$string['grademethod'] = 'Grading method';
|
||||
$string['gradingmethod'] = 'Grading method: $a';
|
||||
$string['gradesdeleted'] = 'Quiz grades deleted';
|
||||
$string['gradesofar'] = '$a->method: $a->mygrade / $a->quizgrade.';
|
||||
$string['gradingdetails'] = 'Marks for this submission: $a->raw/$a->max.';
|
||||
@ -310,6 +314,7 @@ $string['learnwise'] = 'Learnwise format';
|
||||
$string['link'] = 'Link';
|
||||
$string['listitems'] = 'Listing of Items in Quiz';
|
||||
$string['literal'] = 'Literal';
|
||||
$string['loadingquestionsfailed'] = 'Loading questions failed: $a';
|
||||
$string['loguniform'] = 'digits, from a loguniform distribution';
|
||||
$string['makecopy'] = 'Save as new question';
|
||||
$string['managetypes'] = 'Manage question types and servers';
|
||||
@ -365,7 +370,7 @@ $string['noresponse'] = 'No Response';
|
||||
$string['noreview'] = 'You are not allowed to review this quiz';
|
||||
$string['noreviewuntil'] = 'You are not allowed to review this quiz until $a';
|
||||
$string['noscript'] = 'JavaScript must be enabled to continue!';
|
||||
$string['notavailable'] = 'Sorry, this quiz is not available';
|
||||
$string['notavailable'] = 'This quiz is not currently available';
|
||||
$string['notavailabletostudents'] = 'Note: This quiz is not currently available to your students';
|
||||
$string['notenoughanswers'] = 'This type of question requires at least $a answers';
|
||||
$string['notenoughsubquestions'] = 'Not enough sub-questions have been defined!<br />Do you want to go back and fix this question?';
|
||||
@ -434,9 +439,10 @@ $string['quizclose'] = 'Close the quiz';
|
||||
$string['quizclosed'] = 'This quiz closed on $a';
|
||||
$string['quizcloses'] = 'Quiz closes';
|
||||
$string['quizcloseson'] = 'This quiz will close at $a';
|
||||
$string['quiznotavailable'] = 'The quiz will not be available until: $a';
|
||||
$string['quiznotavailable'] = 'The quiz will not be available until $a';
|
||||
$string['quizopen'] = 'Open the quiz';
|
||||
$string['quizopens'] = 'Quiz opens';
|
||||
$string['quizopenedon'] = 'This quiz opened at $a';
|
||||
$string['quizsettings'] = 'Quiz settings';
|
||||
$string['quiztimelimit'] = 'Time limit: $a';
|
||||
$string['quiztimer'] = 'Quiz Timer';
|
||||
@ -491,6 +497,7 @@ $string['reviewbefore'] = 'Allow review while quiz is open';
|
||||
$string['reviewclosed'] = 'After the quiz is closed';
|
||||
$string['reviewimmediately'] = 'Immediately after the attempt';
|
||||
$string['reviewnever'] = 'Never allow review';
|
||||
$string['reviewnotallowed'] = 'You are not allowed to review other users\' attempts at this quiz.';
|
||||
$string['reviewofattempt'] = 'Review of attempt $a';
|
||||
$string['reviewofpreview'] = 'Review of preview';
|
||||
$string['reviewopen'] = 'Later, while the quiz is still open';
|
||||
@ -501,6 +508,7 @@ $string['rqp'] = 'Remote Question';
|
||||
$string['rqps'] = 'Remote Questions';
|
||||
$string['save'] = 'Save';
|
||||
$string['saveandedit'] = 'Save changes and edit questions';
|
||||
$string['saveattemptfailed'] = 'Failed to save the current quiz attempt.';
|
||||
$string['savedfromdeletedcourse'] = 'Saved from deleted course \"$a\"';
|
||||
$string['savegrades'] = 'Save grades';
|
||||
$string['savemyanswers'] = 'Save my answers';
|
||||
@ -547,6 +555,7 @@ $string['startedon'] = 'Started on';
|
||||
$string['stoponerror'] = 'Stop on error';
|
||||
$string['subneterror'] = 'Sorry, this quiz has been locked so that it is only accessible from certain locations. Currently your computer is not one of those allowed to use this quiz.';
|
||||
$string['subnetnotice'] = 'This quiz has been locked so that it is only accessible from certain locations. Your computer is not on an allowed subnet. As teacher you are allowed to preview anyway.';
|
||||
$string['subnetwrong'] = 'This quiz is only accessible from certain locations, and this computer is not on the allowed list.';
|
||||
$string['substitutedby'] = 'will be substituted by';
|
||||
$string['summaryofattempts'] = 'Summary of your previous attempts';
|
||||
$string['temporaryblocked'] = 'You are temporarily not allowed to re-attempt the quiz.<br /> You will be able to take another attempt on:';
|
||||
@ -587,6 +596,7 @@ $string['warningmissingtype'] = '<b>This question is of a type that has not been
|
||||
$string['warningsdetected'] = '$a warning(s) detected';
|
||||
$string['webct'] = 'WebCT format';
|
||||
$string['wheregrade'] = 'Where\'s my grade?';
|
||||
$string['windowclosing'] = 'This window will close shortly.';
|
||||
$string['wildcard'] = 'Wild card';
|
||||
$string['withselected'] = 'With selected';
|
||||
$string['withsummary'] = 'with Summary Statistics';
|
||||
@ -597,6 +607,8 @@ $string['xml'] = 'Moodle XML format';
|
||||
$string['xmlimportnoname'] = 'Missing question name in xml file';
|
||||
$string['xmlimportnoquestion'] = 'Missing question text in xml file';
|
||||
$string['xmltypeunsupported'] = 'Question type $a is not supported by xml import';
|
||||
$string['youmustwait'] = 'You must wait before you may re-attempt this quiz. You will be allowed to start another attempt after $a.';
|
||||
$string['youcannotwait'] = 'This quiz closes before you will be allowed to start another attempt.';
|
||||
$string['youneedtoenrol'] = 'You need to enrol in this course before you can attempt this quiz';
|
||||
$string['yourfinalgradeis'] = 'Your final grade for this quiz is $a.';
|
||||
$string['zerosignificantfiguresnotallowed'] = 'The correct answer cannot have zero significant figures!';
|
||||
|
File diff suppressed because one or more lines are too long
@ -10,14 +10,14 @@
|
||||
* @package quiz
|
||||
*/
|
||||
|
||||
require_once("../../config.php");
|
||||
require_once("locallib.php");
|
||||
require_once('../../config.php');
|
||||
require_once($CFG->dirroot . '/mod/quiz/locallib.php');
|
||||
|
||||
// remember the current time as the time any responses were submitted
|
||||
// (so as to make sure students don't get penalized for slow processing on this page)
|
||||
$timestamp = time();
|
||||
/// remember the current time as the time any responses were submitted
|
||||
/// (so as to make sure students don't get penalized for slow processing on this page)
|
||||
$timenow = time();
|
||||
|
||||
// Get submitted parameters.
|
||||
/// Get submitted parameters.
|
||||
$id = optional_param('id', 0, PARAM_INT); // Course Module ID
|
||||
$q = optional_param('q', 0, PARAM_INT); // or quiz ID
|
||||
$page = optional_param('page', 0, PARAM_INT);
|
||||
@ -48,228 +48,149 @@
|
||||
}
|
||||
}
|
||||
|
||||
// We treat automatically closed attempts just like normally closed attempts
|
||||
/// We treat automatically closed attempts just like normally closed attempts
|
||||
if ($timeup) {
|
||||
$finishattempt = 1;
|
||||
}
|
||||
|
||||
/// Check login and get contexts.
|
||||
require_login($course->id, false, $cm);
|
||||
|
||||
$coursecontext = get_context_instance(CONTEXT_COURSE, $cm->course); // course context
|
||||
$coursecontext = get_context_instance(CONTEXT_COURSE, $cm->course);
|
||||
$context = get_context_instance(CONTEXT_MODULE, $cm->id);
|
||||
$ispreviewing = has_capability('mod/quiz:preview', $context);
|
||||
$canpreview = has_capability('mod/quiz:preview', $context);
|
||||
|
||||
// if no questions have been set up yet redirect to edit.php
|
||||
if (!$quiz->questions and has_capability('mod/quiz:manage', $context)) {
|
||||
/// Create an object to manage all the other (non-roles) access rules.
|
||||
$accessmanager = new quiz_access_manager($quiz, $timenow,
|
||||
has_capability('mod/quiz:ignoretimelimits', $context, NULL, false));
|
||||
if ($canpreview && $forcenew) {
|
||||
$accessmanager->clear_password_access();
|
||||
}
|
||||
|
||||
/// 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?quizid=' . $quiz->id);
|
||||
}
|
||||
|
||||
if (!$ispreviewing) {
|
||||
/// Check capabilites.
|
||||
if (!$canpreview) {
|
||||
require_capability('mod/quiz:attempt', $context);
|
||||
}
|
||||
|
||||
/// Get number for the next or unfinished attempt
|
||||
if(!$attemptnumber = (int)get_field_sql('SELECT MAX(attempt)+1 FROM ' .
|
||||
"{$CFG->prefix}quiz_attempts WHERE quiz = '{$quiz->id}' AND " .
|
||||
"userid = '{$USER->id}' AND timefinish > 0 AND preview != 1")) {
|
||||
$attemptnumber = 1;
|
||||
}
|
||||
|
||||
$strattemptnum = get_string('attempt', 'quiz', $attemptnumber);
|
||||
$strquizzes = get_string("modulenameplural", "quiz");
|
||||
$popup = $quiz->popup && !$ispreviewing; // Controls whether this is shown in a javascript-protected window.
|
||||
|
||||
/// We intentionally do not check open and close times here. Instead we do it lower down.
|
||||
/// This is to deal with what happens when someone submits close to the exact moment when the quiz closes.
|
||||
|
||||
/// Check number of attempts
|
||||
$numberofpreviousattempts = count_records_select('quiz_attempts', "quiz = '{$quiz->id}' AND " .
|
||||
"userid = '{$USER->id}' AND timefinish > 0 AND preview != 1");
|
||||
if ($quiz->attempts and $numberofpreviousattempts >= $quiz->attempts) {
|
||||
error(get_string('nomoreattempts', 'quiz'), "view.php?id={$cm->id}");
|
||||
}
|
||||
|
||||
/// Check subnet access
|
||||
if (!$ispreviewing && $quiz->subnet && !address_in_subnet(getremoteaddr(), $quiz->subnet)) {
|
||||
error(get_string("subneterror", "quiz"), "view.php?id=$cm->id");
|
||||
}
|
||||
|
||||
/// Check password access
|
||||
if ($ispreviewing && $forcenew) {
|
||||
unset($SESSION->passwordcheckedquizzes[$quiz->id]);
|
||||
}
|
||||
|
||||
if ($quiz->password and empty($SESSION->passwordcheckedquizzes[$quiz->id])) {
|
||||
$enteredpassword = optional_param('quizpassword', '', PARAM_RAW);
|
||||
if (optional_param('cancelpassword', false)) {
|
||||
// User clicked cancel in the password form.
|
||||
redirect($CFG->wwwroot . '/mod/quiz/view.php?q=' . $quiz->id);
|
||||
} else if (strcmp($quiz->password, $enteredpassword) === 0) {
|
||||
// User entered the correct password.
|
||||
$SESSION->passwordcheckedquizzes[$quiz->id] = true;
|
||||
} else {
|
||||
// User entered the wrong password, or has not entered one yet.
|
||||
$url = $CFG->wwwroot . '/mod/quiz/attempt.php?q=' . $quiz->id;
|
||||
|
||||
if (empty($popup)) {
|
||||
print_header('', '', '', 'quizpassword');
|
||||
}
|
||||
|
||||
if (trim(strip_tags($quiz->intro))) {
|
||||
$formatoptions->noclean = true;
|
||||
print_box(format_text($quiz->intro, FORMAT_MOODLE, $formatoptions), 'generalbox', 'intro');
|
||||
}
|
||||
print_box_start('generalbox', 'passwordbox');
|
||||
if (!empty($enteredpassword)) {
|
||||
echo '<p class="notifyproblem">', get_string('passworderror', 'quiz'), '</p>';
|
||||
}
|
||||
?>
|
||||
<p><?php print_string('requirepasswordmessage', 'quiz'); ?></p>
|
||||
<form id="passwordform" method="post" action="<?php echo $url; ?>" onclick="this.autocomplete='off'">
|
||||
<div>
|
||||
<label for="quizpassword"><?php print_string('password'); ?></label>
|
||||
<input name="quizpassword" id="quizpassword" type="password" value=""/>
|
||||
<input type="submit" value="<?php print_string('ok'); ?>" />
|
||||
<input type="submit" name="cancelpassword" value="<?php print_string('cancel'); ?>" />
|
||||
</div>
|
||||
</form>
|
||||
<?php
|
||||
print_box_end();
|
||||
if (empty($popup)) {
|
||||
print_footer();
|
||||
}
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
if ($quiz->delay1 or $quiz->delay2) {
|
||||
//quiz enforced time delay
|
||||
if ($attempts = quiz_get_user_attempts($quiz->id, $USER->id)) {
|
||||
$numattempts = count($attempts);
|
||||
} else {
|
||||
$numattempts = 0;
|
||||
}
|
||||
$timenow = time();
|
||||
$lastattempt_obj = get_record_select('quiz_attempts', "quiz = $quiz->id AND attempt = $numattempts AND userid = $USER->id", 'timefinish');
|
||||
if ($lastattempt_obj) {
|
||||
$lastattempt = $lastattempt_obj->timefinish;
|
||||
}
|
||||
if ($numattempts == 1 && $quiz->delay1) {
|
||||
if ($timenow - $quiz->delay1 < $lastattempt) {
|
||||
error(get_string('timedelay', 'quiz'), 'view.php?q='.$quiz->id);
|
||||
}
|
||||
} else if($numattempts > 1 && $quiz->delay2) {
|
||||
if ($timenow - $quiz->delay2 < $lastattempt) {
|
||||
error(get_string('timedelay', 'quiz'), 'view.php?q='.$quiz->id);
|
||||
}
|
||||
}
|
||||
}
|
||||
/// We intentionally do not check otehr access rules until after we have processed
|
||||
/// any submitted responses (which would be sesskey protected). This is so that when
|
||||
/// someone submits close to the exact moment when the quiz closes, there responses are not lost.
|
||||
|
||||
/// Load attempt or create a new attempt if there is no unfinished one
|
||||
|
||||
if ($ispreviewing and $forcenew) { // teacher wants a new preview
|
||||
// so we set a finish time on the current attempt (if any).
|
||||
// It will then automatically be deleted below
|
||||
set_field('quiz_attempts', 'timefinish', $timestamp, 'quiz', $quiz->id, 'userid', $USER->id);
|
||||
/// Check to see if a new preview was requested.
|
||||
if ($canpreview && $forcenew) {
|
||||
/// Teacher wants a new preview, so we set a finish time on the
|
||||
/// current attempt (if any). It will then automatically be deleted below
|
||||
set_field('quiz_attempts', 'timefinish', $timenow, 'quiz', $quiz->id, 'userid', $USER->id);
|
||||
}
|
||||
|
||||
$attempt = quiz_get_user_attempt_unfinished($quiz->id, $USER->id);
|
||||
|
||||
/// Look for an existing attempt.
|
||||
$newattempt = false;
|
||||
if (!$attempt) {
|
||||
// Delete any previous preview attempts belonging to this user.
|
||||
$lastattempt = quiz_get_latest_attempt_by_user($quiz->id, $USER->id);
|
||||
|
||||
if ($lastattempt && !$lastattempt->timefinish) {
|
||||
/// Continuation of an attempt.
|
||||
$attempt = $lastattempt;
|
||||
$lastattemptid = false;
|
||||
|
||||
/// Log it, but only if some time has elapsed.
|
||||
if (($timenow - $attempt->timemodified) > QUIZ_CONTINUE_ATTEMPT_LOG_INTERVAL) {
|
||||
/// This action used to be 'continue attempt' but the database field has only 15 characters.
|
||||
add_to_log($course->id, 'quiz', 'continue attemp', "review.php?attempt=$attempt->id",
|
||||
"$quiz->id", $cm->id);
|
||||
}
|
||||
|
||||
} else {
|
||||
/// Start a new attempt.
|
||||
$newattempt = true;
|
||||
|
||||
/// Get number for the next or unfinished attempt
|
||||
if ($lastattempt && !$lastattempt->preview && !$canpreview) {
|
||||
$attemptnumber = $lastattempt->attempt + 1;
|
||||
$lastattemptid = $lastattempt->id;
|
||||
} else {
|
||||
$lastattempt = false;
|
||||
$lastattemptid = false;
|
||||
$attemptnumber = 1;
|
||||
}
|
||||
|
||||
/// Check access.
|
||||
$messages = $accessmanager->prevent_access() +
|
||||
$accessmanager->prevent_new_attempt($attemptnumber - 1, $lastattempt);
|
||||
if (!$canpreview && $messages) {
|
||||
error($accessmanager->print_messages($messages, true),
|
||||
$CFG->wwwroot . '/mod/quiz/view.php?q=' . $quiz->id);
|
||||
}
|
||||
$accessmanager->do_password_check($canpreview);
|
||||
|
||||
/// Delete any previous preview attempts belonging to this user.
|
||||
if ($oldattempts = get_records_select('quiz_attempts', "quiz = '$quiz->id'
|
||||
AND userid = '$USER->id' AND preview = 1")) {
|
||||
foreach ($oldattempts as $oldattempt) {
|
||||
quiz_delete_attempt($oldattempt, $quiz);
|
||||
}
|
||||
}
|
||||
$newattempt = true;
|
||||
// Start a new attempt and initialize the question sessions
|
||||
$attempt = quiz_create_attempt($quiz, $attemptnumber);
|
||||
// If this is an attempt by a teacher mark it as a preview
|
||||
if ($ispreviewing) {
|
||||
$attempt->preview = 1;
|
||||
}
|
||||
// Save the attempt
|
||||
|
||||
/// Create the new attempt and initialize the question sessions
|
||||
$attempt = quiz_create_attempt($quiz, $attemptnumber, $lastattempt, $timenow, $canpreview);
|
||||
|
||||
/// Save the attempt in the database.
|
||||
if (!$attempt->id = insert_record('quiz_attempts', $attempt)) {
|
||||
error('Could not create new attempt');
|
||||
quiz_error($quiz, 'newattemptfail');
|
||||
}
|
||||
// make log entries
|
||||
if ($ispreviewing) {
|
||||
add_to_log($course->id, 'quiz', 'preview',
|
||||
"attempt.php?id=$cm->id",
|
||||
"$quiz->id", $cm->id);
|
||||
|
||||
/// Log the new attempt.
|
||||
if ($attempt->preview) {
|
||||
add_to_log($course->id, 'quiz', 'preview', "attempt.php?id=$cm->id",
|
||||
"$quiz->id", $cm->id);
|
||||
} else {
|
||||
add_to_log($course->id, 'quiz', 'attempt',
|
||||
"review.php?attempt=$attempt->id",
|
||||
"$quiz->id", $cm->id);
|
||||
}
|
||||
} else {
|
||||
// log continuation of attempt only if some time has lapsed
|
||||
if (($timestamp - $attempt->timemodified) > 600) { // 10 minutes have elapsed
|
||||
add_to_log($course->id, 'quiz', 'continue attemp', // this action used to be called 'continue attempt' but the database field has only 15 characters
|
||||
"review.php?attempt=$attempt->id",
|
||||
"$quiz->id", $cm->id);
|
||||
add_to_log($course->id, 'quiz', 'attempt', "review.php?attempt=$attempt->id",
|
||||
"$quiz->id", $cm->id);
|
||||
}
|
||||
}
|
||||
if (!$attempt->timestart) { // shouldn't really happen, just for robustness
|
||||
|
||||
/// This shouldn't really happen, just for robustness
|
||||
if (!$attempt->timestart) {
|
||||
debugging('timestart was not set for this attempt. That should be impossible.', DEBUG_DEVELOPER);
|
||||
$attempt->timestart = $timestamp - 1;
|
||||
$attempt->timestart = $timenow - 1;
|
||||
}
|
||||
|
||||
/// Load all the questions and states needed by this script
|
||||
|
||||
// list of questions needed by page
|
||||
/// Get the list of questions needed by this page.
|
||||
$pagelist = quiz_questions_on_page($attempt->layout, $page);
|
||||
|
||||
if ($newattempt) {
|
||||
if ($newattempt || $finishattempt) {
|
||||
$questionlist = quiz_questions_in_quiz($attempt->layout);
|
||||
} else {
|
||||
$questionlist = $pagelist;
|
||||
}
|
||||
|
||||
// add all questions that are on the submitted form
|
||||
/// Add all questions that are on the submitted form
|
||||
if ($questionids) {
|
||||
$questionlist .= ','.$questionids;
|
||||
}
|
||||
|
||||
if (!$questionlist) {
|
||||
error(get_string('noquestionsfound', 'quiz'), 'view.php?q='.$quiz->id);
|
||||
quiz_error($quiz, 'noquestionsfound');
|
||||
}
|
||||
|
||||
$sql = "SELECT q.*, i.grade AS maxgrade, i.id AS instance".
|
||||
" FROM {$CFG->prefix}question q,".
|
||||
" {$CFG->prefix}quiz_question_instances i".
|
||||
" WHERE i.quiz = '$quiz->id' AND q.id = i.question".
|
||||
" AND q.id IN ($questionlist)";
|
||||
|
||||
// Load the questions
|
||||
if (!$questions = get_records_sql($sql)) {
|
||||
error(get_string('noquestionsfound', 'quiz'), 'view.php?q='.$quiz->id);
|
||||
$questions = question_load_questions($questionlist, 'qqi.grade AS maxgrade, qqi.id AS instance',
|
||||
'quiz_question_instances qqi ON qqi.quiz = ' . $quiz->id . ' AND q.id = qqi.question');
|
||||
if (is_string($questions)) {
|
||||
quiz_error($quiz, 'loadingquestionsfailed', $questions);
|
||||
}
|
||||
|
||||
// Load the question type specific information
|
||||
if (!get_question_options($questions)) {
|
||||
error('Could not load question options');
|
||||
}
|
||||
|
||||
// If the new attempt is to be based on a previous attempt find its id
|
||||
$lastattemptid = false;
|
||||
if ($newattempt and $attempt->attempt > 1 and $quiz->attemptonlast and !$attempt->preview) {
|
||||
// Find the previous attempt
|
||||
if (!$lastattemptid = get_field('quiz_attempts', 'uniqueid', 'quiz', $attempt->quiz, 'userid', $attempt->userid, 'attempt', $attempt->attempt-1)) {
|
||||
error('Could not find previous attempt to build on');
|
||||
}
|
||||
}
|
||||
|
||||
// Restore the question sessions to their most recent states
|
||||
// creating new sessions where required
|
||||
/// Restore the question sessions to their most recent states creating new sessions where required.
|
||||
if (!$states = get_question_states($questions, $quiz, $attempt, $lastattemptid)) {
|
||||
error('Could not restore question sessions');
|
||||
}
|
||||
|
||||
// Save all the newly created states
|
||||
/// If we are starting a new attempt, save all the newly created states.
|
||||
if ($newattempt) {
|
||||
foreach ($questions as $i => $question) {
|
||||
save_question_session($questions[$i], $states[$i]);
|
||||
@ -277,14 +198,18 @@
|
||||
}
|
||||
|
||||
/// Process form data /////////////////////////////////////////////////
|
||||
|
||||
if ($responses = data_submitted() and empty($_POST['quizpassword'])) {
|
||||
|
||||
// set the default event. This can be overruled by individual buttons.
|
||||
$event = (array_key_exists('markall', $responses)) ? QUESTION_EVENTSUBMIT :
|
||||
($finishattempt ? QUESTION_EVENTCLOSE : QUESTION_EVENTSAVE);
|
||||
/// Set the default event. This can be overruled by individual buttons.
|
||||
if (array_key_exists('markall', $responses)) {
|
||||
$event = QUESTION_EVENTSUBMIT;
|
||||
} else if ($finishattempt) {
|
||||
$event = QUESTION_EVENTCLOSE;
|
||||
} else {
|
||||
$event = QUESTION_EVENTSAVE;
|
||||
}
|
||||
|
||||
// Unset any variables we know are not responses
|
||||
/// Unset any variables we know are not responses
|
||||
unset($responses->id);
|
||||
unset($responses->q);
|
||||
unset($responses->oldpage);
|
||||
@ -296,93 +221,74 @@
|
||||
unset($responses->markall);
|
||||
unset($responses->forcenewattempt);
|
||||
|
||||
// extract responses
|
||||
// $actions is an array indexed by the questions ids
|
||||
/// Extract the responses. $actions will be an array indexed by the questions ids.
|
||||
$actions = question_extract_responses($questions, $responses, $event);
|
||||
|
||||
// Process each question in turn
|
||||
|
||||
/// Process each question in turn
|
||||
$questionidarray = explode(',', $questionids);
|
||||
foreach($questionidarray as $i) {
|
||||
if (!isset($actions[$i])) {
|
||||
$actions[$i]->responses = array('' => '');
|
||||
$actions[$i]->event = QUESTION_EVENTOPEN;
|
||||
}
|
||||
$actions[$i]->timestamp = $timestamp;
|
||||
$actions[$i]->timestamp = $timenow;
|
||||
question_process_responses($questions[$i], $states[$i], $actions[$i], $quiz, $attempt);
|
||||
save_question_session($questions[$i], $states[$i]);
|
||||
}
|
||||
|
||||
$attempt->timemodified = $timestamp;
|
||||
|
||||
// We have now finished processing form data
|
||||
$attempt->timemodified = $timenow;
|
||||
if (!update_record('quiz_attempts', $attempt)) {
|
||||
quiz_error($quiz, 'saveattemptfailed');
|
||||
}
|
||||
}
|
||||
|
||||
/// Finish attempt if requested
|
||||
if ($finishattempt) {
|
||||
|
||||
// Set the attempt to be finished
|
||||
$attempt->timefinish = $timestamp;
|
||||
/// Set the attempt to be finished
|
||||
$attempt->timefinish = $timenow;
|
||||
|
||||
// load all the questions
|
||||
$closequestionlist = quiz_questions_in_quiz($attempt->layout);
|
||||
$sql = "SELECT q.*, i.grade AS maxgrade, i.id AS instance".
|
||||
" FROM {$CFG->prefix}question q,".
|
||||
" {$CFG->prefix}quiz_question_instances i".
|
||||
" WHERE i.quiz = '$quiz->id' AND q.id = i.question".
|
||||
" AND q.id IN ($closequestionlist)";
|
||||
if (!$closequestions = get_records_sql($sql)) {
|
||||
error('Questions missing');
|
||||
}
|
||||
|
||||
// Load the question type specific information
|
||||
if (!get_question_options($closequestions)) {
|
||||
error('Could not load question options');
|
||||
}
|
||||
|
||||
// Restore the question sessions
|
||||
if (!$closestates = get_question_states($closequestions, $quiz, $attempt)) {
|
||||
error('Could not restore question sessions');
|
||||
}
|
||||
|
||||
foreach($closequestions as $key => $question) {
|
||||
/// Move each question to the closed state.
|
||||
foreach ($questions as $key => $question) {
|
||||
$action->event = QUESTION_EVENTCLOSE;
|
||||
$action->responses = $closestates[$key]->responses;
|
||||
$action->timestamp = $closestates[$key]->timestamp;
|
||||
question_process_responses($question, $closestates[$key], $action, $quiz, $attempt);
|
||||
save_question_session($question, $closestates[$key]);
|
||||
$action->responses = $states[$key]->responses;
|
||||
$action->timestamp = $states[$key]->timestamp;
|
||||
question_process_responses($question, $states[$key], $action, $quiz, $attempt);
|
||||
save_question_session($question, $states[$key]);
|
||||
}
|
||||
|
||||
add_to_log($course->id, 'quiz', 'close attempt',
|
||||
"review.php?attempt=$attempt->id",
|
||||
"$quiz->id", $cm->id);
|
||||
}
|
||||
/// Log the end of this attempt.
|
||||
add_to_log($course->id, 'quiz', 'close attempt', "review.php?attempt=$attempt->id",
|
||||
"$quiz->id", $cm->id);
|
||||
|
||||
/// Update the quiz attempt and the overall grade for the quiz
|
||||
if ($responses || $finishattempt) {
|
||||
/// Update the quiz attempt record.
|
||||
if (!update_record('quiz_attempts', $attempt)) {
|
||||
error('Failed to save the current quiz attempt!');
|
||||
quiz_error($quiz, 'saveattemptfailed');
|
||||
}
|
||||
if (($attempt->attempt > 1 || $attempt->timefinish > 0) and !$attempt->preview) {
|
||||
|
||||
if (!$attempt->preview) {
|
||||
/// Record this user's best grade (if this is not a preview).
|
||||
quiz_save_best_grade($quiz);
|
||||
}
|
||||
}
|
||||
|
||||
/// Send emails to those who have the capability set
|
||||
if ($finishattempt && !$attempt->preview) {
|
||||
quiz_send_notification_emails($course, $quiz, $attempt, $context, $cm);
|
||||
}
|
||||
|
||||
if ($finishattempt) {
|
||||
if (!empty($SESSION->passwordcheckedquizzes[$quiz->id])) {
|
||||
unset($SESSION->passwordcheckedquizzes[$quiz->id]);
|
||||
/// Send any notification emails (if this is not a preview).
|
||||
quiz_send_notification_emails($course, $quiz, $attempt, $context, $cm);
|
||||
}
|
||||
|
||||
/// Clear the password check flag in the session.
|
||||
$accessmanager->clear_password_access();
|
||||
|
||||
/// Send the user to the review page.
|
||||
redirect($CFG->wwwroot . '/mod/quiz/review.php?attempt='.$attempt->id, 0);
|
||||
}
|
||||
|
||||
// Now is the right time to check the open and close times.
|
||||
if (!$ispreviewing && ($timestamp < $quiz->timeopen || ($quiz->timeclose && $timestamp > $quiz->timeclose))) {
|
||||
error(get_string('notavailable', 'quiz'), "view.php?id={$cm->id}");
|
||||
/// Now is the right time to check access (unless we are starting a new attempt, and did it above).
|
||||
if (!$newattempt) {
|
||||
$messages = $accessmanager->prevent_access();
|
||||
if (!$canpreview && $messages) {
|
||||
error($accessmanager->print_messages($messages, true),
|
||||
$CFG->wwwroot . '/mod/quiz/view.php?q=' . $quiz->id);
|
||||
}
|
||||
$accessmanager->do_password_check($canpreview);
|
||||
}
|
||||
|
||||
/// Print the quiz page ////////////////////////////////////////////////////////
|
||||
@ -390,11 +296,10 @@
|
||||
// Print the page header
|
||||
require_js($CFG->wwwroot . '/mod/quiz/quiz.js');
|
||||
$pagequestions = explode(',', $pagelist);
|
||||
$strattemptnum = get_string('attempt', 'quiz', $attempt->attempt);
|
||||
$headtags = get_html_head_contributions($pagequestions, $questions, $states);
|
||||
if (!empty($popup)) {
|
||||
define('MESSAGE_WINDOW', true); // This prevents the message window coming up
|
||||
print_header($course->shortname.': '.format_string($quiz->name), '', '', '', $headtags, false, '', '', false, '');
|
||||
include('protect_js.php');
|
||||
if ($accessmanager->securewindow_required($canpreview)) {
|
||||
$accessmanager->setup_secure_page($course->shortname.': '.format_string($quiz->name), $headtags);
|
||||
} else {
|
||||
$strupdatemodule = has_capability('moodle/course:manageactivities', $coursecontext)
|
||||
? update_module_button($cm->id, $course->id, get_string('modulename', 'quiz'))
|
||||
@ -402,32 +307,26 @@
|
||||
$navigation = build_navigation($strattemptnum, $cm);
|
||||
print_header_simple(format_string($quiz->name), "", $navigation, "", $headtags, true, $strupdatemodule);
|
||||
}
|
||||
|
||||
echo '<div id="overDiv" style="position:absolute; visibility:hidden; z-index:1000;"></div>'; // for overlib
|
||||
|
||||
// Print the quiz name heading and tabs for teacher, etc.
|
||||
if ($ispreviewing) {
|
||||
if ($canpreview) {
|
||||
/// Show the tab bar.
|
||||
$currenttab = 'preview';
|
||||
include('tabs.php');
|
||||
|
||||
/// Heading and tab bar.
|
||||
print_heading(get_string('previewquiz', 'quiz', format_string($quiz->name)));
|
||||
unset($buttonoptions);
|
||||
$buttonoptions['q'] = $quiz->id;
|
||||
$buttonoptions['forcenew'] = true;
|
||||
echo '<div class="controls">';
|
||||
print_single_button($CFG->wwwroot.'/mod/quiz/attempt.php', $buttonoptions, get_string('startagain', 'quiz'));
|
||||
echo '</div>';
|
||||
/// Notices about restrictions that would affect students.
|
||||
if ($quiz->popup) {
|
||||
notify(get_string('popupnotice', 'quiz'));
|
||||
}
|
||||
if ($timestamp < $quiz->timeopen || ($quiz->timeclose && $timestamp > $quiz->timeclose)) {
|
||||
notify(get_string('notavailabletostudents', 'quiz'));
|
||||
}
|
||||
if ($quiz->subnet && !address_in_subnet(getremoteaddr(), $quiz->subnet)) {
|
||||
notify(get_string('subnetnotice', 'quiz'));
|
||||
print_restart_preview_button($quiz);
|
||||
|
||||
/// Inform teachers of any restrictions that would apply to students at this point.
|
||||
if ($messages) {
|
||||
print_box_start('quizaccessnotices');
|
||||
print_heading(get_string('accessnoticesheader', 'quiz'), '', 3);
|
||||
$accessmanager->print_messages($messages);
|
||||
print_box_end();
|
||||
}
|
||||
} else {
|
||||
/// Just a heading.
|
||||
if ($quiz->attempts != 1) {
|
||||
print_heading(format_string($quiz->name).' - '.$strattemptnum);
|
||||
} else {
|
||||
@ -492,31 +391,18 @@
|
||||
echo '</div>';
|
||||
echo '<input type="hidden" name="timeup" id="timeup" value="0" />';
|
||||
|
||||
// Add a hidden field with questionids. Do this at the end of the form, so
|
||||
// Add a hidden field with questionids. Do this at the end of the form, so
|
||||
// if you navigate before the form has finished loading, it does not wipe all
|
||||
// the student's answers.
|
||||
echo '<input type="hidden" name="questionids" value="'.$pagelist."\" />\n";
|
||||
|
||||
echo "</form>\n";
|
||||
|
||||
// If the quiz has a time limit, or if we are close to the close time, include a floating timer.
|
||||
$showtimer = false;
|
||||
$timerstartvalue = 999999999999;
|
||||
if ($quiz->timeclose) {
|
||||
$timerstartvalue = min($timerstartvalue, $quiz->timeclose - time());
|
||||
$showtimer = $timerstartvalue < 60*60; // Show the timer if we are less than 60 mins from the deadline.
|
||||
}
|
||||
if ($quiz->timelimit > 0 && !has_capability('mod/quiz:ignoretimelimits', $context, NULL, false)) {
|
||||
$timerstartvalue = min($timerstartvalue, $attempt->timestart + $quiz->timelimit*60- time());
|
||||
$showtimer = true;
|
||||
}
|
||||
if ($showtimer && (!$ispreviewing || $timerstartvalue > 0)) {
|
||||
$timerstartvalue = max($timerstartvalue, 1); // Make sure it starts just above zero.
|
||||
require('jstimer.php');
|
||||
}
|
||||
|
||||
// Finish the page
|
||||
if (empty($popup)) {
|
||||
$accessmanager->show_attempt_timer_if_needed($attempt, time());
|
||||
if ($accessmanager->securewindow_required($canpreview)) {
|
||||
print_footer('empty');
|
||||
} else {
|
||||
print_footer($course);
|
||||
}
|
||||
?>
|
||||
|
@ -1,27 +0,0 @@
|
||||
<?php defined('MOODLE_INTERNAL') or die('Direct access to this script is forbidden.');?>
|
||||
|
||||
<div class="controls">
|
||||
<?php
|
||||
if (!empty($popup)) {
|
||||
?>
|
||||
|
||||
<script type="text/javascript">
|
||||
//<![CDATA[
|
||||
|
||||
document.write('<input type="button" value="<?php print_string('closewindow') ?>" '+
|
||||
'onclick="javascript: window.opener.location.href=\'view.php?id=<?php echo $cm->id ?>\'; '+
|
||||
'window.close();" />');
|
||||
//]]>
|
||||
</script>
|
||||
<noscript>
|
||||
<div>
|
||||
<?php print_string('closewindow'); ?>
|
||||
</div>
|
||||
</noscript>
|
||||
|
||||
<?php
|
||||
} else {
|
||||
print_single_button("view.php", array( 'id' => $cm->id ), get_string('finishreview', 'quiz'));
|
||||
}
|
||||
?>
|
||||
</div>
|
@ -20,6 +20,7 @@
|
||||
* Include those library functions that are also used by core Moodle or other modules
|
||||
*/
|
||||
require_once($CFG->dirroot . '/mod/quiz/lib.php');
|
||||
require_once($CFG->dirroot . '/mod/quiz/accessrules.php');
|
||||
require_once($CFG->dirroot . '/question/editlib.php');
|
||||
|
||||
/// Constants ///////////////////////////////////////////////////////////////////
|
||||
@ -37,13 +38,25 @@ define("QUIZ_ATTEMPTLAST", "4");
|
||||
/**#@+
|
||||
* Constants to describe the various states a quiz attempt can be in.
|
||||
*/
|
||||
define('QUIZ_STATE_DURING', 'during');
|
||||
define('QUIZ_STATE_IMMEDIATELY', 'immedately');
|
||||
define('QUIZ_STATE_OPEN', 'open');
|
||||
define('QUIZ_STATE_CLOSED', 'closed');
|
||||
define('QUIZ_STATE_DURING', 'during');
|
||||
define('QUIZ_STATE_IMMEDIATELY', 'immedately');
|
||||
define('QUIZ_STATE_OPEN', 'open');
|
||||
define('QUIZ_STATE_CLOSED', 'closed');
|
||||
define('QUIZ_STATE_TEACHERACCESS', 'teacheraccess'); // State only relevant if you are in a studenty role.
|
||||
/**#@-*/
|
||||
|
||||
/**
|
||||
* We don't log every single hit on attempt.php, only significant ones like starting and
|
||||
* ending an attempt, and periodically during the attempt, as defined by this constant. (10 mins)
|
||||
*/
|
||||
define('QUIZ_CONTINUE_ATTEMPT_LOG_INTERVAL', '600');
|
||||
|
||||
/**
|
||||
* We show the countdown timer if there is less than this amount of time left before the
|
||||
* the quiz close date. (1 hour)
|
||||
*/
|
||||
define('QUIZ_SHOW_TIME_BEFORE_DEADLINE', '3600');
|
||||
|
||||
/// Functions related to attempts /////////////////////////////////////////
|
||||
|
||||
/**
|
||||
@ -52,15 +65,22 @@ define('QUIZ_STATE_TEACHERACCESS', 'teacheraccess'); // State only relevant if y
|
||||
* Creates an attempt object to represent an attempt at the quiz by the current
|
||||
* user starting at the current time. The ->id field is not set. The object is
|
||||
* NOT written to the database.
|
||||
* @return object The newly created attempt object.
|
||||
* @param object $quiz The quiz to create an attempt for.
|
||||
* @param integer $attemptnumber The sequence number for the attempt.
|
||||
*
|
||||
* @param object $quiz the quiz to create an attempt for.
|
||||
* @param integer $attemptnumber the sequence number for the attempt.
|
||||
* @param object $lastattempt the previous attempt by this user, if any. Only needed
|
||||
* if $attemptnumber > 1 and $quiz->attemptonlast is true.
|
||||
* @param integer $timenow the time the attempt was started at.
|
||||
* @param boolean $ispreview whether this new attempt is a preview.
|
||||
*
|
||||
* @return object the newly created attempt object.
|
||||
*/
|
||||
function quiz_create_attempt($quiz, $attemptnumber) {
|
||||
global $USER, $CFG;
|
||||
function quiz_create_attempt($quiz, $attemptnumber, $lastattempt, $timenow, $ispreview = false) {
|
||||
global $USER;
|
||||
|
||||
if (!$attemptnumber > 1 or !$quiz->attemptonlast or !$attempt = get_record('quiz_attempts', 'quiz', $quiz->id, 'userid', $USER->id, 'attempt', $attemptnumber-1)) {
|
||||
// we are not building on last attempt so create a new attempt
|
||||
if ($attemptnumber = 1 || !$quiz->attemptonlast) {
|
||||
/// We are not building on last attempt so create a new attempt.
|
||||
$attempt = new stdClass;
|
||||
$attempt->quiz = $quiz->id;
|
||||
$attempt->userid = $USER->id;
|
||||
$attempt->preview = 0;
|
||||
@ -69,9 +89,14 @@ function quiz_create_attempt($quiz, $attemptnumber) {
|
||||
} else {
|
||||
$attempt->layout = $quiz->questions;
|
||||
}
|
||||
} else {
|
||||
/// Build on last attempt.
|
||||
if (empty($lastattempt)) {
|
||||
error(get_string('cannotfindprevattempt', 'quiz'));
|
||||
}
|
||||
$attempt = $lastattempt;
|
||||
}
|
||||
|
||||
$timenow = time();
|
||||
$attempt->attempt = $attemptnumber;
|
||||
$attempt->sumgrades = 0.0;
|
||||
$attempt->timestart = $timenow;
|
||||
@ -79,12 +104,17 @@ function quiz_create_attempt($quiz, $attemptnumber) {
|
||||
$attempt->timemodified = $timenow;
|
||||
$attempt->uniqueid = question_new_attempt_uniqueid();
|
||||
|
||||
/// If this is a preview, mark it as such.
|
||||
if ($ispreview) {
|
||||
$attempt->preview = 1;
|
||||
}
|
||||
|
||||
return $attempt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an unfinished attempt (if there is one) for the given
|
||||
* user on the given quiz. This function does not return preview attempts.
|
||||
* Returns the unfinished attempt for the given
|
||||
* user on the given quiz, if there is one.
|
||||
*
|
||||
* @param integer $quizid the id of the quiz.
|
||||
* @param integer $userid the id of the user.
|
||||
@ -100,6 +130,41 @@ function quiz_get_user_attempt_unfinished($quizid, $userid) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the most recent attempt by a given user on a given quiz.
|
||||
* May be finished, or may not.
|
||||
*
|
||||
* @param integer $quizid the id of the quiz.
|
||||
* @param integer $userid the id of the user.
|
||||
*
|
||||
* @return mixed the unfinished attempt if there is one, false if not.
|
||||
*/
|
||||
function quiz_get_latest_attempt_by_user($quizid, $userid) {
|
||||
global $CFG;
|
||||
return get_record_sql('SELECT qa.* FROM ' . $CFG->prefix . 'quiz_attempts qa
|
||||
WHERE qa.quiz=' . $quizid . ' AND qa.userid=' . $userid . ' AND qa.timestart = (
|
||||
SELECT MAX(timestart) FROM ' . $CFG->prefix . 'quiz_attempts ssqa
|
||||
WHERE ssqa.quiz=' . $quizid . ' AND ssqa.userid=' . $userid . ')');
|
||||
}
|
||||
|
||||
/**
|
||||
* Load an attempt by id. You need to use this method instead of get_record, because
|
||||
* of some ancient history to do with the upgrade from Moodle 1.4 to 1.5, See the comment
|
||||
* after CREATE TABLE `prefix_quiz_newest_states` in mod/quiz/db/mysql.php.
|
||||
*
|
||||
* @param integer $attemptid the id of the attempt to load.
|
||||
*/
|
||||
function quiz_load_attempt($attemptid) {
|
||||
$attempt = get_record('quiz_attempts', 'id', $attemptid);
|
||||
|
||||
if (!record_exists('question_sessions', 'attemptid', $attempt->uniqueid)) {
|
||||
/// this attempt has not yet been upgraded to the new model
|
||||
quiz_upgrade_states($attempt);
|
||||
}
|
||||
|
||||
return $attempt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a quiz attempt.
|
||||
*/
|
||||
@ -698,12 +763,12 @@ function quiz_get_reviewoptions($quiz, $attempt, $context=null) {
|
||||
$options->questioncommentlink = '/mod/quiz/comment.php';
|
||||
}
|
||||
|
||||
if (!is_null($context) && has_capability('mod/quiz:viewreports', $context) &&
|
||||
if (!is_null($context) && has_capability('mod/quiz:viewreports', $context) &&
|
||||
has_capability('moodle/grade:viewhidden', $context) && !$attempt->preview) {
|
||||
// People who can see reports and hidden grades should be shown everything,
|
||||
// except during preview when teachers want to see what students see.
|
||||
$options->responses = true;
|
||||
$options->scores = true;
|
||||
$options->scores = true;
|
||||
$options->feedback = true;
|
||||
$options->correct_responses = true;
|
||||
$options->solutions = false;
|
||||
@ -723,7 +788,7 @@ function quiz_get_reviewoptions($quiz, $attempt, $context=null) {
|
||||
$options->quizstate = QUIZ_STATE_CLOSED;
|
||||
}
|
||||
|
||||
// ... and hence extract the appropriate review options.
|
||||
// ... and hence extract the appropriate review options.
|
||||
$options->responses = ($quiz->review & $quiz_state_mask & QUIZ_REVIEW_RESPONSES) ? 1 : 0;
|
||||
$options->scores = ($quiz->review & $quiz_state_mask & QUIZ_REVIEW_SCORES) ? 1 : 0;
|
||||
$options->feedback = ($quiz->review & $quiz_state_mask & QUIZ_REVIEW_FEEDBACK) ? 1 : 0;
|
||||
@ -769,6 +834,14 @@ function quiz_get_combined_reviewoptions($quiz, $attempts, $context=null) {
|
||||
return array($someoptions, $alloptions);
|
||||
}
|
||||
|
||||
function print_restart_preview_button($quiz) {
|
||||
global $CFG;
|
||||
echo '<div class="controls">';
|
||||
print_single_button($CFG->wwwroot . '/mod/quiz/attempt.php',
|
||||
array('q' => $quiz->id, 'forcenew' => true), get_string('startagain', 'quiz'));
|
||||
echo '</div>';
|
||||
}
|
||||
|
||||
/// FUNCTIONS FOR SENDING NOTIFICATION EMAILS ///////////////////////////////
|
||||
|
||||
/**
|
||||
@ -938,4 +1011,19 @@ function quiz_send_notification_emails($course, $quiz, $attempt, $context, $cm)
|
||||
// return the number of successfully sent emails
|
||||
return $emailresult['good'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Print a quiz error message. This is a thin wrapper around print_error, for convinience.
|
||||
*
|
||||
* @param mixed $quiz either the quiz object, or the interger quiz id.
|
||||
* @param string $errorcode the name of the string from quiz.php to print.
|
||||
* @param object $a any extra data required by the error string.
|
||||
*/
|
||||
function quiz_error($quiz, $errorcode, $a = null) {
|
||||
global $CFG;
|
||||
if (is_object($quiz)) {
|
||||
$quiz = $quiz->id;
|
||||
}
|
||||
print_error($errorcode, 'quiz', $CFG->wwwroot . '/mod/quiz/view.php?q=' . $quiz, $a);
|
||||
}
|
||||
?>
|
||||
|
263
mod/quiz/quiz.js
263
mod/quiz/quiz.js
@ -16,7 +16,7 @@ function navigate(page) {
|
||||
ourForm.submit();
|
||||
}
|
||||
|
||||
/* Use this in an onkeypress handler, to stop enter submitting the forum unless you
|
||||
/* Use this in an onkeypress handler, to stop enter submitting the forum unless you
|
||||
are actually on the submit button. Don't stop the user typing things in text areas. */
|
||||
function check_enter(e) {
|
||||
var target = e.target ? e.target : e.srcElement;
|
||||
@ -28,65 +28,218 @@ function check_enter(e) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Used to update the on-screen countdown clock for quizzes with a time limit */
|
||||
function countdown_clock(theTimer) {
|
||||
var timeout_id = null;
|
||||
quiz_timer = {
|
||||
// The outer div, so we can get at it to move it when the page scrolls.
|
||||
timerouter: null,
|
||||
|
||||
quizTimerValue = Math.floor((ec_quiz_finish - new Date().getTime())/1000);
|
||||
// The element that the time should be displayed in.
|
||||
timerdisplay: null,
|
||||
|
||||
if(quizTimerValue <= 0) {
|
||||
clearTimeout(timeout_id);
|
||||
document.getElementById('timeup').value = 1;
|
||||
var ourForm = document.getElementById('responseform');
|
||||
if (ourForm.onsubmit) {
|
||||
ourForm.onsubmit();
|
||||
// The main quiz for, which we will need to submit when the time expires.
|
||||
quizform: null,
|
||||
|
||||
// String that is displayed after the time has run out.
|
||||
strtimeup: '',
|
||||
|
||||
// How long is left, in seconds.
|
||||
endtime: 0,
|
||||
|
||||
// How often we update the clock display. Delay in milliseconds.
|
||||
updatedelay: 500,
|
||||
|
||||
// This records the id of the timeout that updates the clock periodically, so we can cancel it
|
||||
// Once time has run out.
|
||||
timeoutid: null,
|
||||
|
||||
// Desired position of the top of timer_outer: 100px from the top of the window.
|
||||
targettop: 100,
|
||||
|
||||
// How often we check to positing and adjust it. Delay in milliseconds.
|
||||
movedelay: 100,
|
||||
|
||||
// Last known postion of timer_outer.
|
||||
oldtop: this.target_top,
|
||||
|
||||
// Colours used to change the timer bacground colour when time had nearly run out.
|
||||
// This array is indexed by number of seconds left.
|
||||
finalcolours: [
|
||||
'#ff0000',
|
||||
'#ff1111',
|
||||
'#ff2222',
|
||||
'#ff3333',
|
||||
'#ff4444',
|
||||
'#ff5555',
|
||||
'#ff6666',
|
||||
'#ff7777',
|
||||
'#ff8888',
|
||||
'#ff9999',
|
||||
'#ffaaaa',
|
||||
'#ffbbbb',
|
||||
'#ffcccc',
|
||||
'#ffdddd',
|
||||
'#ffeeee',
|
||||
'#ffffff',
|
||||
],
|
||||
|
||||
// Initialise method.
|
||||
initialise: function(strtimeup, timeleft) {
|
||||
// Set some fields.
|
||||
quiz_timer.strtimeup = strtimeup;
|
||||
quiz_timer.endtime = new Date().getTime() + timeleft*1000;
|
||||
|
||||
// Get references to some bits of the DOM we need.
|
||||
quiz_timer.timerouter = document.getElementById('quiz-timer-outer'),
|
||||
quiz_timer.timerdisplay = document.getElementById('quiz-timer-display'),
|
||||
quiz_timer.quizform = document.getElementById('responseform'),
|
||||
|
||||
// Get things starte.
|
||||
quiz_timer.move();
|
||||
quiz_timer.update_time();
|
||||
},
|
||||
|
||||
// Stop method. Stops the timer if it is running.
|
||||
stop: function() {
|
||||
if (quiz_timer.timeoutid) {
|
||||
clearTimeout(quiz_timer.timeoutid);
|
||||
}
|
||||
ourForm.submit();
|
||||
return;
|
||||
},
|
||||
|
||||
// Function that updates the text displayed in element timer_display.
|
||||
set_displayed_time: function(str) {
|
||||
var display = quiz_timer.timerdisplay
|
||||
if (!display.firstChild) {
|
||||
display.appendChild(document.createTextNode(str))
|
||||
} else if (display.firstChild.nodeType == 3) {
|
||||
display.firstChild.replaceData(0, display.firstChild.length, str);
|
||||
} else {
|
||||
display.replaceChild(document.createTextNode(str), display.firstChild);
|
||||
}
|
||||
},
|
||||
|
||||
// Function to convert a number between 0 and 99 to a two-digit string.
|
||||
two_digit: function(num) {
|
||||
if (num < 10) {
|
||||
return '0' + num;
|
||||
} else {
|
||||
return num;
|
||||
}
|
||||
},
|
||||
|
||||
// Function to update the clock with the current time left, and submit the quiz if necessary.
|
||||
update_time: function() {
|
||||
var secondsleft = Math.floor((quiz_timer.endtime - new Date().getTime())/1000);
|
||||
|
||||
// If time has expired, Set the hidden form field that says time has expired.
|
||||
if (secondsleft < 0) {
|
||||
quiz_timer.stop();
|
||||
quiz_timer.set_displayed_time(quiz_timer.strtimeup);
|
||||
document.getElementById('timeup').value = 1;
|
||||
if (quiz_timer.quizform.onsubmit) {
|
||||
quiz_timer.quizform.onsubmit();
|
||||
}
|
||||
quiz_timer.quizform.submit();
|
||||
return;
|
||||
}
|
||||
|
||||
// If time has nearly expired, change the colour.
|
||||
if (secondsleft < quiz_timer.finalcolours.length) {
|
||||
quiz_timer.timerouter.style.backgroundColor = quiz_timer.finalcolours[secondsleft];
|
||||
}
|
||||
|
||||
// Update the time display.
|
||||
var hours = Math.floor(secondsleft/3600);
|
||||
secondsleft -= hours*3600;
|
||||
var minutes = Math.floor(secondsleft/60);
|
||||
secondsleft -= minutes*60;
|
||||
var seconds = secondsleft;
|
||||
quiz_timer.set_displayed_time('' + hours + ':' + quiz_timer.two_digit(minutes) + ':' +
|
||||
quiz_timer.two_digit(seconds));
|
||||
|
||||
// Arrange for this method to be called again soon.
|
||||
quiz_timer.timeoutid = setTimeout(quiz_timer.update_time, quiz_timer.updatedelay);
|
||||
},
|
||||
|
||||
// Function to keep the clock in the same place on the screen.
|
||||
move: function() {
|
||||
// Work out where the top of the window is.
|
||||
var pos;
|
||||
if (window.innerHeight) {
|
||||
pos = window.pageYOffset
|
||||
} else if (document.documentElement && document.documentElement.scrollTop) {
|
||||
pos = document.documentElement.scrollTop
|
||||
} else if (document.body) {
|
||||
pos = document.body.scrollTop
|
||||
}
|
||||
|
||||
// We want the timer target_top pixels from the top of the window,
|
||||
// or the top of the document, whichever is lower.
|
||||
pos += quiz_timer.targettop;
|
||||
if (pos < quiz_timer.targettop) {
|
||||
pos = quiz_timer.targettop;
|
||||
}
|
||||
|
||||
// Only move the timer if the window has stopped moving, and the position has stabilised.
|
||||
if (pos == quiz_timer.oldtop) {
|
||||
quiz_timer.timerouter.style.top = pos + 'px';
|
||||
}
|
||||
quiz_timer.oldtop = pos;
|
||||
|
||||
// Arrange for this method to be called again soon.
|
||||
setTimeout(quiz_timer.move, quiz_timer.movedelay);
|
||||
}
|
||||
};
|
||||
|
||||
now = quizTimerValue;
|
||||
var hours = Math.floor(now/3600);
|
||||
now = now - (hours*3600);
|
||||
var minutes = Math.floor(now/60);
|
||||
now = now - (minutes*60);
|
||||
var seconds = now;
|
||||
quiz_secure_window = {
|
||||
// The message displayed when the secure window interferes with the user.
|
||||
protection_message: null,
|
||||
|
||||
var t = "" + hours;
|
||||
t += ((minutes < 10) ? ":0" : ":") + minutes;
|
||||
t += ((seconds < 10) ? ":0" : ":") + seconds;
|
||||
window.status = t.toString();
|
||||
// Used by close. The URL to redirect to, if we find we are not acutally in a pop-up window.
|
||||
close_next_url: '',
|
||||
|
||||
if(hours == 0 && minutes == 0 && seconds <= 15) {
|
||||
//go from fff0f0 to ffe0e0 to ffd0d0...ff2020, ff1010, ff0000 in 15 steps
|
||||
var hexascii = "0123456789ABCDEF";
|
||||
var col = '#' + 'ff' + hexascii.charAt(seconds) + '0' + hexascii.charAt(seconds) + 0;
|
||||
theTimer.style.backgroundColor = col;
|
||||
// Code for secure window. This used to be in protect_js.php. I don't understand it,
|
||||
// I have just moved it for clenliness reasons.
|
||||
initialise: function(strmessage) {
|
||||
quiz_secure_window.protection_message = strmessage;
|
||||
if (document.layers) {
|
||||
document.captureEvents(Event.MOUSEDOWN);
|
||||
}
|
||||
document.onmousedown = quiz_secure_window.intercept_click;
|
||||
document.oncontextmenu = new Function("alert(quiz_secure_window.protection_message); return false")
|
||||
},
|
||||
|
||||
// Code for secure window. This used to be in protect_js.php. I don't understand it,
|
||||
// I have just moved it for clenliness reasons.
|
||||
intercept_click: function(e) {
|
||||
if (document.all) {
|
||||
if (event.button==1) {
|
||||
return false;
|
||||
}
|
||||
if (event.button==2) {
|
||||
alert(quiz_securewindow_message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (document.layers) {
|
||||
if (e.which > 1) {
|
||||
alert(quiz_securewindow_message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
close: function(url, delay) {
|
||||
if (url != '') {
|
||||
quiz_secure_window.close_next_url = url;
|
||||
}
|
||||
if (delay > 0) {
|
||||
setTimeout('quiz_close_securewindow("", 0)', delay*1000);
|
||||
} else {
|
||||
if (window.opener) {
|
||||
window.opener.document.location.reload();
|
||||
window.close();
|
||||
} else if (quiz_secure_window.close_next_url != '') {
|
||||
window.location.href = quiz_secure_window.close_next_url;
|
||||
}
|
||||
}
|
||||
}
|
||||
document.getElementById('time').value = t.toString();
|
||||
timeout_id = setTimeout("countdown_clock(theTimer)", 1000);
|
||||
}
|
||||
|
||||
/* Use to keep the quiz timer on-screen as the user scrolls. */
|
||||
function movecounter(timerbox) {
|
||||
var pos;
|
||||
|
||||
if (window.innerHeight) {
|
||||
pos = window.pageYOffset
|
||||
} else if (document.documentElement && document.documentElement.scrollTop) {
|
||||
pos = document.documentElement.scrollTop
|
||||
} else if (document.body) {
|
||||
pos = document.body.scrollTop
|
||||
}
|
||||
|
||||
if (pos < theTop) {
|
||||
pos = theTop;
|
||||
} else {
|
||||
pos += 100;
|
||||
}
|
||||
if (pos == old) {
|
||||
timerbox.style.top = pos + 'px';
|
||||
}
|
||||
old = pos;
|
||||
temp = setTimeout('movecounter(timerbox)',100);
|
||||
}
|
||||
};
|
@ -10,14 +10,14 @@
|
||||
* @package quiz
|
||||
*/
|
||||
|
||||
require_once("../../config.php");
|
||||
require_once("locallib.php");
|
||||
require_once('../../config.php');
|
||||
require_once($CFG->dirroot . '/mod/quiz/locallib.php');
|
||||
|
||||
$attempt = required_param('attempt', PARAM_INT); // A particular attempt ID for review
|
||||
$attemptid = required_param('attempt', PARAM_INT); // A particular attempt ID for review
|
||||
$page = optional_param('page', 0, PARAM_INT); // The required page
|
||||
$showall = optional_param('showall', 0, PARAM_BOOL);
|
||||
|
||||
if (! $attempt = get_record("quiz_attempts", "id", $attempt)) {
|
||||
if (!$attempt = quiz_load_attempt($attemptid)) {
|
||||
error("No such attempt ID exists");
|
||||
}
|
||||
if (! $quiz = get_record("quiz", "id", $attempt->quiz)) {
|
||||
@ -30,102 +30,74 @@
|
||||
error("The course module for the quiz with id $quiz->id is missing");
|
||||
}
|
||||
|
||||
$grade = quiz_rescale_grade($attempt->sumgrades, $quiz);
|
||||
$feedback = quiz_feedback_for_grade($grade, $attempt->quiz);
|
||||
|
||||
if (!count_records('question_sessions', 'attemptid', $attempt->uniqueid)) {
|
||||
// this question has not yet been upgraded to the new model
|
||||
quiz_upgrade_states($attempt);
|
||||
}
|
||||
|
||||
/// Check login and get contexts.
|
||||
require_login($course->id, false, $cm);
|
||||
$context = get_context_instance(CONTEXT_MODULE, $cm->id);
|
||||
$coursecontext = get_context_instance(CONTEXT_COURSE, $cm->course);
|
||||
$isteacher = has_capability('mod/quiz:preview', get_context_instance(CONTEXT_MODULE, $cm->id));
|
||||
$options = quiz_get_reviewoptions($quiz, $attempt, $context);
|
||||
$popup = $isteacher ? 0 : $quiz->popup; // Controls whether this is shown in a javascript-protected window.
|
||||
$context = get_context_instance(CONTEXT_MODULE, $cm->id);
|
||||
$canpreview = has_capability('mod/quiz:preview', get_context_instance(CONTEXT_MODULE, $cm->id));
|
||||
|
||||
/// Create an object to manage all the other (non-roles) access rules.
|
||||
$timenow = time();
|
||||
$accessmanager = new quiz_access_manager($quiz, $timenow,
|
||||
has_capability('mod/quiz:ignoretimelimits', $context, NULL, false));
|
||||
$options = quiz_get_reviewoptions($quiz, $attempt, $context);
|
||||
|
||||
/// Work out if this is a student viewing their own attempt/teacher previewing,
|
||||
/// or someone with 'mod/quiz:viewreports' reviewing someone elses attempt.
|
||||
$reviewofownattempt = $attempt->userid == $USER->id && (!$canpreview || $attempt->preview);
|
||||
|
||||
/// Permissions checks for normal users who do not have quiz:viewreports capability.
|
||||
if (!has_capability('mod/quiz:viewreports', $context)) {
|
||||
// Can't review during the attempt.
|
||||
/// Can't review during the attempt - send them back to the attempt page.
|
||||
if (!$attempt->timefinish) {
|
||||
redirect('attempt.php?q=' . $quiz->id);
|
||||
redirect($CFG->wwwroot . '/mod/quiz/attempt.php?q=' . $quiz->id);
|
||||
}
|
||||
// Can't review other student's attempts.
|
||||
if ($attempt->userid != $USER->id) {
|
||||
error("This is not your attempt!", 'view.php?q=' . $quiz->id);
|
||||
if ($messages = $accessmanager->prevent_review($options)) {
|
||||
|
||||
}
|
||||
// Can't review if Student's may review ... Responses is turned on.
|
||||
/// Can't review other users' attempts.
|
||||
if (!$reviewofownattempt) {
|
||||
quiz_error($quiz, 'reviewnotallowed');
|
||||
}
|
||||
/// Can't review unless Students may review -> Responses option is turned on.
|
||||
if (!$options->responses) {
|
||||
if ($options->quizstate == QUIZ_STATE_IMMEDIATELY) {
|
||||
$message = '';
|
||||
} else if ($options->quizstate == QUIZ_STATE_OPEN && $quiz->timeclose &&
|
||||
($quiz->review & QUIZ_REVIEW_CLOSED & QUIZ_REVIEW_RESPONSES)) {
|
||||
$message = get_string('noreviewuntil', 'quiz', userdate($quiz->timeclose));
|
||||
} else {
|
||||
$message = get_string('noreview', 'quiz');
|
||||
}
|
||||
if (empty($popup)) {
|
||||
redirect('view.php?q=' . $quiz->id, $message);
|
||||
} else {
|
||||
?><script type="text/javascript">
|
||||
opener.document.location.reload();
|
||||
self.close();
|
||||
</script><?php
|
||||
die();
|
||||
}
|
||||
$accessmanager->back_to_view_page($canpreview,
|
||||
$accessmanager->cannot_review_message($options));
|
||||
}
|
||||
}
|
||||
|
||||
/// Log this review.
|
||||
add_to_log($course->id, "quiz", "review", "review.php?id=$cm->id&attempt=$attempt->id", "$quiz->id", "$cm->id");
|
||||
|
||||
/// Load all the questions and states needed by this script
|
||||
|
||||
// load the questions needed by page
|
||||
$pagelist = $showall ? quiz_questions_in_quiz($attempt->layout) : quiz_questions_on_page($attempt->layout, $page);
|
||||
$sql = "SELECT q.*, i.grade AS maxgrade, i.id AS instance".
|
||||
" FROM {$CFG->prefix}question q,".
|
||||
" {$CFG->prefix}quiz_question_instances i".
|
||||
" WHERE i.quiz = '$quiz->id' AND q.id = i.question".
|
||||
" AND q.id IN ($pagelist)";
|
||||
if (!$questions = get_records_sql($sql)) {
|
||||
error('No questions found');
|
||||
/// load the questions needed by page
|
||||
if ($showall) {
|
||||
$questionlist = quiz_questions_in_quiz($attempt->layout);
|
||||
} else {
|
||||
$questionlist = quiz_questions_on_page($attempt->layout, $page);
|
||||
}
|
||||
$pagequestions = explode(',', $questionlist);
|
||||
$questions = question_load_questions($questionlist, 'qqi.grade AS maxgrade, qqi.id AS instance',
|
||||
'quiz_question_instances qqi ON qqi.quiz = ' . $quiz->id . ' AND q.id = qqi.question');
|
||||
if (is_string($questions)) {
|
||||
quiz_error($quiz, 'loadingquestionsfailed', $questions);
|
||||
}
|
||||
|
||||
// Load the question type specific information
|
||||
if (!get_question_options($questions)) {
|
||||
error('Could not load question options');
|
||||
}
|
||||
|
||||
// Restore the question sessions to their most recent states
|
||||
// creating new sessions where required
|
||||
/// Restore the question sessions to their most recent states creating new sessions where required.
|
||||
if (!$states = get_question_states($questions, $quiz, $attempt)) {
|
||||
error('Could not restore question sessions');
|
||||
}
|
||||
|
||||
/// Print the page header
|
||||
|
||||
$strscore = get_string("score", "quiz");
|
||||
$strgrade = get_string("grade");
|
||||
$strbestgrade = get_string("bestgrade", "quiz");
|
||||
$strtimetaken = get_string("timetaken", "quiz");
|
||||
$strtimecompleted = get_string("completedon", "quiz");
|
||||
$stroverdue = get_string("overdue", "quiz");
|
||||
|
||||
/// Work out appropriate title.
|
||||
if ($isteacher and $attempt->userid == $USER->id) {
|
||||
if ($canpreview && $reviewofownattempt) {
|
||||
$strreviewtitle = get_string('reviewofpreview', 'quiz');
|
||||
} else {
|
||||
$strreviewtitle = get_string('reviewofattempt', 'quiz', $attempt->attempt);
|
||||
}
|
||||
|
||||
$pagequestions = explode(',', $pagelist);
|
||||
/// Print the page header
|
||||
$headtags = get_html_head_contributions($pagequestions, $questions, $states);
|
||||
if (!empty($popup)) {
|
||||
define('MESSAGE_WINDOW', true); // This prevents the message window coming up
|
||||
print_header($course->shortname.': '.format_string($quiz->name), '', '', '', $headtags, false, '', '', false, '');
|
||||
/// Include Javascript protection for this page
|
||||
include('protect_js.php');
|
||||
if ($accessmanager->securewindow_required($canpreview)) {
|
||||
$accessmanager->setup_secure_page($course->shortname.': '.format_string($quiz->name), $headtags);
|
||||
} else {
|
||||
$strupdatemodule = has_capability('moodle/course:manageactivities', $coursecontext)
|
||||
? update_module_button($cm->id, $course->id, get_string('modulename', 'quiz'))
|
||||
@ -136,9 +108,9 @@
|
||||
}
|
||||
echo '<div id="overDiv" style="position:absolute; visibility:hidden; z-index:1000;"></div>'; // for overlib
|
||||
|
||||
/// Print heading and tabs if this is part of a preview
|
||||
if (has_capability('mod/quiz:preview', $context)) {
|
||||
if ($attempt->userid == $USER->id) { // this is the report on a preview
|
||||
/// Print tabs if they should be there.
|
||||
if ($canpreview) {
|
||||
if ($reviewofownattempt) {
|
||||
$currenttab = 'preview';
|
||||
} else {
|
||||
$currenttab = 'reports';
|
||||
@ -146,27 +118,24 @@
|
||||
}
|
||||
include('tabs.php');
|
||||
}
|
||||
|
||||
/// Print heading.
|
||||
print_heading(format_string($quiz->name));
|
||||
if ($isteacher and $attempt->userid == $USER->id) {
|
||||
// the teacher is at the end of a preview. Print button to start new preview
|
||||
unset($buttonoptions);
|
||||
$buttonoptions['q'] = $quiz->id;
|
||||
$buttonoptions['forcenew'] = true;
|
||||
echo '<div class="controls">';
|
||||
print_single_button($CFG->wwwroot.'/mod/quiz/attempt.php', $buttonoptions, get_string('startagain', 'quiz'));
|
||||
echo '</div>';
|
||||
if ($canpreview && $reviewofownattempt) {
|
||||
print_restart_preview_button($quiz);
|
||||
}
|
||||
print_heading($strreviewtitle);
|
||||
|
||||
// print javascript button to close the window, if necessary
|
||||
if (!$isteacher) {
|
||||
include('attempt_close_js.php');
|
||||
/// Finish review link.
|
||||
if ($reviewofownattempt) {
|
||||
$accessmanager->print_finish_review_link($canpreview);
|
||||
}
|
||||
|
||||
/// Print infobox
|
||||
|
||||
$timelimit = (int)$quiz->timelimit * 60;
|
||||
$overtime = 0;
|
||||
$grade = quiz_rescale_grade($attempt->sumgrades, $quiz);
|
||||
$feedback = quiz_feedback_for_grade($grade, $attempt->quiz);
|
||||
|
||||
if ($attempt->timefinish) {
|
||||
if ($timetaken = ($attempt->timefinish - $attempt->timestart)) {
|
||||
@ -205,13 +174,13 @@
|
||||
echo '<tr><th scope="row" class="cell">', get_string('startedon', 'quiz'), '</th><td class="cell">',
|
||||
userdate($attempt->timestart), '</td></tr>';
|
||||
if ($attempt->timefinish) {
|
||||
echo '<tr><th scope="row" class="cell">', $strtimecompleted, '</th><td class="cell">',
|
||||
echo '<tr><th scope="row" class="cell">', get_string('completedon', 'quiz'), '</th><td class="cell">',
|
||||
userdate($attempt->timefinish), '</td></tr>';
|
||||
echo '<tr><th scope="row" class="cell">', $strtimetaken, '</th><td class="cell">',
|
||||
echo '<tr><th scope="row" class="cell">', get_string('timetaken', 'quiz'), '</th><td class="cell">',
|
||||
$timetaken, '</td></tr>';
|
||||
}
|
||||
if (!empty($overtime)) {
|
||||
echo '<tr><th scope="row" class="cell">', $stroverdue, '</th><td class="cell">',$overtime, '</td></tr>';
|
||||
echo '<tr><th scope="row" class="cell">', get_string('overdue', 'quiz'), '</th><td class="cell">',$overtime, '</td></tr>';
|
||||
}
|
||||
//if the student is allowed to see their score
|
||||
if ($options->scores) {
|
||||
@ -226,9 +195,9 @@
|
||||
$a->grade = $grade;
|
||||
$a->maxgrade = $quiz->grade;
|
||||
$rawscore = round($attempt->sumgrades, $CFG->quiz_decimalpoints);
|
||||
echo '<tr><th scope="row" class="cell">', $strscore, '</th><td class="cell">',
|
||||
echo '<tr><th scope="row" class="cell">', get_string('score', 'quiz'), '</th><td class="cell">',
|
||||
"$rawscore/$quiz->sumgrades ($percentage%)", '</td></tr>';
|
||||
echo '<tr><th scope="row" class="cell">', $strgrade, '</th><td class="cell">',
|
||||
echo '<tr><th scope="row" class="cell">', get_string('grade'), '</th><td class="cell">',
|
||||
get_string('outof', 'quiz', $a), '</td></tr>';
|
||||
}
|
||||
}
|
||||
@ -248,8 +217,7 @@
|
||||
}
|
||||
|
||||
/// Print all the questions
|
||||
|
||||
$number = quiz_first_questionnumber($attempt->layout, $pagelist);
|
||||
$number = quiz_first_questionnumber($attempt->layout, $questionlist);
|
||||
foreach ($pagequestions as $i) {
|
||||
if (!isset($questions[$i])) {
|
||||
print_simple_box_start('center', '90%');
|
||||
@ -260,7 +228,7 @@
|
||||
continue;
|
||||
}
|
||||
$options->validation = QUESTION_EVENTVALIDATE === $states[$i]->event;
|
||||
$options->history = ($isteacher and !$attempt->preview) ? 'all' : 'graded';
|
||||
$options->history = ($canpreview and !$attempt->preview) ? 'all' : 'graded';
|
||||
// Print the question
|
||||
print_question($questions[$i], $states[$i], $number, $quiz, $options);
|
||||
$number += $questions[$i]->length;
|
||||
@ -272,11 +240,13 @@
|
||||
}
|
||||
|
||||
// print javascript button to close the window, if necessary
|
||||
if (!$isteacher) {
|
||||
include('attempt_close_js.php');
|
||||
if ($reviewofownattempt) {
|
||||
$accessmanager->print_finish_review_link($canpreview);
|
||||
}
|
||||
|
||||
if (empty($popup)) {
|
||||
if ($accessmanager->securewindow_required($canpreview)) {
|
||||
print_footer('empty');
|
||||
} else {
|
||||
print_footer($course);
|
||||
}
|
||||
?>
|
||||
|
@ -356,25 +356,25 @@ class password_access_rule_test extends UnitTestCase {
|
||||
$rule->clear_access_allowed(-1);
|
||||
$_POST['cancelpassword'] = false;
|
||||
$_POST['quizpassword'] = '';
|
||||
$html = $rule->do_password_check(true);
|
||||
$html = $rule->do_password_check(false, null, true);
|
||||
$this->assertPattern($reqpwregex, $html);
|
||||
$this->assertPattern('/SOME INTRO TEXT/', $html);
|
||||
$this->assertNoPattern($pwerrregex, $html);
|
||||
|
||||
$_POST['quizpassword'] = 'toad';
|
||||
$html = $rule->do_password_check(true);
|
||||
$html = $rule->do_password_check(false, null, true);
|
||||
$this->assertPattern($reqpwregex, $html);
|
||||
$this->assertPattern($pwerrregex, $html);
|
||||
|
||||
$_POST['quizpassword'] = 'frog';
|
||||
$this->assertNull($rule->do_password_check(true));
|
||||
$this->assertNull($rule->do_password_check(false, null, true));
|
||||
|
||||
// Check that once you are in, the password isn't checked again.
|
||||
$_POST['quizpassword'] = 'newt';
|
||||
$this->assertNull($rule->do_password_check(true));
|
||||
$this->assertNull($rule->do_password_check(false, null, true));
|
||||
|
||||
$rule->clear_access_allowed(-1);
|
||||
$html = $rule->do_password_check(true);
|
||||
$html = $rule->do_password_check(false, null, true);
|
||||
$this->assertPattern($reqpwregex, $html);
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
<?php // $Id$
|
||||
|
||||
// This page prints a particular instance of quiz
|
||||
/// This page prints a particular instance of quiz
|
||||
|
||||
require_once("../../config.php");
|
||||
require_once($CFG->libdir.'/blocklib.php');
|
||||
@ -8,9 +8,8 @@
|
||||
require_once($CFG->dirroot.'/mod/quiz/locallib.php');
|
||||
require_once($CFG->dirroot.'/mod/quiz/pagelib.php');
|
||||
|
||||
$id = optional_param('id', 0, PARAM_INT); // Course Module ID, or
|
||||
$q = optional_param('q', 0, PARAM_INT); // quiz ID
|
||||
$edit = optional_param('edit', -1, PARAM_BOOL);
|
||||
$id = optional_param('id', 0, PARAM_INT); // Course Module ID, or
|
||||
$q = optional_param('q', 0, PARAM_INT); // quiz ID
|
||||
|
||||
if ($id) {
|
||||
if (! $cm = get_coursemodule_from_id('quiz', $id)) {
|
||||
@ -34,125 +33,112 @@
|
||||
}
|
||||
}
|
||||
|
||||
// Check login and get context.
|
||||
/// 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);
|
||||
|
||||
// if no questions have been set up yet redirect to edit.php
|
||||
if (!$quiz->questions and has_capability('mod/quiz:manage', $context)) {
|
||||
/// Cache some other capabilites we use several times.
|
||||
$canattempt = has_capability('mod/quiz:attempt', $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, $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('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
|
||||
/// Initialize $PAGE, compute blocks
|
||||
$PAGE = page_create_instance($quiz->id);
|
||||
$pageblocks = blocks_setup($PAGE);
|
||||
$blocks_preferred_width = bounded_number(180, blocks_preferred_width($pageblocks[BLOCK_POS_LEFT]), 210);
|
||||
|
||||
// Print the page header
|
||||
if ($edit != -1 and $PAGE->user_allowed_editing()) {
|
||||
$edit = optional_param('edit', -1, PARAM_BOOL);
|
||||
if ($edit != -1 && $PAGE->user_allowed_editing()) {
|
||||
$USER->editing = $edit;
|
||||
}
|
||||
|
||||
//only check pop ups if the user is not a teacher, and popup is set
|
||||
|
||||
$bodytags = (has_capability('mod/quiz:attempt', $context) && $quiz->popup)?'onload="popupchecker(\'' . get_string('popupblockerwarning', 'quiz') . '\');"':'';
|
||||
/// Print the page header
|
||||
$bodytags = '';
|
||||
if ($accessmanager->securewindow_required($canpreview)) {
|
||||
$bodytags = 'onload="popupchecker(\'' . get_string('popupblockerwarning', 'quiz') . '\');"';
|
||||
}
|
||||
require_js('yui_yahoo');
|
||||
require_js('yui_event');
|
||||
$PAGE->print_header($course->shortname.': %fullname%','',$bodytags);
|
||||
|
||||
/// Print any blocks on the left of the page.
|
||||
echo '<table id="layout-table"><tr>';
|
||||
|
||||
if(!empty($CFG->showblocksonmodpages) && (blocks_have_content($pageblocks, BLOCK_POS_LEFT) || $PAGE->user_is_editing())) {
|
||||
echo '<td style="width: '.$blocks_preferred_width.'px;" id="left-column">';
|
||||
print_container_start();
|
||||
blocks_print_group($PAGE, $pageblocks, BLOCK_POS_LEFT);
|
||||
print_container_end();
|
||||
echo '</td>';
|
||||
echo "</td>\n";
|
||||
}
|
||||
|
||||
/// Start the main part of the page
|
||||
echo '<td id="middle-column">';
|
||||
print_container_start();
|
||||
|
||||
// Print the main part of the page
|
||||
|
||||
// Print heading and tabs (if there is more than one).
|
||||
/// Print heading and tabs (if there is more than one).
|
||||
$currenttab = 'info';
|
||||
include('tabs.php');
|
||||
|
||||
// Print quiz name
|
||||
|
||||
/// Print quiz name and description
|
||||
print_heading(format_string($quiz->name));
|
||||
|
||||
if (has_capability('mod/quiz:view', $context)) {
|
||||
|
||||
// Print quiz description
|
||||
if (trim(strip_tags($quiz->intro))) {
|
||||
$formatoptions->noclean = true;
|
||||
print_box(format_text($quiz->intro, FORMAT_MOODLE, $formatoptions), 'generalbox', 'intro');
|
||||
}
|
||||
|
||||
echo '<div class="quizinfo">';
|
||||
|
||||
// Print information about number of attempts and grading method.
|
||||
if ($quiz->attempts > 1) {
|
||||
echo "<p>".get_string("attemptsallowed", "quiz").": $quiz->attempts</p>";
|
||||
}
|
||||
if ($quiz->attempts != 1) {
|
||||
echo "<p>".get_string("grademethod", "quiz").": ".quiz_get_grading_option_name($quiz->grademethod)."</p>";
|
||||
}
|
||||
|
||||
// Print information about timings.
|
||||
$timenow = time();
|
||||
$available = ($quiz->timeopen < $timenow and ($timenow < $quiz->timeclose or !$quiz->timeclose));
|
||||
if ($available) {
|
||||
if ($quiz->timelimit) {
|
||||
echo "<p>".get_string("quiztimelimit","quiz", format_time($quiz->timelimit * 60))."</p>";
|
||||
}
|
||||
if ($quiz->timeopen) {
|
||||
echo '<p>', get_string('quizopens', 'quiz'), ': ', userdate($quiz->timeopen), '</p>';
|
||||
}
|
||||
if ($quiz->timeclose) {
|
||||
echo '<p>', get_string('quizcloses', 'quiz'), ': ', userdate($quiz->timeclose), '</p>';
|
||||
}
|
||||
} else if ($timenow < $quiz->timeopen) {
|
||||
echo "<p>".get_string("quiznotavailable", "quiz", userdate($quiz->timeopen))."</p>";
|
||||
} else {
|
||||
echo "<p>".get_string("quizclosed", "quiz", userdate($quiz->timeclose))."</p>";
|
||||
}
|
||||
echo '</div>';
|
||||
} else {
|
||||
$available = false;
|
||||
if (trim(strip_tags($quiz->intro))) {
|
||||
$formatoptions->noclean = true;
|
||||
print_box(format_text($quiz->intro, FORMAT_MOODLE, $formatoptions), 'generalbox', 'intro');
|
||||
}
|
||||
|
||||
// Show number of attempts summary to those who can view reports.
|
||||
/// Display information about this quiz.
|
||||
$messages = $accessmanager->describe_rules();
|
||||
if ($quiz->attempts != 1) {
|
||||
$messages[] = get_string('gradingmethod', 'quiz', quiz_get_grading_option_name($quiz->grademethod));
|
||||
}
|
||||
print_box_start('quizinfo');
|
||||
$accessmanager->print_messages($messages);
|
||||
print_box_end();
|
||||
|
||||
/// Show number of attempts summary to those who can view reports.
|
||||
if (has_capability('mod/quiz:viewreports', $context)) {
|
||||
if ($strattemptnum = quiz_num_attempt_summary($quiz, $cm)) {
|
||||
echo '<div class="quizattemptcounts"><a href="report.php?mode=overview&id=' .
|
||||
$cm->id . '">' . $strattemptnum . '</a></div>';
|
||||
$cm->id . '">' . $strattemptnum . "</a></div>\n";
|
||||
}
|
||||
}
|
||||
|
||||
// Guests can't do a quiz, so offer them a choice of logging in or going back.
|
||||
/// Guests can't do a quiz, so offer them a choice of logging in or going back.
|
||||
if (isguestuser()) {
|
||||
$loginurl = $CFG->wwwroot.'/login/index.php';
|
||||
if (!empty($CFG->loginhttps)) {
|
||||
$loginurl = str_replace('http:','https:', $loginurl);
|
||||
}
|
||||
|
||||
notice_yesno('<p>' . get_string('guestsno', 'quiz') . "</p>\n\n</p>" .
|
||||
get_string('liketologin') . '</p>', $loginurl, get_referer(false));
|
||||
notice_yesno('<p>' . get_string('guestsno', 'quiz') . "</p>\n\n<p>" .
|
||||
get_string('liketologin') . "</p>\n", $loginurl, get_referer(false));
|
||||
finish_page($course);
|
||||
}
|
||||
|
||||
if (!(has_capability('mod/quiz:attempt', $context) || has_capability('mod/quiz:preview', $context))) {
|
||||
print_box('<p>' . get_string('youneedtoenrol', 'quiz') . '</p><p>' .
|
||||
/// If they are not using guest access, and they can't do the quiz, tell them that.
|
||||
if (!($canattempt || $canpreview)) {
|
||||
print_box('<p>' . get_string('youneedtoenrol', 'quiz') . "</p>\n\n<p>" .
|
||||
print_continue($CFG->wwwroot . '/course/view.php?id=' . $course->id, true) .
|
||||
'</p>', 'generalbox', 'notice');
|
||||
"</p>\n", 'generalbox', 'notice');
|
||||
finish_page($course);
|
||||
}
|
||||
|
||||
// Get this user's attempts.
|
||||
/// Get this user's attempts.
|
||||
$attempts = quiz_get_user_attempts($quiz->id, $USER->id);
|
||||
$lastfinishedattempt = end($attempts);
|
||||
$unfinished = false;
|
||||
if ($unfinishedattempt = quiz_get_user_attempt_unfinished($quiz->id, $USER->id)) {
|
||||
$attempts[] = $unfinishedattempt;
|
||||
@ -160,7 +146,7 @@
|
||||
}
|
||||
$numattempts = count($attempts);
|
||||
|
||||
// Work out the final grade, checking whether it was overridden in the gradebook.
|
||||
/// Work out the final grade, checking whether it was overridden in the gradebook.
|
||||
$mygrade = quiz_get_best_grade($quiz, $USER->id);
|
||||
$mygradeoverridden = false;
|
||||
$gradebookfeedback = '';
|
||||
@ -181,7 +167,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
// Print table with existing attempts
|
||||
/// Print table with existing attempts
|
||||
if ($attempts) {
|
||||
|
||||
print_heading(get_string('summaryofattempts', 'quiz'));
|
||||
@ -204,7 +190,7 @@
|
||||
$feedbackcolumn = quiz_has_feedback($quiz->id);
|
||||
$overallfeedback = $feedbackcolumn && $alloptions->overallfeedback;
|
||||
|
||||
// prepare table header
|
||||
// Prepare table header
|
||||
$table->class = 'generaltable quizattemptsummary';
|
||||
$table->head = array($strattempt, $strtimecompleted);
|
||||
$table->align = array("center", "left");
|
||||
@ -237,9 +223,9 @@
|
||||
|
||||
// Add the attempt number, making it a link, if appropriate.
|
||||
if ($attempt->preview) {
|
||||
$row[] = make_review_link(get_string('preview', 'quiz'), $quiz, $attempt);
|
||||
$row[] = $accessmanager->make_review_link(get_string('preview', 'quiz'), $attempt, $canpreview, $attemptoptions);
|
||||
} else {
|
||||
$row[] = make_review_link($attempt->attempt, $quiz, $attempt);
|
||||
$row[] = $accessmanager->make_review_link($attempt->attempt, $attempt, $canpreview, $attemptoptions);
|
||||
}
|
||||
|
||||
// prepare strings for time taken and date completed
|
||||
@ -249,24 +235,20 @@
|
||||
// attempt has finished
|
||||
$timetaken = format_time($attempt->timefinish - $attempt->timestart);
|
||||
$datecompleted = userdate($attempt->timefinish);
|
||||
} else if ($available) {
|
||||
} else if (!$quiz->timeclose || $timenow < $quiz->timeclose) {
|
||||
// The attempt is still in progress.
|
||||
$timetaken = format_time(time() - $attempt->timestart);
|
||||
$timetaken = format_time($timenow - $attempt->timestart);
|
||||
$datecompleted = '';
|
||||
} else if ($quiz->timeclose) {
|
||||
// The attempt was not completed but is also not available any more becuase the quiz is closed.
|
||||
} else {
|
||||
$timetaken = format_time($quiz->timeclose - $attempt->timestart);
|
||||
$datecompleted = userdate($quiz->timeclose);
|
||||
} else {
|
||||
// Something weird happened.
|
||||
$timetaken = '';
|
||||
$datecompleted = '';
|
||||
}
|
||||
$row[] = $datecompleted;
|
||||
|
||||
if ($markcolumn && $attempt->timefinish > 0) {
|
||||
if ($attemptoptions->scores) {
|
||||
$row[] = make_review_link(round($attempt->sumgrades, $quiz->decimalpoints), $quiz, $attempt);
|
||||
$row[] = $accessmanager->make_review_link(round($attempt->sumgrades, $quiz->decimalpoints),
|
||||
$attempt, $canpreview, $attemptoptions);
|
||||
} else {
|
||||
$row[] = '';
|
||||
}
|
||||
@ -279,11 +261,12 @@
|
||||
if ($attemptoptions->scores && $attempt->timefinish > 0) {
|
||||
$formattedgrade = $attemptgrade;
|
||||
// highlight the highest grade if appropriate
|
||||
if ($overallstats && $numattempts > 1 && !is_null($mygrade) && $attemptgrade == $mygrade && $quiz->grademethod == QUIZ_GRADEHIGHEST) {
|
||||
if ($overallstats && !$attempt->preview && $numattempts > 1 && !is_null($mygrade) &&
|
||||
$attemptgrade == $mygrade && $quiz->grademethod == QUIZ_GRADEHIGHEST) {
|
||||
$table->rowclass[$attempt->attempt] = 'bestrow';
|
||||
}
|
||||
|
||||
$row[] = make_review_link($formattedgrade, $quiz, $attempt);
|
||||
$row[] = $accessmanager->make_review_link($formattedgrade, $attempt, $canpreview, $attemptoptions);
|
||||
} else {
|
||||
$row[] = '';
|
||||
}
|
||||
@ -301,13 +284,17 @@
|
||||
$row[] = $timetaken;
|
||||
}
|
||||
|
||||
$table->data[$attempt->attempt] = $row;
|
||||
if ($attempt->preview) {
|
||||
$table->data['preview'] = $row;
|
||||
} else {
|
||||
$table->data[$attempt->attempt] = $row;
|
||||
}
|
||||
} // End of loop over attempts.
|
||||
print_table($table);
|
||||
}
|
||||
|
||||
// Print information about the student's best score for this quiz if possible.
|
||||
$moreattempts = $unfinished || $numattempts < $quiz->attempts || $quiz->attempts == 0;
|
||||
/// Print information about the student's best score for this quiz if possible.
|
||||
$moreattempts = $unfinished || !$accessmanager->is_finished($numattempts, $lastfinishedattempt);
|
||||
if (!$moreattempts) {
|
||||
print_heading(get_string("nomoreattempts", "quiz"));
|
||||
}
|
||||
@ -316,7 +303,7 @@
|
||||
$resultinfo = '';
|
||||
|
||||
if ($overallstats) {
|
||||
if ($available && $moreattempts) {
|
||||
if ($moreattempts) {
|
||||
$a = new stdClass;
|
||||
$a->method = quiz_get_grading_option_name($quiz->grademethod);
|
||||
$a->mygrade = $mygrade;
|
||||
@ -325,18 +312,18 @@
|
||||
} else {
|
||||
$resultinfo .= print_heading(get_string('yourfinalgradeis', 'quiz', "$mygrade / $quiz->grade"), '', 2, 'main', true);
|
||||
if ($mygradeoverridden) {
|
||||
$resultinfo .= '<p class="overriddennotice">'.get_string('overriddennotice', 'grades').'</p>';
|
||||
$resultinfo .= '<p class="overriddennotice">'.get_string('overriddennotice', 'grades')."</p>\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($gradebookfeedback) {
|
||||
$resultinfo .= print_heading(get_string('comment', 'quiz'), '', 3, 'main', true);
|
||||
$resultinfo .= '<p class="quizteacherfeedback">'.$gradebookfeedback.'</p>';
|
||||
$resultinfo .= '<p class="quizteacherfeedback">'.$gradebookfeedback."</p>\n";
|
||||
}
|
||||
if ($overallfeedback) {
|
||||
$resultinfo .= print_heading(get_string('overallfeedback', 'quiz'), '', 3, 'main', true);
|
||||
$resultinfo .= '<p class="quizgradefeedback">'.quiz_feedback_for_grade($mygrade, $quiz->id).'</p>';
|
||||
$resultinfo .= '<p class="quizgradefeedback">'.quiz_feedback_for_grade($mygrade, $quiz->id)."</p>\n";
|
||||
}
|
||||
|
||||
if ($resultinfo) {
|
||||
@ -344,107 +331,52 @@
|
||||
}
|
||||
}
|
||||
|
||||
// Print a button to start/continue an attempt, if appropriate.
|
||||
/// 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 ($available && $moreattempts) {
|
||||
echo "<br />";
|
||||
echo "<div class=\"quizattempt\">";
|
||||
|
||||
} else {
|
||||
if ($unfinished) {
|
||||
if (has_capability('mod/quiz:preview', $context)) {
|
||||
if ($canpreview) {
|
||||
$buttontext = get_string('continuepreview', 'quiz');
|
||||
} else {
|
||||
$buttontext = get_string('continueattemptquiz', 'quiz');
|
||||
}
|
||||
} else {
|
||||
|
||||
// Work out the appropriate button caption.
|
||||
if (has_capability('mod/quiz:preview', $context)) {
|
||||
$buttontext = get_string('previewquiznow', 'quiz');
|
||||
} else if ($numattempts == 0) {
|
||||
$buttontext = get_string('attemptquiznow', 'quiz');
|
||||
$messages = $accessmanager->prevent_new_attempt($numattempts, $lastfinishedattempt);
|
||||
if (!$canpreview && $messages) {
|
||||
$accessmanager->print_messages($messages);
|
||||
} else {
|
||||
$buttontext = get_string('reattemptquiz', 'quiz');
|
||||
}
|
||||
|
||||
// Work out if the quiz is temporarily unavailable because of the delay option.
|
||||
if (!empty($attempts)) {
|
||||
$tempunavailable = '';
|
||||
$lastattempt = end($attempts);
|
||||
$lastattempttime = $lastattempt->timefinish;
|
||||
if ($numattempts == 1 && $quiz->delay1 && $timenow <= $lastattempttime + $quiz->delay1) {
|
||||
$tempunavailable = get_string('temporaryblocked', 'quiz') .
|
||||
' <strong>'. userdate($lastattempttime + $quiz->delay1). '</strong>';
|
||||
} else if ($numattempts > 1 && $quiz->delay2 && $timenow <= $lastattempttime + $quiz->delay2) {
|
||||
$tempunavailable = get_string('temporaryblocked', 'quiz') .
|
||||
' <strong>'. userdate($lastattempttime + $quiz->delay2). '</strong>';
|
||||
}
|
||||
|
||||
// If so, display a message and prevent the start button from appearing.
|
||||
if ($tempunavailable) {
|
||||
print_simple_box($tempunavailable, "center");
|
||||
print_continue($CFG->wwwroot . '/course/view.php?id=' . $course->id);
|
||||
$buttontext = '';
|
||||
if ($canpreview) {
|
||||
$buttontext = get_string('previewquiznow', 'quiz');
|
||||
} else if ($numattempts == 0) {
|
||||
$buttontext = get_string('attemptquiznow', 'quiz');
|
||||
} else {
|
||||
$buttontext = get_string('reattemptquiz', 'quiz');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Actually print the start button.
|
||||
// If, so far, we think a button should be printed, so check if they will be allowed to access it.
|
||||
if ($buttontext) {
|
||||
$buttontext = htmlspecialchars($buttontext, ENT_QUOTES);
|
||||
|
||||
// Do we need a confirm javascript alert?
|
||||
if ($unfinished) {
|
||||
$strconfirmstartattempt = '';
|
||||
} else if ($quiz->timelimit && $quiz->attempts) {
|
||||
$strconfirmstartattempt = get_string('confirmstartattempttimelimit','quiz', $quiz->attempts);
|
||||
} else if ($quiz->timelimit) {
|
||||
$strconfirmstartattempt = get_string('confirmstarttimelimit','quiz');
|
||||
} else if ($quiz->attempts) {
|
||||
$strconfirmstartattempt = get_string('confirmstartattemptlimit','quiz', $quiz->attempts);
|
||||
} else {
|
||||
$strconfirmstartattempt = '';
|
||||
if (!$moreattempts) {
|
||||
$buttontext = '';
|
||||
} else if (!$canpreview && $messages = $accessmanager->prevent_access()) {
|
||||
$accessmanager->print_messages($messages);
|
||||
$buttontext = '';
|
||||
}
|
||||
// Determine the URL to use.
|
||||
$attempturl = "attempt.php?id=$cm->id";
|
||||
|
||||
// Prepare options depending on whether the quiz should be a popup.
|
||||
if (!empty($quiz->popup)) {
|
||||
$window = 'quizpopup';
|
||||
$windowoptions = "left=0, top=0, height='+window.screen.height+', " .
|
||||
"width='+window.screen.width+', channelmode=yes, fullscreen=yes, " .
|
||||
"scrollbars=yes, resizeable=no, directories=no, toolbar=no, " .
|
||||
"titlebar=no, location=no, status=no, menubar=no";
|
||||
if (!empty($CFG->usesid) && !isset($_COOKIE[session_name()])) {
|
||||
$attempturl = sid_process_url($attempturl);
|
||||
}
|
||||
|
||||
echo '<input type="button" value="'.$buttontext.'" onclick="javascript:';
|
||||
if ($strconfirmstartattempt) {
|
||||
$strconfirmstartattempt = addslashes($strconfirmstartattempt);
|
||||
echo "if (confirm('".addslashes_js($strconfirmstartattempt)."')) ";
|
||||
}
|
||||
echo "window.open('$attempturl','$window','$windowoptions');", '" />';
|
||||
} else {
|
||||
print_single_button("attempt.php", array('id'=>$cm->id), $buttontext, 'get', '', false, '', false, $strconfirmstartattempt);
|
||||
}
|
||||
|
||||
|
||||
?>
|
||||
<noscript>
|
||||
<div>
|
||||
<?php print_heading(get_string('noscript', 'quiz')); ?>
|
||||
</div>
|
||||
</noscript>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
|
||||
echo "</div>\n";
|
||||
/// 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();
|
||||
|
||||
// Should we not be seeing if we need to print right-hand-side blocks?
|
||||
|
||||
@ -459,37 +391,4 @@ function finish_page($course) {
|
||||
print_footer($course);
|
||||
exit;
|
||||
}
|
||||
|
||||
/** Make some text into a link to review the quiz, if that is appropriate. */
|
||||
function make_review_link($linktext, $quiz, $attempt) {
|
||||
// If not even responses are to be shown in review then we don't allow any review
|
||||
if (!($quiz->review & QUIZ_REVIEW_RESPONSES)) {
|
||||
return $linktext;
|
||||
}
|
||||
|
||||
// If the quiz is still open, are reviews allowed?
|
||||
if ((!$quiz->timeclose or time() < $quiz->timeclose) and !($quiz->review & QUIZ_REVIEW_OPEN)) {
|
||||
// If not, don't link.
|
||||
return $linktext;
|
||||
}
|
||||
|
||||
// If the quiz is closed, are reviews allowed?
|
||||
if (($quiz->timeclose and time() > $quiz->timeclose) and !($quiz->review & QUIZ_REVIEW_CLOSED)) {
|
||||
// If not, don't link.
|
||||
return $linktext;
|
||||
}
|
||||
|
||||
// If the attempt is still open, don't link.
|
||||
if (!$attempt->timefinish) {
|
||||
return $linktext;
|
||||
}
|
||||
|
||||
$url = "review.php?q=$quiz->id&attempt=$attempt->id";
|
||||
if ($quiz->popup) {
|
||||
$windowoptions = "left=0, top=0, channelmode=yes, fullscreen=yes, scrollbars=yes, resizeable=no, directories=no, toolbar=no, titlebar=no, location=no, status=no, menubar=no";
|
||||
return link_to_popup_window('/mod/quiz/' . $url, 'quizpopup', $linktext, '+window.screen.height+', '+window.screen.width+', '', $windowoptions, true);
|
||||
} else {
|
||||
return "<a href='$url'>$linktext</a>";
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
@ -3769,7 +3769,7 @@ body#mod-forum-search .introcontent {
|
||||
#mod-quiz-view .generalbox#feedback h2 {
|
||||
margin: 0 0;
|
||||
}
|
||||
body#mod-quiz-view .generalbox#feedback .overriddennotice {
|
||||
#mod-quiz-view .generalbox#feedback .overriddennotice {
|
||||
text-align: center;
|
||||
font-size: 0.7em;
|
||||
}
|
||||
@ -3795,14 +3795,18 @@ body#mod-quiz-view .generalbox#feedback .overriddennotice {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#mod-quiz-attempt #timer .generalbox {
|
||||
width:150px
|
||||
#mod-quiz-attempt #quiz-timer-outer {
|
||||
position: absolute;
|
||||
width: 150px;
|
||||
top: 100px;
|
||||
left: 10px;
|
||||
padding: 0.25em 0;
|
||||
border-width: 1px;
|
||||
border-style: solid;
|
||||
}
|
||||
|
||||
#mod-quiz-attempt #timer {
|
||||
position:absolute;
|
||||
/*top:100px; is set by js*/
|
||||
left:10px
|
||||
#mod-quiz-attempt #quiz-timer-outer h3,
|
||||
#mod-quiz-attempt #quiz-timer-outer p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
body#question-preview .quemodname,
|
||||
@ -3815,7 +3819,8 @@ body#question-preview .quemodname, body#question-preview .controls {
|
||||
}
|
||||
|
||||
#mod-quiz-attempt #page .controls,
|
||||
#mod-quiz-review #page .controls {
|
||||
#mod-quiz-review #page .controls,
|
||||
#mod-quiz-review #page .finishreview {
|
||||
text-align: center;
|
||||
margin: 8px auto;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user