3 * This page prints a review of a particular quiz attempt
5 * @author Martin Dougiamas and many others.
6 * @license http://www.gnu.org/copyleft/gpl.html GNU Public License
10 require_once(dirname(__FILE__) . '/../../config.php');
11 require_once($CFG->dirroot . '/mod/quiz/locallib.php');
12 require_once($CFG->dirroot . '/mod/quiz/report/reportlib.php');
14 $attemptid = required_param('attempt', PARAM_INT);
15 $page = optional_param('page', 0, PARAM_INT);
16 $showall = optional_param('showall', 0, PARAM_BOOL);
18 $url = new moodle_url('/mod/quiz/review.php', array('attempt'=>$attemptid));
20 $url->param('page', $page);
23 $url->param('showall', $showall);
27 $attemptobj = quiz_attempt::create($attemptid);
30 require_login($attemptobj->get_course(), false, $attemptobj->get_cm());
31 $attemptobj->check_review_capability();
33 /// Create an object to manage all the other (non-roles) access rules.
34 $accessmanager = $attemptobj->get_access_manager(time());
35 $options = $attemptobj->get_review_options();
37 /// Permissions checks for normal users who do not have quiz:viewreports capability.
38 if (!$attemptobj->has_capability('mod/quiz:viewreports')) {
39 /// Can't review other users' attempts.
40 if (!$attemptobj->is_own_attempt()) {
41 quiz_error($attemptobj->get_quiz(), 'notyourattempt');
43 /// Can't review during the attempt - send them back to the attempt page.
44 if (!$attemptobj->is_finished()) {
45 redirect($attemptobj->attempt_url(0, $page));
47 /// Can't review unless Students may review -> Responses option is turned on.
48 if (!$options->responses) {
49 $accessmanager->back_to_view_page($attemptobj->is_preview_user(),
50 $accessmanager->cannot_review_message($options));
54 /// Load the questions and states needed by this page.
56 $questionids = $attemptobj->get_question_ids();
58 $questionids = $attemptobj->get_question_ids($page);
60 $attemptobj->load_questions($questionids);
61 $attemptobj->load_question_states($questionids);
63 /// Save the flag states, if they are being changed.
64 if ($options->flags == QUESTION_FLAGSEDITABLE && optional_param('savingflags', false, PARAM_BOOL)) {
66 $formdata = data_submitted();
68 question_save_flags($formdata, $attemptid, $questionids);
69 redirect($attemptobj->review_url(0, $page, $showall));
73 add_to_log($attemptobj->get_courseid(), 'quiz', 'review', 'review.php?attempt=' .
74 $attemptobj->get_attemptid(), $attemptobj->get_quizid(), $attemptobj->get_cmid());
76 /// Work out appropriate title and whether blocks should be shown
77 if ($attemptobj->is_preview_user() && $attemptobj->is_own_attempt()) {
79 $strreviewtitle = get_string('reviewofpreview', 'quiz');
80 navigation_node::override_active_url($attemptobj->start_attempt_url());
83 $strreviewtitle = get_string('reviewofattempt', 'quiz', $attemptobj->get_attempt_number());
84 if (empty($attemptobj->get_quiz()->showblocks) && !$attemptobj->is_preview_user()) {
85 // Only show pretend blocks
86 $PAGE->blocks->show_only_fake_blocks();
90 // Initialise the JavaScript.
91 $headtags = $attemptobj->get_html_head_contributions($page);
93 // Arrange for the navigation to be displayed.
94 $navbc = $attemptobj->get_navigation_panel('quiz_review_nav_panel', $page, $showall);
95 $firstregion = reset($PAGE->blocks->get_regions());
96 $PAGE->blocks->add_pretend_block($navbc, $firstregion);
98 /// Print the page header
99 $headtags = $attemptobj->get_html_head_contributions($page);
100 if ($accessmanager->securewindow_required($attemptobj->is_preview_user())) {
101 $accessmanager->setup_secure_page($attemptobj->get_course()->shortname.': '.format_string($attemptobj->get_quiz_name()), $headtags);
102 } elseif ($accessmanager->safebrowser_required($attemptobj->is_preview_user())) {
103 $PAGE->set_title($attemptobj->get_course()->shortname . ': '.format_string($attemptobj->get_quiz_name()));
104 $PAGE->set_heading($attemptobj->get_course()->fullname);
105 $PAGE->set_cacheable(false);
106 echo $OUTPUT->header();
108 $attemptobj->navigation($strreviewtitle);
109 $PAGE->set_title(format_string($attemptobj->get_quiz_name()));
110 $PAGE->set_heading($attemptobj->get_course()->fullname);
111 echo $OUTPUT->header();
115 if ($attemptobj->is_preview_user() && $attemptobj->is_own_attempt()) {
116 $attemptobj->print_restart_preview_button();
118 echo $OUTPUT->heading($strreviewtitle);
120 /// Summary table start ============================================================================
122 /// Work out some time-related things.
123 $attempt = $attemptobj->get_attempt();
124 $quiz = $attemptobj->get_quiz();
127 if ($attempt->timefinish) {
128 if ($timetaken = ($attempt->timefinish - $attempt->timestart)) {
129 if($quiz->timelimit && $timetaken > ($quiz->timelimit + 60)) {
130 $overtime = $timetaken - $quiz->timelimit;
131 $overtime = format_time($overtime);
133 $timetaken = format_time($timetaken);
138 $timetaken = get_string('unfinished', 'quiz');
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.
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.
147 $student = $DB->get_record('user', array('id' => $attemptobj->get_userid()));
148 $picture = $OUTPUT->user_picture($student, array('courseid'=>$attemptobj->get_courseid()));
149 $rows[] = '<tr><th scope="row" class="cell">' . $picture . '</th><td class="cell"><a href="' .
150 $CFG->wwwroot . '/user/view.php?id=' . $student->id . '&course=' . $attemptobj->get_courseid() . '">' .
151 fullname($student, true) . '</a></td></tr>';
153 if ($attemptobj->has_capability('mod/quiz:viewreports')) {
154 $attemptlist = $attemptobj->links_to_other_attempts($attemptobj->review_url(0, $page, $showall));
156 $rows[] = '<tr><th scope="row" class="cell">' . get_string('attempts', 'quiz') .
157 '</th><td class="cell">' . $attemptlist . '</td></tr>';
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>';
164 if ($attempt->timefinish) {
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>';
170 if (!empty($overtime)) {
171 $rows[] = '<tr><th scope="row" class="cell">' . get_string('overdue', 'quiz') . '</th><td class="cell">' . $overtime . '</td></tr>';
174 /// Show scores (if the user is allowed to see scores at the moment).
175 $grade = quiz_rescale_grade($attempt->sumgrades, $quiz, false);
176 if ($options->scores) {
177 if (quiz_has_grades($quiz)) {
179 $result->sumgrades = "0";
180 $result->grade = "0.0";
183 /// Show raw marks only if they are different from the grade (like on the view page.
184 if ($quiz->grade != $quiz->sumgrades) {
186 $a->grade = quiz_format_grade($quiz, $attempt->sumgrades);
187 $a->maxgrade = quiz_format_grade($quiz, $attempt->sumgrades);
188 $rows[] = '<tr><th scope="row" class="cell">' . get_string('marks', 'quiz') . '</th><td class="cell">' .
189 get_string('outofshort', 'quiz', $a) . '</td></tr>';
192 /// Now the scaled grade.
194 $a->grade = '<b>' . quiz_format_grade($quiz, $grade) . '</b>';
195 $a->maxgrade = quiz_format_grade($quiz, $quiz->grade);
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>';
202 /// Feedback if there is any, and the user is allowed to see it now.
203 $feedback = $attemptobj->get_overall_feedback($grade);
204 if ($options->overallfeedback && $feedback) {
205 $rows[] = '<tr><th scope="row" class="cell">' . get_string('feedback', 'quiz') .
206 '</th><td class="cell">' . $feedback . '</td></tr>';
209 /// Now output the summary table, if there are any rows to be shown.
211 echo '<table class="generaltable generalbox quizreviewsummary"><tbody>', "\n";
212 echo implode("\n", $rows);
213 echo "\n</tbody></table>\n";
216 /// Summary table end ==============================================================================
218 /// Form for saving flags if necessary.
219 if ($options->flags == QUESTION_FLAGSEDITABLE) {
220 echo '<form action="' . s($attemptobj->review_url(0, $page, $showall)) .
221 '" method="post" class="questionflagsaveform"><div>';
222 echo '<input type="hidden" name="sesskey" value="' . sesskey() . '" />';
225 /// Print all the questions.
231 $lastpage = $attemptobj->is_last_page($page);
233 foreach ($attemptobj->get_question_ids($thispage) as $id) {
234 $attemptobj->print_question($id, true, $attemptobj->review_url($id, $page, $showall));
237 /// Close form if we opened it.
238 if ($options->flags == QUESTION_FLAGSEDITABLE) {
239 echo '<div class="submitbtns">' . "\n" .
240 '<input type="submit" class="questionflagsavebutton" name="savingflags" value="' .
241 get_string('saveflags', 'question') . '" />' .
244 $PAGE->requires->js_init_call('M.mod_quiz.init_review_form', null, false, quiz_get_js_module());
247 /// Print a link to the next page.
248 echo '<div class="submitbtns">';
250 $accessmanager->print_finish_review_link($attemptobj->is_preview_user());
252 echo link_arrow_right(get_string('next'), s($attemptobj->review_url(0, $page + 1)));
256 echo $OUTPUT->footer();