2 // This file is part of Moodle - http://moodle.org/
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.
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/>.
18 * Class for rendering user filters on the course participants page.
21 * @copyright 2020 Michael Hawkins <michaelh@moodle.com>
22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24 namespace core_user\output;
33 * Class for rendering user filters on the course participants page.
35 * @copyright 2020 Michael Hawkins <michaelh@moodle.com>
36 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
38 class participants_filter implements renderable, templatable {
40 /** @var context_course $context The context where the filters are being rendered. */
43 /** @var string $tableregionid The table to be updated by this filter */
44 protected $tableregionid;
46 /** @var stdClass $course The course shown */
50 * Participants filter constructor.
52 * @param context_course $context The context where the filters are being rendered.
53 * @param string $tableregionid The table to be updated by this filter
55 public function __construct(context_course $context, string $tableregionid) {
56 $this->context = $context;
57 $this->tableregionid = $tableregionid;
59 $this->course = get_course($context->instanceid);
63 * Get data for all filter types.
67 protected function get_filtertypes(): array {
70 $filtertypes[] = $this->get_keyword_filter();
72 if ($filtertype = $this->get_enrolmentstatus_filter()) {
73 $filtertypes[] = $filtertype;
76 if ($filtertype = $this->get_roles_filter()) {
77 $filtertypes[] = $filtertype;
80 if ($filtertype = $this->get_enrolments_filter()) {
81 $filtertypes[] = $filtertype;
84 if ($filtertype = $this->get_groups_filter()) {
85 $filtertypes[] = $filtertype;
88 if ($filtertype = $this->get_accesssince_filter()) {
89 $filtertypes[] = $filtertype;
96 * Get data for the enrolment status filter.
98 * @return stdClass|null
100 protected function get_enrolmentstatus_filter(): ?stdClass {
101 if (!has_capability('moodle/course:enrolreview', $this->context)) {
105 return $this->get_filter_object(
107 get_string('participationstatus', 'core_enrol'),
113 'value' => ENROL_USER_ACTIVE,
114 'title' => get_string('active'),
117 'value' => ENROL_USER_SUSPENDED,
118 'title' => get_string('inactive'),
125 * Get data for the roles filter.
127 * @return stdClass|null
129 protected function get_roles_filter(): ?stdClass {
131 $roles += [-1 => get_string('noroles', 'role')];
132 $roles += get_viewable_roles($this->context);
134 if (has_capability('moodle/role:assign', $this->context)) {
135 $roles += get_assignable_roles($this->context, ROLENAME_ALIAS);
138 return $this->get_filter_object(
140 get_string('roles', 'core_role'),
144 array_map(function($id, $title) {
149 }, array_keys($roles), array_values($roles))
154 * Get data for the roles filter.
156 * @return stdClass|null
158 protected function get_enrolments_filter(): ?stdClass {
159 if (!has_capability('moodle/course:enrolreview', $this->context)) {
163 if ($this->course->id == SITEID) {
164 // No enrolment methods for the site.
168 $instances = enrol_get_instances($this->course->id, true);
169 $plugins = enrol_get_plugins(false);
171 return $this->get_filter_object(
173 get_string('enrolmentinstances', 'core_enrol'),
177 array_filter(array_map(function($instance) use ($plugins): ?stdClass {
178 if (!array_key_exists($instance->enrol, $plugins)) {
183 'value' => $instance->id,
184 'title' => $plugins[$instance->enrol]->get_instance_name($instance),
186 }, array_values($instances)))
191 * Get data for the groups filter.
193 * @return stdClass|null
195 protected function get_groups_filter(): ?stdClass {
198 // Filter options for groups, if available.
199 $seeallgroups = has_capability('moodle/site:accessallgroups', $this->context);
200 $seeallgroups = $seeallgroups || ($this->course->groupmode != SEPARATEGROUPS);
203 $groups += [USERSWITHOUTGROUP => (object) [
204 'id' => USERSWITHOUTGROUP,
205 'name' => get_string('nogroup', 'group'),
207 $groups += groups_get_all_groups($this->course->id);
209 // Otherwise, just list the groups the user belongs to.
210 $groups = groups_get_all_groups($this->course->id, $USER->id);
213 // Return no data if no groups found (which includes if the only value is 'No group').
214 if (empty($groups) || (count($groups) === 1 && array_key_exists(-1, $groups))) {
218 return $this->get_filter_object(
220 get_string('groups', 'core_group'),
224 array_map(function($group) {
226 'value' => $group->id,
227 'title' => $group->name,
229 }, array_values($groups))
234 * Get data for the accesssince filter.
236 * @return stdClass|null
238 protected function get_accesssince_filter(): ?stdClass {
242 if (!has_capability('moodle/course:viewhiddenuserfields', $this->context)) {
243 $hiddenfields = array_flip(explode(',', $CFG->hiddenuserfields));
246 if (array_key_exists('lastaccess', $hiddenfields)) {
250 // Get minimum lastaccess for this course and display a dropbox to filter by lastaccess going back this far.
251 // We need to make it diferently for normal courses and site course.
252 if (!$this->course->id == SITEID) {
255 'courseid' => $this->course->id,
258 $select = 'courseid = :courseid AND timeaccess != :timeaccess';
259 $minlastaccess = $DB->get_field_select('user_lastaccess', 'MIN(timeaccess)', $select, $params);
260 $lastaccess0exists = $DB->record_exists('user_lastaccess', $params);
263 $params = ['lastaccess' => 0];
264 $select = 'lastaccess != :lastaccess';
265 $minlastaccess = $DB->get_field_select('user', 'MIN(lastaccess)', $select, $params);
266 $lastaccess0exists = $DB->record_exists('user', $params);
269 $now = usergetmidnight(time());
271 $criteria = get_string('usersnoaccesssince');
273 $getoptions = function(int $count, string $singletype, string $type) use ($now, $minlastaccess): array {
275 for ($i = 1; $i <= $count; $i++) {
276 $timestamp = strtotime("-{$i} {$type}", $now);
277 if ($timestamp < $minlastaccess) {
282 $title = get_string("num{$singletype}", 'moodle', $i);
284 $title = get_string("num{$type}", 'moodle', $i);
288 'value' => $timestamp,
296 $values = array_merge(
297 $getoptions(6, 'day', 'days'),
298 $getoptions(10, 'week', 'weeks'),
299 $getoptions(11, 'month', 'months'),
300 $getoptions(1, 'year', 'years')
303 if ($lastaccess0exists) {
306 'title' => get_string('never', 'moodle'),
310 if (count($values) <= 1) {
315 return $this->get_filter_object(
317 get_string('usersnoaccesssince'),
326 * Get data for the keywords filter.
328 * @return stdClass|null
330 protected function get_keyword_filter(): ?stdClass {
331 return $this->get_filter_object(
333 get_string('filterbykeyword', 'core_user'),
336 'core_user/local/participantsfilter/filtertypes/keyword',
343 * Export the renderer data in a mustache template friendly format.
345 * @param renderer_base $output Unused.
346 * @return stdClass Data in a format compatible with a mustache template.
348 public function export_for_template(renderer_base $output): stdClass {
350 'tableregionid' => $this->tableregionid,
351 'courseid' => $this->context->instanceid,
352 'filtertypes' => $this->get_filtertypes(),
360 * Get a standardised filter object.
362 * @param string $name
363 * @param string $title
364 * @param bool $custom
365 * @param bool $multiple
366 * @param string|null $filterclass
367 * @param array $values
368 * @param bool $allowempty
369 * @return stdClass|null
371 protected function get_filter_object(
376 ?string $filterclass,
378 bool $allowempty = false
381 if (!$allowempty && empty($values)) {
382 // Do not show empty filters.
389 'allowcustom' => $custom,
390 'allowmultiple' => $multiple,
391 'filtertypeclass' => $filterclass,