MDL-68169 user: Add keyword filter
[moodle.git] / user / classes / output / participants_filter.php
CommitLineData
77ba77f1
AN
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/>.
16
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 */
24namespace core_user\output;
25
26use context_course;
27use renderable;
28use renderer_base;
29use stdClass;
30use templatable;
31
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 */
38class participants_filter implements renderable, templatable {
39
40 /** @var context_course $context The context where the filters are being rendered. */
41 protected $context;
42
43 /** @var string $tableregionid The table to be updated by this filter */
44 protected $tableregionid;
45
de83d435
AN
46 /** @var stdClass $course The course shown */
47 protected $course;
48
77ba77f1
AN
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;
de83d435
AN
58
59 $this->course = get_course($context->instanceid);
77ba77f1
AN
60 }
61
62 /**
63 * Get data for all filter types.
64 *
65 * @return array
66 */
67 protected function get_filtertypes(): array {
68 $filtertypes = [];
69
028ec17c
AN
70 $filtertypes[] = $this->get_keyword_filter();
71
77ba77f1
AN
72 if ($filtertype = $this->get_enrolmentstatus_filter()) {
73 $filtertypes[] = $filtertype;
74 }
75
ffc933ad
AN
76 if ($filtertype = $this->get_roles_filter()) {
77 $filtertypes[] = $filtertype;
78 }
79
fa3d57fe
AN
80 if ($filtertype = $this->get_enrolments_filter()) {
81 $filtertypes[] = $filtertype;
82 }
83
de83d435
AN
84 if ($filtertype = $this->get_groups_filter()) {
85 $filtertypes[] = $filtertype;
86 }
87
fbcc6577
AN
88 if ($filtertype = $this->get_accesssince_filter()) {
89 $filtertypes[] = $filtertype;
90 }
91
77ba77f1
AN
92 return $filtertypes;
93 }
94
95 /**
96 * Get data for the enrolment status filter.
97 *
98 * @return stdClass|null
99 */
100 protected function get_enrolmentstatus_filter(): ?stdClass {
101 if (!has_capability('moodle/course:enrolreview', $this->context)) {
102 return null;
103 }
104
105 return $this->get_filter_object(
106 'status',
107 get_string('participationstatus', 'core_enrol'),
108 false,
109 true,
110 null,
111 [
112 (object) [
113 'value' => ENROL_USER_ACTIVE,
114 'title' => get_string('active'),
115 ],
116 (object) [
117 'value' => ENROL_USER_SUSPENDED,
118 'title' => get_string('inactive'),
119 ],
120 ]
121 );
122 }
123
ffc933ad
AN
124 /**
125 * Get data for the roles filter.
126 *
127 * @return stdClass|null
128 */
129 protected function get_roles_filter(): ?stdClass {
130 $roles = [];
131 $roles += [-1 => get_string('noroles', 'role')];
132 $roles += get_viewable_roles($this->context);
133
134 if (has_capability('moodle/role:assign', $this->context)) {
135 $roles += get_assignable_roles($this->context, ROLENAME_ALIAS);
136 }
137
138 return $this->get_filter_object(
139 'roles',
140 get_string('roles', 'core_role'),
141 false,
142 true,
143 null,
144 array_map(function($id, $title) {
145 return (object) [
146 'value' => $id,
147 'title' => $title,
148 ];
149 }, array_keys($roles), array_values($roles))
150 );
151 }
152
fa3d57fe
AN
153 /**
154 * Get data for the roles filter.
155 *
156 * @return stdClass|null
157 */
158 protected function get_enrolments_filter(): ?stdClass {
159 if (!has_capability('moodle/course:enrolreview', $this->context)) {
160 return null;
161 }
162
163 if ($this->course->id == SITEID) {
164 // No enrolment methods for the site.
165 return null;
166 }
167
168 $instances = enrol_get_instances($this->course->id, true);
169 $plugins = enrol_get_plugins(false);
170
171 return $this->get_filter_object(
172 'enrolments',
173 get_string('enrolmentinstances', 'core_enrol'),
174 false,
175 true,
176 null,
177 array_filter(array_map(function($instance) use ($plugins): ?stdClass {
178 if (!array_key_exists($instance->enrol, $plugins)) {
179 return null;
180 }
181
182 return (object) [
183 'value' => $instance->id,
184 'title' => $plugins[$instance->enrol]->get_instance_name($instance),
185 ];
186 }, array_values($instances)))
187 );
188 }
189
de83d435
AN
190 /**
191 * Get data for the groups filter.
192 *
193 * @return stdClass|null
194 */
195 protected function get_groups_filter(): ?stdClass {
196 global $USER;
197
198 // Filter options for groups, if available.
199 $seeallgroups = has_capability('moodle/site:accessallgroups', $this->context);
200 $seeallgroups = $seeallgroups || ($this->course->groupmode != SEPARATEGROUPS);
201 if ($seeallgroups) {
202 $groups = [];
203 $groups += [USERSWITHOUTGROUP => (object) [
204 'id' => USERSWITHOUTGROUP,
205 'name' => get_string('nogroup', 'group'),
206 ]];
207 $groups += groups_get_all_groups($this->course->id);
208 } else {
209 // Otherwise, just list the groups the user belongs to.
210 $groups = groups_get_all_groups($this->course->id, $USER->id);
211 }
212
213 if (empty($groups)) {
214 return null;
215 }
216
217 return $this->get_filter_object(
218 'groups',
219 get_string('groups', 'core_group'),
220 false,
221 true,
222 null,
223 array_map(function($group) {
224 return (object) [
225 'value' => $group->id,
226 'title' => $group->name,
227 ];
228 }, array_values($groups))
229 );
230 }
231
fbcc6577
AN
232 /**
233 * Get data for the accesssince filter.
234 *
235 * @return stdClass|null
236 */
237 protected function get_accesssince_filter(): ?stdClass {
238 global $CFG, $DB;
239
240 $hiddenfields = [];
241 if (!has_capability('moodle/course:viewhiddenuserfields', $this->context)) {
242 $hiddenfields = array_flip(explode(',', $CFG->hiddenuserfields));
243 }
244
245 if (array_key_exists('lastaccess', $hiddenfields)) {
246 return null;
247 }
248
249 // Get minimum lastaccess for this course and display a dropbox to filter by lastaccess going back this far.
250 // We need to make it diferently for normal courses and site course.
251 if (!$this->course->id == SITEID) {
252 // Regular course.
253 $params = [
254 'courseid' => $this->course->id,
255 'timeaccess' => 0,
256 ];
257 $select = 'courseid = :courseid AND timeaccess != :timeaccess';
258 $minlastaccess = $DB->get_field_select('user_lastaccess', 'MIN(timeaccess)', $select, $params);
259 $lastaccess0exists = $DB->record_exists('user_lastaccess', $params);
260 } else {
261 // Front page.
262 $params = ['lastaccess' => 0];
263 $select = 'lastaccess != :lastaccess';
264 $minlastaccess = $DB->get_field_select('user', 'MIN(lastaccess)', $select, $params);
265 $lastaccess0exists = $DB->record_exists('user', $params);
266 }
267
268 $now = usergetmidnight(time());
269 $timeoptions = [];
270 $criteria = get_string('usersnoaccesssince');
271
272 $getoptions = function(int $count, string $singletype, string $type) use ($now, $minlastaccess): array {
273 $values = [];
274 for ($i = 1; $i <= $count; $i++) {
275 $timestamp = strtotime("-{$i} {$type}", $now);
276 if ($timestamp < $minlastaccess) {
277 break;
278 }
279
280 if ($i === 1) {
281 $title = get_string("num{$singletype}", 'moodle', $i);
282 } else {
283 $title = get_string("num{$type}", 'moodle', $i);
284 }
285
286 $values[] = [
287 'value' => $timestamp,
288 'title' => $title,
289 ];
290 }
291
292 return $values;
293 };
294
295 $values = array_merge(
296 $getoptions(6, 'day', 'days'),
297 $getoptions(10, 'week', 'weeks'),
298 $getoptions(11, 'month', 'months'),
299 $getoptions(1, 'year', 'years')
300 );
301
302 if ($lastaccess0exists) {
303 $values[] = [
304 'value' => time(),
305 'title' => get_string('never', 'moodle'),
306 ];
307 }
308
309 if (count($values) <= 1) {
310 // Nothing to show.
311 return null;
312 }
313
314 return $this->get_filter_object(
315 'accesssince',
316 get_string('usersnoaccesssince'),
317 false,
318 false,
319 null,
320 $values
321 );
322 }
323
028ec17c
AN
324 /**
325 * Get data for the keywords filter.
326 *
327 * @return stdClass|null
328 */
329 protected function get_keyword_filter(): ?stdClass {
330 return $this->get_filter_object(
331 'keywords',
332 get_string('filterbykeyword', 'core_user'),
333 true,
334 true,
335 'core_user/local/participantsfilter/filtertypes/keyword',
336 [],
337 true
338 );
339 }
340
77ba77f1
AN
341 /**
342 * Export the renderer data in a mustache template friendly format.
343 *
344 * @param renderer_base $output Unused.
345 * @return stdClass Data in a format compatible with a mustache template.
346 */
347 public function export_for_template(renderer_base $output): stdClass {
348 return (object) [
349 'tableregionid' => $this->tableregionid,
350 'courseid' => $this->context->instanceid,
351 'filtertypes' => $this->get_filtertypes(),
352 ];
353
354 return $data;
355 }
356
357 /**
358 * Get a standardised filter object.
359 *
360 * @param string $name
361 * @param string $title
362 * @param bool $custom
363 * @param bool $multiple
364 * @param string|null $filterclass
365 * @param array $values
028ec17c 366 * @param bool $allowempty
77ba77f1
AN
367 * @return stdClass|null
368 */
369 protected function get_filter_object(
370 string $name,
371 string $title,
372 bool $custom,
373 bool $multiple,
374 ?string $filterclass,
028ec17c
AN
375 array $values,
376 bool $allowempty = false
77ba77f1 377 ): ?stdClass {
028ec17c
AN
378
379 if (!$allowempty && empty($values)) {
77ba77f1
AN
380 // Do not show empty filters.
381 return null;
382 }
383
384 return (object) [
385 'name' => $name,
386 'title' => $title,
387 'allowcustom' => $custom,
388 'allowmultiple' => $multiple,
389 'filtertypeclass' => $filterclass,
390 'values' => $values,
391 ];
392 }
393}