Whoops. missed a string.
[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
c52c62d1 13 require_once('../../config.php');
14 require_once($CFG->dirroot . '/mod/quiz/locallib.php');
29d5d0b4 15
c52c62d1 16 $attemptid = required_param('attempt', PARAM_INT); // A particular attempt ID for review
ee1fb969 17 $page = optional_param('page', 0, PARAM_INT); // The required page
18 $showall = optional_param('showall', 0, PARAM_BOOL);
29d5d0b4 19
c52c62d1 20 if (!$attempt = quiz_load_attempt($attemptid)) {
5a2a5331 21 print_error("No such attempt ID exists");
29d5d0b4 22 }
ee1fb969 23 if (! $quiz = get_record("quiz", "id", $attempt->quiz)) {
5a2a5331 24 print_error("The quiz with id $attempt->quiz belonging to attempt $attempt is missing");
ee1fb969 25 }
26 if (! $course = get_record("course", "id", $quiz->course)) {
5a2a5331 27 print_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)) {
5a2a5331 30 print_error("The course module for the quiz with id $quiz->id is missing");
ee1fb969 31 }
77ed3ba5 32
c52c62d1 33/// Check login and get contexts.
ec81373f 34 require_login($course->id, false, $cm);
a18fa6d5 35 $coursecontext = get_context_instance(CONTEXT_COURSE, $cm->course);
c52c62d1 36 $context = get_context_instance(CONTEXT_MODULE, $cm->id);
37 $canpreview = has_capability('mod/quiz:preview', get_context_instance(CONTEXT_MODULE, $cm->id));
29d5d0b4 38
c52c62d1 39/// Create an object to manage all the other (non-roles) access rules.
a4faed69 40 $timenow = time();
c52c62d1 41 $accessmanager = new quiz_access_manager($quiz, $timenow,
42 has_capability('mod/quiz:ignoretimelimits', $context, NULL, false));
43 $options = quiz_get_reviewoptions($quiz, $attempt, $context);
44
45/// Work out if this is a student viewing their own attempt/teacher previewing,
46/// or someone with 'mod/quiz:viewreports' reviewing someone elses attempt.
47 $reviewofownattempt = $attempt->userid == $USER->id && (!$canpreview || $attempt->preview);
48
49/// Permissions checks for normal users who do not have quiz:viewreports capability.
121609d8 50 if (!has_capability('mod/quiz:viewreports', $context)) {
c52c62d1 51 /// Can't review during the attempt - send them back to the attempt page.
ee1fb969 52 if (!$attempt->timefinish) {
c52c62d1 53 redirect($CFG->wwwroot . '/mod/quiz/attempt.php?q=' . $quiz->id);
29d5d0b4 54 }
c52c62d1 55 /// Can't review other users' attempts.
56 if (!$reviewofownattempt) {
57 quiz_error($quiz, 'reviewnotallowed');
58 }
59 /// Can't review unless Students may review -> Responses option is turned on.
a4faed69 60 if (!$options->responses) {
c52c62d1 61 $accessmanager->back_to_view_page($canpreview,
62 $accessmanager->cannot_review_message($options));
34283aa8 63 }
29d5d0b4 64 }
65
c52c62d1 66/// Log this review.
839f2456 67 add_to_log($course->id, "quiz", "review", "review.php?id=$cm->id&amp;attempt=$attempt->id", "$quiz->id", "$cm->id");
29d5d0b4 68
c52c62d1 69/// load the questions needed by page
70 if ($showall) {
71 $questionlist = quiz_questions_in_quiz($attempt->layout);
72 } else {
73 $questionlist = quiz_questions_on_page($attempt->layout, $page);
2d05b472 74 }
c52c62d1 75 $pagequestions = explode(',', $questionlist);
76 $questions = question_load_questions($questionlist, 'qqi.grade AS maxgrade, qqi.id AS instance',
77 'quiz_question_instances qqi ON qqi.quiz = ' . $quiz->id . ' AND q.id = qqi.question');
78 if (is_string($questions)) {
79 quiz_error($quiz, 'loadingquestionsfailed', $questions);
2d05b472 80 }
81
c52c62d1 82/// Restore the question sessions to their most recent states creating new sessions where required.
2d05b472 83 if (!$states = get_question_states($questions, $quiz, $attempt)) {
5a2a5331 84 print_error('Could not restore question sessions');
2d05b472 85 }
86
38e179a4 87/// Work out appropriate title.
c52c62d1 88 if ($canpreview && $reviewofownattempt) {
38e179a4 89 $strreviewtitle = get_string('reviewofpreview', 'quiz');
90 } else {
91 $strreviewtitle = get_string('reviewofattempt', 'quiz', $attempt->attempt);
92 }
93
c52c62d1 94/// Print the page header
2d05b472 95 $headtags = get_html_head_contributions($pagequestions, $questions, $states);
c52c62d1 96 if ($accessmanager->securewindow_required($canpreview)) {
97 $accessmanager->setup_secure_page($course->shortname.': '.format_string($quiz->name), $headtags);
ee1fb969 98 } else {
5cf38a57 99 $strupdatemodule = has_capability('moodle/course:manageactivities', $coursecontext)
ee1fb969 100 ? update_module_button($cm->id, $course->id, get_string('modulename', 'quiz'))
101 : "";
38e179a4 102 get_string('reviewofattempt', 'quiz', $attempt->attempt);
103 $navigation = build_navigation($strreviewtitle, $cm);
2d05b472 104 print_header_simple(format_string($quiz->name), "", $navigation, "", $headtags, true, $strupdatemodule);
ee1fb969 105 }
b7f35820 106 echo '<div id="overDiv" style="position:absolute; visibility:hidden; z-index:1000;"></div>'; // for overlib
107
c52c62d1 108/// Print tabs if they should be there.
109 if ($canpreview) {
110 if ($reviewofownattempt) {
0a5b58af 111 $currenttab = 'preview';
112 } else {
113 $currenttab = 'reports';
114 $mode = '';
115 }
ee1fb969 116 include('tabs.php');
ee1fb969 117 }
c52c62d1 118
119/// Print heading.
38e179a4 120 print_heading(format_string($quiz->name));
c52c62d1 121 if ($canpreview && $reviewofownattempt) {
122 print_restart_preview_button($quiz);
99a1bf3d 123 }
38e179a4 124 print_heading($strreviewtitle);
99a1bf3d 125
c52c62d1 126/// Finish review link.
127 if ($reviewofownattempt) {
128 $accessmanager->print_finish_review_link($canpreview);
99a1bf3d 129 }
ad1a74e0 130
ee1fb969 131/// Print infobox
3a00dbfd 132 $timelimit = (int)$quiz->timelimit * 60;
235987c5 133 $overtime = 0;
c52c62d1 134 $grade = quiz_rescale_grade($attempt->sumgrades, $quiz);
135 $feedback = quiz_feedback_for_grade($grade, $attempt->quiz);
d4961620 136
ee1fb969 137 if ($attempt->timefinish) {
138 if ($timetaken = ($attempt->timefinish - $attempt->timestart)) {
139 if($timelimit && $timetaken > ($timelimit + 60)) {
140 $overtime = $timetaken - $timelimit;
141 $overtime = format_time($overtime);
142 }
143 $timetaken = format_time($timetaken);
144 } else {
145 $timetaken = "-";
d4961620 146 }
29d5d0b4 147 } else {
ee1fb969 148 $timetaken = get_string('unfinished', 'quiz');
29d5d0b4 149 }
99a1bf3d 150 echo '<table class="generaltable generalbox quizreviewsummary"><tbody>';
76cacec8 151 if ($attempt->userid <> $USER->id) {
99a1bf3d 152 $student = get_record('user', 'id', $attempt->userid);
65bcf17b 153 $picture = print_user_picture($student, $course->id, $student->picture, false, true);
99a1bf3d 154 echo '<tr><th scope="row" class="cell">', $picture, '</th><td class="cell"><a href="', $CFG->wwwroot,
155 '/user/view.php?id=', $student->id, '&amp;course='.$course->id.'">',
156 fullname($student, true), '</a></td></tr>';
76cacec8 157 }
99a1bf3d 158 if (has_capability('mod/quiz:grade', $context) and
159 count($attempts = get_records_select('quiz_attempts', "quiz = '$quiz->id' AND userid = '$attempt->userid'", 'attempt ASC')) > 1) {
ee1fb969 160 // print list of attempts
161 $attemptlist = '';
162 foreach ($attempts as $at) {
163 $attemptlist .= ($at->id == $attempt->id)
09275894 164 ? '<strong>'.$at->attempt.'</strong>, '
ee1fb969 165 : '<a href="review.php?attempt='.$at->id.($showall?'&amp;showall=true':'').'">'.$at->attempt.'</a>, ';
166 }
99a1bf3d 167 echo '<tr><th scope="row" class="cell">', get_string('attempts', 'quiz'), '</th><td class="cell">',
168 trim($attemptlist, ' ,'), '</td></tr>';
ee1fb969 169 }
170
99a1bf3d 171 echo '<tr><th scope="row" class="cell">', get_string('startedon', 'quiz'), '</th><td class="cell">',
172 userdate($attempt->timestart), '</td></tr>';
ee1fb969 173 if ($attempt->timefinish) {
c52c62d1 174 echo '<tr><th scope="row" class="cell">', get_string('completedon', 'quiz'), '</th><td class="cell">',
99a1bf3d 175 userdate($attempt->timefinish), '</td></tr>';
c52c62d1 176 echo '<tr><th scope="row" class="cell">', get_string('timetaken', 'quiz'), '</th><td class="cell">',
99a1bf3d 177 $timetaken, '</td></tr>';
ee1fb969 178 }
3a00dbfd 179 if (!empty($overtime)) {
c52c62d1 180 echo '<tr><th scope="row" class="cell">', get_string('overdue', 'quiz'), '</th><td class="cell">',$overtime, '</td></tr>';
d4961620 181 }
15db443c 182 //if the student is allowed to see their score
183 if ($options->scores) {
184 if ($quiz->grade and $quiz->sumgrades) {
185 if($overtime) {
186 $result->sumgrades = "0";
187 $result->grade = "0.0";
188 }
77ed3ba5 189
3b48ba9d 190 $a = new stdClass;
15db443c 191 $percentage = round(($attempt->sumgrades/$quiz->sumgrades)*100, 0);
212b7b8c 192 $a->grade = $grade;
3b48ba9d 193 $a->maxgrade = $quiz->grade;
15db443c 194 $rawscore = round($attempt->sumgrades, $CFG->quiz_decimalpoints);
c52c62d1 195 echo '<tr><th scope="row" class="cell">', get_string('score', 'quiz'), '</th><td class="cell">',
49e7a95a 196 "$rawscore/$quiz->sumgrades ($percentage%)", '</td></tr>';
c52c62d1 197 echo '<tr><th scope="row" class="cell">', get_string('grade'), '</th><td class="cell">',
99a1bf3d 198 get_string('outof', 'quiz', $a), '</td></tr>';
d4961620 199 }
ee1fb969 200 }
77ed3ba5 201 if ($options->overallfeedback && $feedback) {
99a1bf3d 202 echo '<tr><th scope="row" class="cell">', get_string('feedback', 'quiz'), '</th><td class="cell">',
203 $feedback, '</td></tr>';
9bc2d82a 204 }
99a1bf3d 205 echo '</tbody></table>';
9bc2d82a 206
ee1fb969 207/// Print the navigation panel if required
208 $numpages = quiz_number_of_pages($attempt->layout);
209 if ($numpages > 1 and !$showall) {
210 print_paging_bar($numpages, $page, 1, 'review.php?attempt='.$attempt->id.'&amp;');
09275894 211 echo '<div class="controls"><a href="review.php?attempt='.$attempt->id.'&amp;showall=true">';
ee1fb969 212 print_string('showall', 'quiz');
09275894 213 echo '</a></div>';
29d5d0b4 214 }
215
ee1fb969 216/// Print all the questions
c52c62d1 217 $number = quiz_first_questionnumber($attempt->layout, $questionlist);
ee1fb969 218 foreach ($pagequestions as $i) {
7bbe08a2 219 if (!isset($questions[$i])) {
220 print_simple_box_start('center', '90%');
09275894 221 echo '<strong><font size="+1">' . $number . '</font></strong><br />';
7bbe08a2 222 notify(get_string('errormissingquestion', 'quiz', $i));
223 print_simple_box_end();
224 $number++; // Just guessing that the missing question would have lenght 1
225 continue;
226 }
4f48fb42 227 $options->validation = QUESTION_EVENTVALIDATE === $states[$i]->event;
c52c62d1 228 $options->history = ($canpreview and !$attempt->preview) ? 'all' : 'graded';
ee1fb969 229 // Print the question
4f48fb42 230 print_question($questions[$i], $states[$i], $number, $quiz, $options);
ee1fb969 231 $number += $questions[$i]->length;
232 }
29d5d0b4 233
ee1fb969 234 // Print the navigation panel if required
235 if ($numpages > 1 and !$showall) {
236 print_paging_bar($numpages, $page, 1, 'review.php?attempt='.$attempt->id.'&amp;');
29d5d0b4 237 }
238
ee1fb969 239 // print javascript button to close the window, if necessary
c52c62d1 240 if ($reviewofownattempt) {
241 $accessmanager->print_finish_review_link($canpreview);
ee1fb969 242 }
29d5d0b4 243
c52c62d1 244 if ($accessmanager->securewindow_required($canpreview)) {
245 print_footer('empty');
246 } else {
ee1fb969 247 print_footer($course);
248 }
29d5d0b4 249?>