Commit | Line | Data |
---|---|---|
83192608 | 1 | <?php |
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'); |
bbf4f440 | 12 | require_once($CFG->dirroot . '/mod/quiz/report/reportlib.php'); |
29d5d0b4 | 13 | |
4fc3d7e5 | 14 | $attemptid = required_param('attempt', PARAM_INT); |
15 | $page = optional_param('page', 0, PARAM_INT); | |
ee1fb969 | 16 | $showall = optional_param('showall', 0, PARAM_BOOL); |
29d5d0b4 | 17 | |
a6855934 | 18 | $url = new moodle_url('/mod/quiz/review.php', array('attempt'=>$attemptid)); |
55f599f0 | 19 | if ($page !== 0) { |
20 | $url->param('page', $page); | |
21 | } | |
22 | if ($showall !== 0) { | |
23 | $url->param('showall', $showall); | |
24 | } | |
25 | $PAGE->set_url($url); | |
26 | ||
990650f9 | 27 | $attemptobj = quiz_attempt::create($attemptid); |
4fc3d7e5 | 28 | |
29 | /// Check login. | |
56ed242b | 30 | require_login($attemptobj->get_course(), false, $attemptobj->get_cm()); |
96c7d771 | 31 | $attemptobj->check_review_capability(); |
77ed3ba5 | 32 | |
c52c62d1 | 33 | /// Create an object to manage all the other (non-roles) access rules. |
4fc3d7e5 | 34 | $accessmanager = $attemptobj->get_access_manager(time()); |
35 | $options = $attemptobj->get_review_options(); | |
c52c62d1 | 36 | |
c52c62d1 | 37 | /// Permissions checks for normal users who do not have quiz:viewreports capability. |
4fc3d7e5 | 38 | if (!$attemptobj->has_capability('mod/quiz:viewreports')) { |
82fd2e8b | 39 | /// Can't review other users' attempts. |
40 | if (!$attemptobj->is_own_attempt()) { | |
41 | quiz_error($attemptobj->get_quiz(), 'notyourattempt'); | |
42 | } | |
c52c62d1 | 43 | /// Can't review during the attempt - send them back to the attempt page. |
4fc3d7e5 | 44 | if (!$attemptobj->is_finished()) { |
45 | redirect($attemptobj->attempt_url(0, $page)); | |
29d5d0b4 | 46 | } |
c52c62d1 | 47 | /// Can't review unless Students may review -> Responses option is turned on. |
a4faed69 | 48 | if (!$options->responses) { |
4fc3d7e5 | 49 | $accessmanager->back_to_view_page($attemptobj->is_preview_user(), |
c52c62d1 | 50 | $accessmanager->cannot_review_message($options)); |
34283aa8 | 51 | } |
29d5d0b4 | 52 | } |
53 | ||
f62040ed | 54 | /// Load the questions and states needed by this page. |
c52c62d1 | 55 | if ($showall) { |
4fc3d7e5 | 56 | $questionids = $attemptobj->get_question_ids(); |
c52c62d1 | 57 | } else { |
4fc3d7e5 | 58 | $questionids = $attemptobj->get_question_ids($page); |
83192608 | 59 | } |
4fc3d7e5 | 60 | $attemptobj->load_questions($questionids); |
61 | $attemptobj->load_question_states($questionids); | |
2d05b472 | 62 | |
f62040ed | 63 | /// Save the flag states, if they are being changed. |
64 | if ($options->flags == QUESTION_FLAGSEDITABLE && optional_param('savingflags', false, PARAM_BOOL)) { | |
82165a0c | 65 | require_sesskey(); |
f62040ed | 66 | $formdata = data_submitted(); |
67 | ||
68 | question_save_flags($formdata, $attemptid, $questionids); | |
69 | redirect($attemptobj->review_url(0, $page, $showall)); | |
70 | } | |
71 | ||
b55797b8 | 72 | /// Log this review. |
73 | add_to_log($attemptobj->get_courseid(), 'quiz', 'review', 'review.php?attempt=' . | |
74 | $attemptobj->get_attemptid(), $attemptobj->get_quizid(), $attemptobj->get_cmid()); | |
75 | ||
56ed242b | 76 | /// Work out appropriate title and whether blocks should be shown |
b55797b8 | 77 | if ($attemptobj->is_preview_user() && $attemptobj->is_own_attempt()) { |
56ed242b | 78 | // Normal blocks |
38e179a4 | 79 | $strreviewtitle = get_string('reviewofpreview', 'quiz'); |
80 | } else { | |
b9b3aa94 | 81 | $strreviewtitle = get_string('reviewofattempt', 'quiz', $attemptobj->get_attempt_number()); |
56ed242b SH |
82 | if (empty($attemptobj->get_quiz()->showblocks) && !$attemptobj->is_preview_user()) { |
83 | // Only show pretend blocks | |
84 | $PAGE->blocks->show_only_fake_blocks(); | |
85 | } | |
38e179a4 | 86 | } |
87 | ||
d4a03c00 | 88 | /// Arrange for the navigation to be displayed. |
d18675a8 | 89 | $navbc = $attemptobj->get_navigation_panel('quiz_review_nav_panel', $page, $showall); |
d4a03c00 | 90 | $firstregion = reset($PAGE->blocks->get_regions()); |
91 | $PAGE->blocks->add_pretend_block($navbc, $firstregion); | |
92 | ||
c52c62d1 | 93 | /// Print the page header |
4fc3d7e5 | 94 | $headtags = $attemptobj->get_html_head_contributions($page); |
95 | if ($accessmanager->securewindow_required($attemptobj->is_preview_user())) { | |
f330327c | 96 | $accessmanager->setup_secure_page($attemptobj->get_course()->shortname.': '.format_string($attemptobj->get_quiz_name()), $headtags); |
7d4dfc48 | 97 | } elseif ($accessmanager->safebrowser_required($attemptobj->is_preview_user())) { |
a09f21d0 | 98 | $PAGE->set_title($attemptobj->get_course()->shortname . ': '.format_string($attemptobj->get_quiz_name())); |
56ed242b | 99 | $PAGE->set_heading($attemptobj->get_course()->fullname); |
a09f21d0 | 100 | $PAGE->set_cacheable(false); |
101 | echo $OUTPUT->header(); | |
ee1fb969 | 102 | } else { |
03da0c39 | 103 | $attemptobj->navigation($strreviewtitle); |
104 | $PAGE->set_title(format_string($attemptobj->get_quiz_name())); | |
56ed242b | 105 | $PAGE->set_heading($attemptobj->get_course()->fullname); |
03da0c39 | 106 | echo $OUTPUT->header(); |
ee1fb969 | 107 | } |
b7f35820 | 108 | |
c52c62d1 | 109 | /// Print heading. |
b55797b8 | 110 | if ($attemptobj->is_preview_user() && $attemptobj->is_own_attempt()) { |
78e7a3dd | 111 | $attemptobj->print_restart_preview_button(); |
99a1bf3d | 112 | } |
90cd54cb | 113 | echo $OUTPUT->heading($strreviewtitle); |
99a1bf3d | 114 | |
4fc3d7e5 | 115 | /// Summary table start ============================================================================ |
116 | ||
9c22b42e | 117 | /// Work out some time-related things. |
4fc3d7e5 | 118 | $attempt = $attemptobj->get_attempt(); |
119 | $quiz = $attemptobj->get_quiz(); | |
235987c5 | 120 | $overtime = 0; |
d4961620 | 121 | |
ee1fb969 | 122 | if ($attempt->timefinish) { |
123 | if ($timetaken = ($attempt->timefinish - $attempt->timestart)) { | |
84e628a0 | 124 | if($quiz->timelimit && $timetaken > ($quiz->timelimit + 60)) { |
125 | $overtime = $timetaken - $quiz->timelimit; | |
ee1fb969 | 126 | $overtime = format_time($overtime); |
127 | } | |
128 | $timetaken = format_time($timetaken); | |
129 | } else { | |
130 | $timetaken = "-"; | |
d4961620 | 131 | } |
29d5d0b4 | 132 | } else { |
ee1fb969 | 133 | $timetaken = get_string('unfinished', 'quiz'); |
29d5d0b4 | 134 | } |
9c22b42e | 135 | |
136 | /// Print summary table about the whole attempt. | |
137 | /// First we assemble all the rows that are appopriate to the current situation in | |
138 | /// an array, then later we only output the table if there are any rows to show. | |
139 | $rows = array(); | |
a733c4b9 | 140 | if (!$attemptobj->get_quiz()->showuserpicture && $attemptobj->get_userid() <> $USER->id) { |
141 | /// If showuserpicture is true, the picture is shown elsewhere, so don't repeat it. | |
b9b3aa94 | 142 | $student = $DB->get_record('user', array('id' => $attemptobj->get_userid())); |
812dbaf7 | 143 | $picture = $OUTPUT->user_picture($student, array('courseid'=>$attemptobj->get_courseid())); |
9c22b42e | 144 | $rows[] = '<tr><th scope="row" class="cell">' . $picture . '</th><td class="cell"><a href="' . |
b9b3aa94 | 145 | $CFG->wwwroot . '/user/view.php?id=' . $student->id . '&course=' . $attemptobj->get_courseid() . '">' . |
9c22b42e | 146 | fullname($student, true) . '</a></td></tr>'; |
76cacec8 | 147 | } |
b55797b8 | 148 | if ($attemptobj->has_capability('mod/quiz:viewreports')) { |
149 | $attemptlist = $attemptobj->links_to_other_attempts($attemptobj->review_url(0, $page, $showall)); | |
150 | if ($attemptlist) { | |
151 | $rows[] = '<tr><th scope="row" class="cell">' . get_string('attempts', 'quiz') . | |
152 | '</th><td class="cell">' . $attemptlist . '</td></tr>'; | |
ee1fb969 | 153 | } |
ee1fb969 | 154 | } |
155 | ||
9c22b42e | 156 | /// Timing information. |
157 | $rows[] = '<tr><th scope="row" class="cell">' . get_string('startedon', 'quiz') . | |
158 | '</th><td class="cell">' . userdate($attempt->timestart) . '</td></tr>'; | |
ee1fb969 | 159 | if ($attempt->timefinish) { |
9c22b42e | 160 | $rows[] = '<tr><th scope="row" class="cell">' . get_string('completedon', 'quiz') . '</th><td class="cell">' . |
161 | userdate($attempt->timefinish) . '</td></tr>'; | |
162 | $rows[] = '<tr><th scope="row" class="cell">' . get_string('timetaken', 'quiz') . '</th><td class="cell">' . | |
163 | $timetaken . '</td></tr>'; | |
ee1fb969 | 164 | } |
3a00dbfd | 165 | if (!empty($overtime)) { |
9c22b42e | 166 | $rows[] = '<tr><th scope="row" class="cell">' . get_string('overdue', 'quiz') . '</th><td class="cell">' . $overtime . '</td></tr>'; |
d4961620 | 167 | } |
9c22b42e | 168 | |
169 | /// Show scores (if the user is allowed to see scores at the moment). | |
2b38499d | 170 | $grade = quiz_rescale_grade($attempt->sumgrades, $quiz, false); |
15db443c | 171 | if ($options->scores) { |
739b0711 | 172 | if (quiz_has_grades($quiz)) { |
15db443c | 173 | if($overtime) { |
174 | $result->sumgrades = "0"; | |
175 | $result->grade = "0.0"; | |
176 | } | |
77ed3ba5 | 177 | |
9c22b42e | 178 | /// Show raw marks only if they are different from the grade (like on the view page. |
179 | if ($quiz->grade != $quiz->sumgrades) { | |
180 | $a = new stdClass; | |
f88fb62c | 181 | $a->grade = quiz_format_grade($quiz, $attempt->sumgrades); |
182 | $a->maxgrade = quiz_format_grade($quiz, $attempt->sumgrades); | |
9c22b42e | 183 | $rows[] = '<tr><th scope="row" class="cell">' . get_string('marks', 'quiz') . '</th><td class="cell">' . |
184 | get_string('outofshort', 'quiz', $a) . '</td></tr>'; | |
185 | } | |
186 | ||
187 | /// Now the scaled grade. | |
3b48ba9d | 188 | $a = new stdClass; |
2b38499d | 189 | $a->grade = '<b>' . quiz_format_grade($quiz, $grade) . '</b>'; |
f88fb62c | 190 | $a->maxgrade = quiz_format_grade($quiz, $quiz->grade); |
9c22b42e | 191 | $a->percent = '<b>' . round(($attempt->sumgrades/$quiz->sumgrades)*100, 0) . '</b>'; |
192 | $rows[] = '<tr><th scope="row" class="cell">' . get_string('grade') . '</th><td class="cell">' . | |
193 | get_string('outofpercent', 'quiz', $a) . '</td></tr>'; | |
d4961620 | 194 | } |
ee1fb969 | 195 | } |
9c22b42e | 196 | |
197 | /// Feedback if there is any, and the user is allowed to see it now. | |
198 | $feedback = quiz_feedback_for_grade($grade, $attempt->quiz); | |
77ed3ba5 | 199 | if ($options->overallfeedback && $feedback) { |
9c22b42e | 200 | $rows[] = '<tr><th scope="row" class="cell">' . get_string('feedback', 'quiz') . |
201 | '</th><td class="cell">' . $feedback . '</td></tr>'; | |
202 | } | |
203 | ||
204 | /// Now output the summary table, if there are any rows to be shown. | |
205 | if (!empty($rows)) { | |
206 | echo '<table class="generaltable generalbox quizreviewsummary"><tbody>', "\n"; | |
207 | echo implode("\n", $rows); | |
208 | echo "\n</tbody></table>\n"; | |
9bc2d82a | 209 | } |
210 | ||
4fc3d7e5 | 211 | /// Summary table end ============================================================================== |
212 | ||
f62040ed | 213 | /// Form for saving flags if necessary. |
214 | if ($options->flags == QUESTION_FLAGSEDITABLE) { | |
d4ad9adf | 215 | echo '<form action="' . s($attemptobj->review_url(0, $page, $showall)) . |
f62040ed | 216 | '" method="post"><div>'; |
217 | echo '<input type="hidden" name="sesskey" value="' . sesskey() . '" />'; | |
218 | } | |
219 | ||
220 | /// Print all the questions. | |
4fc3d7e5 | 221 | if ($showall) { |
baef998b | 222 | $thispage = 'all'; |
223 | $lastpage = true; | |
224 | } else { | |
225 | $thispage = $page; | |
226 | $lastpage = $attemptobj->is_last_page($page); | |
ee1fb969 | 227 | } |
baef998b | 228 | foreach ($attemptobj->get_question_ids($thispage) as $id) { |
aafdb447 | 229 | $attemptobj->print_question($id, true, $attemptobj->review_url($id, $page, $showall)); |
29d5d0b4 | 230 | } |
231 | ||
f62040ed | 232 | /// Close form if we opened it. |
233 | if ($options->flags == QUESTION_FLAGSEDITABLE) { | |
234 | echo '<div class="submitbtns">' . "\n" . | |
235 | '<input type="submit" id="savingflagssubmit" name="savingflags" value="' . | |
236 | get_string('saveflags', 'question') . '" />' . | |
237 | "</div>\n" . | |
29305001 | 238 | "\n</div></form>\n"; |
cf615522 | 239 | $PAGE->requires->js_function_call('question_flag_changer.init_flag_save_form', array('savingflagssubmit')); |
f62040ed | 240 | } |
241 | ||
baef998b | 242 | /// Print a link to the next page. |
243 | echo '<div class="submitbtns">'; | |
244 | if ($lastpage) { | |
4fc3d7e5 | 245 | $accessmanager->print_finish_review_link($attemptobj->is_preview_user()); |
baef998b | 246 | } else { |
d18675a8 | 247 | echo link_arrow_right(get_string('next'), s($attemptobj->review_url(0, $page + 1))); |
ee1fb969 | 248 | } |
baef998b | 249 | echo "</div>"; |
250 | ||
867847e3 | 251 | echo $OUTPUT->footer(); |
83192608 | 252 |