Proyectos de Subversion Moodle

Rev

Rev 1 | | Comparar con el anterior | Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 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
 
1441 ariadna 26
use core\output\notification;
1 efrain 27
use mod_quiz\access_manager;
28
use mod_quiz\output\list_of_attempts;
29
use mod_quiz\output\renderer;
30
use mod_quiz\output\view_page;
31
use mod_quiz\quiz_attempt;
32
use mod_quiz\quiz_settings;
33
 
34
require_once(__DIR__ . '/../../config.php');
35
require_once($CFG->libdir.'/gradelib.php');
36
require_once($CFG->dirroot.'/mod/quiz/locallib.php');
37
require_once($CFG->libdir . '/completionlib.php');
38
require_once($CFG->dirroot . '/course/format/lib.php');
39
 
40
$id = optional_param('id', 0, PARAM_INT); // Course Module ID, or ...
41
$q = optional_param('q',  0, PARAM_INT);  // Quiz ID.
42
 
43
if ($id) {
44
    $quizobj = quiz_settings::create_for_cmid($id, $USER->id);
45
} else {
46
    $quizobj = quiz_settings::create($q, $USER->id);
47
}
48
$quiz = $quizobj->get_quiz();
49
$cm = $quizobj->get_cm();
50
$course = $quizobj->get_course();
51
 
52
// Check login and get context.
53
require_login($course, false, $cm);
54
$context = $quizobj->get_context();
55
require_capability('mod/quiz:view', $context);
56
 
57
// Cache some other capabilities we use several times.
58
$canattempt = has_capability('mod/quiz:attempt', $context);
59
$canreviewmine = has_capability('mod/quiz:reviewmyattempts', $context);
60
$canpreview = has_capability('mod/quiz:preview', $context);
61
 
62
// Create an object to manage all the other (non-roles) access rules.
63
$timenow = time();
64
$accessmanager = new access_manager($quizobj, $timenow,
65
        has_capability('mod/quiz:ignoretimelimits', $context, null, false));
66
 
67
// Trigger course_module_viewed event and completion.
68
quiz_view($quiz, $course, $cm, $context);
69
 
70
// Initialize $PAGE, compute blocks.
71
$PAGE->set_url('/mod/quiz/view.php', ['id' => $cm->id]);
1441 ariadna 72
// On the quiz view page, the browser back/forwards buttons should force a reload.
73
$PAGE->set_cacheable(false);
1 efrain 74
 
75
// Create view object which collects all the information the renderer will need.
76
$viewobj = new view_page();
77
$viewobj->accessmanager = $accessmanager;
78
$viewobj->canreviewmine = $canreviewmine || $canpreview;
79
 
80
// Get this user's attempts.
81
$attempts = quiz_get_user_attempts($quiz->id, $USER->id, 'finished', true);
82
$lastfinishedattempt = end($attempts);
83
$unfinished = false;
84
$unfinishedattemptid = null;
85
if ($unfinishedattempt = quiz_get_user_attempt_unfinished($quiz->id, $USER->id)) {
86
    $attempts[] = $unfinishedattempt;
87
 
88
    // If the attempt is now overdue, deal with that - and pass isonline = false.
89
    // We want the student notified in this case.
90
    $quizobj->create_attempt_object($unfinishedattempt)->handle_if_time_expired(time(), false);
91
 
92
    $unfinished = $unfinishedattempt->state == quiz_attempt::IN_PROGRESS ||
93
            $unfinishedattempt->state == quiz_attempt::OVERDUE;
94
    if (!$unfinished) {
95
        $lastfinishedattempt = $unfinishedattempt;
96
    }
97
    $unfinishedattemptid = $unfinishedattempt->id;
98
    $unfinishedattempt = null; // To make it clear we do not use this again.
99
}
100
$numattempts = count($attempts);
101
 
102
$gradeitemmarks = $quizobj->get_grade_calculator()->compute_grade_item_totals_for_attempts(
103
    array_column($attempts, 'uniqueid'));
104
 
105
$viewobj->attempts = $attempts;
106
$viewobj->attemptobjs = [];
107
foreach ($attempts as $attempt) {
108
    $attemptobj = new quiz_attempt($attempt, $quiz, $cm, $course, false);
109
    $attemptobj->set_grade_item_totals($gradeitemmarks[$attempt->uniqueid]);
110
    $viewobj->attemptobjs[] = $attemptobj;
111
 
112
}
113
$viewobj->attemptslist = new list_of_attempts($timenow);
114
foreach (array_reverse($viewobj->attemptobjs) as $attemptobj) {
115
    $viewobj->attemptslist->add_attempt($attemptobj);
116
}
117
 
118
// Work out the final grade, checking whether it was overridden in the gradebook.
1441 ariadna 119
// First, get an initial grade to display.
1 efrain 120
if (!$canpreview) {
121
    $mygrade = quiz_get_best_grade($quiz, $USER->id);
122
} else if ($lastfinishedattempt) {
123
    // Users who can preview the quiz don't get a proper grade, so work out a
124
    // plausible value to display instead, so the page looks right.
125
    $mygrade = quiz_rescale_grade($lastfinishedattempt->sumgrades, $quiz, false);
126
} else {
127
    $mygrade = null;
128
}
129
 
1441 ariadna 130
// Now, check the grade in the gradebook, if there is one.
1 efrain 131
$mygradeoverridden = false;
132
$gradebookfeedback = '';
133
 
134
$gradeitem = grade_item::fetch([
135
    'itemtype' => 'mod',
136
    'itemmodule' => 'quiz',
137
    'iteminstance' => $quiz->id,
138
    'itemnumber' => 0,
139
    'courseid' => $course->id,
140
]);
141
 
1441 ariadna 142
// If there's a grade item grade, then get that grade for this user.
143
// Users who can preview the quiz (eg teachers) won't have a proper grade,
144
// so no point getting their grades here.
145
if (!$canpreview && $gradeitem) {
146
    $grade = $gradeitem->get_grade($USER->id, false);
147
    $mygrade = $grade->finalgrade; // Use this grade to display in the view page.
1 efrain 148
 
1441 ariadna 149
    if ($grade->overridden) {
150
        if ($gradeitem->needsupdate) {
151
            // It is Error, but let's be consistent with the old code.
152
            $mygrade = 0;
1 efrain 153
        }
1441 ariadna 154
        $mygradeoverridden = true;
1 efrain 155
    }
1441 ariadna 156
 
157
    if (!empty($grade->feedback)) {
158
        $gradebookfeedback = $grade->feedback;
159
    }
1 efrain 160
}
161
 
162
$title = $course->shortname . ': ' . format_string($quiz->name);
163
$PAGE->set_title($title);
164
$PAGE->set_heading($course->fullname);
165
if (html_is_blank($quiz->intro)) {
166
    $PAGE->activityheader->set_description('');
167
}
168
$PAGE->add_body_class('limitedwidth');
169
/** @var renderer $output */
170
$output = $PAGE->get_renderer('mod_quiz');
171
 
1441 ariadna 172
// Print overall stats and table with existing attempts.
1 efrain 173
if ($attempts) {
174
    // Work out which columns we need, taking account what data is available in each attempt.
175
    list($someoptions, $alloptions) = quiz_get_combined_reviewoptions($quiz, $attempts);
176
 
177
    $viewobj->attemptcolumn  = $quiz->attempts != 1;
178
 
179
    $viewobj->gradecolumn    = $someoptions->marks >= question_display_options::MARK_AND_MAX &&
180
            quiz_has_grades($quiz);
181
    $viewobj->markcolumn     = $viewobj->gradecolumn && ($quiz->grade != $quiz->sumgrades);
182
    $viewobj->overallstats   = $lastfinishedattempt && $alloptions->marks >= question_display_options::MARK_AND_MAX;
183
 
184
    $viewobj->feedbackcolumn = quiz_has_feedback($quiz) && $alloptions->overallfeedback;
185
}
186
 
187
$viewobj->timenow = $timenow;
188
$viewobj->numattempts = $numattempts;
189
$viewobj->mygrade = $mygrade;
190
$viewobj->moreattempts = $unfinished ||
191
        !$accessmanager->is_finished($numattempts, $lastfinishedattempt);
192
$viewobj->mygradeoverridden = $mygradeoverridden;
193
$viewobj->gradebookfeedback = $gradebookfeedback;
194
$viewobj->lastfinishedattempt = $lastfinishedattempt;
195
$viewobj->canedit = has_capability('mod/quiz:manage', $context);
196
$viewobj->editurl = new moodle_url('/mod/quiz/edit.php', ['cmid' => $cm->id]);
197
$viewobj->backtocourseurl = new moodle_url('/course/view.php', ['id' => $course->id]);
198
$viewobj->startattempturl = $quizobj->start_attempt_url();
199
 
200
if ($accessmanager->is_preflight_check_required($unfinishedattemptid)) {
201
    $viewobj->preflightcheckform = $accessmanager->get_preflight_check_form(
202
            $viewobj->startattempturl, $unfinishedattemptid);
203
}
204
$viewobj->popuprequired = $accessmanager->attempt_must_be_in_popup();
205
$viewobj->popupoptions = $accessmanager->get_popup_options();
206
 
207
// Display information about this quiz.
208
$viewobj->infomessages = $viewobj->accessmanager->describe_rules();
209
if ($quiz->attempts != 1) {
210
    $viewobj->infomessages[] = get_string('gradingmethod', 'quiz',
211
            quiz_get_grading_option_name($quiz->grademethod));
212
}
213
 
214
// Inform user of the grade to pass if non-zero.
215
if ($gradeitem && grade_floats_different($gradeitem->gradepass, 0)) {
216
    $a = new stdClass();
217
    $a->grade = quiz_format_grade($quiz, $gradeitem->gradepass);
218
    $a->maxgrade = quiz_format_grade($quiz, $quiz->grade);
219
    $viewobj->infomessages[] = get_string('gradetopassoutof', 'quiz', $a);
220
}
221
 
222
// Determine whether a start attempt button should be displayed.
223
$viewobj->quizhasquestions = $quizobj->has_questions();
224
$viewobj->preventmessages = [];
225
if (!$viewobj->quizhasquestions) {
226
    $viewobj->buttontext = '';
227
 
228
} else {
229
    if ($unfinished) {
230
        if ($canpreview) {
231
            $viewobj->buttontext = get_string('continuepreview', 'quiz');
232
        } else if ($canattempt) {
233
            $viewobj->buttontext = get_string('continueattemptquiz', 'quiz');
234
        }
235
    } else {
236
        if ($canpreview) {
237
            $viewobj->buttontext = get_string('previewquizstart', 'quiz');
238
        } else if ($canattempt) {
239
            $viewobj->preventmessages = $viewobj->accessmanager->prevent_new_attempt(
240
                    $viewobj->numattempts, $viewobj->lastfinishedattempt);
241
            if ($viewobj->preventmessages) {
242
                $viewobj->buttontext = '';
243
            } else if ($viewobj->numattempts == 0) {
244
                $viewobj->buttontext = get_string('attemptquiz', 'quiz');
245
            } else {
246
                $viewobj->buttontext = get_string('reattemptquiz', 'quiz');
247
            }
248
        }
249
    }
250
 
251
    // Users who can preview the quiz should be able to see all messages for not being able to access the quiz.
252
    if ($canpreview) {
253
        $viewobj->preventmessages = $viewobj->accessmanager->prevent_access();
254
    } else if ($viewobj->buttontext) {
255
        // If, so far, we think a button should be printed, so check if they will be allowed to access it.
256
        if (!$viewobj->moreattempts) {
257
            $viewobj->buttontext = '';
258
        } else if ($canattempt) {
259
            $viewobj->preventmessages = $viewobj->accessmanager->prevent_access();
260
            if ($viewobj->preventmessages) {
261
                $viewobj->buttontext = '';
262
            }
263
        }
264
    }
1441 ariadna 265
 
266
    // If the quiz has any invalid questions, we cannot attempt it.
267
    if (in_array('missingtype', $quizobj->get_all_question_types_used())) {
268
        $viewobj->preventmessages[] = $OUTPUT->notification(
269
            get_string('quizinvalidquestions', 'mod_quiz'), notification::NOTIFY_ERROR, false);
270
        $viewobj->buttontext = '';
271
    }
1 efrain 272
}
273
 
274
$viewobj->showbacktocourse = ($viewobj->buttontext === '' &&
275
        course_get_format($course)->has_view_page());
276
 
277
echo $OUTPUT->header();
278
 
279
if (!empty($gradinginfo->errors)) {
280
    foreach ($gradinginfo->errors as $error) {
1441 ariadna 281
        $errortext = new notification($error, notification::NOTIFY_ERROR);
1 efrain 282
        echo $OUTPUT->render($errortext);
283
    }
284
}
285
 
286
if (isguestuser()) {
287
    // Guests can't do a quiz, so offer them a choice of logging in or going back.
288
    echo $output->view_page_guest($course, $quiz, $cm, $context, $viewobj->infomessages, $viewobj);
289
} else if (!isguestuser() && !($canattempt || $canpreview
290
          || $viewobj->canreviewmine)) {
291
    // If they are not enrolled in this course in a good enough role, tell them to enrol.
292
    echo $output->view_page_notenrolled($course, $quiz, $cm, $context, $viewobj->infomessages, $viewobj);
293
} else {
294
    echo $output->view_page($course, $quiz, $cm, $context, $viewobj);
295
}
296
 
297
echo $OUTPUT->footer();