Commit | Line | Data |
---|---|---|
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 |
31 | require_once(dirname(__FILE__) . '/../../config.php'); |
32 | require_once($CFG->dirroot . '/mod/quiz/locallib.php'); | |
33 | require_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)); | |
40 | if ($page !== 0) { | |
41 | $url->param('page', $page); | |
42 | } | |
43 | if ($showall !== 0) { | |
44 | $url->param('showall', $showall); | |
45 | } | |
46 | $PAGE->set_url($url); | |
47 | ||
48 | $attemptobj = quiz_attempt::create($attemptid); | |
49 | ||
50 | // Check login. | |
51 | require_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. |
59 | if ($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. | |
72 | if ($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. | |
79 | if ($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. | |
86 | add_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 | |
90 | if ($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); | |
108 | if ($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 | ||
129 | if ($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(); | |
147 | if (!$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 . '&course=' . $attemptobj->get_courseid() . '">' . | |
153 | fullname($student, true) . '</a></td></tr>'; | |
154 | } | |
155 | if ($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 |
163 | if ($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. | |
223 | if (!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. | |
232 | if ($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. | |
239 | if ($showall) { | |
240 | $thispage = 'all'; | |
241 | $lastpage = true; | |
242 | } else { | |
243 | $thispage = $page; | |
244 | $lastpage = $attemptobj->is_last_page($page); | |
245 | } | |
246 | foreach ($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. | |
251 | if ($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. | |
261 | echo '<div class="submitbtns">'; | |
262 | if ($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 | } | |
267 | echo '</div>'; | |
268 | echo $OUTPUT->footer(); |