c88ec0459965f3fbcd383c10cd82b0d3ac597596
[moodle.git] / user / classes / output / participants_filter.php
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.
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/>.
17 /**
18  * Class for rendering user filters on the course participants page.
19  *
20  * @package    core_user
21  * @copyright  2020 Michael Hawkins <michaelh@moodle.com>
22  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23  */
24 namespace core_user\output;
26 use context_course;
27 use renderable;
28 use renderer_base;
29 use stdClass;
30 use templatable;
32 /**
33  * Class for rendering user filters on the course participants page.
34  *
35  * @copyright  2020 Michael Hawkins <michaelh@moodle.com>
36  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
37  */
38 class participants_filter implements renderable, templatable {
40     /** @var context_course $context The context where the filters are being rendered. */
41     protected $context;
43     /** @var string $tableregionid The table to be updated by this filter */
44     protected $tableregionid;
46     /** @var stdClass $course The course shown */
47     protected $course;
49     /**
50      * Participants filter constructor.
51      *
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
54      */
55     public function __construct(context_course $context, string $tableregionid) {
56         $this->context = $context;
57         $this->tableregionid = $tableregionid;
59         $this->course = get_course($context->instanceid);
60     }
62     /**
63      * Get data for all filter types.
64      *
65      * @return array
66      */
67     protected function get_filtertypes(): array {
68         $filtertypes = [];
70         if ($filtertype = $this->get_enrolmentstatus_filter()) {
71             $filtertypes[] = $filtertype;
72         }
74         if ($filtertype = $this->get_roles_filter()) {
75             $filtertypes[] = $filtertype;
76         }
78         if ($filtertype = $this->get_enrolments_filter()) {
79             $filtertypes[] = $filtertype;
80         }
82         if ($filtertype = $this->get_groups_filter()) {
83             $filtertypes[] = $filtertype;
84         }
86         if ($filtertype = $this->get_accesssince_filter()) {
87             $filtertypes[] = $filtertype;
88         }
90         return $filtertypes;
91     }
93     /**
94      * Get data for the enrolment status filter.
95      *
96      * @return stdClass|null
97      */
98     protected function get_enrolmentstatus_filter(): ?stdClass {
99         if (!has_capability('moodle/course:enrolreview', $this->context)) {
100             return null;
101         }
103         return $this->get_filter_object(
104             'status',
105             get_string('participationstatus', 'core_enrol'),
106             false,
107             true,
108             null,
109             [
110                 (object) [
111                     'value' => ENROL_USER_ACTIVE,
112                     'title' => get_string('active'),
113                 ],
114                 (object) [
115                     'value' => ENROL_USER_SUSPENDED,
116                     'title'  => get_string('inactive'),
117                 ],
118             ]
119         );
120     }
122     /**
123      * Get data for the roles filter.
124      *
125      * @return stdClass|null
126      */
127     protected function get_roles_filter(): ?stdClass {
128         $roles = [];
129         $roles += [-1 => get_string('noroles', 'role')];
130         $roles += get_viewable_roles($this->context);
132         if (has_capability('moodle/role:assign', $this->context)) {
133             $roles += get_assignable_roles($this->context, ROLENAME_ALIAS);
134         }
136         return $this->get_filter_object(
137             'roles',
138             get_string('roles', 'core_role'),
139             false,
140             true,
141             null,
142             array_map(function($id, $title) {
143                 return (object) [
144                     'value' => $id,
145                     'title' => $title,
146                 ];
147             }, array_keys($roles), array_values($roles))
148         );
149     }
151     /**
152      * Get data for the roles filter.
153      *
154      * @return stdClass|null
155      */
156     protected function get_enrolments_filter(): ?stdClass {
157         if (!has_capability('moodle/course:enrolreview', $this->context)) {
158             return null;
159         }
161         if ($this->course->id == SITEID) {
162             // No enrolment methods for the site.
163             return null;
164         }
166         $instances = enrol_get_instances($this->course->id, true);
167         $plugins = enrol_get_plugins(false);
169         return $this->get_filter_object(
170             'enrolments',
171             get_string('enrolmentinstances', 'core_enrol'),
172             false,
173             true,
174             null,
175             array_filter(array_map(function($instance) use ($plugins): ?stdClass {
176                 if (!array_key_exists($instance->enrol, $plugins)) {
177                     return null;
178                 }
180                 return (object) [
181                     'value' => $instance->id,
182                     'title' => $plugins[$instance->enrol]->get_instance_name($instance),
183                 ];
184             }, array_values($instances)))
185         );
186     }
188     /**
189      * Get data for the groups filter.
190      *
191      * @return stdClass|null
192      */
193     protected function get_groups_filter(): ?stdClass {
194         global $USER;
196         // Filter options for groups, if available.
197         $seeallgroups = has_capability('moodle/site:accessallgroups', $this->context);
198         $seeallgroups = $seeallgroups || ($this->course->groupmode != SEPARATEGROUPS);
199         if ($seeallgroups) {
200             $groups = [];
201             $groups += [USERSWITHOUTGROUP => (object) [
202                     'id' => USERSWITHOUTGROUP,
203                     'name' => get_string('nogroup', 'group'),
204                 ]];
205             $groups += groups_get_all_groups($this->course->id);
206         } else {
207             // Otherwise, just list the groups the user belongs to.
208             $groups = groups_get_all_groups($this->course->id, $USER->id);
209         }
211         if (empty($groups)) {
212             return null;
213         }
215         return $this->get_filter_object(
216             'groups',
217             get_string('groups', 'core_group'),
218             false,
219             true,
220             null,
221             array_map(function($group) {
222                 return (object) [
223                     'value' => $group->id,
224                     'title' => $group->name,
225                 ];
226             }, array_values($groups))
227         );
228     }
230     /**
231      * Get data for the accesssince filter.
232      *
233      * @return stdClass|null
234      */
235     protected function get_accesssince_filter(): ?stdClass {
236         global $CFG, $DB;
238         $hiddenfields = [];
239         if (!has_capability('moodle/course:viewhiddenuserfields', $this->context)) {
240             $hiddenfields = array_flip(explode(',', $CFG->hiddenuserfields));
241         }
243         if (array_key_exists('lastaccess', $hiddenfields)) {
244             return null;
245         }
247         // Get minimum lastaccess for this course and display a dropbox to filter by lastaccess going back this far.
248         // We need to make it diferently for normal courses and site course.
249         if (!$this->course->id == SITEID) {
250             // Regular course.
251             $params = [
252                 'courseid' => $this->course->id,
253                 'timeaccess' => 0,
254             ];
255             $select = 'courseid = :courseid AND timeaccess != :timeaccess';
256             $minlastaccess = $DB->get_field_select('user_lastaccess', 'MIN(timeaccess)', $select, $params);
257             $lastaccess0exists = $DB->record_exists('user_lastaccess', $params);
258         } else {
259             // Front page.
260             $params = ['lastaccess' => 0];
261             $select = 'lastaccess != :lastaccess';
262             $minlastaccess = $DB->get_field_select('user', 'MIN(lastaccess)', $select, $params);
263             $lastaccess0exists = $DB->record_exists('user', $params);
264         }
266         $now = usergetmidnight(time());
267         $timeoptions = [];
268         $criteria = get_string('usersnoaccesssince');
270         $getoptions = function(int $count, string $singletype, string $type) use ($now, $minlastaccess): array {
271             $values = [];
272             for ($i = 1; $i <= $count; $i++) {
273                 $timestamp = strtotime("-{$i} {$type}", $now);
274                 if ($timestamp < $minlastaccess) {
275                     break;
276                 }
278                 if ($i === 1) {
279                     $title = get_string("num{$singletype}", 'moodle', $i);
280                 } else {
281                     $title = get_string("num{$type}", 'moodle', $i);
282                 }
284                 $values[] = [
285                     'value' => $timestamp,
286                     'title' => $title,
287                 ];
288             }
290             return $values;
291         };
293         $values = array_merge(
294             $getoptions(6, 'day', 'days'),
295             $getoptions(10, 'week', 'weeks'),
296             $getoptions(11, 'month', 'months'),
297             $getoptions(1, 'year', 'years')
298         );
300         if ($lastaccess0exists) {
301             $values[] = [
302                 'value' => time(),
303                 'title' => get_string('never', 'moodle'),
304             ];
305         }
307         if (count($values) <= 1) {
308             // Nothing to show.
309             return null;
310         }
312         return $this->get_filter_object(
313             'accesssince',
314             get_string('usersnoaccesssince'),
315             false,
316             false,
317             null,
318             $values
319         );
320     }
322     /**
323      * Export the renderer data in a mustache template friendly format.
324      *
325      * @param renderer_base $output Unused.
326      * @return stdClass Data in a format compatible with a mustache template.
327      */
328     public function export_for_template(renderer_base $output): stdClass {
329         return (object) [
330             'tableregionid' => $this->tableregionid,
331             'courseid' => $this->context->instanceid,
332             'filtertypes' => $this->get_filtertypes(),
333         ];
335         return $data;
336     }
338     /**
339      * Get a standardised filter object.
340      *
341      * @param string $name
342      * @param string $title
343      * @param bool $custom
344      * @param bool $multiple
345      * @param string|null $filterclass
346      * @param array $values
347      * @return stdClass|null
348      */
349     protected function get_filter_object(
350         string $name,
351         string $title,
352         bool $custom,
353         bool $multiple,
354         ?string $filterclass,
355         array $values
356     ): ?stdClass {
357         if (empty($values)) {
358             // Do not show empty filters.
359             return null;
360         }
362         return (object) [
363             'name' => $name,
364             'title' => $title,
365             'allowcustom' => $custom,
366             'allowmultiple' => $multiple,
367             'filtertypeclass' => $filterclass,
368             'values' => $values,
369         ];
370     }