Merge branch 'MDL-81713-main' of https://github.com/junpataleta/moodle
[moodle.git] / mod / quiz / overrides.php
blobbe467be62435fc802fe2d228c85d0f4c560f70f6
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.
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/>.
17 /**
18 * This page handles listing of quiz overrides
20 * @package mod_quiz
21 * @copyright 2010 Matt Petro
22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
25 use mod_quiz\quiz_settings;
27 require_once(__DIR__ . '/../../config.php');
28 require_once($CFG->dirroot.'/mod/quiz/lib.php');
29 require_once($CFG->dirroot.'/mod/quiz/locallib.php');
31 $cmid = required_param('cmid', PARAM_INT);
32 $mode = optional_param('mode', '', PARAM_ALPHA); // One of 'user' or 'group', default is 'group'.
34 $quizobj = quiz_settings::create_for_cmid($cmid);
35 $quiz = $quizobj->get_quiz();
36 $cm = $quizobj->get_cm();
37 $course = $quizobj->get_course();
38 $context = $quizobj->get_context();
40 require_login($course, false, $cm);
42 // Check the user has the required capabilities to list overrides.
43 $canedit = has_capability('mod/quiz:manageoverrides', $context);
44 if (!$canedit) {
45 require_capability('mod/quiz:viewoverrides', $context);
48 $quizgroupmode = groups_get_activity_groupmode($cm);
49 $showallgroups = ($quizgroupmode == NOGROUPS) || has_capability('moodle/site:accessallgroups', $context);
51 // Get the course groups that the current user can access.
52 $groups = $showallgroups ? groups_get_all_groups($cm->course) : groups_get_activity_allowed_groups($cm);
54 // Default mode is "group", unless there are no groups.
55 if ($mode != "user" and $mode != "group") {
56 if (!empty($groups)) {
57 $mode = "group";
58 } else {
59 $mode = "user";
62 $groupmode = ($mode == "group");
64 $url = new moodle_url('/mod/quiz/overrides.php', ['cmid' => $cm->id, 'mode' => $mode]);
66 $title = get_string('overridesforquiz', 'quiz',
67 format_string($quiz->name, true, ['context' => $context]));
68 $PAGE->set_url($url);
69 $PAGE->set_pagelayout('admin');
70 $PAGE->add_body_class('limitedwidth');
71 $PAGE->set_title($title);
72 $PAGE->set_heading($course->fullname);
73 $PAGE->activityheader->disable();
75 // Activate the secondary nav tab.
76 $PAGE->set_secondary_active_tab("mod_quiz_useroverrides");
78 // Delete orphaned group overrides.
79 $sql = 'SELECT o.id
80 FROM {quiz_overrides} o
81 LEFT JOIN {groups} g ON o.groupid = g.id
82 WHERE o.groupid IS NOT NULL
83 AND g.id IS NULL
84 AND o.quiz = ?';
85 $params = [$quiz->id];
86 $orphaned = $DB->get_records_sql($sql, $params);
87 if (!empty($orphaned)) {
88 $DB->delete_records_list('quiz_overrides', 'id', array_keys($orphaned));
91 $overrides = [];
92 $colclasses = [];
93 $headers = [];
95 // Fetch all overrides.
96 if ($groupmode) {
97 $headers[] = get_string('group');
98 // To filter the result by the list of groups that the current user has access to.
99 if ($groups) {
100 $params = ['quizid' => $quiz->id];
101 list($insql, $inparams) = $DB->get_in_or_equal(array_keys($groups), SQL_PARAMS_NAMED);
102 $params += $inparams;
104 $sql = "SELECT o.*, g.name
105 FROM {quiz_overrides} o
106 JOIN {groups} g ON o.groupid = g.id
107 WHERE o.quiz = :quizid AND g.id $insql
108 ORDER BY g.name";
110 $overrides = $DB->get_records_sql($sql, $params);
113 } else {
114 // User overrides.
115 $colclasses[] = 'colname';
116 $headers[] = get_string('user');
117 $userfieldsapi = \core_user\fields::for_identity($context)->with_name()->with_userpic();
118 $extrauserfields = $userfieldsapi->get_required_fields([\core_user\fields::PURPOSE_IDENTITY]);
119 $userfieldssql = $userfieldsapi->get_sql('u', true, '', 'userid', false);
120 foreach ($extrauserfields as $field) {
121 $colclasses[] = 'col' . $field;
122 $headers[] = \core_user\fields::get_display_name($field);
125 list($sort, $params) = users_order_by_sql('u', null, $context, $extrauserfields);
126 $params['quizid'] = $quiz->id;
128 if ($showallgroups) {
129 $groupsjoin = '';
130 $groupswhere = '';
132 } else if ($groups) {
133 list($insql, $inparams) = $DB->get_in_or_equal(array_keys($groups), SQL_PARAMS_NAMED);
134 $groupsjoin = 'JOIN {groups_members} gm ON u.id = gm.userid';
135 $groupswhere = ' AND gm.groupid ' . $insql;
136 $params += $inparams;
138 } else {
139 // User cannot see any data.
140 $groupsjoin = '';
141 $groupswhere = ' AND 1 = 2';
144 $overrides = $DB->get_records_sql("
145 SELECT o.*, {$userfieldssql->selects}
146 FROM {quiz_overrides} o
147 JOIN {user} u ON o.userid = u.id
148 {$userfieldssql->joins}
149 $groupsjoin
150 WHERE o.quiz = :quizid
151 $groupswhere
152 ORDER BY $sort
153 ", array_merge($params, $userfieldssql->params));
156 // Initialise table.
157 $table = new html_table();
158 $table->head = $headers;
159 $table->colclasses = $colclasses;
160 $table->headspan = array_fill(0, count($headers), 1);
162 $table->head[] = get_string('overrides', 'quiz');
163 $table->colclasses[] = 'colsetting';
164 $table->colclasses[] = 'colvalue';
165 $table->headspan[] = 2;
167 if ($canedit) {
168 $table->head[] = get_string('action');
169 $table->colclasses[] = 'colaction';
170 $table->headspan[] = 1;
172 $userurl = new moodle_url('/user/view.php', []);
173 $groupurl = new moodle_url('/group/overview.php', ['id' => $cm->course]);
175 $overridedeleteurl = new moodle_url('/mod/quiz/overridedelete.php');
176 $overrideediturl = new moodle_url('/mod/quiz/overrideedit.php');
178 $hasinactive = false; // Whether there are any inactive overrides.
180 foreach ($overrides as $override) {
182 // Check if this override is active.
183 $active = true;
184 if (!$groupmode) {
185 if (!has_capability('mod/quiz:attempt', $context, $override->userid)) {
186 // User not allowed to take the quiz.
187 $active = false;
188 } else if (!\core_availability\info_module::is_user_visible($cm, $override->userid)) {
189 // User cannot access the module.
190 $active = false;
193 if (!$active) {
194 $hasinactive = true;
197 // Prepare the information about which settings are overridden.
198 $fields = [];
199 $values = [];
201 // Format timeopen.
202 if (isset($override->timeopen)) {
203 $fields[] = get_string('quizopens', 'quiz');
204 $values[] = $override->timeopen > 0 ?
205 userdate($override->timeopen) : get_string('noopen', 'quiz');
207 // Format timeclose.
208 if (isset($override->timeclose)) {
209 $fields[] = get_string('quizcloses', 'quiz');
210 $values[] = $override->timeclose > 0 ?
211 userdate($override->timeclose) : get_string('noclose', 'quiz');
213 // Format timelimit.
214 if (isset($override->timelimit)) {
215 $fields[] = get_string('timelimit', 'quiz');
216 $values[] = $override->timelimit > 0 ?
217 format_time($override->timelimit) : get_string('none', 'quiz');
219 // Format number of attempts.
220 if (isset($override->attempts)) {
221 $fields[] = get_string('attempts', 'quiz');
222 $values[] = $override->attempts > 0 ?
223 $override->attempts : get_string('unlimited');
225 // Format password.
226 if (isset($override->password)) {
227 $fields[] = get_string('requirepassword', 'quiz');
228 $values[] = $override->password !== '' ?
229 get_string('enabled', 'quiz') : get_string('none', 'quiz');
232 // Prepare the information about who this override applies to.
233 $extranamebit = $active ? '' : '*';
234 $usercells = [];
235 if ($groupmode) {
236 $groupcell = new html_table_cell();
237 $groupcell->rowspan = count($fields);
238 $groupcell->text = html_writer::link(new moodle_url($groupurl, ['group' => $override->groupid]),
239 format_string($override->name, true, ['context' => $context]) . $extranamebit);
240 $usercells[] = $groupcell;
241 } else {
242 $usercell = new html_table_cell();
243 $usercell->rowspan = count($fields);
244 $usercell->text = html_writer::link(new moodle_url($userurl, ['id' => $override->userid]),
245 fullname($override) . $extranamebit);
246 $usercells[] = $usercell;
248 foreach ($extrauserfields as $field) {
249 $usercell = new html_table_cell();
250 $usercell->rowspan = count($fields);
251 $usercell->text = s($override->$field);
252 $usercells[] = $usercell;
256 // Prepare the actions.
257 if ($canedit) {
258 // Icons.
259 $iconstr = '';
261 // Edit.
262 $editurlstr = $overrideediturl->out(true, ['id' => $override->id]);
263 $iconstr = '<a title="' . get_string('edit') . '" href="' . $editurlstr . '">' .
264 $OUTPUT->pix_icon('t/edit', get_string('edit')) . '</a> ';
265 // Duplicate.
266 $copyurlstr = $overrideediturl->out(true,
267 ['id' => $override->id, 'action' => 'duplicate']);
268 $iconstr .= '<a title="' . get_string('copy') . '" href="' . $copyurlstr . '">' .
269 $OUTPUT->pix_icon('t/copy', get_string('copy')) . '</a> ';
270 // Delete.
271 $deleteurlstr = $overridedeleteurl->out(true,
272 ['id' => $override->id, 'sesskey' => sesskey()]);
273 $iconstr .= '<a title="' . get_string('delete') . '" href="' . $deleteurlstr . '">' .
274 $OUTPUT->pix_icon('t/delete', get_string('delete')) . '</a> ';
276 $actioncell = new html_table_cell();
277 $actioncell->rowspan = count($fields);
278 $actioncell->text = $iconstr;
281 // Add the data to the table.
282 for ($i = 0; $i < count($fields); ++$i) {
283 $row = new html_table_row();
284 if (!$active) {
285 $row->attributes['class'] = 'dimmed_text';
288 if ($i == 0) {
289 $row->cells = $usercells;
292 $labelcell = new html_table_cell();
293 $labelcell->text = $fields[$i];
294 $row->cells[] = $labelcell;
295 $valuecell = new html_table_cell();
296 $valuecell->text = $values[$i];
297 $row->cells[] = $valuecell;
299 if ($canedit && $i == 0) {
300 $row->cells[] = $actioncell;
303 $table->data[] = $row;
307 // Work out what else needs to be displayed.
308 $addenabled = true;
309 $warningmessage = '';
310 if ($canedit) {
311 if ($groupmode) {
312 if (empty($groups)) {
313 // There are no groups.
314 $warningmessage = get_string('groupsnone', 'quiz');
315 $addenabled = false;
317 } else {
318 // See if there are any students in the quiz.
319 if ($showallgroups) {
320 $users = get_users_by_capability($context, 'mod/quiz:attempt', 'u.id');
321 $nousermessage = get_string('usersnone', 'quiz');
322 } else if ($groups) {
323 $users = get_users_by_capability($context, 'mod/quiz:attempt', 'u.id', '', '', '', array_keys($groups));
324 $nousermessage = get_string('usersnone', 'quiz');
325 } else {
326 $users = [];
327 $nousermessage = get_string('groupsnone', 'quiz');
329 $info = new \core_availability\info_module($cm);
330 $users = $info->filter_user_list($users);
332 if (empty($users)) {
333 // There are no students.
334 $warningmessage = $nousermessage;
335 $addenabled = false;
340 // Tertiary navigation.
341 echo $OUTPUT->header();
342 $renderer = $PAGE->get_renderer('mod_quiz');
343 $tertiarynav = new \mod_quiz\output\overrides_actions($cmid, $mode, $canedit, $addenabled);
344 echo $renderer->render($tertiarynav);
346 if ($mode === 'user') {
347 echo $OUTPUT->heading(get_string('useroverrides', 'quiz'));
348 } else {
349 echo $OUTPUT->heading(get_string('groupoverrides', 'quiz'));
352 // Output the table and button.
353 echo html_writer::start_tag('div', ['id' => 'quizoverrides']);
354 if (count($table->data)) {
355 echo html_writer::table($table);
356 } else {
357 if ($groupmode) {
358 echo $OUTPUT->notification(get_string('overridesnoneforgroups', 'quiz'), 'info', false);
359 } else {
360 echo $OUTPUT->notification(get_string('overridesnoneforusers', 'quiz'), 'info', false);
363 if ($hasinactive) {
364 echo $OUTPUT->notification(get_string('inactiveoverridehelp', 'quiz'), 'info', false);
367 if ($warningmessage) {
368 echo $OUTPUT->notification($warningmessage, 'error');
371 echo html_writer::end_tag('div');
373 // Finish the page.
374 echo $OUTPUT->footer();