MDL-15452 - Put the OU quiz navigation improvements into the Moodle codebase - quite a lot of progress, sorry I am committing this as a big lump, but it took me a while to get the code back to a working state.
MDL-15537 - create oo attemptlib.php to hold shared code between attempt, summary and review.php MDL-15541 - Refactor starting a new attempt into a new file startattempt.php MDL-15538 - Rework attempt.php to use attemptlib.php
This commit is contained in:
@@ -398,6 +398,7 @@ $string['numberabbr'] = '#';
|
||||
$string['numerical'] = 'Numerical';
|
||||
$string['onlyteachersexport'] = 'Only teachers can export questions';
|
||||
$string['onlyteachersimport'] = 'Only teachers with editing rights can import questions';
|
||||
$string['open'] = 'Started';
|
||||
$string['openclosedatesupdated'] = 'Quiz open and close dates updated';
|
||||
$string['optional'] = 'optional';
|
||||
$string['outof'] = '$a->grade out of a maximum of $a->maxgrade';
|
||||
|
||||
+46
-12
@@ -756,32 +756,61 @@ function questionbank_navigation_tabs(&$row, $contexts, $querystring) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a set of questions, given a list of ids. The $join and $extrafields arguments can be used
|
||||
* together to pull in extra data. See, for example, the usage in mod/quiz/attempt.php, and
|
||||
* read the code below to see how the SQL is assembled.
|
||||
* Given a list of ids, load the basic information about a set of questions from the questions table.
|
||||
* The $join and $extrafields arguments can be used together to pull in extra data.
|
||||
* See, for example, the usage in mod/quiz/attemptlib.php, and
|
||||
* read the code below to see how the SQL is assembled. Throws exceptions on error.
|
||||
*
|
||||
* @param string $questionlist list of comma-separated question ids.
|
||||
* @param string $extrafields
|
||||
* @param string $join
|
||||
* @param array $questionids array of question ids.
|
||||
* @param string $extrafields extra SQL code to be added to the query.
|
||||
* @param string $join extra SQL code to be added to the query.
|
||||
* @param array $extraparams values for any placeholders in $join.
|
||||
* You are strongly recommended to use named placeholder.
|
||||
*
|
||||
* @return mixed array of question objects on success, a string error message on failure.
|
||||
* @return array partially complete question objects. You need to call get_question_options
|
||||
* on them before they can be properly used.
|
||||
*/
|
||||
function question_load_questions($questionlist, $extrafields = '', $join = '') {
|
||||
function question_preload_questions($questionids, $extrafields = '', $join = '', $extraparams = array()) {
|
||||
global $CFG, $DB;
|
||||
if ($join) {
|
||||
$join = ' JOIN '.$join.'';
|
||||
$join = ' JOIN '.$join;
|
||||
}
|
||||
if ($extrafields) {
|
||||
$extrafields = ', ' . $extrafields;
|
||||
}
|
||||
list($questionidcondition, $params) = $DB->get_in_or_equal(
|
||||
$questionids, SQL_PARAMS_NAMED, 'qid0000');
|
||||
$sql = 'SELECT q.*' . $extrafields . ' FROM {question} q' . $join .
|
||||
' WHERE q.id IN (' . $questionlist . ')';
|
||||
' WHERE q.id ' . $questionidcondition;
|
||||
|
||||
// Load the questions
|
||||
if (!$questions = $DB->get_records_sql($sql)) {
|
||||
if (!$questions = $DB->get_records_sql($sql, $extraparams + $params)) {
|
||||
return 'Could not load questions.';
|
||||
}
|
||||
|
||||
foreach ($questions as $question) {
|
||||
$question->_partiallyloaded = true;
|
||||
}
|
||||
|
||||
return $questions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a set of questions, given a list of ids. The $join and $extrafields arguments can be used
|
||||
* together to pull in extra data. See, for example, the usage in mod/quiz/attempt.php, and
|
||||
* read the code below to see how the SQL is assembled. Throws exceptions on error.
|
||||
*
|
||||
* @param array $questionids array of question ids.
|
||||
* @param string $extrafields extra SQL code to be added to the query.
|
||||
* @param string $join extra SQL code to be added to the query.
|
||||
* @param array $extraparams values for any placeholders in $join.
|
||||
* You are strongly recommended to use named placeholder.
|
||||
*
|
||||
* @return array question objects.
|
||||
*/
|
||||
function question_load_questions($questionids, $extrafields = '', $join = '') {
|
||||
$questions = question_preload_questions($questionids, $extrafields, $join);
|
||||
|
||||
// Load the question type specific information
|
||||
if (!get_question_options($questions)) {
|
||||
return 'Could not load the question options';
|
||||
@@ -803,7 +832,12 @@ function _tidy_question(&$question) {
|
||||
$question->questiontext = '<p>' . get_string('warningmissingtype', 'quiz') . '</p>' . $question->questiontext;
|
||||
}
|
||||
$question->name_prefix = question_make_name_prefix($question->id);
|
||||
return $QTYPES[$question->qtype]->get_question_options($question);
|
||||
if ($success = $QTYPES[$question->qtype]->get_question_options($question)) {
|
||||
if (isset($question->_partiallyloaded)) {
|
||||
unset($question->_partiallyloaded);
|
||||
}
|
||||
}
|
||||
return $success;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+42
-37
@@ -4,7 +4,7 @@
|
||||
* quiz, with convinient methods for seeing whether access is allowed.
|
||||
*/
|
||||
class quiz_access_manager {
|
||||
private $_quiz;
|
||||
private $_quizobj;
|
||||
private $_timenow;
|
||||
private $_passwordrule = null;
|
||||
private $_securewindowrule = null;
|
||||
@@ -17,32 +17,33 @@ class quiz_access_manager {
|
||||
* @param boolean $canpreview whether the current user has the
|
||||
* @param boolean $ignoretimelimits
|
||||
*/
|
||||
public function __construct($quiz, $timenow, $canignoretimelimits) {
|
||||
$this->_quiz = $quiz;
|
||||
public function __construct($quizobj, $timenow, $canignoretimelimits) {
|
||||
$this->_quizobj = $quizobj;
|
||||
$this->_timenow = $timenow;
|
||||
$this->create_standard_rules($canignoretimelimits);
|
||||
}
|
||||
|
||||
private function create_standard_rules($canignoretimelimits) {
|
||||
if ($this->_quiz->attempts > 0) {
|
||||
$this->_rules[] = new num_attempts_access_rule($this->_quiz, $this->_timenow);
|
||||
$quiz = $this->_quizobj->get_quiz();
|
||||
if ($quiz->attempts > 0) {
|
||||
$this->_rules[] = new num_attempts_access_rule($this->_quizobj, $this->_timenow);
|
||||
}
|
||||
$this->_rules[] = new open_close_date_access_rule($this->_quiz, $this->_timenow);
|
||||
if ($this->_quiz->timelimit && !$canignoretimelimits) {
|
||||
$this->_rules[] = new time_limit_access_rule($this->_quiz, $this->_timenow);
|
||||
$this->_rules[] = new open_close_date_access_rule($this->_quizobj, $this->_timenow);
|
||||
if ($quiz->timelimit && !$canignoretimelimits) {
|
||||
$this->_rules[] = new time_limit_access_rule($this->_quizobj, $this->_timenow);
|
||||
}
|
||||
if ($this->_quiz->delay1 || $this->_quiz->delay2) {
|
||||
$this->_rules[] = new inter_attempt_delay_access_rule($this->_quiz, $this->_timenow);
|
||||
if ($quiz->delay1 || $quiz->delay2) {
|
||||
$this->_rules[] = new inter_attempt_delay_access_rule($this->_quizobj, $this->_timenow);
|
||||
}
|
||||
if ($this->_quiz->subnet) {
|
||||
$this->_rules[] = new ipaddress_access_rule($this->_quiz, $this->_timenow);
|
||||
if ($quiz->subnet) {
|
||||
$this->_rules[] = new ipaddress_access_rule($this->_quizobj, $this->_timenow);
|
||||
}
|
||||
if ($this->_quiz->password) {
|
||||
$this->_passwordrule = new password_access_rule($this->_quiz, $this->_timenow);
|
||||
if ($quiz->password) {
|
||||
$this->_passwordrule = new password_access_rule($this->_quizobj, $this->_timenow);
|
||||
$this->_rules[] = $this->_passwordrule;
|
||||
}
|
||||
if ($this->_quiz->popup) {
|
||||
$this->_securewindowrule = new securewindow_access_rule($this->_quiz, $this->_timenow);
|
||||
if ($quiz->popup) {
|
||||
$this->_securewindowrule = new securewindow_access_rule($this->_quizobj, $this->_timenow);
|
||||
$this->_rules[] = $this->_securewindowrule;
|
||||
}
|
||||
}
|
||||
@@ -211,8 +212,9 @@ class quiz_access_manager {
|
||||
if ($this->securewindow_required($canpreview)) {
|
||||
$this->_securewindowrule->print_start_attempt_button($buttontext, $strconfirmstartattempt);
|
||||
} else {
|
||||
print_single_button("attempt.php", array('q' => $this->_quiz->id), $buttontext,
|
||||
'get', '', false, '', false, $strconfirmstartattempt);
|
||||
print_single_button($this->_quizobj->start_attempt_url(),
|
||||
array('cmid' => $this->_quizobj->get_cmid(), 'sesskey' => sesskey()),
|
||||
$buttontext, 'post', '', false, '', false, $strconfirmstartattempt);
|
||||
}
|
||||
echo "</div>\n";
|
||||
|
||||
@@ -238,7 +240,7 @@ class quiz_access_manager {
|
||||
*/
|
||||
public function back_to_view_page($canpreview, $message = '') {
|
||||
global $CFG;
|
||||
$url = $CFG->wwwroot . '/mod/quiz/view.php?q=' . $this->_quiz->id;
|
||||
$url = $this->_quizobj->view_url();
|
||||
if (securewindow_required($canpreview)) {
|
||||
print_header();
|
||||
print_box_start();
|
||||
@@ -268,7 +270,7 @@ class quiz_access_manager {
|
||||
*/
|
||||
public function print_finish_review_link($canpreview) {
|
||||
global $CFG;
|
||||
$url = $CFG->wwwroot . '/mod/quiz/view.php?q=' . $this->_quiz->id;
|
||||
$url = $this->_quizobj->view_url();
|
||||
echo '<div class="finishreview">';
|
||||
if ($this->securewindow_required($canpreview)) {
|
||||
$url = addslashes_js(htmlspecialchars($url));
|
||||
@@ -313,12 +315,13 @@ class quiz_access_manager {
|
||||
* in a javascript alert on the start attempt button.
|
||||
*/
|
||||
public function confirm_start_attempt_message() {
|
||||
if ($this->_quiz->timelimit && $this->_quiz->attempts) {
|
||||
return get_string('confirmstartattempttimelimit','quiz', $this->_quiz->attempts);
|
||||
} else if ($this->_quiz->timelimit) {
|
||||
$quiz = $this->_quizobj->get_quiz();
|
||||
if ($quiz->timelimit && $quiz->attempts) {
|
||||
return get_string('confirmstartattempttimelimit','quiz', $quiz->attempts);
|
||||
} else if ($quiz->timelimit) {
|
||||
return get_string('confirmstarttimelimit','quiz');
|
||||
} else if ($this->_quiz->attempts) {
|
||||
return get_string('confirmstartattemptlimit','quiz', $this->_quiz->attempts);
|
||||
} else if ($quiz->attempts) {
|
||||
return get_string('confirmstartattemptlimit','quiz', $quiz->attempts);
|
||||
}
|
||||
return '';
|
||||
}
|
||||
@@ -342,8 +345,7 @@ class quiz_access_manager {
|
||||
if ($this->securewindow_required($canpreview)) {
|
||||
return $this->_securewindowrule->make_review_link($linktext, $attempt->id);
|
||||
} else {
|
||||
return '<a href="' . $CFG->wwwroot . '/mod/quiz/review.php?q=' . $this->_quiz->id .
|
||||
'&attempt=' . $attempt->id . '">' . $linktext . '</a>';
|
||||
return '<a href="' . $this->_quizobj->review_url($attempt->id) . '">' . $linktext . '</a>';
|
||||
}
|
||||
}
|
||||
/**
|
||||
@@ -354,11 +356,12 @@ class quiz_access_manager {
|
||||
* @return string an appropraite message.
|
||||
*/
|
||||
public function cannot_review_message($reviewoptions) {
|
||||
$quiz = $this->_quizobj->get_quiz();
|
||||
if ($reviewoptions->quizstate == QUIZ_STATE_IMMEDIATELY) {
|
||||
return '';
|
||||
} else if ($reviewoptions->quizstate == QUIZ_STATE_OPEN && $this->_quiz->timeclose &&
|
||||
($this->_quiz->review & QUIZ_REVIEW_CLOSED & QUIZ_REVIEW_RESPONSES)) {
|
||||
return get_string('noreviewuntil', 'quiz', userdate($this->_quiz->timeclose));
|
||||
} else if ($reviewoptions->quizstate == QUIZ_STATE_OPEN && $quiz->timeclose &&
|
||||
($quiz->review & QUIZ_REVIEW_CLOSED & QUIZ_REVIEW_RESPONSES)) {
|
||||
return get_string('noreviewuntil', 'quiz', userdate($quiz->timeclose));
|
||||
} else {
|
||||
return get_string('noreview', 'quiz');
|
||||
}
|
||||
@@ -376,13 +379,15 @@ class quiz_access_manager {
|
||||
*/
|
||||
abstract class quiz_access_rule_base {
|
||||
protected $_quiz;
|
||||
protected $_quizobj;
|
||||
protected $_timenow;
|
||||
/**
|
||||
* Create an instance of this rule for a particular quiz.
|
||||
* @param object $quiz the quiz we will be controlling access to.
|
||||
*/
|
||||
public function __construct($quiz, $timenow) {
|
||||
$this->_quiz = $quiz;
|
||||
public function __construct($quizobj, $timenow) {
|
||||
$this->_quizobj = $quizobj;
|
||||
$this->_quiz = $quizobj->get_quiz();
|
||||
$this->_timenow = $timenow;
|
||||
}
|
||||
/**
|
||||
@@ -620,7 +625,7 @@ class password_access_rule extends quiz_access_rule_base {
|
||||
/// Print the password entry form.
|
||||
$output .= '<p>' . get_string('requirepasswordmessage', 'quiz') . "</p>\n";
|
||||
$output .= '<form id="passwordform" method="post" action="' . $CFG->wwwroot .
|
||||
'/mod/quiz/attempt.php?q=' . $this->_quiz->id .
|
||||
'/mod/quiz/startattempt.php?q=' . $this->_quiz->id .
|
||||
'" onclick="this.autocomplete=\'off\'">' . "\n";
|
||||
$output .= "<div>\n";
|
||||
$output .= '<label for="quizpassword">' . get_string('password') . "</label>\n";
|
||||
@@ -674,7 +679,8 @@ class securewindow_access_rule extends quiz_access_rule_base {
|
||||
public function print_start_attempt_button($buttontext, $strconfirmstartattempt) {
|
||||
global $CFG, $SESSION;
|
||||
|
||||
$attempturl = $CFG->wwwroot . '/mod/quiz/attempt.php?q=' . $this->_quiz->id;
|
||||
$attempturl = $this->_quizobj->start_attempt_url() . '?cmid=' . $this->_quizobj->get_cmid() .
|
||||
'&sesskey=' . sesskey();
|
||||
$window = 'quizpopup';
|
||||
|
||||
if (!empty($CFG->usesid) && !isset($_COOKIE[session_name()])) {
|
||||
@@ -696,9 +702,8 @@ class securewindow_access_rule extends quiz_access_rule_base {
|
||||
* @return string HTML for the link.
|
||||
*/
|
||||
public function make_review_link($linktext, $attemptid) {
|
||||
global $CFG;
|
||||
return link_to_popup_window($CFG->wwwroot . '/mod/quiz/review.php?q=' . $this->_quiz->id .
|
||||
'&attempt=' . $attemptid, 'quizpopup', $linktext, '', '', '', $this->windowoptions, true);
|
||||
return link_to_popup_window($this->_quizobj->review_url($attemptid),
|
||||
'quizpopup', $linktext, '', '', '', $this->windowoptions, true);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+100
-234
@@ -10,7 +10,7 @@
|
||||
* @package quiz
|
||||
*/
|
||||
|
||||
require_once('../../config.php');
|
||||
require_once(dirname(__FILE__) . '/../../config.php');
|
||||
require_once($CFG->dirroot . '/mod/quiz/locallib.php');
|
||||
|
||||
/// remember the current time as the time any responses were submitted
|
||||
@@ -18,35 +18,13 @@
|
||||
$timenow = time();
|
||||
|
||||
/// Get submitted parameters.
|
||||
$id = optional_param('id', 0, PARAM_INT); // Course Module ID
|
||||
$q = optional_param('q', 0, PARAM_INT); // or quiz ID
|
||||
$attemptid = required_param('attempt', PARAM_INT);
|
||||
$page = optional_param('page', 0, PARAM_INT);
|
||||
$questionids = optional_param('questionids', '');
|
||||
$submittedquestionids = optional_param('questionids', '', PARAM_SEQUENCE);
|
||||
$finishattempt = optional_param('finishattempt', 0, PARAM_BOOL);
|
||||
$timeup = optional_param('timeup', 0, PARAM_BOOL); // True if form was submitted by timer.
|
||||
$forcenew = optional_param('forcenew', false, PARAM_BOOL); // Teacher has requested new preview
|
||||
|
||||
if ($id) {
|
||||
if (! $cm = get_coursemodule_from_id('quiz', $id)) {
|
||||
print_error('invalidcoursemodule');
|
||||
}
|
||||
if (! $course = $DB->get_record('course', array('id' => $cm->course))) {
|
||||
print_error("coursemisconf");
|
||||
}
|
||||
if (! $quiz = $DB->get_record('quiz', array('id' => $cm->instance))) {
|
||||
print_error('invalidcoursemodule');
|
||||
}
|
||||
} else {
|
||||
if (! $quiz = $DB->get_record('quiz', array('id' => $q))) {
|
||||
print_error('invalidcoursemodule');
|
||||
}
|
||||
if (! $course = $DB->get_record('course', array('id' => $quiz->course))) {
|
||||
print_error('invalidcourseid');
|
||||
}
|
||||
if (! $cm = get_coursemodule_from_instance("quiz", $quiz->id, $course->id)) {
|
||||
print_error('invalidcoursemodule');
|
||||
}
|
||||
}
|
||||
$attemptobj = new quiz_attempt($attemptid);
|
||||
|
||||
/// We treat automatically closed attempts just like normally closed attempts
|
||||
if ($timeup) {
|
||||
@@ -54,148 +32,50 @@
|
||||
}
|
||||
|
||||
/// Check login and get contexts.
|
||||
require_login($course->id, false, $cm);
|
||||
$coursecontext = get_context_instance(CONTEXT_COURSE, $cm->course);
|
||||
$context = get_context_instance(CONTEXT_MODULE, $cm->id);
|
||||
$canpreview = has_capability('mod/quiz:preview', $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?cmid=' . $cm->id);
|
||||
}
|
||||
require_login($attemptobj->get_courseid(), false, $attemptobj->get_cm());
|
||||
|
||||
/// Check capabilites.
|
||||
if (!$canpreview) {
|
||||
if (!$attemptobj->is_preview_user()) {
|
||||
require_capability('mod/quiz:attempt', $context);
|
||||
}
|
||||
/// 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
|
||||
|
||||
/// 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
|
||||
$DB->set_field('quiz_attempts', 'timefinish', $timenow, array('quiz' => $quiz->id, 'userid' => $USER->id));
|
||||
/// Log continuation of the attempt, but only if some time has passed.
|
||||
if (($timenow - $attemptobj->get_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($attemptobj->get_courseid(), 'quiz', 'continue attemp',
|
||||
'review.php?attempt=' . $attemptobj->get_attemptid(),
|
||||
$attemptobj->get_quizid(), $attemptobj->get_cmid());
|
||||
}
|
||||
|
||||
/// Look for an existing attempt.
|
||||
$newattempt = false;
|
||||
$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) {
|
||||
//TODO: need more detailed error info
|
||||
print_error('attempterror', 'quiz', $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 = $DB->get_records_select('quiz_attempts', "quiz = ?
|
||||
AND userid = ? AND preview = 1", array($quiz->id, $USER->id))) {
|
||||
foreach ($oldattempts as $oldattempt) {
|
||||
quiz_delete_attempt($oldattempt, $quiz);
|
||||
}
|
||||
}
|
||||
|
||||
/// 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 = $DB->insert_record('quiz_attempts', $attempt)) {
|
||||
quiz_error($quiz, 'newattemptfail');
|
||||
}
|
||||
|
||||
/// 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);
|
||||
}
|
||||
}
|
||||
|
||||
/// 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 = $timenow - 1;
|
||||
}
|
||||
|
||||
/// Load all the questions and states needed by this script
|
||||
/// Work out which questions we need.
|
||||
$attemptobj->preload_questions();
|
||||
|
||||
/// Get the list of questions needed by this page.
|
||||
$pagelist = quiz_questions_on_page($attempt->layout, $page);
|
||||
|
||||
if ($newattempt || $finishattempt) {
|
||||
$questionlist = quiz_questions_in_quiz($attempt->layout);
|
||||
if ($finishattempt) {
|
||||
$questionids = $attemptobj->get_question_ids();
|
||||
} else if ($page >= 0) {
|
||||
$questionids = $attemptobj->get_question_ids($page);
|
||||
} else {
|
||||
$questionlist = $pagelist;
|
||||
$questionids = array();
|
||||
}
|
||||
|
||||
/// Add all questions that are on the submitted form
|
||||
if ($questionids) {
|
||||
$questionlist .= ','.$questionids;
|
||||
if ($submittedquestionids) {
|
||||
$submittedquestionids = explode(',', $submittedquestionids);
|
||||
$questionids = $questionids + $submittedquestionids;
|
||||
} else {
|
||||
$submittedquestionids = array();
|
||||
}
|
||||
|
||||
if (!$questionlist) {
|
||||
/// Check.
|
||||
if (empty($questionids)) {
|
||||
quiz_error($quiz, 'noquestionsfound');
|
||||
}
|
||||
|
||||
$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 those questions and the associated states.
|
||||
$attemptobj->load_questions($questionids);
|
||||
$attemptobj->load_question_states($questionids);
|
||||
|
||||
/// Restore the question sessions to their most recent states creating new sessions where required.
|
||||
if (!$states = get_question_states($questions, $quiz, $attempt, $lastattemptid)) {
|
||||
print_error('cannotrestore', 'quiz');
|
||||
}
|
||||
|
||||
/// 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]);
|
||||
}
|
||||
}
|
||||
|
||||
/// Process form data /////////////////////////////////////////////////
|
||||
|
||||
@@ -223,33 +103,31 @@
|
||||
unset($responses->forcenewattempt);
|
||||
|
||||
/// Extract the responses. $actions will be an array indexed by the questions ids.
|
||||
$actions = question_extract_responses($questions, $responses, $event);
|
||||
$actions = question_extract_responses($attemptobj->get_questions(), $responses, $event);
|
||||
|
||||
/// Process each question in turn
|
||||
$questionidarray = explode(',', $questionids);
|
||||
$success = true;
|
||||
foreach($questionidarray as $i) {
|
||||
if (!isset($actions[$i])) {
|
||||
$actions[$i]->responses = array('' => '');
|
||||
$actions[$i]->event = QUESTION_EVENTOPEN;
|
||||
foreach($submittedquestionids as $id) {
|
||||
if (!isset($actions[$id])) {
|
||||
$actions[$id]->responses = array('' => '');
|
||||
$actions[$id]->event = QUESTION_EVENTOPEN;
|
||||
}
|
||||
$actions[$i]->timestamp = $timenow;
|
||||
if (question_process_responses($questions[$i], $states[$i], $actions[$i], $quiz, $attempt)) {
|
||||
save_question_session($questions[$i], $states[$i]);
|
||||
$actions[$id]->timestamp = $timenow;
|
||||
if (question_process_responses($attemptobj->get_question($id),
|
||||
$attemptobj->get_question_state($id), $actions[$id],
|
||||
$attemptobj->get_quiz(), $attemptobj->get_attempt())) {
|
||||
save_question_session($attemptobj->get_question($id),
|
||||
$attemptobj->get_question_state($id));
|
||||
} else {
|
||||
$success = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$success) {
|
||||
$pagebit = '';
|
||||
if ($page) {
|
||||
$pagebit = '&page=' . $page;
|
||||
}
|
||||
print_error('errorprocessingresponses', 'question',
|
||||
$CFG->wwwroot . '/mod/quiz/attempt.php?q=' . $quiz->id . $pagebit);
|
||||
print_error('errorprocessingresponses', 'question', $attemptobj->attempt_url(0, $page));
|
||||
}
|
||||
|
||||
$attempt = $attemptobj->get_attempt();
|
||||
$attempt->timemodified = $timenow;
|
||||
if (!$DB->update_record('quiz_attempts', $attempt)) {
|
||||
quiz_error($quiz, 'saveattemptfailed');
|
||||
@@ -259,36 +137,36 @@
|
||||
/// Finish attempt if requested
|
||||
if ($finishattempt) {
|
||||
|
||||
/// Set the attempt to be finished
|
||||
$attempt->timefinish = $timenow;
|
||||
|
||||
/// Move each question to the closed state.
|
||||
$success = true;
|
||||
foreach ($questions as $key => $question) {
|
||||
foreach ($attemptobj->get_questions() as $id => $question) {
|
||||
$action = new stdClass;
|
||||
$action->event = QUESTION_EVENTCLOSE;
|
||||
$action->responses = $states[$key]->responses;
|
||||
$action->timestamp = $states[$key]->timestamp;
|
||||
if (question_process_responses($question, $closestates[$key], $action, $quiz, $attempt)) {
|
||||
save_question_session($question, $closestates[$key]);
|
||||
$action->responses = $attemptobj->get_question_state($id)->responses;
|
||||
$action->timestamp = $attemptobj->get_question_state($id)->timestamp;
|
||||
if (question_process_responses($attemptobj->get_question($id),
|
||||
$attemptobj->get_question_state($id), $action,
|
||||
$attemptobj->get_quiz(), $attemptobj->get_attempt())) {
|
||||
save_question_session($attemptobj->get_question($id),
|
||||
$attemptobj->get_question_state($id));
|
||||
} else {
|
||||
$success = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$success) {
|
||||
$pagebit = '';
|
||||
if ($page) {
|
||||
$pagebit = '&page=' . $page;
|
||||
}
|
||||
print_error('errorprocessingresponses', 'question',
|
||||
$CFG->wwwroot . '/mod/quiz/attempt.php?q=' . $quiz->id . $pagebit);
|
||||
print_error('errorprocessingresponses', 'question', $attemptobj->attempt_url(0, $page));
|
||||
}
|
||||
|
||||
/// Log the end of this attempt.
|
||||
add_to_log($course->id, 'quiz', 'close attempt', "review.php?attempt=$attempt->id",
|
||||
"$quiz->id", $cm->id);
|
||||
add_to_log($attemptobj->get_courseid(), 'quiz', 'close attempt',
|
||||
'review.php?attempt=' . $attemptobj->get_attemptid(),
|
||||
$attemptobj->get_quizid(), $attemptobj->get_cmid());
|
||||
|
||||
/// Update the quiz attempt record.
|
||||
$attempt = $attemptobj->get_attempt();
|
||||
$attempt->timemodified = $timenow;
|
||||
$attempt->timefinish = $timenow;
|
||||
if (!$DB->update_record('quiz_attempts', $attempt)) {
|
||||
quiz_error($quiz, 'saveattemptfailed');
|
||||
}
|
||||
@@ -298,52 +176,54 @@
|
||||
quiz_save_best_grade($quiz);
|
||||
|
||||
/// Send any notification emails (if this is not a preview).
|
||||
quiz_send_notification_emails($course, $quiz, $attempt, $context, $cm);
|
||||
$attemptobj->quiz_send_notification_emails();
|
||||
}
|
||||
|
||||
/// Clear the password check flag in the session.
|
||||
$accessmanager = $attemptobj->get_access_manager($timenow);
|
||||
$accessmanager->clear_password_access();
|
||||
|
||||
/// Send the user to the review page.
|
||||
redirect($CFG->wwwroot . '/mod/quiz/review.php?attempt='.$attempt->id, 0);
|
||||
redirect($attemptobj->review_url());
|
||||
}
|
||||
|
||||
/// 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) {
|
||||
//TODO: need more detailed error info
|
||||
print_error('attempterror', 'quiz', $CFG->wwwroot . '/mod/quiz/view.php?q=' . $quiz->id);
|
||||
}
|
||||
$accessmanager->do_password_check($canpreview);
|
||||
/// Now is the right time to check access.
|
||||
$accessmanager = $attemptobj->get_access_manager($timenow);
|
||||
$messages = $accessmanager->prevent_access();
|
||||
if (!$attemptobj->is_preview_user() && $messages) {
|
||||
print_error('attempterror', 'quiz', $quizobj->view_url(),
|
||||
$accessmanager->print_messages($messages, true));
|
||||
}
|
||||
$accessmanager->do_password_check($attemptobj->is_preview_user());
|
||||
|
||||
/// Having processed the responses, we want to go to the summary page.
|
||||
if ($page == -1) {
|
||||
redirect($attemptobj->summary_url());
|
||||
}
|
||||
|
||||
/// Print the quiz page ////////////////////////////////////////////////////////
|
||||
|
||||
// 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 ($accessmanager->securewindow_required($canpreview)) {
|
||||
$accessmanager->setup_secure_page($course->shortname.': '.format_string($quiz->name), $headtags);
|
||||
$title = get_string('attempt', 'quiz', $attemptobj->get_attempt_number());
|
||||
$headtags = $attemptobj->get_html_head_contributions($page);
|
||||
if ($accessmanager->securewindow_required($attemptobj->is_preview_user())) {
|
||||
$accessmanager->setup_secure_page($attemptobj->get_course()->shortname . ': ' .
|
||||
format_string($attemptobj->get_quiz_name()), $headtags);
|
||||
} else {
|
||||
$strupdatemodule = has_capability('moodle/course:manageactivities', $coursecontext)
|
||||
? update_module_button($cm->id, $course->id, get_string('modulename', 'quiz'))
|
||||
: "";
|
||||
$navigation = build_navigation($strattemptnum, $cm);
|
||||
print_header_simple(format_string($quiz->name), "", $navigation, "", $headtags, true, $strupdatemodule);
|
||||
print_header_simple(format_string($attemptobj->get_quiz_name()), '', $attemptobj->navigation($title),
|
||||
'', $headtags, true, $attemptobj->update_module_button());
|
||||
}
|
||||
echo '<div id="overDiv" style="position:absolute; visibility:hidden; z-index:1000;"></div>'; // for overlib
|
||||
|
||||
if ($canpreview) {
|
||||
if ($attemptobj->is_preview_user()) {
|
||||
/// Show the tab bar.
|
||||
$currenttab = 'preview';
|
||||
include('tabs.php');
|
||||
|
||||
/// Heading and tab bar.
|
||||
print_heading(get_string('previewquiz', 'quiz', format_string($quiz->name)));
|
||||
print_restart_preview_button($quiz);
|
||||
$attemptobj->print_restart_preview_button();
|
||||
|
||||
/// Inform teachers of any restrictions that would apply to students at this point.
|
||||
if ($messages) {
|
||||
@@ -355,17 +235,17 @@
|
||||
} else {
|
||||
/// Just a heading.
|
||||
if ($quiz->attempts != 1) {
|
||||
print_heading(format_string($quiz->name).' - '.$strattemptnum);
|
||||
print_heading(format_string($quiz->name).' - '.$title);
|
||||
} else {
|
||||
print_heading(format_string($quiz->name));
|
||||
}
|
||||
}
|
||||
|
||||
// Start the form
|
||||
echo '<form id="responseform" method="post" action="attempt.php?q=', s($quiz->id), '&page=', s($page),
|
||||
echo '<form id="responseform" method="post" action="', $attemptobj->attempt_url(0, $page),
|
||||
'" enctype="multipart/form-data"' .
|
||||
' onclick="this.autocomplete=\'off\'" onkeypress="return check_enter(event);">', "\n";
|
||||
if($quiz->timelimit > 0) {
|
||||
if($attemptobj->get_quiz()->timelimit > 0) {
|
||||
// Make sure javascript is enabled for time limited quizzes
|
||||
?>
|
||||
<script type="text/javascript">
|
||||
@@ -381,39 +261,24 @@
|
||||
echo '<div>';
|
||||
|
||||
/// Print the navigation panel if required
|
||||
$numpages = quiz_number_of_pages($attempt->layout);
|
||||
if ($numpages > 1) {
|
||||
quiz_print_navigation_panel($page, $numpages);
|
||||
}
|
||||
// TODO!!!
|
||||
quiz_print_navigation_panel($page, $attemptobj->get_num_pages());
|
||||
|
||||
/// Print all the questions
|
||||
$number = quiz_first_questionnumber($attempt->layout, $pagelist);
|
||||
foreach ($pagequestions as $i) {
|
||||
$options = quiz_get_renderoptions($quiz->review, $states[$i]);
|
||||
// Print the question
|
||||
print_question($questions[$i], $states[$i], $number, $quiz, $options);
|
||||
save_question_session($questions[$i], $states[$i]);
|
||||
$number += $questions[$i]->length;
|
||||
foreach ($attemptobj->get_question_ids($page) as $id) {
|
||||
$attemptobj->print_question($id);
|
||||
}
|
||||
|
||||
/// Print the submit buttons
|
||||
$strconfirmattempt = get_string("confirmclose", "quiz");
|
||||
$onclick = "return confirm('$strconfirmattempt')";
|
||||
/// Print a link to the next page.
|
||||
echo "<div class=\"submitbtns mdl-align\">\n";
|
||||
|
||||
echo "<input type=\"submit\" name=\"saveattempt\" value=\"".get_string("savenosubmit", "quiz")."\" />\n";
|
||||
if ($quiz->optionflags & QUESTION_ADAPTIVE) {
|
||||
echo "<input type=\"submit\" name=\"markall\" value=\"".get_string("markall", "quiz")."\" />\n";
|
||||
if ($attemptobj->is_last_page($page)) {
|
||||
$nextpage = -1;
|
||||
} else {
|
||||
$nextpage = $page + 1;
|
||||
}
|
||||
echo "<input type=\"submit\" name=\"finishattempt\" value=\"".get_string("finishattempt", "quiz")."\" onclick=\"$onclick\" />\n";
|
||||
|
||||
echo link_arrow_right(get_string('next'), 'javascript:navigate(' . $nextpage . ')');
|
||||
echo "</div>";
|
||||
|
||||
// Print the navigation panel if required
|
||||
if ($numpages > 1) {
|
||||
quiz_print_navigation_panel($page, $numpages);
|
||||
}
|
||||
|
||||
// Finish the form
|
||||
echo '</div>';
|
||||
echo '<input type="hidden" name="timeup" id="timeup" value="0" />';
|
||||
@@ -421,15 +286,16 @@
|
||||
// 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 '<input type="hidden" name="questionids" value="' .
|
||||
implode(',', $attemptobj->get_question_ids($page)) . "\" />\n";
|
||||
|
||||
echo "</form>\n";
|
||||
|
||||
// Finish the page
|
||||
$accessmanager->show_attempt_timer_if_needed($attempt, time());
|
||||
if ($accessmanager->securewindow_required($canpreview)) {
|
||||
$accessmanager->show_attempt_timer_if_needed($attemptobj->get_attempt(), time());
|
||||
if ($accessmanager->securewindow_required($attemptobj->is_preview_user())) {
|
||||
print_footer('empty');
|
||||
} else {
|
||||
print_footer($course);
|
||||
print_footer($attemptobj->get_course());
|
||||
}
|
||||
?>
|
||||
|
||||
+287
-98
@@ -32,13 +32,14 @@ class quiz {
|
||||
protected $cm;
|
||||
protected $quiz;
|
||||
protected $context;
|
||||
protected $questionids; // All question ids in order that they appear in the quiz.
|
||||
protected $pagequestionids; // array page no => array of questionids on the page in order.
|
||||
|
||||
// Fields set later if that data is needed.
|
||||
protected $questions = null;
|
||||
protected $accessmanager = null;
|
||||
protected $reviewoptions = null;
|
||||
protected $ispreviewuser = null;
|
||||
protected $questions = array();
|
||||
protected $questionsnumbered = false;
|
||||
|
||||
// Constructor =========================================================================
|
||||
/**
|
||||
@@ -53,29 +54,41 @@ class quiz {
|
||||
$this->cm = $cm;
|
||||
$this->course = $course;
|
||||
$this->context = get_context_instance(CONTEXT_MODULE, $cm->id);
|
||||
$this->determine_layout();
|
||||
}
|
||||
|
||||
// Functions for loading more data =====================================================
|
||||
public function load_questions_on_page($page) {
|
||||
$this->load_questions(quiz_questions_on_page($this->quiz->layout, $page));
|
||||
$this->load_questions($this->pagequestionids[$page]);
|
||||
}
|
||||
|
||||
public function preload_questions() {
|
||||
if (empty($this->questionids)) {
|
||||
throw new moodle_quiz_exception($this, 'noquestions', $this->edit_url());
|
||||
}
|
||||
$this->questions = question_preload_questions($this->questionids,
|
||||
'qqi.grade AS maxgrade, qqi.id AS instance',
|
||||
'{quiz_question_instances} qqi ON qqi.quiz = :quizid AND q.id = qqi.question',
|
||||
array('quizid' => $this->quiz->id));
|
||||
$this->number_questions();
|
||||
}
|
||||
|
||||
/**
|
||||
* Load some or all of the queestions for this quiz.
|
||||
*
|
||||
* @param string $questionlist comma-separate list of question ids. Blank for all.
|
||||
* @param array $questionids question ids of the questions to load. null for all.
|
||||
*/
|
||||
public function load_questions($questionlist = '') {
|
||||
if (!$questionlist) {
|
||||
$questionlist = quiz_questions_in_quiz($this->quiz->layout);
|
||||
public function load_questions($questionids = null) {
|
||||
if (is_null($questionids)) {
|
||||
$questionids = $this->questionids;
|
||||
}
|
||||
$newquestions = question_load_questions($questionlist, 'qqi.grade AS maxgrade, qqi.id AS instance',
|
||||
'{quiz_question_instances} qqi ON qqi.quiz = ' . $this->quiz->id . ' AND q.id = qqi.question');
|
||||
if (is_string($newquestions)) {
|
||||
throw new moodle_quiz_exception($this, 'loadingquestionsfailed', $newquestions);
|
||||
$questionstoprocess = array();
|
||||
foreach ($questionids as $id) {
|
||||
$questionstoprocess[$id] = $this->questions[$id];
|
||||
}
|
||||
if (!get_question_options($questionstoprocess)) {
|
||||
throw new moodle_quiz_exception($this, 'loadingquestionsfailed', implode(', ', $questionids));
|
||||
}
|
||||
$this->questions = $this->questions + $newquestions;
|
||||
$this->questionsnumbered = false;
|
||||
}
|
||||
|
||||
// Simple getters ======================================================================
|
||||
@@ -84,6 +97,11 @@ class quiz {
|
||||
return $this->course->id;
|
||||
}
|
||||
|
||||
/** @return object the row of the course table. */
|
||||
public function get_course() {
|
||||
return $this->course;
|
||||
}
|
||||
|
||||
/** @return integer the quiz id. */
|
||||
public function get_quizid() {
|
||||
return $this->quiz->id;
|
||||
@@ -120,6 +138,22 @@ class quiz {
|
||||
return $this->ispreviewuser;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return integer number fo pages in this quiz.
|
||||
*/
|
||||
public function get_num_pages() {
|
||||
return count($this->pagequestionids);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param int $page page number
|
||||
* @return boolean true if this is the last page of the quiz.
|
||||
*/
|
||||
public function is_last_page($page) {
|
||||
return $page == count($this->pagequestionids) - 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param integer $id the question id.
|
||||
* @return object the question object with that id.
|
||||
@@ -129,18 +163,68 @@ class quiz {
|
||||
return $this->questions[$id];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $questionids question ids of the questions to load. null for all.
|
||||
*/
|
||||
public function get_questions($questionids = null) {
|
||||
if (is_null($questionids)) {
|
||||
$questionids = $this->questionids;
|
||||
}
|
||||
$questions = array();
|
||||
foreach ($questionids as $id) {
|
||||
$questions[$id] = $this->questions[$id];
|
||||
$this->ensure_question_loaded($id);
|
||||
}
|
||||
return $questions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the list of question ids for either a given page of the quiz, or for the
|
||||
* whole quiz.
|
||||
*
|
||||
* @param mixed $page string 'all' or integer page number.
|
||||
* @return array the reqested list of question ids.
|
||||
*/
|
||||
public function get_question_ids($page = 'all') {
|
||||
if ($page == 'all') {
|
||||
$list = $this->questionids;
|
||||
} else {
|
||||
$list = $this->pagequestionids[$page];
|
||||
}
|
||||
// Clone the array, so our private arrays cannot be modified.
|
||||
$result = array();
|
||||
foreach ($list as $id) {
|
||||
$result[] = $id;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param integer $timenow the current time as a unix timestamp.
|
||||
* @return object and instance of the quiz_access_manager class for this quiz at this time.
|
||||
*/
|
||||
public function get_access_manager($timenow) {
|
||||
if (is_null($this->accessmanager)) {
|
||||
$this->accessmanager = new quiz_access_manager($this->quiz, $timenow,
|
||||
$this->accessmanager = new quiz_access_manager($this, $timenow,
|
||||
has_capability('mod/quiz:ignoretimelimits', $this->context, NULL, false));
|
||||
}
|
||||
return $this->accessmanager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper round the has_capability funciton that automatically passes in the quiz context.
|
||||
*/
|
||||
public function has_capability($capability, $userid = NULL, $doanything = true) {
|
||||
return has_capability($capability, $this->context, $userid, $doanything);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper round the require_capability funciton that automatically passes in the quiz context.
|
||||
*/
|
||||
public function require_capability($capability, $userid = NULL, $doanything = true) {
|
||||
return require_capability($capability, $this->context, $userid, $doanything);
|
||||
}
|
||||
|
||||
// URLs related to this attempt ========================================================
|
||||
/**
|
||||
* @return string the URL of this quiz's view page.
|
||||
@@ -150,6 +234,40 @@ class quiz {
|
||||
return $CFG->wwwroot . '/mod/quiz/view.php?id=' . $this->cm->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string the URL of this quiz's edit page.
|
||||
*/
|
||||
public function edit_url() {
|
||||
global $CFG;
|
||||
return $CFG->wwwroot . '/mod/quiz/edit.php?cmid=' . $this->cm->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param integer $attemptid the id of an attempt.
|
||||
* @return string the URL of that attempt.
|
||||
*/
|
||||
public function attempt_url($attemptid) {
|
||||
global $CFG;
|
||||
return $CFG->wwwroot . '/mod/quiz/attempt.php?attempt=' . $attemptid . '&page=0';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string the URL of this quiz's edit page. Needs to be POSTed to with a cmid parameter.
|
||||
*/
|
||||
public function start_attempt_url() {
|
||||
global $CFG;
|
||||
return $CFG->wwwroot . '/mod/quiz/startattempt.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param integer $attemptid the id of an attempt.
|
||||
* @return string the URL of the review of that attempt.
|
||||
*/
|
||||
public function review_url($attemptid) {
|
||||
global $CFG;
|
||||
return $CFG->wwwroot . '/mod/quiz/review.php?attempt=' . $attemptid;
|
||||
}
|
||||
|
||||
// Bits of content =====================================================================
|
||||
/**
|
||||
* @return string the HTML snipped that needs to be supplied to print_header_simple
|
||||
@@ -175,11 +293,67 @@ class quiz {
|
||||
|
||||
// Private methods =====================================================================
|
||||
// Check that the definition of a particular question is loaded, and if not throw an exception.
|
||||
private function ensure_question_loaded($id) {
|
||||
if (!array_key_exists($id, $this->questions)) {
|
||||
protected function ensure_question_loaded($id) {
|
||||
if (isset($this->questions[$id]->_partiallyloaded)) {
|
||||
throw new moodle_quiz_exception($this, 'questionnotloaded', $id);
|
||||
}
|
||||
}
|
||||
|
||||
private function determine_layout() {
|
||||
$this->questionids = array();
|
||||
$this->pagequestionids = array();
|
||||
|
||||
// Get the appropriate layout string (from quiz or attempt).
|
||||
$layout = $this->get_layout_string();
|
||||
if (empty($layout)) {
|
||||
// Nothing to do.
|
||||
return;
|
||||
}
|
||||
|
||||
// Break up the layout string into pages.
|
||||
$pagelayouts = explode(',0', $layout);
|
||||
|
||||
// Strip off any empty last page (normally there is one).
|
||||
if (end($pagelayouts) == '') {
|
||||
array_pop($pagelayouts);
|
||||
}
|
||||
|
||||
// File the ids into the arrays.
|
||||
$this->questionids = array();
|
||||
$this->pagequestionids = array();
|
||||
foreach ($pagelayouts as $page => $pagelayout) {
|
||||
$pagelayout = trim($pagelayout, ',');
|
||||
if ($pagelayout == '') continue;
|
||||
$this->pagequestionids[$page] = explode(',', $pagelayout);
|
||||
foreach ($this->pagequestionids[$page] as $id) {
|
||||
$this->questionids[] = $id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Number the questions.
|
||||
private function number_questions() {
|
||||
$number = 1;
|
||||
foreach ($this->pagequestionids as $page => $questionids) {
|
||||
foreach ($questionids as $id) {
|
||||
if ($this->questions[$id]->length > 0) {
|
||||
$this->questions[$id]->_number = $number;
|
||||
$number += $this->questions[$id]->length;
|
||||
} else {
|
||||
$this->questions[$id]->_number = get_string('infoshort', 'quiz');
|
||||
}
|
||||
$this->questions[$id]->_page = $page;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string the layout of this quiz. Used by number_questions to
|
||||
* work out which questions are on which pages.
|
||||
*/
|
||||
protected function get_layout_string() {
|
||||
return $this->quiz->questions;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -215,59 +389,30 @@ class quiz_attempt extends quiz {
|
||||
throw new moodle_exception('invalidcoursemodule');
|
||||
}
|
||||
parent::__construct($quiz, $cm, $course);
|
||||
$this->preload_questions();
|
||||
}
|
||||
|
||||
// Functions for loading more data =====================================================
|
||||
public function load_questions_on_page($page) {
|
||||
$this->load_questions(quiz_questions_on_page($this->attempt->layout, $page));
|
||||
}
|
||||
|
||||
/**
|
||||
* Load some or all of the queestions for this quiz.
|
||||
* Load the state of a number of questions that have already been loaded.
|
||||
*
|
||||
* @param string $questionlist comma-separate list of question ids. Blank for all.
|
||||
* @param array $questionids question ids to process. Blank = all.
|
||||
*/
|
||||
public function load_questions($questionlist = '') {
|
||||
if (!$questionlist) {
|
||||
$questionlist = quiz_questions_in_quiz($this->attempt->layout);
|
||||
public function load_question_states($questionids = null) {
|
||||
if (is_null($questionids)) {
|
||||
$questionids = $this->questionids;
|
||||
}
|
||||
parent::load_questions($questionlist);
|
||||
}
|
||||
|
||||
public function load_question_states() {
|
||||
$questionstodo = array_diff_key($this->questions, $this->states);
|
||||
if (!$newstates = get_question_states($questionstodo, $this->quiz, $this->attempt)) {
|
||||
$questionstoprocess = array();
|
||||
foreach ($questionids as $id) {
|
||||
$this->ensure_question_loaded($id);
|
||||
$questionstoprocess[$id] = $this->questions[$id];
|
||||
}
|
||||
if (!$newstates = get_question_states($questionstoprocess, $this->quiz, $this->attempt)) {
|
||||
throw new moodle_quiz_exception($this, 'cannotrestore');
|
||||
}
|
||||
$this->states = $this->states + $newstates;
|
||||
}
|
||||
|
||||
/**
|
||||
* Number the loaded questions.
|
||||
*
|
||||
* At the moment, this assumes for simplicity that the loaded questions are contiguous.
|
||||
*/
|
||||
public function number_questions($page = 'all') {
|
||||
if ($this->questionsnumbered) {
|
||||
return;
|
||||
}
|
||||
if ($page != 'all') {
|
||||
$pagelist = quiz_questions_in_page($this->attempt->layout, $page);
|
||||
$number = quiz_first_questionnumber($this->attempt->layout, $pagelist);
|
||||
} else {
|
||||
$number = 1;
|
||||
}
|
||||
$questionids = $this->get_question_ids($page);
|
||||
foreach ($questionids as $id) {
|
||||
if ($this->questions[$id]->length > 0) {
|
||||
$this->questions[$id]->number = $number;
|
||||
$number += $this->questions[$id]->length;
|
||||
} else {
|
||||
$this->questions[$id]->number = get_string('infoshort', 'quiz');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Simple getters ======================================================================
|
||||
/** @return integer the attempt id. */
|
||||
public function get_attemptid() {
|
||||
@@ -279,6 +424,11 @@ class quiz_attempt extends quiz {
|
||||
return $this->attempt;
|
||||
}
|
||||
|
||||
/** @return integer the number of this attemp (is it this user's first, second, ... attempt). */
|
||||
public function get_attempt_number() {
|
||||
return $this->attempt->attempt;
|
||||
}
|
||||
|
||||
/** @return integer the id of the user this attempt belongs to. */
|
||||
public function get_userid() {
|
||||
return $this->attempt->userid;
|
||||
@@ -289,6 +439,11 @@ class quiz_attempt extends quiz {
|
||||
return $this->attempt->timefinish != 0;
|
||||
}
|
||||
|
||||
public function get_question_state($questionid) {
|
||||
$this->ensure_state_loaded($questionid);
|
||||
return $this->states[$questionid];
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper that calls quiz_get_reviewoptions with the appropriate arguments.
|
||||
*
|
||||
@@ -301,22 +456,6 @@ class quiz_attempt extends quiz {
|
||||
return $this->reviewoptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the list of question ids for either a given page of the quiz, or for the
|
||||
* whole quiz.
|
||||
*
|
||||
* @param mixed $page string 'all' or integer page number.
|
||||
* @return array the reqested list of question ids.
|
||||
*/
|
||||
public function get_question_ids($page = 'all') {
|
||||
if ($page == 'all') {
|
||||
$questionlist = quiz_questions_in_quiz($this->attempt->layout);
|
||||
} else {
|
||||
$questionlist = quiz_questions_in_page($this->attempt->layout, $page);
|
||||
}
|
||||
return explode(',', $questionlist);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a quiz_attempt_question_iterator for either a page of the quiz, or a whole quiz.
|
||||
* You must have called load_questions with an appropriate argument first.
|
||||
@@ -389,18 +528,14 @@ class quiz_attempt extends quiz {
|
||||
/**
|
||||
* @param integer $page if specified, the URL of this particular page of the attempt, otherwise
|
||||
* the URL will go to the first page.
|
||||
* @param integer $question a question id. If set, will add a fragment to the URL
|
||||
* @param integer $questionid a question id. If set, will add a fragment to the URL
|
||||
* to jump to a particuar question on the page.
|
||||
* @return string the URL to continue this attempt.
|
||||
*/
|
||||
public function attempt_url($page = 0, $question = false) {
|
||||
public function attempt_url($questionid = 0, $page = -1) {
|
||||
global $CFG;
|
||||
$fragment = '';
|
||||
if ($question) {
|
||||
$fragment = '#q' . $question;
|
||||
}
|
||||
return $CFG->wwwroot . '/mod/quiz/attempt.php?id=' .
|
||||
$this->cm->id . '$amp;page=' . $page . $fragment;
|
||||
return $CFG->wwwroot . '/mod/quiz/attempt.php?attempt=' . $this->attempt->id . '&' .
|
||||
$this->page_and_question_fragment($questionid, $page);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -414,28 +549,43 @@ class quiz_attempt extends quiz {
|
||||
/**
|
||||
* @param integer $page if specified, the URL of this particular page of the attempt, otherwise
|
||||
* the URL will go to the first page.
|
||||
* @param integer $question a question id. If set, will add a fragment to the URL
|
||||
* @param integer $questionid a question id. If set, will add a fragment to the URL
|
||||
* to jump to a particuar question on the page.
|
||||
* @param boolean $showall if true, the URL will be to review the entire attempt on one page,
|
||||
* and $page will be ignored.
|
||||
* @return string the URL to review this attempt.
|
||||
*/
|
||||
public function review_url($page = 0, $question = false, $showall = false) {
|
||||
public function review_url($questionid = 0, $page = -1, $showall = false) {
|
||||
global $CFG;
|
||||
$fragment = '';
|
||||
if ($question) {
|
||||
$fragment = '#q' . $question;
|
||||
}
|
||||
$param = '';
|
||||
if ($showall) {
|
||||
$param = '$amp;showall=1';
|
||||
} else if ($page) {
|
||||
$param = '$amp;page=' . $page;
|
||||
}
|
||||
return $CFG->wwwroot . '/mod/quiz/review.php?attempt=' .
|
||||
$this->attempt->id . $param . $fragment;
|
||||
return $CFG->wwwroot . '/mod/quiz/review.php?attempt=' . $this->attempt->id . '&' .
|
||||
$this->page_and_question_fragment($questionid, $page, $showall);
|
||||
}
|
||||
|
||||
// Bits of content =====================================================================
|
||||
public function get_html_head_contributions($page = 'all') {
|
||||
return get_html_head_contributions($this->get_question_ids($page),
|
||||
$this->questions, $this->states);
|
||||
}
|
||||
|
||||
public function print_restart_preview_button() {
|
||||
global $CFG;
|
||||
echo '<div class="controls">';
|
||||
print_single_button($this->start_attempt_url(), array('cmid' => $this->cm->id,
|
||||
'forcenew' => true, 'sesskey' => sesskey()), get_string('startagain', 'quiz'), 'post');
|
||||
echo '</div>';
|
||||
}
|
||||
|
||||
public function print_question($id) {
|
||||
$options = quiz_get_renderoptions($this->quiz->review, $this->states[$id]);
|
||||
print_question($this->questions[$id], $this->states[$id], $this->questions[$id]->_number,
|
||||
$this->quiz, $options);
|
||||
}
|
||||
|
||||
public function quiz_send_notification_emails() {
|
||||
quiz_send_notification_emails($this->course, $this->quiz, $this->attempt,
|
||||
$this->context, $this->cm);
|
||||
}
|
||||
|
||||
|
||||
// Private methods =====================================================================
|
||||
// Check that the state of a particular question is loaded, and if not throw an exception.
|
||||
@@ -444,6 +594,47 @@ class quiz_attempt extends quiz {
|
||||
throw new moodle_quiz_exception($this, 'statenotloaded', $id);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string the layout of this quiz. Used by number_questions to
|
||||
* work out which questions are on which pages.
|
||||
*/
|
||||
protected function get_layout_string() {
|
||||
return $this->attempt->layout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enter description here...
|
||||
*
|
||||
* @param unknown_type $questionid the id of a particular question on the page to jump to.
|
||||
* @param integer $page -1 to look up the page number from the questionid, otherwise the page number to use.
|
||||
* @param boolean $showall
|
||||
* @return string bit to add to the end of a URL.
|
||||
*/
|
||||
private function page_and_question_fragment($questionid, $page, $showall = false) {
|
||||
if ($page = -1) {
|
||||
if ($questionid) {
|
||||
$page = $this->questions[$questionid]->_page;
|
||||
} else {
|
||||
$page = 0;
|
||||
}
|
||||
}
|
||||
if ($showall) {
|
||||
$page = 0;
|
||||
}
|
||||
$fragment = '';
|
||||
if ($questionid && $questionid != reset($this->pagequestionids[$page])) {
|
||||
$fragment = '#q' . $questionid;
|
||||
}
|
||||
$param = '';
|
||||
if ($showall) {
|
||||
$param = 'showall=1';
|
||||
} else if (/*$page > 1*/ true) {
|
||||
// TODO currently needed by the navigate JS, but clean this up later.
|
||||
$param = 'page=' . $page;
|
||||
}
|
||||
return $param . $fragment;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -463,7 +654,6 @@ class quiz_attempt_question_iterator implements Iterator {
|
||||
*/
|
||||
public function __construct(quiz_attempt $attemptobj, $page = 'all') {
|
||||
$this->attemptobj = $attemptobj;
|
||||
$attemptobj->number_questions($page);
|
||||
$this->questionids = $attemptobj->get_question_ids($page);
|
||||
}
|
||||
|
||||
@@ -484,11 +674,10 @@ class quiz_attempt_question_iterator implements Iterator {
|
||||
public function key() {
|
||||
$id = current($this->questionids);
|
||||
if ($id) {
|
||||
return $this->attemptobj->get_question($id)->number;
|
||||
return $this->attemptobj->get_question($id)->_number;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return $this->attemptobj->get_question(current($this->questionids))->number;
|
||||
}
|
||||
|
||||
public function next() {
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
require_once($CFG->dirroot . '/mod/quiz/lib.php');
|
||||
require_once($CFG->dirroot . '/mod/quiz/accessrules.php');
|
||||
require_once($CFG->dirroot . '/question/editlib.php');
|
||||
require_once($CFG->dirroot . '/mod/quiz/attemptlib.php');
|
||||
|
||||
/// Constants ///////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -891,14 +892,6 @@ 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 ///////////////////////////////
|
||||
|
||||
/**
|
||||
|
||||
+3
-2
@@ -38,7 +38,7 @@
|
||||
|
||||
/// Create an object to manage all the other (non-roles) access rules.
|
||||
$timenow = time();
|
||||
$accessmanager = new quiz_access_manager($quiz, $timenow,
|
||||
$accessmanager = new quiz_access_manager(new quiz($quiz, $cm, $course), $timenow,
|
||||
has_capability('mod/quiz:ignoretimelimits', $context, NULL, false));
|
||||
$options = quiz_get_reviewoptions($quiz, $attempt, $context);
|
||||
|
||||
@@ -119,7 +119,8 @@
|
||||
/// Print heading.
|
||||
print_heading(format_string($quiz->name));
|
||||
if ($canpreview && $reviewofownattempt) {
|
||||
print_restart_preview_button($quiz);
|
||||
$attemptobj = new quiz_attempt($attemptid);
|
||||
$attemptobj->print_restart_preview_button();
|
||||
}
|
||||
print_heading($strreviewtitle);
|
||||
|
||||
|
||||
@@ -0,0 +1,135 @@
|
||||
<?php // $Id$
|
||||
/**
|
||||
* This page deals with starting a new attempt at a quiz.
|
||||
*
|
||||
* Normally, it will end up redirecting to attempt.php - unless a password form is displayed.
|
||||
*
|
||||
* @author Tim Hunt.
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU Public License
|
||||
* @package quiz
|
||||
*/
|
||||
|
||||
require_once(dirname(__FILE__) . '/../../config.php');
|
||||
require_once($CFG->dirroot . '/mod/quiz/locallib.php');
|
||||
|
||||
/// Get submitted parameters.
|
||||
$id = required_param('cmid', PARAM_INT); // Course Module ID
|
||||
$forcenew = optional_param('forcenew', false, PARAM_BOOL); // Used to force a new preview
|
||||
|
||||
if (!$cm = get_coursemodule_from_id('quiz', $id)) {
|
||||
print_error('invalidcoursemodule');
|
||||
}
|
||||
if (!$course = $DB->get_record('course', array('id' => $cm->course))) {
|
||||
print_error("coursemisconf");
|
||||
}
|
||||
if (!$quiz = $DB->get_record('quiz', array('id' => $cm->instance))) {
|
||||
print_error('invalidcoursemodule');
|
||||
}
|
||||
|
||||
$quizobj = new quiz($quiz, $cm, $course);
|
||||
|
||||
/// Check login and get contexts.
|
||||
require_login($quizobj->get_courseid(), false, $quizobj->get_cm());
|
||||
|
||||
/// if no questions have been set up yet redirect to edit.php
|
||||
if (!$quizobj->get_question_ids() && $quizobj->has_capability('mod/quiz:manage')) {
|
||||
redirect($quizobj->edit_url());
|
||||
}
|
||||
|
||||
/// Create an object to manage all the other (non-roles) access rules.
|
||||
$accessmanager = $quizobj->get_access_manager(time());
|
||||
if ($quizobj->is_preview_user() && $forcenew) {
|
||||
$accessmanager->clear_password_access();
|
||||
}
|
||||
|
||||
// This page should only respond to post requests, if not, redirect to the view page.
|
||||
// However, becuase 'secure' mode opens in a new window, we cannot do enforce this rule for them.
|
||||
if (!data_submitted() && !$accessmanager->securewindow_required($quizobj->is_preview_user())) {
|
||||
redirect($quizobj->view_url());
|
||||
}
|
||||
if (!confirm_sesskey()) {
|
||||
throw new moodle_exception('confirmsesskeybad', 'error', $quizobj->view_url());
|
||||
}
|
||||
|
||||
/// Check capabilites.
|
||||
if (!$quizobj->is_preview_user()) {
|
||||
$quizobj->require_capability('mod/quiz:attempt');
|
||||
}
|
||||
|
||||
/// Check to see if a new preview was requested.
|
||||
if ($quizobj->is_preview_user() && $forcenew) {
|
||||
/// To force the creation of a new preview, we set a finish time on the
|
||||
/// current attempt (if any). It will then automatically be deleted below
|
||||
$DB->set_field('quiz_attempts', 'timefinish', time(), array('quiz' => $quiz->id, 'userid' => $USER->id));
|
||||
}
|
||||
|
||||
/// Look for an existing attempt.
|
||||
$lastattempt = quiz_get_latest_attempt_by_user($quiz->id, $USER->id);
|
||||
|
||||
if ($lastattempt && !$lastattempt->timefinish) {
|
||||
/// Continuation of an attempt - check password then redirect.
|
||||
$accessmanager->do_password_check($quizobj->is_preview_user());
|
||||
redirect($quizobj->attempt_url($lastattempt->id));
|
||||
}
|
||||
|
||||
/// Get number for the next or unfinished attempt
|
||||
if ($lastattempt && !$lastattempt->preview && !$quizobj->is_preview_user()) {
|
||||
$lastattemptid = $lastattempt->id;
|
||||
$attemptnumber = $lastattempt->attempt + 1;
|
||||
} else {
|
||||
$lastattempt = false;
|
||||
$lastattemptid = false;
|
||||
$attemptnumber = 1;
|
||||
}
|
||||
|
||||
/// Check access.
|
||||
$messages = $accessmanager->prevent_access() +
|
||||
$accessmanager->prevent_new_attempt($attemptnumber - 1, $lastattempt);
|
||||
if (!$quizobj->is_preview_user() && $messages) {
|
||||
print_error('attempterror', 'quiz', $quizobj->view_url(),
|
||||
$accessmanager->print_messages($messages, true));
|
||||
}
|
||||
$accessmanager->do_password_check($quizobj->is_preview_user());
|
||||
|
||||
/// Delete any previous preview attempts belonging to this user.
|
||||
if ($oldattempts = $DB->get_records_select('quiz_attempts', "quiz = ?
|
||||
AND userid = ? AND preview = 1", array($quiz->id, $USER->id))) {
|
||||
foreach ($oldattempts as $oldattempt) {
|
||||
quiz_delete_attempt($oldattempt, $quiz);
|
||||
}
|
||||
}
|
||||
|
||||
/// Create the new attempt and initialize the question sessions
|
||||
$attempt = quiz_create_attempt($quiz, $attemptnumber, $lastattempt, time(), $quizobj->is_preview_user());
|
||||
|
||||
/// Save the attempt in the database.
|
||||
if (!$attempt->id = $DB->insert_record('quiz_attempts', $attempt)) {
|
||||
quiz_error($quiz, 'newattemptfail');
|
||||
}
|
||||
|
||||
/// 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);
|
||||
}
|
||||
|
||||
/// Fully load all the questions in this quiz.
|
||||
$quizobj->preload_questions();
|
||||
$quizobj->load_questions();
|
||||
|
||||
/// Create initial states for all questions in this quiz.
|
||||
if (!$states = get_question_states($quizobj->get_questions(), $quizobj->get_quiz(), $attempt, $lastattemptid)) {
|
||||
print_error('cannotrestore', 'quiz');
|
||||
}
|
||||
|
||||
/// Save all the newly created states.
|
||||
foreach ($quizobj->get_questions() as $i => $question) {
|
||||
save_question_session($question, $states[$i]);
|
||||
}
|
||||
|
||||
/// Redirect to the attempt page.
|
||||
redirect($quizobj->attempt_url($attempt->id));
|
||||
?>
|
||||
+10
-9
@@ -7,14 +7,13 @@
|
||||
* @package quiz
|
||||
*/
|
||||
|
||||
require_once("../../config.php");
|
||||
require_once("locallib.php");
|
||||
require_once("attemptlib.php");
|
||||
require_once(dirname(__FILE__) . '/../../config.php');
|
||||
require_once($CFG->dirroot . '/mod/quiz/locallib.php');
|
||||
|
||||
$attemptid = required_param('attempt', PARAM_INT); // The attempt to summarise.
|
||||
$attemptobj = new quiz_attempt($attemptid);
|
||||
|
||||
/// Check login and get contexts.
|
||||
/// Check login.
|
||||
require_login($attemptobj->get_courseid(), false, $attemptobj->get_cm());
|
||||
|
||||
/// If this is not our own attempt, display an error.
|
||||
@@ -22,7 +21,7 @@ if ($attemptobj->get_userid() != $USER->id) {
|
||||
print_error('notyourattempt', 'quiz', $attemptobj->view_url());
|
||||
}
|
||||
|
||||
/// If the attempt is already closed, redirect them to the review page.
|
||||
/// If the attempt is alreadyuj closed, redirect them to the review page.
|
||||
if ($attemptobj->is_finished()) {
|
||||
redirect($attemptobj->review_url());
|
||||
}
|
||||
@@ -48,7 +47,8 @@ $attemptobj->load_question_states();
|
||||
require_js($CFG->wwwroot . '/mod/quiz/quiz.js');
|
||||
$title = get_string('summaryofattempt', 'quiz');
|
||||
if ($accessmanager->securewindow_required($attemptobj->is_preview_user())) {
|
||||
$accessmanager->setup_secure_page($course->shortname.': '.format_string($quiz->name), $headtags);
|
||||
$accessmanager->setup_secure_page($attemptobj->get_course()->shortname . ': ' .
|
||||
format_string($attemptobj->get_quiz_name()), '');
|
||||
} else {
|
||||
print_header_simple(format_string($attemptobj->get_quiz_name()), '',
|
||||
$attemptobj->navigation($title), '', '', true, $attemptobj->update_module_button());
|
||||
@@ -63,7 +63,7 @@ if ($attemptobj->is_preview_user()) {
|
||||
/// Print heading.
|
||||
print_heading(format_string($attemptobj->get_quiz_name()));
|
||||
if ($attemptobj->is_preview_user()) {
|
||||
print_restart_preview_button($quiz);
|
||||
$attemptobj->print_restart_preview_button();
|
||||
}
|
||||
print_heading($title);
|
||||
|
||||
@@ -83,7 +83,8 @@ $table->data = array();
|
||||
/// Get the summary info for each question.
|
||||
$questionids = $attemptobj->get_question_ids();
|
||||
foreach ($attemptobj->get_question_iterator() as $number => $question) {
|
||||
$row = array($number, $attemptobj->get_question_status($question->id));
|
||||
$row = array('<a href="' . $attemptobj->attempt_url($question->id) . '">' . $number . '</a>',
|
||||
get_string($attemptobj->get_question_status($question->id), 'quiz'));
|
||||
if ($scorescolumn) {
|
||||
$row[] = $attemptobj->get_question_score($question->id);
|
||||
}
|
||||
@@ -110,7 +111,7 @@ $accessmanager->show_attempt_timer_if_needed($attemptobj->get_attempt(), time())
|
||||
if ($accessmanager->securewindow_required($attemptobj->is_preview_user())) {
|
||||
print_footer('empty');
|
||||
} else {
|
||||
print_footer($course);
|
||||
print_footer($attemptobj->get_course());
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
+1
-1
@@ -44,7 +44,7 @@
|
||||
|
||||
/// Create an object to manage all the other (non-roles) access rules.
|
||||
$timenow = time();
|
||||
$accessmanager = new quiz_access_manager($quiz, $timenow,
|
||||
$accessmanager = new quiz_access_manager(new quiz($quiz, $cm, $course), $timenow,
|
||||
has_capability('mod/quiz:ignoretimelimits', $context, NULL, false));
|
||||
|
||||
/// If no questions have been set up yet redirect to edit.php
|
||||
|
||||
Reference in New Issue
Block a user