d1290cec |
1 | <?php // $Id$ |
ee1fb969 |
2 | /** |
f63a4ff2 |
3 | * This page prints a review of a particular quiz attempt |
4 | * |
4fc3d7e5 |
5 | * @author Martin Dougiamas and many others. |
f63a4ff2 |
6 | * @license http://www.gnu.org/copyleft/gpl.html GNU Public License |
7 | * @package quiz |
8 | */ |
29d5d0b4 |
9 | |
4fc3d7e5 |
10 | require_once(dirname(__FILE__) . '/../../config.php'); |
c52c62d1 |
11 | require_once($CFG->dirroot . '/mod/quiz/locallib.php'); |
29d5d0b4 |
12 | |
4fc3d7e5 |
13 | $attemptid = required_param('attempt', PARAM_INT); |
14 | $page = optional_param('page', 0, PARAM_INT); |
ee1fb969 |
15 | $showall = optional_param('showall', 0, PARAM_BOOL); |
29d5d0b4 |
16 | |
4fc3d7e5 |
17 | $attemptobj = new quiz_attempt($attemptid); |
18 | |
19 | /// Check login. |
20 | require_login($attemptobj->get_courseid(), false, $attemptobj->get_cm()); |
77ed3ba5 |
21 | |
29d5d0b4 |
22 | |
c52c62d1 |
23 | /// Create an object to manage all the other (non-roles) access rules. |
4fc3d7e5 |
24 | $accessmanager = $attemptobj->get_access_manager(time()); |
25 | $options = $attemptobj->get_review_options(); |
c52c62d1 |
26 | |
27 | /// Work out if this is a student viewing their own attempt/teacher previewing, |
28 | /// or someone with 'mod/quiz:viewreports' reviewing someone elses attempt. |
4fc3d7e5 |
29 | $reviewofownattempt = $attemptobj->get_userid() == $USER->id && |
30 | (!$attemptobj->is_preview_user() || $attemptobj->is_preview()); |
c52c62d1 |
31 | |
32 | /// Permissions checks for normal users who do not have quiz:viewreports capability. |
4fc3d7e5 |
33 | if (!$attemptobj->has_capability('mod/quiz:viewreports')) { |
c52c62d1 |
34 | /// Can't review during the attempt - send them back to the attempt page. |
4fc3d7e5 |
35 | if (!$attemptobj->is_finished()) { |
36 | redirect($attemptobj->attempt_url(0, $page)); |
29d5d0b4 |
37 | } |
c52c62d1 |
38 | /// Can't review other users' attempts. |
39 | if (!$reviewofownattempt) { |
40 | quiz_error($quiz, 'reviewnotallowed'); |
41 | } |
42 | /// Can't review unless Students may review -> Responses option is turned on. |
a4faed69 |
43 | if (!$options->responses) { |
4fc3d7e5 |
44 | $accessmanager->back_to_view_page($attemptobj->is_preview_user(), |
c52c62d1 |
45 | $accessmanager->cannot_review_message($options)); |
34283aa8 |
46 | } |
29d5d0b4 |
47 | } |
48 | |
c52c62d1 |
49 | /// Log this review. |
4fc3d7e5 |
50 | add_to_log($attemptobj->get_courseid(), 'quiz', 'review', 'review.php?attempt=' . |
51 | $attemptobj->get_attemptid(), $attemptobj->get_quizid(), $attemptobj->get_cmid()); |
29d5d0b4 |
52 | |
4fc3d7e5 |
53 | /// load the questions and states needed by this page. |
c52c62d1 |
54 | if ($showall) { |
4fc3d7e5 |
55 | $questionids = $attemptobj->get_question_ids(); |
c52c62d1 |
56 | } else { |
4fc3d7e5 |
57 | $questionids = $attemptobj->get_question_ids($page); |
58 | } |
59 | $attemptobj->load_questions($questionids); |
60 | $attemptobj->load_question_states($questionids); |
2d05b472 |
61 | |
38e179a4 |
62 | /// Work out appropriate title. |
4fc3d7e5 |
63 | if ($attemptobj->is_preview_user() && $reviewofownattempt) { |
38e179a4 |
64 | $strreviewtitle = get_string('reviewofpreview', 'quiz'); |
65 | } else { |
b9b3aa94 |
66 | $strreviewtitle = get_string('reviewofattempt', 'quiz', $attemptobj->get_attempt_number()); |
38e179a4 |
67 | } |
68 | |
c52c62d1 |
69 | /// Print the page header |
4fc3d7e5 |
70 | $headtags = $attemptobj->get_html_head_contributions($page); |
71 | if ($accessmanager->securewindow_required($attemptobj->is_preview_user())) { |
c52c62d1 |
72 | $accessmanager->setup_secure_page($course->shortname.': '.format_string($quiz->name), $headtags); |
ee1fb969 |
73 | } else { |
4fc3d7e5 |
74 | print_header_simple(format_string($attemptobj->get_quiz_name()), '', $attemptobj->navigation($strreviewtitle), |
75 | '', $headtags, true, $attemptobj->update_module_button()); |
ee1fb969 |
76 | } |
b7f35820 |
77 | echo '<div id="overDiv" style="position:absolute; visibility:hidden; z-index:1000;"></div>'; // for overlib |
78 | |
c52c62d1 |
79 | /// Print tabs if they should be there. |
4fc3d7e5 |
80 | if ($attemptobj->is_preview_user()) { |
c52c62d1 |
81 | if ($reviewofownattempt) { |
0a5b58af |
82 | $currenttab = 'preview'; |
83 | } else { |
84 | $currenttab = 'reports'; |
85 | $mode = ''; |
86 | } |
ee1fb969 |
87 | include('tabs.php'); |
ee1fb969 |
88 | } |
c52c62d1 |
89 | |
90 | /// Print heading. |
38e179a4 |
91 | print_heading(format_string($quiz->name)); |
4fc3d7e5 |
92 | if ($attemptobj->is_preview_user() && $reviewofownattempt) { |
78e7a3dd |
93 | $attemptobj->print_restart_preview_button(); |
99a1bf3d |
94 | } |
38e179a4 |
95 | print_heading($strreviewtitle); |
99a1bf3d |
96 | |
baef998b |
97 | /// Print the navigation panel in a left column. |
98 | print_container_start(); |
99 | echo '<div id="left-column">'; |
100 | $attemptobj->print_navigation_panel('quiz_review_nav_panel', $page); |
101 | echo '</div>'; |
102 | print_container_end(); |
103 | |
104 | /// Start the main column. |
105 | echo '<div id="middle-column">'; |
106 | print_container_start(); |
107 | echo skip_main_destination(); |
ad1a74e0 |
108 | |
4fc3d7e5 |
109 | /// Summary table start ============================================================================ |
110 | |
9c22b42e |
111 | /// Work out some time-related things. |
4fc3d7e5 |
112 | $attempt = $attemptobj->get_attempt(); |
113 | $quiz = $attemptobj->get_quiz(); |
114 | $timelimit = $quiz->timelimit * 60; |
235987c5 |
115 | $overtime = 0; |
d4961620 |
116 | |
ee1fb969 |
117 | if ($attempt->timefinish) { |
118 | if ($timetaken = ($attempt->timefinish - $attempt->timestart)) { |
119 | if($timelimit && $timetaken > ($timelimit + 60)) { |
120 | $overtime = $timetaken - $timelimit; |
121 | $overtime = format_time($overtime); |
122 | } |
123 | $timetaken = format_time($timetaken); |
124 | } else { |
125 | $timetaken = "-"; |
d4961620 |
126 | } |
29d5d0b4 |
127 | } else { |
ee1fb969 |
128 | $timetaken = get_string('unfinished', 'quiz'); |
29d5d0b4 |
129 | } |
9c22b42e |
130 | |
131 | /// Print summary table about the whole attempt. |
132 | /// First we assemble all the rows that are appopriate to the current situation in |
133 | /// an array, then later we only output the table if there are any rows to show. |
134 | $rows = array(); |
b9b3aa94 |
135 | if ($attemptobj->get_userid() <> $USER->id) { |
136 | $student = $DB->get_record('user', array('id' => $attemptobj->get_userid())); |
137 | $picture = print_user_picture($student, $attemptobj->get_courseid(), $student->picture, false, true); |
9c22b42e |
138 | $rows[] = '<tr><th scope="row" class="cell">' . $picture . '</th><td class="cell"><a href="' . |
b9b3aa94 |
139 | $CFG->wwwroot . '/user/view.php?id=' . $student->id . '&course=' . $attemptobj->get_courseid() . '">' . |
9c22b42e |
140 | fullname($student, true) . '</a></td></tr>'; |
76cacec8 |
141 | } |
9c22b42e |
142 | if (has_capability('mod/quiz:viewreports', $context) && |
9cf4a18b |
143 | count($attempts = $DB->get_records_select('quiz_attempts', "quiz = ? AND userid = ?", array($quiz->id, $attempt->userid), 'attempt ASC')) > 1) { |
9c22b42e |
144 | /// List of all this user's attempts for people who can see reports. |
9c22b42e |
145 | $attemptlist = array(); |
ee1fb969 |
146 | foreach ($attempts as $at) { |
9c22b42e |
147 | if ($at->id == $attempt->id) { |
148 | $attemptlist[] = '<strong>' . $at->attempt . '</strong>'; |
149 | } else { |
4fc3d7e5 |
150 | $attemptlist[] = '<a href="' . $attemptobj->review_url(0, $page, $showall, $at->id) . |
151 | '">' . $at->attempt . '</a>'; |
9c22b42e |
152 | } |
ee1fb969 |
153 | } |
9c22b42e |
154 | $rows[] = '<tr><th scope="row" class="cell">' . get_string('attempts', 'quiz') . |
155 | '</th><td class="cell">' . implode(', ', $attemptlist) . '</td></tr>'; |
ee1fb969 |
156 | } |
157 | |
9c22b42e |
158 | /// Timing information. |
159 | $rows[] = '<tr><th scope="row" class="cell">' . get_string('startedon', 'quiz') . |
160 | '</th><td class="cell">' . userdate($attempt->timestart) . '</td></tr>'; |
ee1fb969 |
161 | if ($attempt->timefinish) { |
9c22b42e |
162 | $rows[] = '<tr><th scope="row" class="cell">' . get_string('completedon', 'quiz') . '</th><td class="cell">' . |
163 | userdate($attempt->timefinish) . '</td></tr>'; |
164 | $rows[] = '<tr><th scope="row" class="cell">' . get_string('timetaken', 'quiz') . '</th><td class="cell">' . |
165 | $timetaken . '</td></tr>'; |
ee1fb969 |
166 | } |
3a00dbfd |
167 | if (!empty($overtime)) { |
9c22b42e |
168 | $rows[] = '<tr><th scope="row" class="cell">' . get_string('overdue', 'quiz') . '</th><td class="cell">' . $overtime . '</td></tr>'; |
d4961620 |
169 | } |
9c22b42e |
170 | |
171 | /// Show scores (if the user is allowed to see scores at the moment). |
172 | $grade = quiz_rescale_grade($attempt->sumgrades, $quiz); |
15db443c |
173 | if ($options->scores) { |
174 | if ($quiz->grade and $quiz->sumgrades) { |
175 | if($overtime) { |
176 | $result->sumgrades = "0"; |
177 | $result->grade = "0.0"; |
178 | } |
77ed3ba5 |
179 | |
9c22b42e |
180 | /// Show raw marks only if they are different from the grade (like on the view page. |
181 | if ($quiz->grade != $quiz->sumgrades) { |
182 | $a = new stdClass; |
183 | $a->grade = round($attempt->sumgrades, $CFG->quiz_decimalpoints); |
184 | $a->maxgrade = $quiz->sumgrades; |
185 | $rows[] = '<tr><th scope="row" class="cell">' . get_string('marks', 'quiz') . '</th><td class="cell">' . |
186 | get_string('outofshort', 'quiz', $a) . '</td></tr>'; |
187 | } |
188 | |
189 | /// Now the scaled grade. |
3b48ba9d |
190 | $a = new stdClass; |
9c22b42e |
191 | $a->grade = '<b>' . $grade . '</b>'; |
3b48ba9d |
192 | $a->maxgrade = $quiz->grade; |
9c22b42e |
193 | $a->percent = '<b>' . round(($attempt->sumgrades/$quiz->sumgrades)*100, 0) . '</b>'; |
194 | $rows[] = '<tr><th scope="row" class="cell">' . get_string('grade') . '</th><td class="cell">' . |
195 | get_string('outofpercent', 'quiz', $a) . '</td></tr>'; |
d4961620 |
196 | } |
ee1fb969 |
197 | } |
9c22b42e |
198 | |
199 | /// Feedback if there is any, and the user is allowed to see it now. |
200 | $feedback = quiz_feedback_for_grade($grade, $attempt->quiz); |
77ed3ba5 |
201 | if ($options->overallfeedback && $feedback) { |
9c22b42e |
202 | $rows[] = '<tr><th scope="row" class="cell">' . get_string('feedback', 'quiz') . |
203 | '</th><td class="cell">' . $feedback . '</td></tr>'; |
204 | } |
205 | |
206 | /// Now output the summary table, if there are any rows to be shown. |
207 | if (!empty($rows)) { |
208 | echo '<table class="generaltable generalbox quizreviewsummary"><tbody>', "\n"; |
209 | echo implode("\n", $rows); |
210 | echo "\n</tbody></table>\n"; |
9bc2d82a |
211 | } |
212 | |
4fc3d7e5 |
213 | /// Summary table end ============================================================================== |
214 | |
ee1fb969 |
215 | /// Print all the questions |
4fc3d7e5 |
216 | if ($showall) { |
baef998b |
217 | $thispage = 'all'; |
218 | $lastpage = true; |
219 | } else { |
220 | $thispage = $page; |
221 | $lastpage = $attemptobj->is_last_page($page); |
ee1fb969 |
222 | } |
baef998b |
223 | foreach ($attemptobj->get_question_ids($thispage) as $id) { |
4fc3d7e5 |
224 | $attemptobj->print_question($id); |
29d5d0b4 |
225 | } |
226 | |
baef998b |
227 | /// Print a link to the next page. |
228 | echo '<div class="submitbtns">'; |
229 | if ($lastpage) { |
4fc3d7e5 |
230 | $accessmanager->print_finish_review_link($attemptobj->is_preview_user()); |
baef998b |
231 | } else { |
232 | link_arrow_right(get_string('next'), $attemptobj->review_url(0, $page + 1)); |
ee1fb969 |
233 | } |
baef998b |
234 | echo "</div>"; |
235 | |
236 | // End middle column. |
237 | print_container_end(); |
238 | echo '</div>'; |
239 | |
240 | echo '</div>'; |
241 | echo '<div class="clearer"></div>'; |
29d5d0b4 |
242 | |
baef998b |
243 | // Finish the page |
4fc3d7e5 |
244 | if ($accessmanager->securewindow_required($attemptobj->is_preview_user())) { |
c52c62d1 |
245 | print_footer('empty'); |
246 | } else { |
ee1fb969 |
247 | print_footer($course); |
248 | } |
29d5d0b4 |
249 | ?> |