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
;
31 * Class for rendering user filters on the course participants page.
33 * @copyright 2020 Michael Hawkins <michaelh@moodle.com>
34 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
36 class participants_filter
extends \core\output\datafilter
{
39 * Get data for all filter types.
43 protected function get_filtertypes(): array {
46 $filtertypes[] = $this->get_keyword_filter();
48 if ($filtertype = $this->get_enrolmentstatus_filter()) {
49 $filtertypes[] = $filtertype;
52 if ($filtertype = $this->get_roles_filter()) {
53 $filtertypes[] = $filtertype;
56 if ($filtertype = $this->get_enrolments_filter()) {
57 $filtertypes[] = $filtertype;
60 if ($filtertype = $this->get_groups_filter()) {
61 $filtertypes[] = $filtertype;
64 if ($filtertype = $this->get_accesssince_filter()) {
65 $filtertypes[] = $filtertype;
68 if ($filtertype = $this->get_country_filter()) {
69 $filtertypes[] = $filtertype;
76 * Get data for the enrolment status filter.
78 * @return stdClass|null
80 protected function get_enrolmentstatus_filter(): ?stdClass
{
81 if (!has_capability('moodle/course:enrolreview', $this->context
)) {
85 return $this->get_filter_object(
87 get_string('participationstatus', 'core_enrol'),
93 'value' => ENROL_USER_ACTIVE
,
94 'title' => get_string('active'),
97 'value' => ENROL_USER_SUSPENDED
,
98 'title' => get_string('inactive'),
105 * Get data for the roles filter.
107 * @return stdClass|null
109 protected function get_roles_filter(): ?stdClass
{
111 $roles +
= [-1 => get_string('noroles', 'role')];
112 $roles +
= get_viewable_roles($this->context
, null, ROLENAME_BOTH
);
114 if (has_capability('moodle/role:assign', $this->context
)) {
115 $roles +
= get_assignable_roles($this->context
, ROLENAME_BOTH
);
118 return $this->get_filter_object(
120 get_string('roles', 'core_role'),
124 array_map(function($id, $title) {
129 }, array_keys($roles), array_values($roles))
134 * Get data for the roles filter.
136 * @return stdClass|null
138 protected function get_enrolments_filter(): ?stdClass
{
139 if (!has_capability('moodle/course:enrolreview', $this->context
)) {
143 if ($this->course
->id
== SITEID
) {
144 // No enrolment methods for the site.
148 $instances = enrol_get_instances($this->course
->id
, true);
149 $plugins = enrol_get_plugins(false);
151 return $this->get_filter_object(
153 get_string('enrolmentinstances', 'core_enrol'),
157 array_filter(array_map(function($instance) use ($plugins): ?stdClass
{
158 if (!array_key_exists($instance->enrol
, $plugins)) {
163 'value' => $instance->id
,
164 'title' => $plugins[$instance->enrol
]->get_instance_name($instance),
166 }, array_values($instances)))
171 * Get data for the groups filter.
173 * @return stdClass|null
175 protected function get_groups_filter(): ?stdClass
{
178 // Filter options for groups, if available.
179 $seeallgroups = has_capability('moodle/site:accessallgroups', $this->context
);
180 $seeallgroups = $seeallgroups ||
($this->course
->groupmode
!= SEPARATEGROUPS
);
183 $groups +
= [USERSWITHOUTGROUP
=> (object) [
184 'id' => USERSWITHOUTGROUP
,
185 'name' => get_string('nogroup', 'group'),
187 $groups +
= groups_get_all_groups($this->course
->id
);
189 // Otherwise, just list the groups the user belongs to.
190 $groups = groups_get_all_groups($this->course
->id
, $USER->id
);
193 // Return no data if no groups found (which includes if the only value is 'No group').
194 if (empty($groups) ||
(count($groups) === 1 && array_key_exists(-1, $groups))) {
198 return $this->get_filter_object(
200 get_string('groups', 'core_group'),
204 array_map(function($group) {
206 'value' => $group->id
,
207 'title' => format_string($group->name
, true, ['context' => $this->context
]),
209 }, array_values($groups))
214 * Get data for the accesssince filter.
216 * @return stdClass|null
218 protected function get_accesssince_filter(): ?stdClass
{
222 if (!has_capability('moodle/course:viewhiddenuserfields', $this->context
)) {
223 $hiddenfields = array_flip(explode(',', $CFG->hiddenuserfields
));
226 if (array_key_exists('lastaccess', $hiddenfields)) {
230 // Get minimum lastaccess for this course and display a dropbox to filter by lastaccess going back this far.
231 // We need to make it diferently for normal courses and site course.
232 if (!($this->course
->id
== SITEID
)) {
235 'courseid' => $this->course
->id
,
238 $select = 'courseid = :courseid AND timeaccess != :timeaccess';
239 $minlastaccess = $DB->get_field_select('user_lastaccess', 'MIN(timeaccess)', $select, $params);
241 // Determine enrolled users, who do not have accompanying lastaccess to the course.
242 [$enrolledsql, $enrolledparams] = get_enrolled_sql($this->context
);
246 JOIN ({$enrolledsql}) je ON je.id = u.id
247 LEFT JOIN {user_lastaccess} ula ON ula.userid = je.id AND ula.courseid = :courseid
248 WHERE COALESCE(ula.timeaccess, 0) = :timeaccess";
250 $lastaccess0exists = $DB->record_exists_sql($sql, array_merge($params, $enrolledparams));
253 $params = ['lastaccess' => 0];
254 $select = 'lastaccess != :lastaccess';
255 $minlastaccess = $DB->get_field_select('user', 'MIN(lastaccess)', $select, $params);
256 $lastaccess0exists = $DB->record_exists('user', $params);
259 $now = usergetmidnight(time());
261 $getoptions = function(int $count, string $singletype, string $type) use ($now, $minlastaccess): array {
263 for ($i = 1; $i <= $count; $i++
) {
264 $timestamp = strtotime("-{$i} {$type}", $now);
265 if ($timestamp < $minlastaccess) {
270 $title = get_string("num{$singletype}", 'moodle', $i);
272 $title = get_string("num{$type}", 'moodle', $i);
276 'value' => $timestamp,
284 $values = array_merge(
285 $getoptions(6, 'day', 'days'),
286 $getoptions(10, 'week', 'weeks'),
287 $getoptions(11, 'month', 'months'),
288 $getoptions(1, 'year', 'years')
291 if ($lastaccess0exists) {
294 'title' => get_string('never', 'moodle'),
298 if (count($values) <= 1) {
303 return $this->get_filter_object(
305 get_string('usersnoaccesssince'),
314 * Get data for the country filter
316 * @return stdClass|null
318 protected function get_country_filter(): ?stdClass
{
319 $extrauserfields = fields
::get_identity_fields($this->context
, false);
320 if (array_search('country', $extrauserfields) === false) {
324 $countries = get_string_manager()->get_list_of_countries(true);
326 return $this->get_filter_object(
328 get_string('country'),
331 'core/datafilter/filtertypes/country',
332 array_map(function(string $code, string $name): stdClass
{
337 }, array_keys($countries), array_values($countries))
342 * Get data for the keywords filter.
344 * @return stdClass|null
346 protected function get_keyword_filter(): ?stdClass
{
347 return $this->get_filter_object(
349 get_string('filterbykeyword', 'core_user'),
352 'core/datafilter/filtertypes/keyword',
359 * Export the renderer data in a mustache template friendly format.
361 * @param renderer_base $output Unused.
362 * @return stdClass Data in a format compatible with a mustache template.
364 public function export_for_template(renderer_base
$output): stdClass
{
366 'tableregionid' => $this->tableregionid
,
367 'courseid' => $this->context
->instanceid
,
368 'filtertypes' => $this->get_filtertypes(),