MDL-20636 Finished backup and restore of attempt data. Yay
[moodle.git] / mod / quiz / review.php
CommitLineData
83192608 1<?php
b2607ccc
TH
2
3// This file is part of Moodle - http://moodle.org/
4//
5// Moodle is free software: you can redistribute it and/or modify
6// it under the terms of the GNU General Public License as published by
7// the Free Software Foundation, either version 3 of the License, or
8// (at your option) any later version.
9//
10// Moodle is distributed in the hope that it will be useful,
11// but WITHOUT ANY WARRANTY; without even the implied warranty of
12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13// GNU General Public License for more details.
14//
15// You should have received a copy of the GNU General Public License
16// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
17
ee1fb969 18/**
f63a4ff2 19 * This page prints a review of a particular quiz attempt
20 *
b2607ccc
TH
21 * It is used either by the student whose attempts this is, after the attempt,
22 * or by a teacher reviewing another's attempt during or afterwards.
23 *
ba643847 24 * @package mod
b2607ccc 25 * @subpackage quiz
ba643847
TH
26 * @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com}
27 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
f63a4ff2 28 */
29d5d0b4 29
ba643847 30
b2607ccc
TH
31require_once(dirname(__FILE__) . '/../../config.php');
32require_once($CFG->dirroot . '/mod/quiz/locallib.php');
33require_once($CFG->dirroot . '/mod/quiz/report/reportlib.php');
34
35$attemptid = required_param('attempt', PARAM_INT);
36$page = optional_param('page', 0, PARAM_INT);
37$showall = optional_param('showall', 0, PARAM_BOOL);
38
39$url = new moodle_url('/mod/quiz/review.php', array('attempt'=>$attemptid));
40if ($page !== 0) {
41 $url->param('page', $page);
42}
43if ($showall !== 0) {
44 $url->param('showall', $showall);
45}
46$PAGE->set_url($url);
47
48$attemptobj = quiz_attempt::create($attemptid);
49
50// Check login.
51require_login($attemptobj->get_course(), false, $attemptobj->get_cm());
52$attemptobj->check_review_capability();
53
54// Create an object to manage all the other (non-roles) access rules.
55$accessmanager = $attemptobj->get_access_manager(time());
56$options = $attemptobj->get_display_options(true);
57
8032cd79
TH
58// Check permissions.
59if ($attemptobj->is_own_attempt()) {
b2607ccc
TH
60 if (!$attemptobj->is_finished()) {
61 redirect($attemptobj->attempt_url(0, $page));
8032cd79 62 } else if (!$options->attempt) {
b2607ccc
TH
63 $accessmanager->back_to_view_page($attemptobj->is_preview_user(),
64 $accessmanager->cannot_review_message($attemptobj->get_attempt_state()));
83192608 65 }
8032cd79
TH
66
67} else if (!$attemptobj->is_review_allowed()) {
68 throw new moodle_quiz_exception($attemptobj->get_quizobj(), 'noreviewattempt');
b2607ccc
TH
69}
70
71// Load the questions and states needed by this page.
72if ($showall) {
73 $questionids = $attemptobj->get_slots();
74} else {
75 $questionids = $attemptobj->get_slots($page);
76}
77
78// Save the flag states, if they are being changed.
79if ($options->flags == question_display_options::EDITABLE && optional_param('savingflags', false, PARAM_BOOL)) {
80 require_sesskey();
81 $attemptobj->save_question_flags();
82 redirect($attemptobj->review_url(0, $page, $showall));
83}
84
85// Log this review.
86add_to_log($attemptobj->get_courseid(), 'quiz', 'review', 'review.php?attempt=' .
87 $attemptobj->get_attemptid(), $attemptobj->get_quizid(), $attemptobj->get_cmid());
88
89// Work out appropriate title and whether blocks should be shown
90if ($attemptobj->is_preview_user() && $attemptobj->is_own_attempt()) {
91 $strreviewtitle = get_string('reviewofpreview', 'quiz');
92 navigation_node::override_active_url($attemptobj->start_attempt_url());
93
94} else {
95 $strreviewtitle = get_string('reviewofattempt', 'quiz', $attemptobj->get_attempt_number());
96 if (empty($attemptobj->get_quiz()->showblocks) && !$attemptobj->is_preview_user()) {
97 $PAGE->blocks->show_only_fake_blocks();
f62040ed 98 }
b2607ccc
TH
99}
100
101// Arrange for the navigation to be displayed.
102$navbc = $attemptobj->get_navigation_panel('quiz_review_nav_panel', $page, $showall);
103$firstregion = reset($PAGE->blocks->get_regions());
104$PAGE->blocks->add_fake_block($navbc, $firstregion);
105
106// Print the page header
107$headtags = $attemptobj->get_html_head_contributions($page, $showall);
108if ($accessmanager->securewindow_required($attemptobj->is_preview_user())) {
109 $accessmanager->setup_secure_page($attemptobj->get_course()->shortname.': '.format_string($attemptobj->get_quiz_name()), $headtags);
110} elseif ($accessmanager->safebrowser_required($attemptobj->is_preview_user())) {
111 $PAGE->set_title($attemptobj->get_course()->shortname . ': '.format_string($attemptobj->get_quiz_name()));
112 $PAGE->set_heading($attemptobj->get_course()->fullname);
113 $PAGE->set_cacheable(false);
114 echo $OUTPUT->header();
115} else {
116 $PAGE->navbar->add($strreviewtitle);
117 $PAGE->set_title(format_string($attemptobj->get_quiz_name()));
118 $PAGE->set_heading($attemptobj->get_course()->fullname);
119 echo $OUTPUT->header();
120}
121
b2607ccc
TH
122// Summary table start ============================================================================
123
124// Work out some time-related things.
125$attempt = $attemptobj->get_attempt();
126$quiz = $attemptobj->get_quiz();
127$overtime = 0;
128
129if ($attempt->timefinish) {
130 if ($timetaken = ($attempt->timefinish - $attempt->timestart)) {
131 if($quiz->timelimit && $timetaken > ($quiz->timelimit + 60)) {
132 $overtime = $timetaken - $quiz->timelimit;
133 $overtime = format_time($overtime);
56ed242b 134 }
b2607ccc 135 $timetaken = format_time($timetaken);
ee1fb969 136 } else {
b2607ccc 137 $timetaken = "-";
99a1bf3d 138 }
b2607ccc
TH
139} else {
140 $timetaken = get_string('unfinished', 'quiz');
141}
142
143// Print summary table about the whole attempt.
144// First we assemble all the rows that are appopriate to the current situation in
145// an array, then later we only output the table if there are any rows to show.
146$rows = array();
147if (!$attemptobj->get_quiz()->showuserpicture && $attemptobj->get_userid() != $USER->id) {
148 // If showuserpicture is true, the picture is shown elsewhere, so don't repeat it.
149 $student = $DB->get_record('user', array('id' => $attemptobj->get_userid()));
150 $picture = $OUTPUT->user_picture($student, array('courseid'=>$attemptobj->get_courseid()));
151 $rows[] = '<tr><th scope="row" class="cell">' . $picture . '</th><td class="cell"><a href="' .
152 $CFG->wwwroot . '/user/view.php?id=' . $student->id . '&amp;course=' . $attemptobj->get_courseid() . '">' .
153 fullname($student, true) . '</a></td></tr>';
154}
155if ($attemptobj->has_capability('mod/quiz:viewreports')) {
156 $attemptlist = $attemptobj->links_to_other_attempts($attemptobj->review_url(0, $page, $showall));
157 if ($attemptlist) {
158 $rows[] = '<tr><th scope="row" class="cell">' . get_string('attempts', 'quiz') .
159 '</th><td class="cell">' . $attemptlist . '</td></tr>';
29d5d0b4 160 }
b2607ccc
TH
161}
162
f39cf78c
TH
163if ($page == 0) {
164 // Timing information.
165 $rows[] = '<tr><th scope="row" class="cell">' . get_string('startedon', 'quiz') .
166 '</th><td class="cell">' . userdate($attempt->timestart) . '</td></tr>';
167 if ($attempt->timefinish) {
168 $rows[] = '<tr><th scope="row" class="cell">' . get_string('completedon', 'quiz') . '</th><td class="cell">' .
169 userdate($attempt->timefinish) . '</td></tr>';
170 $rows[] = '<tr><th scope="row" class="cell">' . get_string('timetaken', 'quiz') . '</th><td class="cell">' .
171 $timetaken . '</td></tr>';
172 }
173 if (!empty($overtime)) {
174 $rows[] = '<tr><th scope="row" class="cell">' . get_string('overdue', 'quiz') . '</th><td class="cell">' . $overtime . '</td></tr>';
175 }
b2607ccc 176
f39cf78c
TH
177 // Show marks (if the user is allowed to see marks at the moment).
178 $grade = quiz_rescale_grade($attempt->sumgrades, $quiz, false);
6d03fd98 179 if ($options->marks >= question_display_options::MARK_AND_MAX && quiz_has_grades($quiz)) {
b2607ccc 180
f39cf78c
TH
181 if (!$attempt->timefinish) {
182 $rows[] = '<tr><th scope="row" class="cell">' . get_string('grade') . '</th><td class="cell">' .
183 get_string('attemptstillinprogress', 'quiz') . '</td></tr>';
9c22b42e 184
f39cf78c
TH
185 } else if (is_null($grade)) {
186 $rows[] = '<tr><th scope="row" class="cell">' . get_string('grade') . '</th><td class="cell">' .
187 quiz_format_grade($quiz, $grade) . '</td></tr>';
4fc3d7e5 188
b2607ccc 189 } else {
f39cf78c
TH
190 // Show raw marks only if they are different from the grade (like on the view page).
191 if ($quiz->grade != $quiz->sumgrades) {
192 $a = new stdClass();
193 $a->grade = quiz_format_grade($quiz, $attempt->sumgrades);
194 $a->maxgrade = quiz_format_grade($quiz, $quiz->sumgrades);
195 $rows[] = '<tr><th scope="row" class="cell">' . get_string('marks', 'quiz') . '</th><td class="cell">' .
196 get_string('outofshort', 'quiz', $a) . '</td></tr>';
197 }
198
199 // Now the scaled grade.
200 $a = new stdClass();
201 $a->grade = '<b>' . quiz_format_grade($quiz, $grade) . '</b>';
202 $a->maxgrade = quiz_format_grade($quiz, $quiz->grade);
203 if ($quiz->grade != 100) {
204 $a->percent = '<b>' . round($attempt->sumgrades * 100 / $quiz->sumgrades, 0) . '</b>';
205 $formattedgrade = get_string('outofpercent', 'quiz', $a);
206 } else {
207 $formattedgrade = get_string('outof', 'quiz', $a);
208 }
209 $rows[] = '<tr><th scope="row" class="cell">' . get_string('grade') . '</th><td class="cell">' .
210 $formattedgrade . '</td></tr>';
b2607ccc 211 }
ee1fb969 212 }
b2607ccc 213
f39cf78c
TH
214 // Feedback if there is any, and the user is allowed to see it now.
215 $feedback = $attemptobj->get_overall_feedback($grade);
216 if ($options->overallfeedback && $feedback) {
217 $rows[] = '<tr><th scope="row" class="cell">' . get_string('feedback', 'quiz') .
218 '</th><td class="cell">' . $feedback . '</td></tr>';
219 }
b2607ccc
TH
220}
221
222// Now output the summary table, if there are any rows to be shown.
223if (!empty($rows)) {
224 echo '<table class="generaltable generalbox quizreviewsummary"><tbody>', "\n";
225 echo implode("\n", $rows);
226 echo "\n</tbody></table>\n";
227}
228
229// Summary table end ==============================================================================
230
231// Form for saving flags if necessary.
232if ($options->flags == question_display_options::EDITABLE) {
233 echo '<form action="' . $attemptobj->review_url(0, $page, $showall) .
404da238 234 '" method="post" class="questionflagsaveform"><div>';
b2607ccc
TH
235 echo '<input type="hidden" name="sesskey" value="' . sesskey() . '" />';
236}
237
238// Print all the questions.
239if ($showall) {
240 $thispage = 'all';
241 $lastpage = true;
242} else {
243 $thispage = $page;
244 $lastpage = $attemptobj->is_last_page($page);
245}
246foreach ($attemptobj->get_slots($thispage) as $slot) {
247 echo $attemptobj->render_question($slot, true, $attemptobj->review_url($slot, $page, $showall));
248}
249
250// Close form if we opened it.
251if ($options->flags == question_display_options::EDITABLE) {
252 echo '<div class="submitbtns">' . "\n" .
253 '<input type="submit" class="questionflagsavebutton" name="savingflags" value="' .
254 get_string('saveflags', 'question') . '" />' .
255 "</div>\n" .
256 "\n</div></form>\n";
257 $PAGE->requires->js_init_call('M.mod_quiz.init_review_form', null, false, quiz_get_js_module());
258}
259
260// Print a link to the next page.
261echo '<div class="submitbtns">';
262if ($lastpage) {
263 $accessmanager->print_finish_review_link($attemptobj->is_preview_user());
264} else {
265 echo link_arrow_right(get_string('next'), $attemptobj->review_url(0, $page + 1));
266}
267echo '</div>';
268echo $OUTPUT->footer();