mod_quiz MDL-26178 it let you try to preview a quiz with several empty pages.
[moodle.git] / mod / quiz / view.php
CommitLineData
83192608 1<?php
730fd187 2
c52c62d1 3/// This page prints a particular instance of quiz
730fd187 4
1aab3561 5 require_once(dirname(__FILE__) . '/../../config.php');
14e66f3b 6 require_once($CFG->libdir.'/gradelib.php');
7 require_once($CFG->dirroot.'/mod/quiz/locallib.php');
4e1132a8 8 require_once($CFG->libdir . '/completionlib.php');
14e66f3b 9
c52c62d1 10 $id = optional_param('id', 0, PARAM_INT); // Course Module ID, or
11 $q = optional_param('q', 0, PARAM_INT); // quiz ID
cd06115f 12
730fd187 13 if ($id) {
f9d5371b 14 if (! $cm = get_coursemodule_from_id('quiz', $id)) {
550f9654 15 print_error('invalidcoursemodule');
730fd187 16 }
9cf4a18b 17 if (! $course = $DB->get_record('course', array('id' => $cm->course))) {
550f9654 18 print_error('coursemisconf');
730fd187 19 }
9cf4a18b 20 if (! $quiz = $DB->get_record('quiz', array('id' => $cm->instance))) {
550f9654 21 print_error('invalidcoursemodule');
730fd187 22 }
730fd187 23 } else {
9cf4a18b 24 if (! $quiz = $DB->get_record('quiz', array('id' => $q))) {
550f9654 25 print_error('invalidquizid', 'quiz');
730fd187 26 }
9cf4a18b 27 if (! $course = $DB->get_record('course', array('id' => $quiz->course))) {
550f9654 28 print_error('invalidcourseid');
730fd187 29 }
30 if (! $cm = get_coursemodule_from_instance("quiz", $quiz->id, $course->id)) {
550f9654 31 print_error('invalidcoursemodule');
730fd187 32 }
33 }
1751eebc 34
c52c62d1 35/// Check login and get context.
ec81373f 36 require_login($course->id, false, $cm);
cd06115f 37 $context = get_context_instance(CONTEXT_MODULE, $cm->id);
c52c62d1 38 require_capability('mod/quiz:view', $context);
39
eab8ed9f 40/// Cache some other capabilities we use several times.
c52c62d1 41 $canattempt = has_capability('mod/quiz:attempt', $context);
96c7d771 42 $canreviewmine = has_capability('mod/quiz:reviewmyattempts', $context);
c52c62d1 43 $canpreview = has_capability('mod/quiz:preview', $context);
ee1fb969 44
c52c62d1 45/// Create an object to manage all the other (non-roles) access rules.
46 $timenow = time();
990650f9 47 $accessmanager = new quiz_access_manager(quiz::create($quiz->id, $USER->id), $timenow,
c52c62d1 48 has_capability('mod/quiz:ignoretimelimits', $context, NULL, false));
49
50/// If no questions have been set up yet redirect to edit.php
51 if (!$quiz->questions && has_capability('mod/quiz:manage', $context)) {
44847eb8 52 redirect($CFG->wwwroot . '/mod/quiz/edit.php?cmid=' . $cm->id);
34283aa8 53 }
730fd187 54
c52c62d1 55/// Log this request.
037f99aa 56 add_to_log($course->id, "quiz", "view", "view.php?id=$cm->id", $quiz->id, $cm->id);
730fd187 57
c52c62d1 58/// Initialize $PAGE, compute blocks
a6855934 59 $PAGE->set_url('/mod/quiz/view.php', array('id' => $cm->id));
36658798 60
c52c62d1 61 $edit = optional_param('edit', -1, PARAM_BOOL);
62 if ($edit != -1 && $PAGE->user_allowed_editing()) {
d2b23346 63 $USER->editing = $edit;
bdfa14dd 64 }
65
f44b10ed 66 $PAGE->requires->yui2_lib('event');
6bf44482 67
b7b2d0f3 68 // Note: MDL-19010 there will be further changes to printing header and blocks.
69 // The code will be much nicer than this eventually.
6bf44482 70 $title = $course->shortname . ': ' . format_string($quiz->name);
71
59e2121e 72 if ($PAGE->user_allowed_editing()) {
4aea3cc7 73 $buttons = '<table><tr><td><form method="get" action="view.php"><div>'.
6bf44482 74 '<input type="hidden" name="id" value="'.$cm->id.'" />'.
75 '<input type="hidden" name="edit" value="'.($PAGE->user_is_editing()?'off':'on').'" />'.
92059c7e
SH
76 '<input type="submit" value="'.get_string($PAGE->user_is_editing()?'blockseditoff':'blocksediton').'" /></div></form></td></tr></table>';
77 $PAGE->set_button($buttons);
6bf44482 78 }
6bf44482 79
2698e9c1 80 $PAGE->set_title($title);
81 $PAGE->set_heading($course->fullname);
990650f9 82
2698e9c1 83 echo $OUTPUT->header();
579ddad5 84
c52c62d1 85/// Print quiz name and description
90cd54cb 86 echo $OUTPUT->heading(format_string($quiz->name));
c52c62d1 87 if (trim(strip_tags($quiz->intro))) {
3b1d5cc4 88 echo $OUTPUT->box(format_module_intro('quiz', $quiz, $cm->id), 'generalbox', 'intro');
c52c62d1 89 }
36658798 90
c52c62d1 91/// Display information about this quiz.
92 $messages = $accessmanager->describe_rules();
93 if ($quiz->attempts != 1) {
94 $messages[] = get_string('gradingmethod', 'quiz', quiz_get_grading_option_name($quiz->grademethod));
bb6f1955 95 }
3b1d5cc4 96 echo $OUTPUT->box_start('quizinfo');
c52c62d1 97 $accessmanager->print_messages($messages);
3b1d5cc4 98 echo $OUTPUT->box_end();
36658798 99
c52c62d1 100/// Show number of attempts summary to those who can view reports.
cd06115f 101 if (has_capability('mod/quiz:viewreports', $context)) {
a49cb927
TH
102 if ($strattemptnum = quiz_attempt_summary_link_to_reports($quiz, $cm, $context)) {
103 echo '<div class="quizattemptcounts">' . $strattemptnum . "</div>\n";
fc44ee0d 104 }
ee1fb969 105 }
106
c52c62d1 107/// Guests can't do a quiz, so offer them a choice of logging in or going back.
645dff1c 108 if (isguestuser()) {
39e37019 109 echo $OUTPUT->confirm('<p>' . get_string('guestsno', 'quiz') . "</p>\n\n<p>" .
93f66983 110 get_string('liketologin') . "</p>\n", get_login_url(), get_referer(false));
867847e3 111 echo $OUTPUT->footer();
d4a03c00 112 exit;
1751eebc 113 }
114
96c7d771 115/// If they are not enrolled in this course in a good enough role, tell them to enrol.
116 if (!($canattempt || $canpreview || $canreviewmine)) {
3b1d5cc4 117 echo $OUTPUT->box('<p>' . get_string('youneedtoenrol', 'quiz') . "</p>\n\n<p>" .
825116fb 118 $OUTPUT->continue_button($CFG->wwwroot . '/course/view.php?id=' . $course->id) .
c52c62d1 119 "</p>\n", 'generalbox', 'notice');
867847e3 120 echo $OUTPUT->footer();
d4a03c00 121 exit;
e63faf2b 122 }
8d94f5a0 123
990650f9
TH
124/// Update the quiz with overrides for the current user
125 $quiz = quiz_update_effective_access($quiz, $USER->id);
126
c52c62d1 127/// Get this user's attempts.
e63faf2b 128 $attempts = quiz_get_user_attempts($quiz->id, $USER->id);
c52c62d1 129 $lastfinishedattempt = end($attempts);
e63faf2b 130 $unfinished = false;
131 if ($unfinishedattempt = quiz_get_user_attempt_unfinished($quiz->id, $USER->id)) {
132 $attempts[] = $unfinishedattempt;
133 $unfinished = true;
134 }
135 $numattempts = count($attempts);
136
c52c62d1 137/// Work out the final grade, checking whether it was overridden in the gradebook.
e63faf2b 138 $mygrade = quiz_get_best_grade($quiz, $USER->id);
14e66f3b 139 $mygradeoverridden = false;
140 $gradebookfeedback = '';
141
142 $grading_info = grade_get_grades($course->id, 'mod', 'quiz', $quiz->id, $USER->id);
3bdca852 143 if (!empty($grading_info->items)) {
144 $item = $grading_info->items[0];
145 if (isset($item->grades[$USER->id])) {
146 $grade = $item->grades[$USER->id];
c5659019 147
3bdca852 148 if ($grade->overridden) {
149 $mygrade = $grade->grade + 0; // Convert to number.
150 $mygradeoverridden = true;
151 }
152 if (!empty($grade->str_feedback)) {
153 $gradebookfeedback = $grade->str_feedback;
154 }
155 }
156 }
157
c52c62d1 158/// Print table with existing attempts
e63faf2b 159 if ($attempts) {
cd06115f 160
90cd54cb 161 echo $OUTPUT->heading(get_string('summaryofattempts', 'quiz'));
bacd044d 162
e63faf2b 163 // Work out which columns we need, taking account what data is available in each attempt.
164 list($someoptions, $alloptions) = quiz_get_combined_reviewoptions($quiz, $attempts, $context);
165
152d3c02 166 $attemptcolumn = $quiz->attempts != 1;
167
739b0711 168 $gradecolumn = $someoptions->scores && quiz_has_grades($quiz);
e63faf2b 169 $markcolumn = $gradecolumn && ($quiz->grade != $quiz->sumgrades);
170 $overallstats = $alloptions->scores;
171
739b0711 172 $feedbackcolumn = quiz_has_feedback($quiz) && $alloptions->overallfeedback;
e63faf2b 173
c52c62d1 174 // Prepare table header
39e37019 175 $table = new html_table();
16be8974 176 $table->attributes['class'] = 'generaltable quizattemptsummary';
152d3c02 177 $table->head = array();
178 $table->align = array();
179 $table->size = array();
180 if ($attemptcolumn) {
e10367a4 181 $table->head[] = get_string('attemptnumber', 'quiz');
152d3c02 182 $table->align[] = 'center';
183 $table->size[] = '';
184 }
75b7cac0 185 $table->head[] = get_string('timecompleted', 'quiz');
152d3c02 186 $table->align[] = 'left';
187 $table->size[] = '';
e63faf2b 188 if ($markcolumn) {
f9a2cf86 189 $table->head[] = get_string('marks', 'quiz') . ' / ' . quiz_format_grade($quiz, $quiz->sumgrades);
e63faf2b 190 $table->align[] = 'center';
191 $table->size[] = '';
192 }
193 if ($gradecolumn) {
f9a2cf86 194 $table->head[] = get_string('grade') . ' / ' . quiz_format_grade($quiz, $quiz->grade);
e63faf2b 195 $table->align[] = 'center';
196 $table->size[] = '';
197 }
96c7d771 198 if ($canreviewmine) {
199 $table->head[] = get_string('review', 'quiz');
200 $table->align[] = 'center';
201 $table->size[] = '';
202 }
e63faf2b 203 if ($feedbackcolumn) {
9c22b42e 204 $table->head[] = get_string('feedback', 'quiz');
e63faf2b 205 $table->align[] = 'left';
206 $table->size[] = '';
207 }
208 if (isset($quiz->showtimetaken)) {
9c22b42e 209 $table->head[] = get_string('timetaken', 'quiz');
e63faf2b 210 $table->align[] = 'left';
211 $table->size[] = '';
212 }
77ed3ba5 213
e63faf2b 214 // One row for each attempt
215 foreach ($attempts as $attempt) {
216 $attemptoptions = quiz_get_reviewoptions($quiz, $attempt, $context);
217 $row = array();
cd06115f 218
e63faf2b 219 // Add the attempt number, making it a link, if appropriate.
152d3c02 220 if ($attemptcolumn) {
221 if ($attempt->preview) {
222 $row[] = get_string('preview', 'quiz');
223 } else {
224 $row[] = $attempt->attempt;
225 }
212b7b8c 226 }
bacd044d 227
e63faf2b 228 // prepare strings for time taken and date completed
229 $timetaken = '';
230 $datecompleted = '';
231 if ($attempt->timefinish > 0) {
232 // attempt has finished
233 $timetaken = format_time($attempt->timefinish - $attempt->timestart);
234 $datecompleted = userdate($attempt->timefinish);
c52c62d1 235 } else if (!$quiz->timeclose || $timenow < $quiz->timeclose) {
e63faf2b 236 // The attempt is still in progress.
c52c62d1 237 $timetaken = format_time($timenow - $attempt->timestart);
e63faf2b 238 $datecompleted = '';
c52c62d1 239 } else {
e63faf2b 240 $timetaken = format_time($quiz->timeclose - $attempt->timestart);
241 $datecompleted = userdate($quiz->timeclose);
e63faf2b 242 }
243 $row[] = $datecompleted;
212b7b8c 244
98a31ac1
TH
245 if ($markcolumn) {
246 if ($attemptoptions->scores && $attempt->timefinish > 0) {
152d3c02 247 $row[] = quiz_format_grade($quiz, $attempt->sumgrades);
e63faf2b 248 } else {
249 $row[] = '';
309c0d55 250 }
e63faf2b 251 }
cd06115f 252
e63faf2b 253 // Ouside the if because we may be showing feedback but not grades.
84e628a0 254 $attemptgrade = quiz_rescale_grade($attempt->sumgrades, $quiz, false);
cd06115f 255
e63faf2b 256 if ($gradecolumn) {
99731878 257 if ($attemptoptions->scores && $attempt->timefinish > 0) {
84e628a0 258 $formattedgrade = quiz_format_grade($quiz, $attemptgrade);
e63faf2b 259 // highlight the highest grade if appropriate
c52c62d1 260 if ($overallstats && !$attempt->preview && $numattempts > 1 && !is_null($mygrade) &&
261 $attemptgrade == $mygrade && $quiz->grademethod == QUIZ_GRADEHIGHEST) {
704ac344 262 $table->rowclasses[$attempt->attempt] = 'bestrow';
cd06115f 263 }
cd06115f 264
152d3c02 265 $row[] = $formattedgrade;
e63faf2b 266 } else {
267 $row[] = '';
cd06115f 268 }
e63faf2b 269 }
cd06115f 270
96c7d771 271 if ($canreviewmine) {
272 $row[] = $accessmanager->make_review_link($attempt, $canpreview, $attemptoptions);
273 }
152d3c02 274
99731878 275 if ($feedbackcolumn && $attempt->timefinish > 0) {
e63faf2b 276 if ($attemptoptions->overallfeedback) {
fe6ce234 277 $row[] = quiz_feedback_for_grade($attemptgrade, $quiz, $context, $cm);
e63faf2b 278 } else {
279 $row[] = '';
cd06115f 280 }
e63faf2b 281 }
cd06115f 282
e63faf2b 283 if (isset($quiz->showtimetaken)) {
284 $row[] = $timetaken;
285 }
36658798 286
c52c62d1 287 if ($attempt->preview) {
288 $table->data['preview'] = $row;
289 } else {
290 $table->data[$attempt->attempt] = $row;
291 }
e63faf2b 292 } // End of loop over attempts.
16be8974 293 echo html_writer::table($table);
e63faf2b 294 }
cd06115f 295
c52c62d1 296/// Print information about the student's best score for this quiz if possible.
297 $moreattempts = $unfinished || !$accessmanager->is_finished($numattempts, $lastfinishedattempt);
e63faf2b 298 if (!$moreattempts) {
90cd54cb 299 echo $OUTPUT->heading(get_string("nomoreattempts", "quiz"));
e63faf2b 300 }
cd06115f 301
739b0711 302 if ($numattempts && $gradecolumn && !is_null($mygrade)) {
14e66f3b 303 $resultinfo = '';
c5659019 304
e63faf2b 305 if ($overallstats) {
c52c62d1 306 if ($moreattempts) {
e63faf2b 307 $a = new stdClass;
308 $a->method = quiz_get_grading_option_name($quiz->grademethod);
5626f5d2 309 $a->mygrade = quiz_format_grade($quiz, $mygrade);
310 $a->quizgrade = quiz_format_grade($quiz, $quiz->grade);
90cd54cb 311 $resultinfo .= $OUTPUT->heading(get_string('gradesofar', 'quiz', $a), 2, 'main');
e63faf2b 312 } else {
2b38499d
TH
313 $a = new stdClass;
314 $a->grade = quiz_format_grade($quiz, $mygrade);
315 $a->maxgrade = quiz_format_grade($quiz, $quiz->grade);
316 $a = get_string('outofshort', 'quiz', $a);
90cd54cb 317 $resultinfo .= $OUTPUT->heading(get_string('yourfinalgradeis', 'quiz', $a), 2, 'main');
212b7b8c 318 }
319 }
320
e285b6a8 321 if ($mygradeoverridden) {
322 $resultinfo .= '<p class="overriddennotice">'.get_string('overriddennotice', 'grades')."</p>\n";
323 }
14e66f3b 324 if ($gradebookfeedback) {
90cd54cb 325 $resultinfo .= $OUTPUT->heading(get_string('comment', 'quiz'), 3, 'main');
c52c62d1 326 $resultinfo .= '<p class="quizteacherfeedback">'.$gradebookfeedback."</p>\n";
14e66f3b 327 }
316defd2 328 if ($feedbackcolumn) {
90cd54cb 329 $resultinfo .= $OUTPUT->heading(get_string('overallfeedback', 'quiz'), 3, 'main');
fe6ce234 330 $resultinfo .= '<p class="quizgradefeedback">'.quiz_feedback_for_grade($mygrade, $quiz, $context, $cm)."</p>\n";
14e66f3b 331 }
332
333 if ($resultinfo) {
3b1d5cc4 334 echo $OUTPUT->box($resultinfo, 'generalbox', 'feedback');
e63faf2b 335 }
336 }
77ed3ba5 337
c52c62d1 338/// Determine if we should be showing a start/continue attempt button,
339/// or a button to go back to the course page.
3b1d5cc4 340 echo $OUTPUT->box_start('quizattempt');
c52c62d1 341 $buttontext = ''; // This will be set something if as start/continue attempt button should appear.
2cb01c92 342 if (!quiz_clean_layout($quiz->questions, true)) {
90cd54cb 343 echo $OUTPUT->heading(get_string("noquestions", "quiz"));
c52c62d1 344 } else {
e63faf2b 345 if ($unfinished) {
150ce068 346 if ($canpreview) {
96c7d771 347 $buttontext = get_string('continuepreview', 'quiz');
150ce068
TH
348 } else if ($canattempt) {
349 $buttontext = get_string('continueattemptquiz', 'quiz');
e63faf2b 350 }
351 } else {
150ce068
TH
352 if ($canpreview) {
353 $buttontext = get_string('previewquiznow', 'quiz');
354 } else if ($canattempt) {
96c7d771 355 $messages = $accessmanager->prevent_new_attempt($numattempts, $lastfinishedattempt);
356 if ($messages) {
357 $accessmanager->print_messages($messages);
c52c62d1 358 } else if ($numattempts == 0) {
359 $buttontext = get_string('attemptquiznow', 'quiz');
360 } else {
361 $buttontext = get_string('reattemptquiz', 'quiz');
cd06115f 362 }
363 }
e63faf2b 364 }
212b7b8c 365
c52c62d1 366 // If, so far, we think a button should be printed, so check if they will be allowed to access it.
e63faf2b 367 if ($buttontext) {
c52c62d1 368 if (!$moreattempts) {
369 $buttontext = '';
96c7d771 370 } else if ($canattempt && $messages = $accessmanager->prevent_access()) {
c52c62d1 371 $accessmanager->print_messages($messages);
372 $buttontext = '';
e63faf2b 373 }
212b7b8c 374 }
c52c62d1 375 }
e63faf2b 376
c52c62d1 377/// Now actually print the appropriate button.
378 if ($buttontext) {
379 $accessmanager->print_start_attempt_button($canpreview, $buttontext, $unfinished);
e63faf2b 380 } else {
825116fb 381 echo $OUTPUT->continue_button($CFG->wwwroot . '/course/view.php?id=' . $course->id);
5190eed8 382 }
3b1d5cc4 383 echo $OUTPUT->box_end();
730fd187 384
4e781c7b 385 // Mark module as viewed (note, we do this here and not in finish_page,
3b1d5cc4 386 // otherwise the 'not enrolled' error conditions would result in marking
4e781c7b 387 // 'viewed', I think it's better if they don't.)
388 $completion=new completion_info($course);
d4a03c00 389 $completion->set_module_viewed($cm);
cd06115f 390
867847e3 391 echo $OUTPUT->footer();