course)) { error("Course is misconfigured"); } if (! $quiz = get_record("quiz", "id", $cm->instance)) { error("The quiz with id $cm->instance corresponding to this coursemodule $id is missing"); } } else { if (! $quiz = get_record("quiz", "id", $q)) { error("There is no quiz with id $q"); } if (! $course = get_record("course", "id", $quiz->course)) { error("The course with id $quiz->course that the quiz with id $q belongs to is missing"); } if (! $cm = get_coursemodule_from_instance("quiz", $quiz->id, $course->id)) { error("The course module for the quiz with id $q is missing"); } } // We treat automatically closed attempts just like normally closed attempts if ($timeup) { $finishattempt = 1; } require_login($course->id, false, $cm); $coursecontext = get_context_instance(CONTEXT_COURSE, $cm->course); // course context $context = get_context_instance(CONTEXT_MODULE, $cm->id); $ispreviewing = 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)) { redirect('edit.php?quizid=' . $quiz->id); } // 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. // Check availability if (isguestuser()) { print_heading(get_string('guestsno', 'quiz')); if (empty($popup)) { print_footer($course); } exit; } $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 ($quiz->subnet and !address_in_subnet(getremoteaddr(), $quiz->subnet)) { if ($ispreviewing) { notify(get_string('subnetnotice', 'quiz')); } else { error(get_string("subneterror", "quiz"), "view.php?id=$cm->id"); } } // Check password access if ($quiz->password and empty($_POST['q'])) { if (empty($_POST['quizpassword'])) { if (trim(strip_tags($quiz->intro))) { print_simple_box(format_text($quiz->intro), "center"); } echo "
\n"; echo "
id\" onclick=\"this.autocomplete='off'\">\n"; echo '
'; print_simple_box_start("center"); echo "
\n"; print_string("requirepasswordmessage", "quiz"); echo "

\n"; echo " "; echo " \n"; echo "
\n"; print_simple_box_end(); echo '
'; echo "
\n"; if (empty($popup)) { print_footer(); } exit; } else { if (strcmp($quiz->password, $_POST['quizpassword']) !== 0) { error(get_string("passworderror", "quiz"), "view.php?id=$cm->id"); } } } 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); } } } // 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); } $attempt = get_record('quiz_attempts', 'quiz', $quiz->id, 'userid', $USER->id, 'timefinish', 0); $newattempt = false; if (!$attempt) { // Check if this is a preview request from a teacher // in which case the previous previews should be deleted if ($ispreviewing) { if ($oldattempts = get_records_select('quiz_attempts', "quiz = '$quiz->id' AND userid = '$USER->id'")) { delete_records('quiz_attempts', 'quiz', $quiz->id, 'userid', $USER->id); delete_records('quiz_grades', 'quiz', $quiz->id, 'userid', $USER->id); foreach ($oldattempts as $oldattempt) { // there should only be one but we loop just in case delete_attempt($oldattempt->uniqueid); } } } $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 if (!$attempt->id = insert_record('quiz_attempts', $attempt)) { error('Could not create new attempt'); } // make log entries if ($ispreviewing) { 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); } } if (!$attempt->timestart) { // shouldn't really happen, just for robustness debugging('timestart was not set for this attempt. That should be impossible.', DEBUG_DEVELOPER); $attempt->timestart = $timestamp - 1; } /// Load all the questions and states needed by this script // list of questions needed by page $pagelist = quiz_questions_on_page($attempt->layout, $page); if ($newattempt) { $questionlist = quiz_questions_in_quiz($attempt->layout); } else { $questionlist = $pagelist; } // 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); } $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); } // 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 if (!$states = get_question_states($questions, $quiz, $attempt, $lastattemptid)) { error('Could not restore question sessions'); } // Save all the newly created states if ($newattempt) { foreach ($questions as $i => $question) { save_question_session($questions[$i], $states[$i]); } } /// 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); // Unset any variables we know are not responses unset($responses->id); unset($responses->q); unset($responses->oldpage); unset($responses->newpage); unset($responses->review); unset($responses->questionids); unset($responses->saveattempt); // responses get saved anway unset($responses->finishattempt); // same as $finishattempt unset($responses->markall); unset($responses->forcenewattempt); // extract responses // $actions is an array indexed by the questions ids $actions = question_extract_responses($questions, $responses, $event); // 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; 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 } // Finish attempt if requested if ($finishattempt) { // Set the attempt to be finished $attempt->timefinish = $timestamp; // 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) { $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]); } 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) { if (!update_record('quiz_attempts', $attempt)) { error('Failed to save the current quiz attempt!'); } if (($attempt->attempt > 1 || $attempt->timefinish > 0) and !$attempt->preview) { quiz_save_best_grade($quiz); } } /// Check access to quiz page // check the quiz times if ($timestamp < $quiz->timeopen || ($quiz->timeclose and $timestamp > $quiz->timeclose)) { if ($ispreviewing) { notify(get_string('notavailabletostudents', 'quiz')); } else { notice(get_string('notavailable', 'quiz'), "view.php?id={$cm->id}"); } } if ($finishattempt) { redirect('review.php?attempt='.$attempt->id, 0); } /// Print the quiz page //////////////////////////////////////////////////////// // Print the page header $pagequestions = explode(',', $pagelist); $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'); } else { $strupdatemodule = has_capability('moodle/course:manageactivities', $coursecontext) ? update_module_button($cm->id, $course->id, get_string('modulename', 'quiz')) : ""; $crumbs[] = array('name' => $strquizzes, 'link' => "index.php?id=$course->id", 'type' => 'activity'); $crumbs[] = array('name' => format_string($quiz->name), 'link' => "view.php?id=$cm->id", 'type' => 'activityinstance'); $crumbs[] = array('name' => $strattemptnum, 'link' => '', 'type' => 'title'); $navigation = build_navigation($crumbs); print_header_simple(format_string($quiz->name), "", $navigation, "", $headtags, true, $strupdatemodule); } echo ''; // for overlib // Print the quiz name heading and tabs for teacher, etc. if ($ispreviewing) { $currenttab = 'preview'; include('tabs.php'); print_heading(get_string('previewquiz', 'quiz', format_string($quiz->name))); unset($buttonoptions); $buttonoptions['q'] = $quiz->id; $buttonoptions['forcenew'] = true; print_single_button($CFG->wwwroot.'/mod/quiz/attempt.php', $buttonoptions, get_string('startagain', 'quiz')); if ($quiz->popup) { notify(get_string('popupnotice', 'quiz')); } } else { if ($quiz->attempts != 1) { print_heading(format_string($quiz->name).' - '.$strattemptnum); } else { print_heading(format_string($quiz->name)); } } // Start the form echo "
\n"; if($quiz->timelimit > 0) { // Make sure javascript is enabled for time limited quizzes ?> '; echo '\n"; // Print the navigation panel if required $numpages = quiz_number_of_pages($attempt->layout); if ($numpages > 1) { ?> \n"; quiz_print_navigation_panel($page, $numpages); echo "
\n"; } /// Print all the questions // Add a hidden field with questionids echo '\n"; $number = quiz_first_questionnumber($attempt->layout, $pagelist); foreach ($pagequestions as $i) { $options = quiz_get_renderoptions($quiz->review, $states[$i]); // Print the question if ($i > 0) { echo "
\n"; } print_question($questions[$i], $states[$i], $number, $quiz, $options); save_question_session($questions[$i], $states[$i]); $number += $questions[$i]->length; } // Print the submit buttons $strconfirmattempt = addslashes(get_string("confirmclose", "quiz")); $onclick = "return confirm('$strconfirmattempt')"; echo "
\n"; echo "\n"; if ($quiz->optionflags & QUESTION_ADAPTIVE) { echo "\n"; } echo "\n"; echo ''; echo "
"; // Print the navigation panel if required if ($numpages > 1) { echo "
\n"; quiz_print_navigation_panel($page, $numpages); echo '
'; } // Finish the form echo ''; echo "
\n"; $secondsleft = ($quiz->timeclose ? $quiz->timeclose : 999999999999) - time(); if ($ispreviewing) { // For teachers ignore the quiz closing time $secondsleft = 999999999999; } // If time limit is set include floating timer. // MDL-7495, no timer for users with disability if ($quiz->timelimit > 0 && !has_capability('mod/quiz:ignoretimelimits', $context, NULL, false)) { $timesincestart = time() - $attempt->timestart; $timerstartvalue = min($quiz->timelimit*60 - $timesincestart, $secondsleft); if ($timerstartvalue <= 0) { $timerstartvalue = 1; } require('jstimer.php'); } else { // Add the javascript timer in the title bar if the closing time appears close if ($secondsleft > 0 and $secondsleft < 24*3600) { // less than a day remaining include('jsclock.php'); } } // Finish the page if (empty($popup)) { print_footer($course); } ?>