1<?php
2// This file is part of Moodle - http://moodle.org/
3//
4// Moodle is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// Moodle is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
16
17/**
18 * This page is the entry page into the quiz UI. Displays information about the
19 * quiz to students and teachers, and lets students see their previous attempts.
20 *
21 * @package   mod_quiz
22 * @copyright 1999 onwards Martin Dougiamas  {@link http://moodle.com}
23 * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24 */
25
26
27require_once(__DIR__ . '/../../config.php');
28require_once($CFG->libdir.'/gradelib.php');
29require_once($CFG->dirroot.'/mod/quiz/locallib.php');
30require_once($CFG->libdir . '/completionlib.php');
31require_once($CFG->dirroot . '/course/format/lib.php');
32
33$id = optional_param('id', 0, PARAM_INT); // Course Module ID, or ...
34$q = optional_param('q',  0, PARAM_INT);  // Quiz ID.
35
36if ($id) {
37    if (!$cm = get_coursemodule_from_id('quiz', $id)) {
38        print_error('invalidcoursemodule');
39    }
40    if (!$course = $DB->get_record('course', array('id' => $cm->course))) {
41        print_error('coursemisconf');
42    }
43} else {
44    if (!$quiz = $DB->get_record('quiz', array('id' => $q))) {
45        print_error('invalidquizid', 'quiz');
46    }
47    if (!$course = $DB->get_record('course', array('id' => $quiz->course))) {
48        print_error('invalidcourseid');
49    }
50    if (!$cm = get_coursemodule_from_instance("quiz", $quiz->id, $course->id)) {
51        print_error('invalidcoursemodule');
52    }
53}
54
55// Check login and get context.
56require_login($course, false, $cm);
57$context = context_module::instance($cm->id);
58require_capability('mod/quiz:view', $context);
59
60// Cache some other capabilities we use several times.
61$canattempt = has_capability('mod/quiz:attempt', $context);
62$canreviewmine = has_capability('mod/quiz:reviewmyattempts', $context);
63$canpreview = has_capability('mod/quiz:preview', $context);
64
65// Create an object to manage all the other (non-roles) access rules.
66$timenow = time();
67$quizobj = quiz::create($cm->instance, $USER->id);
68$accessmanager = new quiz_access_manager($quizobj, $timenow,
69        has_capability('mod/quiz:ignoretimelimits', $context, null, false));
70$quiz = $quizobj->get_quiz();
71
72// Trigger course_module_viewed event and completion.
73quiz_view($quiz, $course, $cm, $context);
74
75// Initialize $PAGE, compute blocks.
76$PAGE->set_url('/mod/quiz/view.php', array('id' => $cm->id));
77
78// Create view object which collects all the information the renderer will need.
79$viewobj = new mod_quiz_view_object();
80$viewobj->accessmanager = $accessmanager;
81$viewobj->canreviewmine = $canreviewmine || $canpreview;
82
83// Get this user's attempts.
84$attempts = quiz_get_user_attempts($quiz->id, $USER->id, 'finished', true);
85$lastfinishedattempt = end($attempts);
86$unfinished = false;
87$unfinishedattemptid = null;
88if ($unfinishedattempt = quiz_get_user_attempt_unfinished($quiz->id, $USER->id)) {
89    $attempts[] = $unfinishedattempt;
90
91    // If the attempt is now overdue, deal with that - and pass isonline = false.
92    // We want the student notified in this case.
93    $quizobj->create_attempt_object($unfinishedattempt)->handle_if_time_expired(time(), false);
94
95    $unfinished = $unfinishedattempt->state == quiz_attempt::IN_PROGRESS ||
96            $unfinishedattempt->state == quiz_attempt::OVERDUE;
97    if (!$unfinished) {
98        $lastfinishedattempt = $unfinishedattempt;
99    }
100    $unfinishedattemptid = $unfinishedattempt->id;
101    $unfinishedattempt = null; // To make it clear we do not use this again.
102}
103$numattempts = count($attempts);
104
105$viewobj->attempts = $attempts;
106$viewobj->attemptobjs = array();
107foreach ($attempts as $attempt) {
108    $viewobj->attemptobjs[] = new quiz_attempt($attempt, $quiz, $cm, $course, false);
109}
110
111// Work out the final grade, checking whether it was overridden in the gradebook.
112if (!$canpreview) {
113    $mygrade = quiz_get_best_grade($quiz, $USER->id);
114} else if ($lastfinishedattempt) {
115    // Users who can preview the quiz don't get a proper grade, so work out a
116    // plausible value to display instead, so the page looks right.
117    $mygrade = quiz_rescale_grade($lastfinishedattempt->sumgrades, $quiz, false);
118} else {
119    $mygrade = null;
120}
121
122$mygradeoverridden = false;
123$gradebookfeedback = '';
124
125$grading_info = grade_get_grades($course->id, 'mod', 'quiz', $quiz->id, $USER->id);
126if (!empty($grading_info->items)) {
127    $item = $grading_info->items[0];
128    if (isset($item->grades[$USER->id])) {
129        $grade = $item->grades[$USER->id];
130
131        if ($grade->overridden) {
132            $mygrade = $grade->grade + 0; // Convert to number.
133            $mygradeoverridden = true;
134        }
135        if (!empty($grade->str_feedback)) {
136            $gradebookfeedback = $grade->str_feedback;
137        }
138    }
139}
140
141$title = $course->shortname . ': ' . format_string($quiz->name);
142$PAGE->set_title($title);
143$PAGE->set_heading($course->fullname);
144$output = $PAGE->get_renderer('mod_quiz');
145
146// Print table with existing attempts.
147if ($attempts) {
148    // Work out which columns we need, taking account what data is available in each attempt.
149    list($someoptions, $alloptions) = quiz_get_combined_reviewoptions($quiz, $attempts);
150
151    $viewobj->attemptcolumn  = $quiz->attempts != 1;
152
153    $viewobj->gradecolumn    = $someoptions->marks >= question_display_options::MARK_AND_MAX &&
154            quiz_has_grades($quiz);
155    $viewobj->markcolumn     = $viewobj->gradecolumn && ($quiz->grade != $quiz->sumgrades);
156    $viewobj->overallstats   = $lastfinishedattempt && $alloptions->marks >= question_display_options::MARK_AND_MAX;
157
158    $viewobj->feedbackcolumn = quiz_has_feedback($quiz) && $alloptions->overallfeedback;
159}
160
161$viewobj->timenow = $timenow;
162$viewobj->numattempts = $numattempts;
163$viewobj->mygrade = $mygrade;
164$viewobj->moreattempts = $unfinished ||
165        !$accessmanager->is_finished($numattempts, $lastfinishedattempt);
166$viewobj->mygradeoverridden = $mygradeoverridden;
167$viewobj->gradebookfeedback = $gradebookfeedback;
168$viewobj->lastfinishedattempt = $lastfinishedattempt;
169$viewobj->canedit = has_capability('mod/quiz:manage', $context);
170$viewobj->editurl = new moodle_url('/mod/quiz/edit.php', array('cmid' => $cm->id));
171$viewobj->backtocourseurl = new moodle_url('/course/view.php', array('id' => $course->id));
172$viewobj->startattempturl = $quizobj->start_attempt_url();
173
174if ($accessmanager->is_preflight_check_required($unfinishedattemptid)) {
175    $viewobj->preflightcheckform = $accessmanager->get_preflight_check_form(
176            $viewobj->startattempturl, $unfinishedattemptid);
177}
178$viewobj->popuprequired = $accessmanager->attempt_must_be_in_popup();
179$viewobj->popupoptions = $accessmanager->get_popup_options();
180
181// Display information about this quiz.
182$viewobj->infomessages = $viewobj->accessmanager->describe_rules();
183if ($quiz->attempts != 1) {
184    $viewobj->infomessages[] = get_string('gradingmethod', 'quiz',
185            quiz_get_grading_option_name($quiz->grademethod));
186}
187
188// Determine wheter a start attempt button should be displayed.
189$viewobj->quizhasquestions = $quizobj->has_questions();
190$viewobj->preventmessages = array();
191if (!$viewobj->quizhasquestions) {
192    $viewobj->buttontext = '';
193
194} else {
195    if ($unfinished) {
196        if ($canattempt) {
197            $viewobj->buttontext = get_string('continueattemptquiz', 'quiz');
198        } else if ($canpreview) {
199            $viewobj->buttontext = get_string('continuepreview', 'quiz');
200        }
201
202    } else {
203        if ($canattempt) {
204            $viewobj->preventmessages = $viewobj->accessmanager->prevent_new_attempt(
205                    $viewobj->numattempts, $viewobj->lastfinishedattempt);
206            if ($viewobj->preventmessages) {
207                $viewobj->buttontext = '';
208            } else if ($viewobj->numattempts == 0) {
209                $viewobj->buttontext = get_string('attemptquiznow', 'quiz');
210            } else {
211                $viewobj->buttontext = get_string('reattemptquiz', 'quiz');
212            }
213
214        } else if ($canpreview) {
215            $viewobj->buttontext = get_string('previewquiznow', 'quiz');
216        }
217    }
218
219    // If, so far, we think a button should be printed, so check if they will be
220    // allowed to access it.
221    if ($viewobj->buttontext) {
222        if (!$viewobj->moreattempts) {
223            $viewobj->buttontext = '';
224        } else if ($canattempt
225                && $viewobj->preventmessages = $viewobj->accessmanager->prevent_access()) {
226            $viewobj->buttontext = '';
227        }
228    }
229}
230
231$viewobj->showbacktocourse = ($viewobj->buttontext === '' &&
232        course_get_format($course)->has_view_page());
233
234echo $OUTPUT->header();
235
236if (isguestuser()) {
237    // Guests can't do a quiz, so offer them a choice of logging in or going back.
238    echo $output->view_page_guest($course, $quiz, $cm, $context, $viewobj->infomessages);
239} else if (!isguestuser() && !($canattempt || $canpreview
240          || $viewobj->canreviewmine)) {
241    // If they are not enrolled in this course in a good enough role, tell them to enrol.
242    echo $output->view_page_notenrolled($course, $quiz, $cm, $context, $viewobj->infomessages);
243} else {
244    echo $output->view_page($course, $quiz, $cm, $context, $viewobj);
245}
246
247echo $OUTPUT->footer();
248