7b727b9d060ce52283869a84aceb087d1f1343d3
[moodle.git] / mod / quiz / review.php
1 <?php
2 /**
3  * This page prints a review of a particular quiz attempt
4  *
5  * @author Martin Dougiamas and many others.
6  * @license http://www.gnu.org/copyleft/gpl.html GNU Public License
7  * @package quiz
8  */
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));
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);
27     $attemptobj = quiz_attempt::create($attemptid);
29 /// Check login.
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');
42         }
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));
46         }
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));
51         }
52     }
54 /// Load the questions and states needed by this page.
55     if ($showall) {
56         $questionids = $attemptobj->get_question_ids();
57     } else {
58         $questionids = $attemptobj->get_question_ids($page);
59     }
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)) {
65         require_sesskey();
66         $formdata = data_submitted();
68         question_save_flags($formdata, $attemptid, $questionids);
69         redirect($attemptobj->review_url(0, $page, $showall));
70     }
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());
76 /// Work out appropriate title and whether blocks should be shown
77     if ($attemptobj->is_preview_user() && $attemptobj->is_own_attempt()) {
78         // Normal blocks
79         $strreviewtitle = get_string('reviewofpreview', 'quiz');
80         navigation_node::override_active_url($attemptobj->start_attempt_url());
82     } else {
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();
87         }
88     }
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();
107     } else {
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();
112     }
114 /// Print heading.
115     if ($attemptobj->is_preview_user() && $attemptobj->is_own_attempt()) {
116         $attemptobj->print_restart_preview_button();
117     }
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();
125     $overtime = 0;
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);
132             }
133             $timetaken = format_time($timetaken);
134         } else {
135             $timetaken = "-";
136         }
137     } else {
138         $timetaken = get_string('unfinished', 'quiz');
139     }
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();
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 . '&amp;course=' . $attemptobj->get_courseid() . '">' .
151                 fullname($student, true) . '</a></td></tr>';
152     }
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>';
158         }
159     }
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>';
169     }
170     if (!empty($overtime)) {
171         $rows[] = '<tr><th scope="row" class="cell">' . get_string('overdue', 'quiz') . '</th><td class="cell">' . $overtime . '</td></tr>';
172     }
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)) {
178             if($overtime) {
179                 $result->sumgrades = "0";
180                 $result->grade = "0.0";
181             }
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;
186                 $a->grade = quiz_format_grade($quiz, $attempt->sumgrades);
187                 $a->maxgrade = quiz_format_grade($quiz, $quiz->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>';
190             }
192         /// Now the scaled grade.
193             $a = new stdClass;
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>';
199         }
200     }
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>';
207     }
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";
214     }
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() . '" />';
223     }
225 /// Print all the questions.
226     if ($showall) {
227         $thispage = 'all';
228         $lastpage = true;
229     } else {
230         $thispage = $page;
231         $lastpage = $attemptobj->is_last_page($page);
232     }
233     foreach ($attemptobj->get_question_ids($thispage) as $id) {
234         $attemptobj->print_question($id, true, $attemptobj->review_url($id, $page, $showall));
235     }
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') . '" />' .
242                 "</div>\n" .
243                 "\n</div></form>\n";
244         $PAGE->requires->js_init_call('M.mod_quiz.init_review_form', null, false, quiz_get_js_module());
245     }
247 /// Print a link to the next page.
248     echo '<div class="submitbtns">';
249     if ($lastpage) {
250         $accessmanager->print_finish_review_link($attemptobj->is_preview_user());
251     } else {
252         echo link_arrow_right(get_string('next'), s($attemptobj->review_url(0, $page + 1)));
253     }
254     echo "</div>";
256     echo $OUTPUT->footer();