MDL-68463 core: Add helper functions to toggle checkboxes
[moodle.git] / user / index.php
CommitLineData
aa6c1ced 1<?php
a2ed6e69
SH
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 * Lists all the users within a given course.
19 *
20 * @copyright 1999 Martin Dougiamas http://dougiamas.com
21 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
22 * @package core_user
23 */
24
25require_once('../config.php');
0ff203b6 26require_once($CFG->dirroot.'/user/lib.php');
5fa2d502 27require_once($CFG->dirroot.'/course/lib.php');
b5b81de3 28require_once($CFG->dirroot.'/notes/lib.php');
a2ed6e69
SH
29require_once($CFG->libdir.'/tablelib.php');
30require_once($CFG->libdir.'/filelib.php');
a78ed71c 31require_once($CFG->dirroot.'/enrol/locallib.php');
a2ed6e69 32
8146335f
SL
33use core_table\local\filter\filter;
34use core_table\local\filter\integer_filter;
35use core_table\local\filter\string_filter;
8146335f 36
a2ed6e69
SH
37define('DEFAULT_PAGE_SIZE', 20);
38define('SHOW_ALL_PAGE_SIZE', 5000);
a2ed6e69
SH
39
40$page = optional_param('page', 0, PARAM_INT); // Which page to show.
41$perpage = optional_param('perpage', DEFAULT_PAGE_SIZE, PARAM_INT); // How many per page.
a2ed6e69
SH
42$contextid = optional_param('contextid', 0, PARAM_INT); // One of this or.
43$courseid = optional_param('id', 0, PARAM_INT); // This are required.
5fa2d502 44$newcourse = optional_param('newcourse', false, PARAM_BOOL);
5b7c500a 45$selectall = optional_param('selectall', false, PARAM_BOOL); // When rendering checkboxes against users mark them all checked.
fe214295 46$roleid = optional_param('roleid', 0, PARAM_INT);
9f33d5ac 47$groupparam = optional_param('group', 0, PARAM_INT);
a2ed6e69
SH
48
49$PAGE->set_url('/user/index.php', array(
50 'page' => $page,
51 'perpage' => $perpage,
a2ed6e69 52 'contextid' => $contextid,
5fa2d502
DW
53 'id' => $courseid,
54 'newcourse' => $newcourse));
a2ed6e69
SH
55
56if ($contextid) {
57 $context = context::instance_by_id($contextid, MUST_EXIST);
58 if ($context->contextlevel != CONTEXT_COURSE) {
59 print_error('invalidcontext');
60 }
61 $course = $DB->get_record('course', array('id' => $context->instanceid), '*', MUST_EXIST);
62} else {
63 $course = $DB->get_record('course', array('id' => $courseid), '*', MUST_EXIST);
64 $context = context_course::instance($course->id, MUST_EXIST);
65}
66// Not needed anymore.
67unset($contextid);
68unset($courseid);
f9903ed0 69
a2ed6e69 70require_login($course);
f9903ed0 71
a2ed6e69
SH
72$systemcontext = context_system::instance();
73$isfrontpage = ($course->id == SITEID);
f9903ed0 74
a2ed6e69 75$frontpagectx = context_course::instance(SITEID);
4f0c2d00 76
a2ed6e69
SH
77if ($isfrontpage) {
78 $PAGE->set_pagelayout('admin');
93b47710 79 course_require_view_participants($systemcontext);
a2ed6e69
SH
80} else {
81 $PAGE->set_pagelayout('incourse');
93b47710 82 course_require_view_participants($context);
a2ed6e69 83}
224aa44a 84
0ff203b6
JL
85// Trigger events.
86user_list_view($course, $context);
a2ed6e69
SH
87
88$bulkoperations = has_capability('moodle/course:bulkmessaging', $context);
89
a2ed6e69
SH
90$PAGE->set_title("$course->shortname: ".get_string('participants'));
91$PAGE->set_heading($course->fullname);
92$PAGE->set_pagetype('course-view-' . $course->format);
93$PAGE->add_body_class('path-user'); // So we can style it independently.
94$PAGE->set_other_editing_capability('moodle/course:manageactivities');
2cb2ce61 95
b34fd2cc
DW
96// Expand the users node in the settings navigation when it exists because those pages
97// are related to this one.
98$node = $PAGE->settingsnav->find('users', navigation_node::TYPE_CONTAINER);
99if ($node) {
b0b6ff3c 100 $node->force_open();
b34fd2cc
DW
101}
102
a2ed6e69 103echo $OUTPUT->header();
4e1f6047 104echo $OUTPUT->heading(get_string('participants'));
caa8363f 105
9651e491 106// Get the currently applied filters.
7cf4331a 107$filtersapplied = optional_param_array('unified-filters', [], PARAM_NOTAGS);
32e5109d 108$filterwassubmitted = optional_param('unified-filter-submitted', 0, PARAM_BOOL);
a78ed71c 109
fe214295
MN
110// If they passed a role make sure they can view that role.
111if ($roleid) {
112 $viewableroles = get_profile_roles($context);
113
114 // Check if the user can view this role.
115 if (array_key_exists($roleid, $viewableroles)) {
116 $filtersapplied[] = USER_FILTER_ROLE . ':' . $roleid;
117 } else {
118 $roleid = 0;
119 }
120}
121
9651e491
JP
122// Default group ID.
123$groupid = false;
124$canaccessallgroups = has_capability('moodle/site:accessallgroups', $context);
125if ($course->groupmode != NOGROUPS) {
126 if ($canaccessallgroups) {
9f33d5ac
MN
127 // Change the group if the user can access all groups and has specified group in the URL.
128 if ($groupparam) {
129 $groupid = $groupparam;
130 }
9651e491
JP
131 } else {
132 // Otherwise, get the user's default group.
133 $groupid = groups_get_course_group($course, true);
134 if ($course->groupmode == SEPARATEGROUPS && !$groupid) {
135 // The user is not in the group so show message and exit.
136 echo $OUTPUT->notification(get_string('notingroup'));
137 echo $OUTPUT->footer();
138 exit;
139 }
140 }
a2ed6e69 141}
9651e491
JP
142$hasgroupfilter = false;
143$lastaccess = 0;
144$searchkeywords = [];
9651e491 145$enrolid = 0;
8146335f
SL
146
147$filterset = new \core_user\table\participants_filterset();
148$filterset->add_filter(new integer_filter('courseid', filter::JOINTYPE_DEFAULT, [(int)$course->id]));
149$enrolfilter = new integer_filter('enrolments');
150$groupfilter = new integer_filter('groups');
151$keywordfilter = new string_filter('keywords');
152$lastaccessfilter = new integer_filter('accesssince');
153$rolefilter = new integer_filter('roles');
154$statusfilter = new integer_filter('status');
155
9651e491
JP
156foreach ($filtersapplied as $filter) {
157 $filtervalue = explode(':', $filter, 2);
158 $value = null;
159 if (count($filtervalue) == 2) {
160 $key = clean_param($filtervalue[0], PARAM_INT);
161 $value = clean_param($filtervalue[1], PARAM_INT);
162 } else {
163 // Search string.
a997dc9d
AN
164 $key = USER_FILTER_STRING;
165 $value = clean_param($filtervalue[0], PARAM_TEXT);
9651e491 166 }
99cca847 167
9651e491
JP
168 switch ($key) {
169 case USER_FILTER_ENROLMENT:
170 $enrolid = $value;
8146335f 171 $enrolfilter->add_filter_value($value);
9651e491
JP
172 break;
173 case USER_FILTER_GROUP:
174 $groupid = $value;
8146335f 175 $groupfilter->add_filter_value($value);
9651e491
JP
176 $hasgroupfilter = true;
177 break;
178 case USER_FILTER_LAST_ACCESS:
179 $lastaccess = $value;
8146335f 180 $lastaccessfilter->add_filter_value($value);
9651e491
JP
181 break;
182 case USER_FILTER_ROLE:
183 $roleid = $value;
8146335f 184 $rolefilter->add_filter_value($value);
9651e491
JP
185 break;
186 case USER_FILTER_STATUS:
187 // We only accept active/suspended statuses.
188 if ($value == ENROL_USER_ACTIVE || $value == ENROL_USER_SUSPENDED) {
189 $status = $value;
8146335f 190 $statusfilter->add_filter_value($value);
9651e491
JP
191 }
192 break;
193 default:
194 // Search string.
a997dc9d 195 $searchkeywords[] = $value;
8146335f 196 $keywordfilter->add_filter_value($value);
9651e491
JP
197 break;
198 }
a2ed6e69 199}
32e5109d 200// If course supports groups we may need to set a default.
58af736d 201if (!empty($groupid)) {
9f33d5ac
MN
202 if ($canaccessallgroups) {
203 // User can access all groups, let them filter by whatever was selected.
204 $filtersapplied[] = USER_FILTER_GROUP . ':' . $groupid;
ef4109d2 205 $groupfilter->add_filter_value((int)$groupid);
9f33d5ac
MN
206 } else if (!$filterwassubmitted && $course->groupmode == VISIBLEGROUPS) {
207 // If we are in a course with visible groups and the user has not submitted anything and does not have
208 // access to all groups, then set a default group.
32e5109d 209 $filtersapplied[] = USER_FILTER_GROUP . ':' . $groupid;
ef4109d2 210 $groupfilter->add_filter_value((int)$groupid);
9f33d5ac 211 } else if (!$hasgroupfilter && $course->groupmode != VISIBLEGROUPS) {
32e5109d
MN
212 // The user can't access all groups and has not set a group filter in a course where the groups are not visible
213 // then apply a default group filter.
214 $filtersapplied[] = USER_FILTER_GROUP . ':' . $groupid;
ef4109d2 215 $groupfilter->add_filter_value((int)$groupid);
32e5109d
MN
216 } else if (!$hasgroupfilter) { // No need for the group id to be set.
217 $groupid = false;
218 }
24c3db91 219}
a2ed6e69 220
5290d060 221if ($groupid > 0 && ($course->groupmode != SEPARATEGROUPS || $canaccessallgroups)) {
349b705d
AG
222 $grouprenderer = $PAGE->get_renderer('core_group');
223 $groupdetailpage = new \core_group\output\group_details($groupid);
224 echo $grouprenderer->group_details($groupdetailpage);
225}
226
9651e491
JP
227// Manage enrolments.
228$manager = new course_enrolment_manager($PAGE, $course);
229$enrolbuttons = $manager->get_manual_enrol_buttons();
230$enrolrenderer = $PAGE->get_renderer('core_enrol');
231$enrolbuttonsout = '';
232foreach ($enrolbuttons as $enrolbutton) {
233 $enrolbuttonsout .= $enrolrenderer->render($enrolbutton);
bc47b706 234}
5db1ce53 235echo html_writer::div($enrolbuttonsout, 'float-right');
bc47b706 236
9651e491
JP
237// Should use this variable so that we don't break stuff every time a variable is added or changed.
238$baseurl = new moodle_url('/user/index.php', array(
239 'contextid' => $context->id,
240 'id' => $course->id,
241 'perpage' => $perpage));
a2ed6e69 242
bb4a7923
SA
243// Render the unified filter.
244$renderer = $PAGE->get_renderer('core_user');
245echo $renderer->unified_filter($course, $context, $filtersapplied, $baseurl);
246
247echo '<div class="userlist">';
248
7fde8358 249// Add filters to the baseurl after creating unified_filter to avoid losing them.
89e27d99
JF
250foreach (array_unique($filtersapplied) as $filterix => $filter) {
251 $baseurl->param('unified-filters[' . $filterix . ']', $filter);
7fde8358 252}
8146335f
SL
253
254if (count($groupfilter)) {
255 $filterset->add_filter($groupfilter);
256}
257
258if (count($lastaccessfilter)) {
259 $filterset->add_filter($lastaccessfilter);
260}
261
262if (count($rolefilter)) {
263 $filterset->add_filter($rolefilter);
264}
265
266if (count($enrolfilter)) {
267 $filterset->add_filter($enrolfilter);
268}
269
270if (count($statusfilter)) {
271 $filterset->add_filter($statusfilter);
272}
273
274if (count($keywordfilter)) {
275 $filterset->add_filter($keywordfilter);
276}
277
ff475522 278$participanttable = new \core_user\table\participants("user-index-participants-{$course->id}");
8146335f
SL
279$participanttable->set_selectall($selectall);
280$participanttable->set_filterset($filterset);
bc47b706 281$participanttable->define_baseurl($baseurl);
77c645df 282
bc47b706
MN
283// Do this so we can get the total number of rows.
284ob_start();
285$participanttable->out($perpage, true);
286$participanttablehtml = ob_get_contents();
287ob_end_clean();
3e219038 288
6afa07ed
FK
289echo html_writer::tag('p', get_string('participantscount', 'moodle', $participanttable->totalrows));
290
a2ed6e69 291if ($bulkoperations) {
bae72dd0
AN
292 echo html_writer::start_tag('form', [
293 'action' => 'action_redir.php',
294 'method' => 'post',
295 'id' => 'participantsform',
296 'data-course-id' => $course->id,
297 'data-table-unique-id' => $participanttable->uniqueid,
298 ]);
a2ed6e69
SH
299 echo '<div>';
300 echo '<input type="hidden" name="sesskey" value="'.sesskey().'" />';
301 echo '<input type="hidden" name="returnto" value="'.s($PAGE->url->out(false)).'" />';
302}
77c645df 303
bc47b706 304echo $participanttablehtml;
4c7593ff 305
8dd42b38
AH
306$perpageurl = clone($baseurl);
307$perpageurl->remove_params('perpage');
bc47b706 308if ($perpage == SHOW_ALL_PAGE_SIZE && $participanttable->totalrows > DEFAULT_PAGE_SIZE) {
8dd42b38
AH
309 $perpageurl->param('perpage', DEFAULT_PAGE_SIZE);
310 echo $OUTPUT->container(html_writer::link($perpageurl, get_string('showperpage', '', DEFAULT_PAGE_SIZE)), array(), 'showall');
311
bc47b706 312} else if ($participanttable->get_page_size() < $participanttable->totalrows) {
8dd42b38 313 $perpageurl->param('perpage', SHOW_ALL_PAGE_SIZE);
bc47b706
MN
314 echo $OUTPUT->container(html_writer::link($perpageurl, get_string('showall', '', $participanttable->totalrows)),
315 array(), 'showall');
8dd42b38
AH
316}
317
a2ed6e69 318if ($bulkoperations) {
8df785f9 319 echo '<br /><div class="buttons"><div class="form-inline">';
5b7c500a 320
bc47b706 321 if ($participanttable->get_page_size() < $participanttable->totalrows) {
5b7c500a
AH
322 $perpageurl = clone($baseurl);
323 $perpageurl->remove_params('perpage');
324 $perpageurl->param('perpage', SHOW_ALL_PAGE_SIZE);
325 $perpageurl->param('selectall', true);
326 $showalllink = $perpageurl;
327 } else {
328 $showalllink = false;
329 }
330
b27c8d81 331 echo html_writer::start_tag('div', array('class' => 'btn-group'));
bc47b706 332 if ($participanttable->get_page_size() < $participanttable->totalrows) {
5b7c500a 333 // Select all users, refresh page showing all users and mark them all selected.
bc47b706 334 $label = get_string('selectalluserswithcount', 'moodle', $participanttable->totalrows);
cf480263 335 echo html_writer::empty_tag('input', array('type' => 'button', 'id' => 'checkall', 'class' => 'btn btn-secondary',
b27c8d81 336 'value' => $label, 'data-showallink' => $showalllink));
5b7c500a 337 }
b27c8d81 338 echo html_writer::end_tag('div');
a2ed6e69 339 $displaylist = array();
713934f9
ND
340 if (!empty($CFG->messaging)) {
341 $displaylist['#messageselect'] = get_string('messageselectadd');
342 }
a2ed6e69 343 if (!empty($CFG->enablenotes) && has_capability('moodle/notes:manage', $context) && $context->id != $frontpagectx->id) {
b5b81de3 344 $displaylist['#addgroupnote'] = get_string('addnewnote', 'notes');
a2ed6e69
SH
345 }
346
e5abe2af
LB
347 $params = ['operation' => 'download_participants'];
348
349 $downloadoptions = [];
350 $formats = core_plugin_manager::instance()->get_plugins_of_type('dataformat');
351 foreach ($formats as $format) {
352 if ($format->is_enabled()) {
353 $params = ['operation' => 'download_participants', 'dataformat' => $format->name];
354 $url = new moodle_url('bulkchange.php', $params);
355 $downloadoptions[$url->out(false)] = get_string('dataformat', $format->component);
356 }
357 }
358
359 if (!empty($downloadoptions)) {
360 $displaylist[] = [get_string('downloadas', 'table') => $downloadoptions];
361 }
362
ca560952 363 if ($context->id != $frontpagectx->id) {
f4ddc4ec
DW
364 $instances = $manager->get_enrolment_instances();
365 $plugins = $manager->get_enrolment_plugins(false);
366 foreach ($instances as $key => $instance) {
367 if (!isset($plugins[$instance->enrol])) {
368 // Weird, some broken stuff in plugin.
369 continue;
370 }
371 $plugin = $plugins[$instance->enrol];
ca560952
DW
372 $bulkoperations = $plugin->get_bulk_operations($manager);
373
374 $pluginoptions = [];
375 foreach ($bulkoperations as $key => $bulkoperation) {
376 $params = ['plugin' => $plugin->get_name(), 'operation' => $key];
377 $url = new moodle_url('bulkchange.php', $params);
378 $pluginoptions[$url->out(false)] = $bulkoperation->get_title();
379 }
380 if (!empty($pluginoptions)) {
381 $name = get_string('pluginname', 'enrol_' . $plugin->get_name());
382 $displaylist[] = [$name => $pluginoptions];
383 }
689ccae5
DW
384 }
385 }
386
df92be9d
JP
387 $selectactionparams = array(
388 'id' => 'formactionid',
389 'class' => 'ml-2',
390 'data-action' => 'toggle',
391 'data-togglegroup' => 'participants-table',
392 'data-toggle' => 'action',
393 'disabled' => empty($selectall)
394 );
8aecd857
SR
395 $label = html_writer::tag('label', get_string("withselectedusers"),
396 ['for' => 'formactionid', 'class' => 'col-form-label d-inline']);
397 $select = html_writer::select($displaylist, 'formaction', '', ['' => 'choosedots'], $selectactionparams);
398 echo html_writer::tag('div', $label . $select);
399
400 echo '<input type="hidden" name="id" value="' . $course->id . '" />';
bae72dd0 401 echo '<div class="d-none" data-region="state-help-icon">' . $OUTPUT->help_icon('publishstate', 'notes') . '</div>';
8df785f9 402 echo '</div></div></div>';
a2ed6e69
SH
403 echo '</form>';
404
bae72dd0
AN
405 $options = (object) [
406 'uniqueid' => $participanttable->uniqueid,
407 'noteStateNames' => note_get_state_names(),
408 ];
b5b81de3 409 $PAGE->requires->js_call_amd('core_user/participants', 'init', [$options]);
a2ed6e69 410}
b90e2f19 411
a2ed6e69 412echo '</div>'; // Userlist.
f9903ed0 413
a78ed71c 414$enrolrenderer = $PAGE->get_renderer('core_enrol');
5db1ce53 415echo '<div class="float-right">';
3dec4c6c
SR
416// Need to re-generate the buttons to avoid having elements with duplicate ids on the page.
417$enrolbuttons = $manager->get_manual_enrol_buttons();
a78ed71c
DW
418foreach ($enrolbuttons as $enrolbutton) {
419 echo $enrolrenderer->render($enrolbutton);
420}
421echo '</div>';
422
5fa2d502
DW
423if ($newcourse == 1) {
424 $str = get_string('proceedtocourse', 'enrol');
5fa2d502 425 // The margin is to make it line up with the enrol users button when they are both on the same line.
d470c68b 426 $classes = 'my-1';
5fa2d502
DW
427 $url = course_get_url($course);
428 echo $OUTPUT->single_button($url, $str, 'GET', array('class' => $classes));
429}
430
a2ed6e69 431echo $OUTPUT->footer();