$string['answertoolong'] = 'Answer too long after line $a (255 char. max)';
$string['aon'] = 'AON format';
$string['attempt'] = 'Attempt $a';
+$string['attemptalreadyclosed'] = 'This attempt has already be finished.';
$string['attemptclosed'] = 'Attempt has not closed yet';
$string['attemptduration'] = 'Time taken';
$string['attemptedon'] = 'Attempted on';
$string['nocommentsyet'] = 'No comments yet.';
$string['noconnection'] = 'There is currently no connection to a web service that can process this question. Please contact your administrator';
$string['nodataset'] = 'nothing - it is not a wild card';
+$string['nodatasubmitted'] = 'No data was submitted.';
$string['noessayquestionsfound'] = 'No manually graded questions found';
$string['nomatchinganswer'] = 'You must specify an answer matching the question \'$a\'.';
$string['nominal'] = 'Nominal';
redirect($CFG->wwwroot . '/mod/quiz/startattempt.php?cmid=' . $cm->id . '&sesskey=' . sesskey());
}
-/// 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.
$attemptid = required_param('attempt', PARAM_INT);
$page = optional_param('page', 0, PARAM_INT);
- $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.
$attemptobj = new quiz_attempt($attemptid);
-/// Because IE is buggy (see http://www.peterbe.com/plog/button-tag-in-IE) we cannot
-/// do the quiz navigation buttons as <button type="submit" name="page" value="N">Caption</button>.
-/// Instead we have to do them as <input type="submit" name="gotopageN" value="Caption"/> -
-/// at lest that seemed like the least horrible work-around to me. Therefore, we need to
-/// intercept gotopageN parameters here, and adjust $pgae accordingly.
- if (optional_param('gotosummary', false, PARAM_BOOL)) {
- $page = -1;
- } else {
- $numpagesinquiz = $attemptobj->get_num_pages();
- for ($i = 0; $i < $numpagesinquiz; $i++) {
- if (optional_param('gotopage' . $i, false, PARAM_BOOL)) {
- $page = $i;
- break;
- }
- }
- }
-
-/// We treat automatically closed attempts just like normally closed attempts
- if ($timeup) {
- $finishattempt = 1;
- }
-
/// Check login.
require_login($attemptobj->get_courseid(), false, $attemptobj->get_cm());
$attemptobj->require_capability('mod/quiz:attempt');
}
+/// If the attempt is already closed, send them to the review page.
+ if ($attemptobj->is_finished()) {
+ redirect($attemptobj->review_url(0, $page));
+ }
+
+/// Check the access rules.
+ $accessmanager = $attemptobj->get_access_manager(time());
+ $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());
+
/// Log continuation of the attempt, but only if some time has passed.
- if (($timenow - $attemptobj->get_attempt()->timemodified) > QUIZ_CONTINUE_ATTEMPT_LOG_INTERVAL) {
+ if ((time() - $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(),
}
/// Get the list of questions needed by this page.
- if ($finishattempt) {
- $questionids = $attemptobj->get_question_ids();
- } else if ($page >= 0) {
- $questionids = $attemptobj->get_question_ids($page);
- } else {
- $questionids = array();
- }
-
-/// Add all questions that are on the submitted form
- if ($submittedquestionids) {
- $submittedquestionids = explode(',', $submittedquestionids);
- $questionids = array_unique(array_merge($questionids, $submittedquestionids));
- } else {
- $submittedquestionids = array();
- }
+ $questionids = $attemptobj->get_question_ids($page);
/// Check.
if (empty($questionids)) {
$attemptobj->load_questions($questionids);
$attemptobj->load_question_states($questionids);
-
-/// Process form data /////////////////////////////////////////////////
-
- if ($responses = data_submitted() and empty($responses->quizpassword)) {
-
- /// 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($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 the responses. $actions will be an array indexed by the questions ids.
- $actions = question_extract_responses($attemptobj->get_questions($questionids),
- $responses, $event);
-
- /// Process each question in turn
- $success = true;
- $attempt = $attemptobj->get_attempt();
- foreach($submittedquestionids as $id) {
- if (!isset($actions[$id])) {
- $actions[$id]->responses = array('' => '');
- $actions[$id]->event = QUESTION_EVENTOPEN;
- }
- $actions[$id]->timestamp = $timenow;
- $state = $attemptobj->get_question_state($id);
- if (question_process_responses($attemptobj->get_question($id),
- $state, $actions[$id], $attemptobj->get_quiz(), $attempt)) {
- save_question_session($attemptobj->get_question($id), $state);
- } else {
- $success = false;
- }
- }
-
- if (!$success) {
- print_error('errorprocessingresponses', 'question', $attemptobj->attempt_url(0, $page));
- }
-
- $attempt->timemodified = $timenow;
- if (!$DB->update_record('quiz_attempts', $attempt)) {
- quiz_error($quiz, 'saveattemptfailed');
- }
-
- // For now, reload the states to pick up the changes:
- $attemptobj->load_question_states($questionids);
- }
-
-/// Finish attempt if requested
- if ($finishattempt) {
-
- /// Move each question to the closed state.
- $success = true;
- $attempt = $attemptobj->get_attempt();
- foreach ($attemptobj->get_questions() as $id => $question) {
- $action = new stdClass;
- $action->event = QUESTION_EVENTCLOSE;
- $action->responses = $attemptobj->get_question_state($id)->responses;
- $action->timestamp = $attemptobj->get_question_state($id)->timestamp;
- $state = $attemptobj->get_question_state($id);
- if (question_process_responses($attemptobj->get_question($id),
- $state, $action, $attemptobj->get_quiz(), $attempt)) {
- save_question_session($attemptobj->get_question($id), $state);
- } else {
- $success = false;
- }
- }
-
- if (!$success) {
- print_error('errorprocessingresponses', 'question', $attemptobj->attempt_url(0, $page));
- }
-
- /// Log the end of this attempt.
- 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->timemodified = $timenow;
- $attempt->timefinish = $timenow;
- if (!$DB->update_record('quiz_attempts', $attempt)) {
- quiz_error($quiz, 'saveattemptfailed');
- }
-
- if (!$attempt->preview) {
- /// Record this user's best grade (if this is not a preview).
- quiz_save_best_grade($quiz);
-
- /// Send any notification emails (if this is not a preview).
- $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($attemptobj->review_url());
- }
-
-/// 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
}
// Start the form
- echo '<form id="responseform" method="post" action="', $attemptobj->attempt_url(0, $page),
+ echo '<form id="responseform" method="post" action="', $attemptobj->processattempt_url(),
'" enctype="multipart/form-data"' .
' onclick="this.autocomplete=\'off\'" onkeypress="return check_enter(event);">', "\n";
if($attemptobj->get_quiz()->timelimit > 0) {
/// Print a link to the next page.
echo '<div class="submitbtns">';
if ($attemptobj->is_last_page($page)) {
- $nextpage = 'gotosummary';
+ $nextpage = -1;
+ $nextpageforie = 'gotosummary';
} else {
- $nextpage = 'gotopage' . ($page + 1);
+ $nextpage = $page + 1;
+ $nextpageforie = 'gotopage' . $nextpage;
}
- echo '<input type="submit" name="' . $nextpage . '" value="' . get_string('next') . '" />';
+ echo '<input type="submit" name="' . $nextpageforie . '" value="' . get_string('next') . '" />';
echo "</div>";
- // Finish the form
- echo '</div>';
+ // Some hidden fields to trach what is going on.
+ echo '<input type="hidden" name="attempt" value="' . $attemptobj->get_attemptid() . '" />';
+ echo '<input type="hidden" name="nextpage" value="' . $nextpage . '" />';
echo '<input type="hidden" name="timeup" id="timeup" value="0" />';
+ echo '<input type="hidden" name="sesskey" value="' . sesskey() . '" />';
// 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
echo '<input type="hidden" name="questionids" value="' .
implode(',', $attemptobj->get_question_ids($page)) . "\" />\n";
+ // Finish the form
+ echo '</div>';
echo "</form>\n";
// End middle column.
return $CFG->wwwroot . '/mod/quiz/summary.php?attempt=' . $this->attempt->id;
}
+ /**
+ * @return string the URL of this quiz's summary page.
+ */
+ public function processattempt_url() {
+ global $CFG;
+ return $CFG->wwwroot . '/mod/quiz/processattempt.php';
+ }
+
/**
* @param integer $page if specified, the URL of this particular page of the attempt, otherwise
* the URL will go to the first page.
--- /dev/null
+<?php // $Id$
+/**
+ * This page deals with processing responses during an attempt at a quiz.
+ *
+ * People will normally arrive here from a form submission on attempt.php or
+ * summary.php, and once the responses are processed, they will be redirected to
+ * attempt.php or summary.php.
+ *
+ * This code used to be near the top of attempt.php, if you are looking for CVS history.
+ *
+ * @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');
+
+/// 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.
+$attemptid = required_param('attempt', PARAM_INT);
+$nextpage = optional_param('nextpage', 0, PARAM_INT);
+$submittedquestionids = required_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.
+
+$attemptobj = new quiz_attempt($attemptid);
+
+/// Because IE is buggy (see http://www.peterbe.com/plog/button-tag-in-IE) we cannot
+/// do the quiz navigation buttons as <button type="submit" name="page" value="N">Caption</button>.
+/// Instead we have to do them as <input type="submit" name="gotopageN" value="Caption"/> -
+/// at lest that seemed like the least horrible work-around to me. Therefore, we need to
+/// intercept gotopageN parameters here, and adjust $pgae accordingly.
+if (optional_param('gotosummary', false, PARAM_BOOL)) {
+ $nextpage = -1;
+} else {
+ $numpagesinquiz = $attemptobj->get_num_pages();
+ for ($i = 0; $i < $numpagesinquiz; $i++) {
+ if (optional_param('gotopage' . $i, false, PARAM_BOOL)) {
+ $nextpage = $i;
+ break;
+ }
+ }
+}
+
+/// Set $nexturl now. It will be updated if a particular question was sumbitted in
+/// adaptive mode.
+if ($nextpage == -1) {
+ $nexturl = $attemptobj->summary_url();
+} else {
+ $nexturl = $attemptobj->attempt_url($nextpage);
+}
+
+/// We treat automatically closed attempts just like normally closed attempts
+if ($timeup) {
+ $finishattempt = 1;
+}
+
+/// Check login.
+require_login($attemptobj->get_courseid(), false, $attemptobj->get_cm());
+confirm_sesskey();
+
+/// Check that this attempt belongs to this user.
+if ($attemptobj->get_userid() != $USER->id) {
+ quiz_error($attemptobj->get_quiz(), 'notyourattempt');
+}
+
+/// Check capabilites.
+if (!$attemptobj->is_preview_user()) {
+ $attemptobj->require_capability('mod/quiz:attempt');
+}
+
+/// If the attempt is already closed, send them to the review page.
+if ($attemptobj->is_finished()) {
+ quiz_error($attemptobj->get_quiz(), 'attemptalreadyclosed');
+}
+
+/// Don't log - we will end with a redirect to a page that is logged.
+
+/// Get the list of questions needed by this page.
+$submittedquestionids = explode(',', $submittedquestionids);
+if ($finishattempt) {
+ $questionids = $attemptobj->get_question_ids();
+} else {
+ $questionids = $submittedquestionids;
+}
+
+/// Load those questions we need, and just the submitted states for now.
+$attemptobj->load_questions($questionids);
+$attemptobj->load_question_states($submittedquestionids);
+
+/// Process the responses /////////////////////////////////////////////////
+if (!$responses = data_submitted()) {
+ quiz_error($attemptobj->get_quiz(), 'nodatasubmitted');
+}
+
+/// Set the default event. This can be overruled by individual buttons.
+if ($finishattempt) {
+ $event = QUESTION_EVENTCLOSE;
+} else {
+ $event = 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->finishattempt); // same as $finishattempt
+unset($responses->forcenewattempt);
+
+/// Extract the responses. $actions will be an array indexed by the questions ids.
+$actions = question_extract_responses($attemptobj->get_questions($submittedquestionids),
+ $responses, $event);
+
+/// Process each question in turn
+$success = true;
+$attempt = $attemptobj->get_attempt();
+foreach($submittedquestionids as $id) {
+ if (!isset($actions[$id])) {
+ $actions[$id]->responses = array('' => '');
+ $actions[$id]->event = QUESTION_EVENTOPEN;
+ }
+ $actions[$id]->timestamp = $timenow;
+
+/// If a particular question was submitted, update the nexturl to go back to that question.
+ if ($actions[$id]->event == QUESTION_EVENTSUBMIT) {
+ $nexturl = $attemptobj->attempt_url($id);
+ }
+
+
+ $state = $attemptobj->get_question_state($id);
+ if (question_process_responses($attemptobj->get_question($id),
+ $state, $actions[$id], $attemptobj->get_quiz(), $attempt)) {
+ save_question_session($attemptobj->get_question($id), $state);
+ } else {
+ $success = false;
+ }
+}
+
+if (!$success) {
+ print_error('errorprocessingresponses', 'question', $attemptobj->attempt_url(0, $page));
+}
+
+/// If we do not have to finish the attempts (if we are only processing responses)
+/// save the attempt and redirect to the next page.
+if (!$finishattempt) {
+ $attempt->timemodified = $timenow;
+ if (!$DB->update_record('quiz_attempts', $attempt)) {
+ quiz_error($quiz, 'saveattemptfailed');
+ }
+
+ redirect($nexturl);
+}
+
+/// We have been asked to finish attempt, so do that //////////////////////
+
+/// Load the states of questions we have not done anything with, and reload the
+/// ones we changed above.
+$attemptobj->load_question_states();
+
+/// Move each question to the closed state.
+$success = true;
+$attempt = $attemptobj->get_attempt();
+foreach ($attemptobj->get_questions() as $id => $question) {
+ $action = new stdClass;
+ $action->event = QUESTION_EVENTCLOSE;
+ $action->responses = $attemptobj->get_question_state($id)->responses;
+ $action->timestamp = $attemptobj->get_question_state($id)->timestamp;
+ $state = $attemptobj->get_question_state($id);
+ if (question_process_responses($attemptobj->get_question($id),
+ $state, $action, $attemptobj->get_quiz(), $attempt)) {
+ save_question_session($attemptobj->get_question($id), $state);
+ } else {
+ $success = false;
+ }
+}
+
+if (!$success) {
+ print_error('errorprocessingresponses', 'question', $attemptobj->attempt_url(0, $page));
+}
+
+/// Log the end of this attempt.
+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->timemodified = $timenow;
+$attempt->timefinish = $timenow;
+if (!$DB->update_record('quiz_attempts', $attempt)) {
+ quiz_error($quiz, 'saveattemptfailed');
+}
+
+if (!$attempt->preview) {
+/// Record this user's best grade (if this is not a preview).
+ quiz_save_best_grade($quiz);
+
+/// Send any notification emails (if this is not a preview).
+ $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($attemptobj->review_url());
+?>
\ No newline at end of file
/// Finish attempt button.
echo "<div class=\"submitbtns mdl-align\">\n";
$options = array(
+ 'attempt' => $attemptobj->get_attemptid(),
'finishattempt' => 1,
'timeup' => 0,
'questionids' => '',
- 'sesskey' => sesskey()
+ 'sesskey' => sesskey(),
);
-print_single_button($attemptobj->attempt_url(), $options, get_string('finishattempt', 'quiz'),
+print_single_button($attemptobj->processattempt_url(), $options, get_string('finishattempt', 'quiz'),
'post', '', false, '', false, get_string('confirmclose', 'quiz'));
echo "</div>\n";
* @param object $options An object describing the rendering options.
*/
function print_question_submit_buttons(&$question, &$state, $cmoptions, $options) {
- /* The default implementation should be suitable for most question
- types. It prints a mark button in the case where individual marking is
- allowed. */
-
+ // The default implementation should be suitable for most question types.
+ // It prints a mark button in the case where individual marking is allowed.
if (($cmoptions->optionflags & QUESTION_ADAPTIVE) and !$options->readonly) {
echo '<input type="submit" name="', $question->name_prefix, 'submit" value="',
- get_string('mark', 'quiz'), '" class="submit btn" onclick="',
- "form.action = form.action + '#q", $question->id, "'; return true;", '" />';
+ get_string('mark', 'quiz'), '" class="submit btn" />';
}
}