Clarify unused argument.
[moodle.git] / mod / quiz / review.php
CommitLineData
d1290cec 1<?php // $Id$
ee1fb969 2/**
f63a4ff2 3 * This page prints a review of a particular quiz attempt
4 *
5 * @author Martin Dougiamas and many others. This has recently been completely
6 * rewritten by Alex Smith, Julian Sedding and Gustav Delius as part of
7 * the Serving Mathematics project
8 * {@link http://maths.york.ac.uk/serving_maths}
9 * @license http://www.gnu.org/copyleft/gpl.html GNU Public License
10 * @package quiz
11 */
29d5d0b4 12
13 require_once("../../config.php");
76cacec8 14 require_once("locallib.php");
29d5d0b4 15
ee1fb969 16 $attempt = required_param('attempt', PARAM_INT); // A particular attempt ID for review
17 $page = optional_param('page', 0, PARAM_INT); // The required page
18 $showall = optional_param('showall', 0, PARAM_BOOL);
29d5d0b4 19
20 if (! $attempt = get_record("quiz_attempts", "id", $attempt)) {
21 error("No such attempt ID exists");
22 }
ee1fb969 23 if (! $quiz = get_record("quiz", "id", $attempt->quiz)) {
7bbe08a2 24 error("The quiz with id $attempt->quiz belonging to attempt $attempt is missing");
ee1fb969 25 }
26 if (! $course = get_record("course", "id", $quiz->course)) {
7bbe08a2 27 error("The course with id $quiz->course that the quiz with id $quiz->id belongs to is missing");
ee1fb969 28 }
29 if (! $cm = get_coursemodule_from_instance("quiz", $quiz->id, $course->id)) {
7bbe08a2 30 error("The course module for the quiz with id $quiz->id is missing");
ee1fb969 31 }
77ed3ba5 32
212b7b8c 33 $grade = quiz_rescale_grade($attempt->sumgrades, $quiz);
34 $feedback = quiz_feedback_for_grade($grade, $attempt->quiz);
29d5d0b4 35
03d1753c 36 if (!count_records('question_sessions', 'attemptid', $attempt->uniqueid)) {
ee1fb969 37 // this question has not yet been upgraded to the new model
38 quiz_upgrade_states($attempt);
39 }
29d5d0b4 40
ec81373f 41 require_login($course->id, false, $cm);
5cf38a57 42 $context = get_context_instance(CONTEXT_MODULE, $cm->id);
a18fa6d5 43 $coursecontext = get_context_instance(CONTEXT_COURSE, $cm->course);
4f83ae95 44 $isteacher = has_capability('mod/quiz:preview', get_context_instance(CONTEXT_MODULE, $cm->id));
cd06115f 45 $options = quiz_get_reviewoptions($quiz, $attempt, $context);
bacd044d 46 $popup = $isteacher ? 0 : $quiz->popup; // Controls whether this is shown in a javascript-protected window.
29d5d0b4 47
a4faed69 48 $timenow = time();
121609d8 49 if (!has_capability('mod/quiz:viewreports', $context)) {
a4faed69 50 // Can't review during the attempt.
ee1fb969 51 if (!$attempt->timefinish) {
a4faed69 52 redirect('attempt.php?q=' . $quiz->id);
29d5d0b4 53 }
a4faed69 54 // Can't review other student's attempts.
55 if ($attempt->userid != $USER->id) {
56 error("This is not your attempt!", 'view.php?q=' . $quiz->id);
57 }
58 // Can't review if Student's may review ... Responses is turned on.
59 if (!$options->responses) {
60 if ($options->quizstate == QUIZ_STATE_IMMEDIATELY) {
61 $message = '';
62 } else if ($options->quizstate == QUIZ_STATE_OPEN && $quiz->timeclose &&
63 ($quiz->review & QUIZ_REVIEW_CLOSED & QUIZ_REVIEW_RESPONSES)) {
64 $message = get_string('noreviewuntil', 'quiz', userdate($quiz->timeclose));
65 } else {
66 $message = get_string('noreview', 'quiz');
67 }
e484e8f9 68 if (empty($popup)) {
a4faed69 69 redirect('view.php?q=' . $quiz->id, $message);
e484e8f9 70 } else {
71 ?><script type="text/javascript">
72 opener.document.location.reload();
73 self.close();
74 </script><?php
75 die();
76 }
34283aa8 77 }
29d5d0b4 78 }
79
839f2456 80 add_to_log($course->id, "quiz", "review", "review.php?id=$cm->id&amp;attempt=$attempt->id", "$quiz->id", "$cm->id");
29d5d0b4 81
2d05b472 82/// Load all the questions and states needed by this script
83
84 // load the questions needed by page
85 $pagelist = $showall ? quiz_questions_in_quiz($attempt->layout) : quiz_questions_on_page($attempt->layout, $page);
86 $sql = "SELECT q.*, i.grade AS maxgrade, i.id AS instance".
87 " FROM {$CFG->prefix}question q,".
88 " {$CFG->prefix}quiz_question_instances i".
89 " WHERE i.quiz = '$quiz->id' AND q.id = i.question".
90 " AND q.id IN ($pagelist)";
91 if (!$questions = get_records_sql($sql)) {
92 error('No questions found');
93 }
94
95 // Load the question type specific information
96 if (!get_question_options($questions)) {
97 error('Could not load question options');
98 }
99
100 // Restore the question sessions to their most recent states
101 // creating new sessions where required
102 if (!$states = get_question_states($questions, $quiz, $attempt)) {
103 error('Could not restore question sessions');
104 }
105
ee1fb969 106/// Print the page header
29d5d0b4 107
29d5d0b4 108 $strscore = get_string("score", "quiz");
109 $strgrade = get_string("grade");
110 $strbestgrade = get_string("bestgrade", "quiz");
111 $strtimetaken = get_string("timetaken", "quiz");
ee1fb969 112 $strtimecompleted = get_string("completedon", "quiz");
d4961620 113 $stroverdue = get_string("overdue", "quiz");
29d5d0b4 114
38e179a4 115/// Work out appropriate title.
116 if ($isteacher and $attempt->userid == $USER->id) {
117 $strreviewtitle = get_string('reviewofpreview', 'quiz');
118 } else {
119 $strreviewtitle = get_string('reviewofattempt', 'quiz', $attempt->attempt);
120 }
121
2d05b472 122 $pagequestions = explode(',', $pagelist);
123 $headtags = get_html_head_contributions($pagequestions, $questions, $states);
ee1fb969 124 if (!empty($popup)) {
125 define('MESSAGE_WINDOW', true); // This prevents the message window coming up
2d05b472 126 print_header($course->shortname.': '.format_string($quiz->name), '', '', '', $headtags, false, '', '', false, '');
ee1fb969 127 /// Include Javascript protection for this page
128 include('protect_js.php');
129 } else {
5cf38a57 130 $strupdatemodule = has_capability('moodle/course:manageactivities', $coursecontext)
ee1fb969 131 ? update_module_button($cm->id, $course->id, get_string('modulename', 'quiz'))
132 : "";
38e179a4 133 get_string('reviewofattempt', 'quiz', $attempt->attempt);
134 $navigation = build_navigation($strreviewtitle, $cm);
2d05b472 135 print_header_simple(format_string($quiz->name), "", $navigation, "", $headtags, true, $strupdatemodule);
ee1fb969 136 }
b7f35820 137 echo '<div id="overDiv" style="position:absolute; visibility:hidden; z-index:1000;"></div>'; // for overlib
138
ee1fb969 139/// Print heading and tabs if this is part of a preview
5cf38a57 140 if (has_capability('mod/quiz:preview', $context)) {
0a5b58af 141 if ($attempt->userid == $USER->id) { // this is the report on a preview
142 $currenttab = 'preview';
143 } else {
144 $currenttab = 'reports';
145 $mode = '';
146 }
ee1fb969 147 include('tabs.php');
ee1fb969 148 }
38e179a4 149 print_heading(format_string($quiz->name));
99a1bf3d 150 if ($isteacher and $attempt->userid == $USER->id) {
151 // the teacher is at the end of a preview. Print button to start new preview
152 unset($buttonoptions);
153 $buttonoptions['q'] = $quiz->id;
154 $buttonoptions['forcenew'] = true;
155 echo '<div class="controls">';
156 print_single_button($CFG->wwwroot.'/mod/quiz/attempt.php', $buttonoptions, get_string('startagain', 'quiz'));
157 echo '</div>';
99a1bf3d 158 }
38e179a4 159 print_heading($strreviewtitle);
99a1bf3d 160
161 // print javascript button to close the window, if necessary
162 if (!$isteacher) {
163 include('attempt_close_js.php');
164 }
ad1a74e0 165
ee1fb969 166/// Print infobox
167
3a00dbfd 168 $timelimit = (int)$quiz->timelimit * 60;
235987c5 169 $overtime = 0;
d4961620 170
ee1fb969 171 if ($attempt->timefinish) {
172 if ($timetaken = ($attempt->timefinish - $attempt->timestart)) {
173 if($timelimit && $timetaken > ($timelimit + 60)) {
174 $overtime = $timetaken - $timelimit;
175 $overtime = format_time($overtime);
176 }
177 $timetaken = format_time($timetaken);
178 } else {
179 $timetaken = "-";
d4961620 180 }
29d5d0b4 181 } else {
ee1fb969 182 $timetaken = get_string('unfinished', 'quiz');
29d5d0b4 183 }
99a1bf3d 184 echo '<table class="generaltable generalbox quizreviewsummary"><tbody>';
76cacec8 185 if ($attempt->userid <> $USER->id) {
99a1bf3d 186 $student = get_record('user', 'id', $attempt->userid);
65bcf17b 187 $picture = print_user_picture($student, $course->id, $student->picture, false, true);
99a1bf3d 188 echo '<tr><th scope="row" class="cell">', $picture, '</th><td class="cell"><a href="', $CFG->wwwroot,
189 '/user/view.php?id=', $student->id, '&amp;course='.$course->id.'">',
190 fullname($student, true), '</a></td></tr>';
76cacec8 191 }
99a1bf3d 192 if (has_capability('mod/quiz:grade', $context) and
193 count($attempts = get_records_select('quiz_attempts', "quiz = '$quiz->id' AND userid = '$attempt->userid'", 'attempt ASC')) > 1) {
ee1fb969 194 // print list of attempts
195 $attemptlist = '';
196 foreach ($attempts as $at) {
197 $attemptlist .= ($at->id == $attempt->id)
09275894 198 ? '<strong>'.$at->attempt.'</strong>, '
ee1fb969 199 : '<a href="review.php?attempt='.$at->id.($showall?'&amp;showall=true':'').'">'.$at->attempt.'</a>, ';
200 }
99a1bf3d 201 echo '<tr><th scope="row" class="cell">', get_string('attempts', 'quiz'), '</th><td class="cell">',
202 trim($attemptlist, ' ,'), '</td></tr>';
ee1fb969 203 }
204
99a1bf3d 205 echo '<tr><th scope="row" class="cell">', get_string('startedon', 'quiz'), '</th><td class="cell">',
206 userdate($attempt->timestart), '</td></tr>';
ee1fb969 207 if ($attempt->timefinish) {
99a1bf3d 208 echo '<tr><th scope="row" class="cell">', $strtimecompleted, '</th><td class="cell">',
209 userdate($attempt->timefinish), '</td></tr>';
210 echo '<tr><th scope="row" class="cell">', $strtimetaken, '</th><td class="cell">',
211 $timetaken, '</td></tr>';
ee1fb969 212 }
3a00dbfd 213 if (!empty($overtime)) {
99a1bf3d 214 echo '<tr><th scope="row" class="cell">', $stroverdue, '</th><td class="cell">',$overtime, '</td></tr>';
d4961620 215 }
15db443c 216 //if the student is allowed to see their score
217 if ($options->scores) {
218 if ($quiz->grade and $quiz->sumgrades) {
219 if($overtime) {
220 $result->sumgrades = "0";
221 $result->grade = "0.0";
222 }
77ed3ba5 223
3b48ba9d 224 $a = new stdClass;
15db443c 225 $percentage = round(($attempt->sumgrades/$quiz->sumgrades)*100, 0);
212b7b8c 226 $a->grade = $grade;
3b48ba9d 227 $a->maxgrade = $quiz->grade;
15db443c 228 $rawscore = round($attempt->sumgrades, $CFG->quiz_decimalpoints);
99a1bf3d 229 echo '<tr><th scope="row" class="cell">', $strscore, '</th><td class="cell">',
49e7a95a 230 "$rawscore/$quiz->sumgrades ($percentage%)", '</td></tr>';
99a1bf3d 231 echo '<tr><th scope="row" class="cell">', $strgrade, '</th><td class="cell">',
232 get_string('outof', 'quiz', $a), '</td></tr>';
d4961620 233 }
ee1fb969 234 }
77ed3ba5 235 if ($options->overallfeedback && $feedback) {
99a1bf3d 236 echo '<tr><th scope="row" class="cell">', get_string('feedback', 'quiz'), '</th><td class="cell">',
237 $feedback, '</td></tr>';
9bc2d82a 238 }
99a1bf3d 239 echo '</tbody></table>';
9bc2d82a 240
ee1fb969 241/// Print the navigation panel if required
242 $numpages = quiz_number_of_pages($attempt->layout);
243 if ($numpages > 1 and !$showall) {
244 print_paging_bar($numpages, $page, 1, 'review.php?attempt='.$attempt->id.'&amp;');
09275894 245 echo '<div class="controls"><a href="review.php?attempt='.$attempt->id.'&amp;showall=true">';
ee1fb969 246 print_string('showall', 'quiz');
09275894 247 echo '</a></div>';
29d5d0b4 248 }
249
ee1fb969 250/// Print all the questions
251
ee1fb969 252 $number = quiz_first_questionnumber($attempt->layout, $pagelist);
253 foreach ($pagequestions as $i) {
7bbe08a2 254 if (!isset($questions[$i])) {
255 print_simple_box_start('center', '90%');
09275894 256 echo '<strong><font size="+1">' . $number . '</font></strong><br />';
7bbe08a2 257 notify(get_string('errormissingquestion', 'quiz', $i));
258 print_simple_box_end();
259 $number++; // Just guessing that the missing question would have lenght 1
260 continue;
261 }
4f48fb42 262 $options->validation = QUESTION_EVENTVALIDATE === $states[$i]->event;
ee1fb969 263 $options->history = ($isteacher and !$attempt->preview) ? 'all' : 'graded';
264 // Print the question
4f48fb42 265 print_question($questions[$i], $states[$i], $number, $quiz, $options);
ee1fb969 266 $number += $questions[$i]->length;
267 }
29d5d0b4 268
ee1fb969 269 // Print the navigation panel if required
270 if ($numpages > 1 and !$showall) {
271 print_paging_bar($numpages, $page, 1, 'review.php?attempt='.$attempt->id.'&amp;');
29d5d0b4 272 }
273
ee1fb969 274 // print javascript button to close the window, if necessary
275 if (!$isteacher) {
276 include('attempt_close_js.php');
277 }
29d5d0b4 278
ee1fb969 279 if (empty($popup)) {
280 print_footer($course);
281 }
29d5d0b4 282?>