MDL-68464 enrol_manual: Reload participants table after enroling
[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
e0e7b19f
AN
227// Should use this variable so that we don't break stuff every time a variable is added or changed.
228$baseurl = new moodle_url('/user/index.php', array(
229 'contextid' => $context->id,
230 'id' => $course->id,
231 'perpage' => $perpage));
232
233$participanttable = new \core_user\table\participants("user-index-participants-{$course->id}");
234$participanttable->set_selectall($selectall);
235$participanttable->define_baseurl($baseurl);
236
9651e491
JP
237// Manage enrolments.
238$manager = new course_enrolment_manager($PAGE, $course);
239$enrolbuttons = $manager->get_manual_enrol_buttons();
240$enrolrenderer = $PAGE->get_renderer('core_enrol');
241$enrolbuttonsout = '';
242foreach ($enrolbuttons as $enrolbutton) {
243 $enrolbuttonsout .= $enrolrenderer->render($enrolbutton);
bc47b706 244}
e0e7b19f
AN
245echo html_writer::div($enrolbuttonsout, 'float-right', [
246 'data-region' => 'wrapper',
247 'data-table-uniqueid' => $participanttable->uniqueid,
248]);
a2ed6e69 249
bb4a7923
SA
250// Render the unified filter.
251$renderer = $PAGE->get_renderer('core_user');
252echo $renderer->unified_filter($course, $context, $filtersapplied, $baseurl);
253
254echo '<div class="userlist">';
255
7fde8358 256// Add filters to the baseurl after creating unified_filter to avoid losing them.
89e27d99
JF
257foreach (array_unique($filtersapplied) as $filterix => $filter) {
258 $baseurl->param('unified-filters[' . $filterix . ']', $filter);
7fde8358 259}
8146335f
SL
260
261if (count($groupfilter)) {
262 $filterset->add_filter($groupfilter);
263}
264
265if (count($lastaccessfilter)) {
266 $filterset->add_filter($lastaccessfilter);
267}
268
269if (count($rolefilter)) {
270 $filterset->add_filter($rolefilter);
271}
272
273if (count($enrolfilter)) {
274 $filterset->add_filter($enrolfilter);
275}
276
277if (count($statusfilter)) {
278 $filterset->add_filter($statusfilter);
279}
280
281if (count($keywordfilter)) {
282 $filterset->add_filter($keywordfilter);
283}
284
bc47b706
MN
285// Do this so we can get the total number of rows.
286ob_start();
e0e7b19f 287$participanttable->set_filterset($filterset);
bc47b706
MN
288$participanttable->out($perpage, true);
289$participanttablehtml = ob_get_contents();
290ob_end_clean();
3e219038 291
6afa07ed
FK
292echo html_writer::tag('p', get_string('participantscount', 'moodle', $participanttable->totalrows));
293
a2ed6e69
SH
294if ($bulkoperations) {
295 echo '<form action="action_redir.php" method="post" id="participantsform">';
296 echo '<div>';
297 echo '<input type="hidden" name="sesskey" value="'.sesskey().'" />';
298 echo '<input type="hidden" name="returnto" value="'.s($PAGE->url->out(false)).'" />';
299}
77c645df 300
bc47b706 301echo $participanttablehtml;
4c7593ff 302
8dd42b38
AH
303$perpageurl = clone($baseurl);
304$perpageurl->remove_params('perpage');
bc47b706 305if ($perpage == SHOW_ALL_PAGE_SIZE && $participanttable->totalrows > DEFAULT_PAGE_SIZE) {
8dd42b38
AH
306 $perpageurl->param('perpage', DEFAULT_PAGE_SIZE);
307 echo $OUTPUT->container(html_writer::link($perpageurl, get_string('showperpage', '', DEFAULT_PAGE_SIZE)), array(), 'showall');
308
bc47b706 309} else if ($participanttable->get_page_size() < $participanttable->totalrows) {
8dd42b38 310 $perpageurl->param('perpage', SHOW_ALL_PAGE_SIZE);
bc47b706
MN
311 echo $OUTPUT->container(html_writer::link($perpageurl, get_string('showall', '', $participanttable->totalrows)),
312 array(), 'showall');
8dd42b38
AH
313}
314
a2ed6e69 315if ($bulkoperations) {
8df785f9 316 echo '<br /><div class="buttons"><div class="form-inline">';
5b7c500a 317
bc47b706 318 if ($participanttable->get_page_size() < $participanttable->totalrows) {
5b7c500a
AH
319 $perpageurl = clone($baseurl);
320 $perpageurl->remove_params('perpage');
321 $perpageurl->param('perpage', SHOW_ALL_PAGE_SIZE);
322 $perpageurl->param('selectall', true);
323 $showalllink = $perpageurl;
324 } else {
325 $showalllink = false;
326 }
327
b27c8d81 328 echo html_writer::start_tag('div', array('class' => 'btn-group'));
bc47b706 329 if ($participanttable->get_page_size() < $participanttable->totalrows) {
5b7c500a 330 // Select all users, refresh page showing all users and mark them all selected.
bc47b706 331 $label = get_string('selectalluserswithcount', 'moodle', $participanttable->totalrows);
cf480263 332 echo html_writer::empty_tag('input', array('type' => 'button', 'id' => 'checkall', 'class' => 'btn btn-secondary',
b27c8d81 333 'value' => $label, 'data-showallink' => $showalllink));
5b7c500a 334 }
b27c8d81 335 echo html_writer::end_tag('div');
a2ed6e69 336 $displaylist = array();
713934f9
ND
337 if (!empty($CFG->messaging)) {
338 $displaylist['#messageselect'] = get_string('messageselectadd');
339 }
a2ed6e69 340 if (!empty($CFG->enablenotes) && has_capability('moodle/notes:manage', $context) && $context->id != $frontpagectx->id) {
b5b81de3 341 $displaylist['#addgroupnote'] = get_string('addnewnote', 'notes');
a2ed6e69
SH
342 }
343
e5abe2af
LB
344 $params = ['operation' => 'download_participants'];
345
346 $downloadoptions = [];
347 $formats = core_plugin_manager::instance()->get_plugins_of_type('dataformat');
348 foreach ($formats as $format) {
349 if ($format->is_enabled()) {
350 $params = ['operation' => 'download_participants', 'dataformat' => $format->name];
351 $url = new moodle_url('bulkchange.php', $params);
352 $downloadoptions[$url->out(false)] = get_string('dataformat', $format->component);
353 }
354 }
355
356 if (!empty($downloadoptions)) {
357 $displaylist[] = [get_string('downloadas', 'table') => $downloadoptions];
358 }
359
ca560952 360 if ($context->id != $frontpagectx->id) {
f4ddc4ec
DW
361 $instances = $manager->get_enrolment_instances();
362 $plugins = $manager->get_enrolment_plugins(false);
363 foreach ($instances as $key => $instance) {
364 if (!isset($plugins[$instance->enrol])) {
365 // Weird, some broken stuff in plugin.
366 continue;
367 }
368 $plugin = $plugins[$instance->enrol];
ca560952
DW
369 $bulkoperations = $plugin->get_bulk_operations($manager);
370
371 $pluginoptions = [];
372 foreach ($bulkoperations as $key => $bulkoperation) {
373 $params = ['plugin' => $plugin->get_name(), 'operation' => $key];
374 $url = new moodle_url('bulkchange.php', $params);
375 $pluginoptions[$url->out(false)] = $bulkoperation->get_title();
376 }
377 if (!empty($pluginoptions)) {
378 $name = get_string('pluginname', 'enrol_' . $plugin->get_name());
379 $displaylist[] = [$name => $pluginoptions];
380 }
689ccae5
DW
381 }
382 }
383
df92be9d
JP
384 $selectactionparams = array(
385 'id' => 'formactionid',
386 'class' => 'ml-2',
387 'data-action' => 'toggle',
388 'data-togglegroup' => 'participants-table',
389 'data-toggle' => 'action',
390 'disabled' => empty($selectall)
391 );
8aecd857
SR
392 $label = html_writer::tag('label', get_string("withselectedusers"),
393 ['for' => 'formactionid', 'class' => 'col-form-label d-inline']);
394 $select = html_writer::select($displaylist, 'formaction', '', ['' => 'choosedots'], $selectactionparams);
395 echo html_writer::tag('div', $label . $select);
396
397 echo '<input type="hidden" name="id" value="' . $course->id . '" />';
8df785f9 398 echo '</div></div></div>';
a2ed6e69
SH
399 echo '</form>';
400
b5b81de3
DW
401 $options = new stdClass();
402 $options->courseid = $course->id;
403 $options->noteStateNames = note_get_state_names();
404 $options->stateHelpIcon = $OUTPUT->help_icon('publishstate', 'notes');
405 $PAGE->requires->js_call_amd('core_user/participants', 'init', [$options]);
a2ed6e69 406}
b90e2f19 407
a2ed6e69 408echo '</div>'; // Userlist.
f9903ed0 409
a78ed71c 410$enrolrenderer = $PAGE->get_renderer('core_enrol');
5db1ce53 411echo '<div class="float-right">';
3dec4c6c
SR
412// Need to re-generate the buttons to avoid having elements with duplicate ids on the page.
413$enrolbuttons = $manager->get_manual_enrol_buttons();
a78ed71c
DW
414foreach ($enrolbuttons as $enrolbutton) {
415 echo $enrolrenderer->render($enrolbutton);
416}
417echo '</div>';
418
5fa2d502
DW
419if ($newcourse == 1) {
420 $str = get_string('proceedtocourse', 'enrol');
5fa2d502 421 // The margin is to make it line up with the enrol users button when they are both on the same line.
d470c68b 422 $classes = 'my-1';
5fa2d502
DW
423 $url = course_get_url($course);
424 echo $OUTPUT->single_button($url, $str, 'GET', array('class' => $classes));
425}
426
a2ed6e69 427echo $OUTPUT->footer();