MDL-20636 Merge and fix quiz unit tests.
[moodle.git] / mod / quiz / review.php
CommitLineData
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');
73eba4be 80 navigation_node::override_active_url($attemptobj->start_attempt_url());
eb02301a 81
38e179a4 82 } else {
b9b3aa94 83 $strreviewtitle = get_string('reviewofattempt', 'quiz', $attemptobj->get_attempt_number());
56ed242b
SH
84 if (empty($attemptobj->get_quiz()->showblocks) && !$attemptobj->is_preview_user()) {
85 // Only show pretend blocks
86 $PAGE->blocks->show_only_fake_blocks();
87 }
38e179a4 88 }
89
ff065f96
TH
90 // Initialise the JavaScript.
91 $headtags = $attemptobj->get_html_head_contributions($page);
92
93 // Arrange for the navigation to be displayed.
d18675a8 94 $navbc = $attemptobj->get_navigation_panel('quiz_review_nav_panel', $page, $showall);
d4a03c00 95 $firstregion = reset($PAGE->blocks->get_regions());
d9c26e21 96 $PAGE->blocks->add_fake_block($navbc, $firstregion);
d4a03c00 97
c52c62d1 98/// Print the page header
4fc3d7e5 99 $headtags = $attemptobj->get_html_head_contributions($page);
100 if ($accessmanager->securewindow_required($attemptobj->is_preview_user())) {
f330327c 101 $accessmanager->setup_secure_page($attemptobj->get_course()->shortname.': '.format_string($attemptobj->get_quiz_name()), $headtags);
7d4dfc48 102 } elseif ($accessmanager->safebrowser_required($attemptobj->is_preview_user())) {
a09f21d0 103 $PAGE->set_title($attemptobj->get_course()->shortname . ': '.format_string($attemptobj->get_quiz_name()));
56ed242b 104 $PAGE->set_heading($attemptobj->get_course()->fullname);
a09f21d0 105 $PAGE->set_cacheable(false);
106 echo $OUTPUT->header();
ee1fb969 107 } else {
8f37f7fb 108 $PAGE->navbar->add($strreviewtitle);
03da0c39 109 $PAGE->set_title(format_string($attemptobj->get_quiz_name()));
56ed242b 110 $PAGE->set_heading($attemptobj->get_course()->fullname);
03da0c39 111 echo $OUTPUT->header();
ee1fb969 112 }
b7f35820 113
c52c62d1 114/// Print heading.
b55797b8 115 if ($attemptobj->is_preview_user() && $attemptobj->is_own_attempt()) {
78e7a3dd 116 $attemptobj->print_restart_preview_button();
99a1bf3d 117 }
90cd54cb 118 echo $OUTPUT->heading($strreviewtitle);
99a1bf3d 119
4fc3d7e5 120/// Summary table start ============================================================================
121
9c22b42e 122/// Work out some time-related things.
4fc3d7e5 123 $attempt = $attemptobj->get_attempt();
124 $quiz = $attemptobj->get_quiz();
235987c5 125 $overtime = 0;
d4961620 126
ee1fb969 127 if ($attempt->timefinish) {
128 if ($timetaken = ($attempt->timefinish - $attempt->timestart)) {
84e628a0 129 if($quiz->timelimit && $timetaken > ($quiz->timelimit + 60)) {
130 $overtime = $timetaken - $quiz->timelimit;
ee1fb969 131 $overtime = format_time($overtime);
132 }
133 $timetaken = format_time($timetaken);
134 } else {
135 $timetaken = "-";
d4961620 136 }
29d5d0b4 137 } else {
ee1fb969 138 $timetaken = get_string('unfinished', 'quiz');
29d5d0b4 139 }
9c22b42e 140
141/// Print summary table about the whole attempt.
142/// First we assemble all the rows that are appopriate to the current situation in
143/// an array, then later we only output the table if there are any rows to show.
144 $rows = array();
a733c4b9 145 if (!$attemptobj->get_quiz()->showuserpicture && $attemptobj->get_userid() <> $USER->id) {
146 /// If showuserpicture is true, the picture is shown elsewhere, so don't repeat it.
b9b3aa94 147 $student = $DB->get_record('user', array('id' => $attemptobj->get_userid()));
812dbaf7 148 $picture = $OUTPUT->user_picture($student, array('courseid'=>$attemptobj->get_courseid()));
9c22b42e 149 $rows[] = '<tr><th scope="row" class="cell">' . $picture . '</th><td class="cell"><a href="' .
b9b3aa94 150 $CFG->wwwroot . '/user/view.php?id=' . $student->id . '&amp;course=' . $attemptobj->get_courseid() . '">' .
9c22b42e 151 fullname($student, true) . '</a></td></tr>';
76cacec8 152 }
b55797b8 153 if ($attemptobj->has_capability('mod/quiz:viewreports')) {
154 $attemptlist = $attemptobj->links_to_other_attempts($attemptobj->review_url(0, $page, $showall));
155 if ($attemptlist) {
156 $rows[] = '<tr><th scope="row" class="cell">' . get_string('attempts', 'quiz') .
157 '</th><td class="cell">' . $attemptlist . '</td></tr>';
ee1fb969 158 }
ee1fb969 159 }
160
9c22b42e 161/// Timing information.
162 $rows[] = '<tr><th scope="row" class="cell">' . get_string('startedon', 'quiz') .
163 '</th><td class="cell">' . userdate($attempt->timestart) . '</td></tr>';
ee1fb969 164 if ($attempt->timefinish) {
9c22b42e 165 $rows[] = '<tr><th scope="row" class="cell">' . get_string('completedon', 'quiz') . '</th><td class="cell">' .
166 userdate($attempt->timefinish) . '</td></tr>';
167 $rows[] = '<tr><th scope="row" class="cell">' . get_string('timetaken', 'quiz') . '</th><td class="cell">' .
168 $timetaken . '</td></tr>';
ee1fb969 169 }
3a00dbfd 170 if (!empty($overtime)) {
9c22b42e 171 $rows[] = '<tr><th scope="row" class="cell">' . get_string('overdue', 'quiz') . '</th><td class="cell">' . $overtime . '</td></tr>';
d4961620 172 }
9c22b42e 173
174/// Show scores (if the user is allowed to see scores at the moment).
2b38499d 175 $grade = quiz_rescale_grade($attempt->sumgrades, $quiz, false);
15db443c 176 if ($options->scores) {
739b0711 177 if (quiz_has_grades($quiz)) {
15db443c 178 if($overtime) {
179 $result->sumgrades = "0";
180 $result->grade = "0.0";
181 }
77ed3ba5 182
9c22b42e 183 /// Show raw marks only if they are different from the grade (like on the view page.
184 if ($quiz->grade != $quiz->sumgrades) {
185 $a = new stdClass;
f88fb62c 186 $a->grade = quiz_format_grade($quiz, $attempt->sumgrades);
6382910a 187 $a->maxgrade = quiz_format_grade($quiz, $quiz->sumgrades);
9c22b42e 188 $rows[] = '<tr><th scope="row" class="cell">' . get_string('marks', 'quiz') . '</th><td class="cell">' .
189 get_string('outofshort', 'quiz', $a) . '</td></tr>';
190 }
191
192 /// Now the scaled grade.
3b48ba9d 193 $a = new stdClass;
2b38499d 194 $a->grade = '<b>' . quiz_format_grade($quiz, $grade) . '</b>';
f88fb62c 195 $a->maxgrade = quiz_format_grade($quiz, $quiz->grade);
9c22b42e 196 $a->percent = '<b>' . round(($attempt->sumgrades/$quiz->sumgrades)*100, 0) . '</b>';
197 $rows[] = '<tr><th scope="row" class="cell">' . get_string('grade') . '</th><td class="cell">' .
198 get_string('outofpercent', 'quiz', $a) . '</td></tr>';
d4961620 199 }
ee1fb969 200 }
9c22b42e 201
202/// Feedback if there is any, and the user is allowed to see it now.
fe6ce234 203 $feedback = $attemptobj->get_overall_feedback($grade);
77ed3ba5 204 if ($options->overallfeedback && $feedback) {
9c22b42e 205 $rows[] = '<tr><th scope="row" class="cell">' . get_string('feedback', 'quiz') .
206 '</th><td class="cell">' . $feedback . '</td></tr>';
207 }
208
209/// Now output the summary table, if there are any rows to be shown.
210 if (!empty($rows)) {
211 echo '<table class="generaltable generalbox quizreviewsummary"><tbody>', "\n";
212 echo implode("\n", $rows);
213 echo "\n</tbody></table>\n";
9bc2d82a 214 }
215
4fc3d7e5 216/// Summary table end ==============================================================================
217
f62040ed 218/// Form for saving flags if necessary.
219 if ($options->flags == QUESTION_FLAGSEDITABLE) {
d4ad9adf 220 echo '<form action="' . s($attemptobj->review_url(0, $page, $showall)) .
ff065f96 221 '" method="post" class="questionflagsaveform"><div>';
f62040ed 222 echo '<input type="hidden" name="sesskey" value="' . sesskey() . '" />';
223 }
224
225/// Print all the questions.
4fc3d7e5 226 if ($showall) {
baef998b 227 $thispage = 'all';
228 $lastpage = true;
229 } else {
230 $thispage = $page;
231 $lastpage = $attemptobj->is_last_page($page);
ee1fb969 232 }
baef998b 233 foreach ($attemptobj->get_question_ids($thispage) as $id) {
aafdb447 234 $attemptobj->print_question($id, true, $attemptobj->review_url($id, $page, $showall));
29d5d0b4 235 }
236
f62040ed 237/// Close form if we opened it.
238 if ($options->flags == QUESTION_FLAGSEDITABLE) {
239 echo '<div class="submitbtns">' . "\n" .
ff065f96 240 '<input type="submit" class="questionflagsavebutton" name="savingflags" value="' .
f62040ed 241 get_string('saveflags', 'question') . '" />' .
242 "</div>\n" .
29305001 243 "\n</div></form>\n";
ff065f96 244 $PAGE->requires->js_init_call('M.mod_quiz.init_review_form', null, false, quiz_get_js_module());
f62040ed 245 }
246
baef998b 247/// Print a link to the next page.
248 echo '<div class="submitbtns">';
249 if ($lastpage) {
4fc3d7e5 250 $accessmanager->print_finish_review_link($attemptobj->is_preview_user());
baef998b 251 } else {
d18675a8 252 echo link_arrow_right(get_string('next'), s($attemptobj->review_url(0, $page + 1)));
ee1fb969 253 }
baef998b 254 echo "</div>";
255
867847e3 256 echo $OUTPUT->footer();
83192608 257