]> git.mjollnir.org Git - moodle.git/commitdiff
MDL-15452 - Put the OU quiz navigation improvements into the Moodle codebase. I was...
authortjhunt <tjhunt>
Tue, 15 Jul 2008 15:30:39 +0000 (15:30 +0000)
committertjhunt <tjhunt>
Tue, 15 Jul 2008 15:30:39 +0000 (15:30 +0000)
lang/en_utf8/quiz.php
lib/questionlib.php
mod/quiz/attemptlib.php
mod/quiz/locallib.php
mod/quiz/report/overview/overview_table.php
mod/quiz/review.php
mod/quiz/reviewquestion.php
question/type/questiontype.php

index cb4c3f01d41ab3052c422a566eb1496f5e712f94..84beb2b6500dc9020f39e0058aa85b8e784222b2 100644 (file)
@@ -90,6 +90,7 @@ $string['cannotloadquestion'] = 'Could not load question options';
 $string['cannotopen'] = 'Cannot open export file ($a)';
 $string['cannotread'] = 'Cannot read import file (or file is empty)';
 $string['cannotrestore'] = 'Could not restore question sessions';
+$string['cannotreviewopen'] = 'You cannot review this attempt, it is still open.';
 $string['cannotsavequestion'] = 'Cannot save question list';
 $string['cannotwrite'] = 'Cannot write to export file ($a)';
 $string['caseno'] = 'No, case is unimportant';
@@ -524,7 +525,6 @@ $string['reviewbefore'] = 'Allow review while quiz is open';
 $string['reviewclosed'] = 'After the quiz is closed';
 $string['reviewimmediately'] = 'Immediately after the attempt';
 $string['reviewnever'] = 'Never allow review';
-$string['reviewnotallowed'] = 'You are not allowed to review other users\' attempts at this quiz.';
 $string['reviewofattempt'] = 'Review of attempt $a';
 $string['reviewofpreview'] = 'Review of preview';
 $string['reviewopen'] = 'Later, while the quiz is still open';
index 9e7f56ad07109cd47879ab99a2537199151c8b9d..300dc0b261bd3279797db8208dff09180c22b481 100644 (file)
@@ -37,6 +37,10 @@ define('QUESTION_EVENTMANUALGRADE', '9');   // Grade was entered by teacher
 define('QUESTION_EVENTS_GRADED', QUESTION_EVENTGRADE.','.
                     QUESTION_EVENTCLOSEANDGRADE.','.
                     QUESTION_EVENTMANUALGRADE);
+global $QUESTION_EVENTS_GRADED;
+$QUESTION_EVENTS_GRADED = array(QUESTION_EVENTGRADE, QUESTION_EVENTCLOSEANDGRADE,
+        QUESTION_EVENTMANUALGRADE);
+
 /**#@-*/
 
 /**#@+
@@ -792,6 +796,12 @@ function question_preload_questions($questionids, $extrafields = '', $join = '',
         $question->_partiallyloaded = true;
     }
 
+    // Note, a possible optimisation here would be to not load the TEXT fields
+    // (that is, questiontext and generalfeedback) here, and instead load them in
+    // question_load_questions. That would add one DB query, but reduce the amount
+    // of data transferred most of the time. I am not going to do this optimisation
+    // until it is shown to be worthwhile.
+
     return $questions;
 }
 
@@ -995,6 +1005,53 @@ function get_question_states(&$questions, $cmoptions, $attempt, $lastattemptid =
     return $states;
 }
 
+/**
+ * Load a particular previous state of a question.
+ *
+ * @param array $question The question to load the state for.
+ * @param object $cmoptions Options from the specifica activity module, e.g. $quiz.
+ * @param object $attempt The attempt for which the question sessions are to be loaded.
+ * @param integer $stateid The id of a specific state of this question.
+ * @return object the requested state. False on error.
+ */
+function question_load_specific_state($question, $cmoptions, $attempt, $stateid) {
+    global $DB, $QUESTION_EVENTS_GRADED;
+    // Load specified states for the question.
+    $sql = 'SELECT st.*, sess.sumpenalty, sess.manualcomment
+              FROM {question_states} st, {question_sessions} sess
+             WHERE st.id = ?
+               AND st.attempt = ?
+               AND sess.attemptid = st.attempt
+               AND st.question = ?
+               AND sess.questionid = st.question';
+    $state = $DB->get_record_sql($sql, array($stateid, $attempt->id, $question->id));
+    if (!$state) {
+        return false;
+    }
+    restore_question_state($question, $state);
+
+    // Load the most recent graded states for the questions before the specified one.
+    list($eventinsql, $params) = $DB->get_in_or_equal($QUESTION_EVENTS_GRADED);
+    $sql = 'SELECT st.*, sess.sumpenalty, sess.manualcomment
+              FROM {question_states} st, {question_sessions} sess
+             WHERE st.seq_number <= ?
+               AND st.attempt = ?
+               AND sess.attemptid = st.attempt
+               AND st.question = ?
+               AND sess.questionid = st.question
+               AND st.event ' . $eventinsql .
+           'ORDER BY st.seq_number DESC';
+    $gradedstates = $DB->get_records_sql($sql, array_merge(
+            array($state->seq_number, $attempt->id, $question->id), $params), 0, 1);
+    if (empty($gradedstates)) {
+        $state->last_graded = clone($state);
+    } else {
+        $gradedstate = reset($gradedstates);
+        restore_question_state($question, $gradedstate);
+        $state->last_graded = $gradedstate;
+    }
+    return $state;
+}
 
 /**
 * Creates the run-time fields for the states
index b972db15222cf379c59020aa152996f3ab3b2fbd..31b03ff3119d3275aefd333ae04853e58df8b61c 100644 (file)
@@ -419,6 +419,16 @@ class quiz_attempt extends quiz {
         $this->states = $newstates + $this->states;
     }
 
+    public function load_specific_question_state($questionid, $stateid) {
+        global $DB;
+        $state = question_load_specific_state($this->questions[$questionid],
+                $this->quiz, $this->attempt, $stateid);
+        if ($state === false) {
+            throw new moodle_quiz_exception($this, 'invalidstateid');
+        }
+        $this->states[$questionid] = $state;
+    }
+
     // Simple getters ======================================================================
     /** @return integer the attempt id. */
     public function get_attemptid() {
@@ -450,6 +460,20 @@ class quiz_attempt extends quiz {
         return $this->attempt->preview;
     }
 
+    /**
+     * Is this a student dealing with their own attempt/teacher previewing,
+     * or someone with 'mod/quiz:viewreports' reviewing someone elses attempt.
+     * 
+     * @return boolean whether this situation should be treated as someone looking at their own
+     * attempt. The distinction normally only matters when an attempt is being reviewed.
+     */
+    public function is_own_attempt() {
+        global $USER;
+        return $this->attempt->userid == $USER->id &&
+                (!$this->is_preview_user() || $this->attempt->preview);
+        
+    }
+
     public function get_question_state($questionid) {
         $this->ensure_state_loaded($questionid);
         return $this->states[$questionid];
@@ -576,12 +600,9 @@ class quiz_attempt extends quiz {
      * @param $otherattemptid if given, link to another attempt, instead of the one we represent.
      * @return string the URL to review this attempt.
      */
-    public function review_url($questionid = 0, $page = -1, $showall = false, $otherattemptid = null) {
+    public function review_url($questionid = 0, $page = -1, $showall = false) {
         global $CFG;
-        if (is_null($otherattemptid)) {
-            $otherattemptid = $this->attempt->id;
-        }
-        return $CFG->wwwroot . '/mod/quiz/review.php?attempt=' . $otherattemptid .
+        return $CFG->wwwroot . '/mod/quiz/review.php?attempt=' . $this->attempt->id .
                 $this->page_and_question_fragment($questionid, $page, $showall);
     }
 
@@ -619,6 +640,22 @@ class quiz_attempt extends quiz {
         $panel->display();
     }
 
+        /// List of all this user's attempts for people who can see reports.
+    public function links_to_other_attempts($url) {
+        $search = '/\battempt=' . $this->attempt->id . '\b/';
+        $attempts = quiz_get_user_attempts($this->quiz->id, $this->attempt->userid, 'all');
+        $attemptlist = array();
+        foreach ($attempts as $at) {
+            if ($at->id == $this->attempt->id) {
+                $attemptlist[] = '<strong>' . $at->attempt . '</strong>';
+            } else {
+                $changedurl = preg_replace($search, 'attempt=' . $at->id, $url);
+                $attemptlist[] = '<a href="' . $changedurl . '">' . $at->attempt . '</a>';
+            }
+        }
+        return implode(', ', $attemptlist);
+    }
+
     // Private methods =====================================================================
     // Check that the state of a particular question is loaded, and if not throw an exception.
     private function ensure_state_loaded($id) {
@@ -750,7 +787,7 @@ abstract class quiz_nav_panel_base {
     abstract protected function get_end_bits();
 
     protected function get_question_state($question) {
-        $state = 'todo'; // TODO
+        $state = 'todo'; // TODO MDL-15653
         if ($question->_page == $this->page) {
             $state .= ' thispage';
         }
index be9209536cdb4d5c341227913a00c9e83b4588d3..6b845bebbd276b3fd4aec56a159beca53ced0022 100644 (file)
@@ -821,7 +821,9 @@ function quiz_get_reviewoptions($quiz, $attempt, $context=null) {
     $options->readonly = true;
 
     // Provide the links to the question review and comment script
-    $options->questionreviewlink = '/mod/quiz/reviewquestion.php';
+    if (!empty($attempt->id)) {
+        $options->questionreviewlink = '/mod/quiz/reviewquestion.php?attempt=' . $attempt->id;
+    }
 
     // Show a link to the comment box only for closed attempts
     if ($attempt->timefinish && !is_null($context) && has_capability('mod/quiz:grade', $context)) {
index fbd3ac6d4677da770a3e6398d8f096d816d8c8ed..ce2e69ef439993aaa97be3ba117fa34ed6f28931 100644 (file)
@@ -190,6 +190,14 @@ class quiz_report_overview_table extends table_sql {
             return '-';
         }
     }
+
+    /**
+     * @param string $colname the name of the column.
+     * @param object $attempt the row of data - see the SQL in display() in
+     * mod/quiz/report/overview/report.php to see what fields are present,
+     * and what they are called.
+     * @return string the contents of the cell.
+     */
     function other_cols($colname, $attempt){
 
         if (preg_match('/^qsgrade([0-9]+)$/', $colname, $matches)){
@@ -211,8 +219,8 @@ class quiz_report_overview_table extends table_sql {
                         $grade = '<del>'.$oldgrade.'</del><br />'.
                                 $newgrade;
                     }
-                    return link_to_popup_window('/mod/quiz/reviewquestion.php?state='.
-                            $stateforqinattempt->id.'&amp;number='.$question->number,
+                    return link_to_popup_window('/mod/quiz/reviewquestion.php?attempt=' .
+                            $attempt->attempt . '&amp;question=' . $question->id,
                             'reviewquestion', $grade, 450, 650, get_string('reviewresponse', 'quiz'),
                             'none', true);
                 } else {
index b5fd3f90718ebf5198792747574a58c6c87293ad..be4a2ae184ab80e161ab1984e4c4ef7fa38657ae 100644 (file)
 /// Check login.
     require_login($attemptobj->get_courseid(), false, $attemptobj->get_cm());
 
-
 /// Create an object to manage all the other (non-roles) access rules.
     $accessmanager = $attemptobj->get_access_manager(time());
     $options = $attemptobj->get_review_options();
 
-/// Work out if this is a student viewing their own attempt/teacher previewing,
-/// or someone with 'mod/quiz:viewreports' reviewing someone elses attempt.
-    $reviewofownattempt = $attemptobj->get_userid() == $USER->id &&
-            (!$attemptobj->is_preview_user() || $attemptobj->is_preview());
-
 /// Permissions checks for normal users who do not have quiz:viewreports capability.
     if (!$attemptobj->has_capability('mod/quiz:viewreports')) {
     /// Can't review during the attempt - send them back to the attempt page.
@@ -36,8 +30,8 @@
             redirect($attemptobj->attempt_url(0, $page));
         }
     /// Can't review other users' attempts.
-        if (!$reviewofownattempt) {
-            quiz_error($quiz, 'reviewnotallowed');
+        if (!$attemptobj->is_own_attempt()) {
+            quiz_error($quiz, 'notyourattempt');
         }
     /// Can't review unless Students may review -> Responses option is turned on.
         if (!$options->responses) {
         }
     }
 
-/// Log this review.
-    add_to_log($attemptobj->get_courseid(), 'quiz', 'review', 'review.php?attempt=' .
-            $attemptobj->get_attemptid(), $attemptobj->get_quizid(), $attemptobj->get_cmid());
-
 /// load the questions and states needed by this page.
     if ($showall) {
         $questionids = $attemptobj->get_question_ids();
     $attemptobj->load_questions($questionids);
     $attemptobj->load_question_states($questionids);
 
+/// Log this review.
+    add_to_log($attemptobj->get_courseid(), 'quiz', 'review', 'review.php?attempt=' .
+            $attemptobj->get_attemptid(), $attemptobj->get_quizid(), $attemptobj->get_cmid());
+
 /// Work out appropriate title.
-    if ($attemptobj->is_preview_user() && $reviewofownattempt) {
+    if ($attemptobj->is_preview_user() && $attemptobj->is_own_attempt()) {
         $strreviewtitle = get_string('reviewofpreview', 'quiz');
     } else {
         $strreviewtitle = get_string('reviewofattempt', 'quiz', $attemptobj->get_attempt_number());
@@ -78,7 +72,7 @@
 
 /// Print tabs if they should be there.
     if ($attemptobj->is_preview_user()) {
-        if ($reviewofownattempt) {
+        if ($attemptobj->is_own_attempt()) {
             $currenttab = 'preview';
         } else {
             $currenttab = 'reports';
@@ -88,8 +82,8 @@
     }
 
 /// Print heading.
-    print_heading(format_string($quiz->name));
-    if ($attemptobj->is_preview_user() && $reviewofownattempt) {
+    print_heading(format_string($attemptobj->get_quiz_name()));
+    if ($attemptobj->is_preview_user() && $attemptobj->is_own_attempt()) {
         $attemptobj->print_restart_preview_button();
     }
     print_heading($strreviewtitle);
                 $CFG->wwwroot . '/user/view.php?id=' . $student->id . '&amp;course=' . $attemptobj->get_courseid() . '">' .
                 fullname($student, true) . '</a></td></tr>';
     }
-    if (has_capability('mod/quiz:viewreports', $context) &&
-            count($attempts = $DB->get_records_select('quiz_attempts', "quiz = ? AND userid = ?", array($quiz->id, $attempt->userid), 'attempt ASC')) > 1) {
-    /// List of all this user's attempts for people who can see reports.
-        $attemptlist = array();
-        foreach ($attempts as $at) {
-            if ($at->id == $attempt->id) {
-                $attemptlist[] = '<strong>' . $at->attempt . '</strong>';
-            } else {
-                $attemptlist[] = '<a href="' . $attemptobj->review_url(0, $page, $showall, $at->id) .
-                        '">' . $at->attempt . '</a>';
-            }
+    if ($attemptobj->has_capability('mod/quiz:viewreports')) {
+        $attemptlist = $attemptobj->links_to_other_attempts($attemptobj->review_url(0, $page, $showall));
+        if ($attemptlist) {
+            $rows[] = '<tr><th scope="row" class="cell">' . get_string('attempts', 'quiz') .
+                    '</th><td class="cell">' . $attemptlist . '</td></tr>';
         }
-        $rows[] = '<tr><th scope="row" class="cell">' . get_string('attempts', 'quiz') .
-                '</th><td class="cell">' . implode(', ', $attemptlist) . '</td></tr>';
     }
 
 /// Timing information.
index 30f4d8455a6fd4095e6131ce785b04ebcc1e9458..d7b395ed996760a2f0cd4cf9dbe195cb5e11bedd 100644 (file)
@@ -1,8 +1,9 @@
 <?php  // $Id$
 /**
- * This page prints a review of a particular question attempt
+ * This page prints a review of a particular question attempt.
+ * This page is expected to only be used in a popup window.
  *
- * @author Martin Dougiamas and many others.
+ * @author Martin Dougiamas, Tim Hunt and many others.
  * @license http://www.gnu.org/copyleft/gpl.html GNU Public License
  * @package quiz
  */
     require_once(dirname(__FILE__) . '/../../config.php');
     require_once('locallib.php');
 
-    // Either stateid or (attemptid AND questionid) must be given
+    $attemptid = required_param('attempt', PARAM_INT); // attempt id
+    $questionid = required_param('question', PARAM_INT); // question id
     $stateid = optional_param('state', 0, PARAM_INT); // state id
-    $attemptid = optional_param('attempt', 0, PARAM_INT); // attempt id
-    $questionid = optional_param('question', 0, PARAM_INT); // attempt id
-    $number = optional_param('number', 0, PARAM_INT);  // question number
 
-    if ($stateid) {
-        if (! $state = $DB->get_record('question_states', array('id' => $stateid))) {
-            print_error('invalidstateid', 'quiz');
-        }
-        if (! $attempt = $DB->get_record('quiz_attempts', array('uniqueid' => $state->attempt))) {
-            print_error('invalidattemptid', 'quiz');
-        }
-    } elseif ($attemptid) {
-        if (! $attempt = $DB->get_record('quiz_attempts', array('id' => $attemptid))) {
-            print_error('invalidattemptid', 'quiz');
-        }
-        if (! $neweststateid = $DB->get_field('question_sessions', 'newest', array('attemptid' => $attempt->uniqueid, 'questionid' => $questionid))) {
-            // newest_state not set, probably because this is an old attempt from the old quiz module code
-            if (! $state = $DB->get_record('question_states', array('question' => $questionid, 'attempt' => $attempt->uniqueid))) {
-                print_error('invalidquestionid', 'quiz');
-            }
-        } else {
-            if (! $state = $DB->get_record('question_states', array('id' => $neweststateid))) {
-                print_error('invalidstateid', 'quiz');
-            }
-        }
-    } else {
-        print_error('missingparameter');
-    }
-    if (! $question = $DB->get_record('question', array('id' => $state->question))) {
-        print_error('questionmissing', 'quiz');
-    }
-    if (! $quiz = $DB->get_record('quiz', array('id' => $attempt->quiz))) {
-        print_error('invalidcoursemodule');
-    }
-    if (! $course = $DB->get_record('course', array('id' => $quiz->course))) {
-        print_error('coursemisconf');
-    }
-    if (! $cm = get_coursemodule_from_instance('quiz', $quiz->id, $course->id)) {
-        print_error('invalidcoursemodule');
-    }
+    $attemptobj = new quiz_attempt($attemptid);
 
-    require_login($course->id, false, $cm);
-    $context = get_context_instance(CONTEXT_MODULE, $cm->id);
+/// Check login.
+    require_login($attemptobj->get_courseid(), false, $attemptobj->get_cm());
 
-    if (!has_capability('mod/quiz:viewreports', $context)) {
-        if (!$attempt->timefinish) {
-            redirect('attempt.php?attempt='.$attempt->id);
-        }
-        // If not even responses are to be shown in review then we
-        // don't allow any review
-        if (!($quiz->review & QUIZ_REVIEW_RESPONSES)) {
-            print_error("noreview", "quiz");
+/// Create an object to manage all the other (non-roles) access rules.
+    $accessmanager = $attemptobj->get_access_manager(time());
+    $options = $attemptobj->get_review_options();
+
+/// Permissions checks for normal users who do not have quiz:viewreports capability.
+    if (!$attemptobj->has_capability('mod/quiz:viewreports')) {
+    /// Can't review during the attempt - send them back to the attempt page.
+        if (!$attemptobj->is_finished()) {
+            notify(get_string('cannotreviewopen', 'quiz'));
+            close_window_button();
         }
-        if ((time() - $attempt->timefinish) > 120) { // always allow review right after attempt
-            if ((!$quiz->timeclose or time() < $quiz->timeclose) and !($quiz->review & QUIZ_REVIEW_OPEN)) {
-                print_error("noreviewuntil", "quiz", '', userdate($quiz->timeclose));
-            }
-            if ($quiz->timeclose and time() >= $quiz->timeclose and !($quiz->review & QUIZ_REVIEW_CLOSED)) {
-                print_error("noreview", "quiz");
-            }
+    /// Can't review other users' attempts.
+        if (!$attemptobj->is_own_attempt()) {
+            notify(get_string('notyourattempt', 'quiz'));
+            close_window_button();
         }
-        if ($attempt->userid != $USER->id) {
-            print_error('notyourattempt', 'quiz');
+    /// Can't review unless Students may review -> Responses option is turned on.
+        if (!$options->responses) {
+            notify($accessmanager->cannot_review_message($options));
+            close_window_button();
         }
     }
 
-    //add_to_log($course->id, 'quiz', 'review', "review.php?id=$cm->id&amp;attempt=$attempt->id", "$quiz->id", "$cm->id");
+/// Load the questions and states.
+    $questionids = array($questionid);
+    $attemptobj->load_questions($questionids);
+    $attemptobj->load_question_states($questionids);
 
-/// Print the page header
+/// If it was asked for, load another state, instead of the latest.
+    if ($stateid) {
+        $attemptobj->load_specific_question_state($questionid, $stateid);
+    }
 
-    $strquizzes = get_string('modulenameplural', 'quiz');
+/// Log this review.
+    add_to_log($attemptobj->get_courseid(), 'quiz', 'review', 'reviewquestion.php?attempt=' .
+            $attemptobj->get_attemptid() . '&question=' . $questionid .
+            ($stateid ? '&state=' . $stateid : ''),
+            $attemptobj->get_quizid(), $attemptobj->get_cmid());
 
+/// Print the page header
     print_header();
 
     echo '<div id="overDiv" style="position:absolute; visibility:hidden; z-index:1000;"></div>'; // for overlib
 
-/// Print heading
-    print_heading(format_string($question->name));
-
-    $question->maxgrade = $DB->get_field('quiz_question_instances', 'grade', array('quiz' => $quiz->id, 'question' => $question->id));
-    // Some of the questions code is optimised to work with several questions
-    // at once so it wants the question to be in an array.
-    $key = $question->id;
-    $questions[$key] = &$question;
-    // Add additional questiontype specific information to the question objects.
-    if (!get_question_options($questions)) {
-        print_error('cannotloadtypeinfo', 'quiz');
-    }
-
-    $session = $DB->get_record('question_sessions', array('attemptid' => $attempt->uniqueid, 'questionid' => $question->id));
-    $state->sumpenalty = $session->sumpenalty;
-    $state->manualcomment = $session->manualcomment;
-    restore_question_state($question, $state);
-    $state->last_graded = $state;
-
-    $options = quiz_get_reviewoptions($quiz, $attempt, $context);
-
 /// Print infobox
-    $table->align  = array("right", "left");
-    if ($attempt->userid <> $USER->id) {
+    $rows = array();
+
+/// User picture and name.
+    if ($attemptobj->get_userid() <> $USER->id) {
         // Print user picture and name
-        $student = $DB->get_record('user', array('id' => $attempt->userid));
-        $picture = print_user_picture($student, $course->id, $student->picture, false, true);
-        $table->data[] = array($picture, fullname($student, true));
+        $student = $DB->get_record('user', array('id' => $attemptobj->get_userid()));
+        $picture = print_user_picture($student, $attemptobj->get_courseid(), $student->picture, false, true);
+        $rows[] = '<tr><th scope="row" class="cell">' . $picture . '</th><td class="cell"><a href="' .
+                $CFG->wwwroot . '/user/view.php?id=' . $student->id . '&amp;course=' . $attemptobj->get_courseid() . '">' .
+                fullname($student, true) . '</a></td></tr>';
     }
-    // print quiz name
-    $table->data[] = array(get_string('modulename', 'quiz').':', format_string($quiz->name));
-    if (has_capability('mod/quiz:viewreports', $context) and
-            count($attempts = $DB->get_records_select('quiz_attempts', "quiz = ? AND userid =?", array($quiz->id, $attempt->userid), 'attempt ASC')) > 1) {
-        // print list of attempts
-        $attemptlist = '';
-        foreach ($attempts as $at) {
-            $attemptlist .= ($at->id == $attempt->id)
-                ? '<b>'.$at->attempt.'</b>, '
-                : '<a href="reviewquestion.php?attempt='.$at->id.'&amp;question='.$question->id.'&amp;number='.$number.'">'.$at->attempt.'</a>, ';
+
+/// Quiz name.
+    $rows[] = '<tr><th scope="row" class="cell">' . get_string('modulename', 'quiz') .
+            '</th><td class="cell">' . format_string($attemptobj->get_quiz_name()) . '</td></tr>';
+
+/// Question name.
+    $rows[] = '<tr><th scope="row" class="cell">' . get_string('question', 'quiz') .
+            '</th><td class="cell">' . format_string(
+            $attemptobj->get_question($questionid)->name) . '</td></tr>';
+
+/// Other attempts at the quiz.
+    if ($attemptobj->has_capability('mod/quiz:viewreports')) {
+        $attemptlist = $attemptobj->links_to_other_attempts(
+                'reviewquestion.php?attempt=' . $attemptobj->get_attemptid() .
+                '&question=' . $questionid);
+        if ($attemptlist) {
+            $rows[] = '<tr><th scope="row" class="cell">' . get_string('attempts', 'quiz') .
+                    '</th><td class="cell">' . $attemptlist . '</td></tr>';
         }
-        $table->data[] = array(get_string('attempts', 'quiz').':', trim($attemptlist, ' ,'));
     }
-    if ($state->timestamp) {
-        // print time stamp
-        $table->data[] = array(get_string("completedon", "quiz").':', userdate($state->timestamp));
+
+/// Timestamp of this action.
+    $timestamp = $attemptobj->get_question_state($questionid)->timestamp;
+    if ($timestamp) {
+        $rows[] = '<tr><th scope="row" class="cell">' . get_string('completedon', 'quiz') .
+                '</th><td class="cell">' . userdate($timestamp) . '</td></tr>';
     }
-    // Print info box unless it is empty
-    if ($table->data) {
-        print_table($table);
+
+/// Now output the summary table, if there are any rows to be shown.
+    if (!empty($rows)) {
+        echo '<table class="generaltable generalbox quizreviewsummary"><tbody>', "\n";
+        echo implode("\n", $rows);
+        echo "\n</tbody></table>\n";
     }
 
-    print_question($question, $state, $number, $quiz, $options);
+/// Print the question in the requested state.
+    $attemptobj->print_question($questionid);
 
+/// Finish the page
     print_footer();
-
 ?>
index fa08a6f10718f2ee04af9f24eb6fc1210ac8a6ac..27c1b97c24a88df12e98ddc60240768323ec60fc 100644 (file)
@@ -923,8 +923,10 @@ class default_questiontype {
                         $link = '<b>'.$st->seq_number.'</b>';
                     } else {
                         if(isset($options->questionreviewlink)) {
-                            $link = link_to_popup_window ($options->questionreviewlink.'?state='.$st->id.'&amp;number='.$number,
-                             'reviewquestion', $st->seq_number, 450, 650, $strreviewquestion, 'none', true);
+                            $link = link_to_popup_window($options->questionreviewlink .
+                                    '&amp;question=' . $question->id . '&amp;state=' . $st->id,
+                                    'reviewquestion', $st->seq_number, 450, 650, $strreviewquestion,
+                                    'none', true);
                         } else {
                             $link = $st->seq_number;
                         }