Commit | Line | Data |
---|---|---|
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 | */ | |
24 | namespace core_user\output; | |
25 | ||
26 | use context_course; | |
27 | use renderable; | |
28 | use renderer_base; | |
29 | use stdClass; | |
30 | use 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 | */ | |
38 | class 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 | ||
d85315ee MH |
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))) { | |
de83d435 AN |
215 | return null; |
216 | } | |
217 | ||
218 | return $this->get_filter_object( | |
219 | 'groups', | |
220 | get_string('groups', 'core_group'), | |
221 | false, | |
222 | true, | |
223 | null, | |
224 | array_map(function($group) { | |
225 | return (object) [ | |
226 | 'value' => $group->id, | |
227 | 'title' => $group->name, | |
228 | ]; | |
229 | }, array_values($groups)) | |
230 | ); | |
231 | } | |
232 | ||
fbcc6577 AN |
233 | /** |
234 | * Get data for the accesssince filter. | |
235 | * | |
236 | * @return stdClass|null | |
237 | */ | |
238 | protected function get_accesssince_filter(): ?stdClass { | |
239 | global $CFG, $DB; | |
240 | ||
241 | $hiddenfields = []; | |
242 | if (!has_capability('moodle/course:viewhiddenuserfields', $this->context)) { | |
243 | $hiddenfields = array_flip(explode(',', $CFG->hiddenuserfields)); | |
244 | } | |
245 | ||
246 | if (array_key_exists('lastaccess', $hiddenfields)) { | |
247 | return null; | |
248 | } | |
249 | ||
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) { | |
253 | // Regular course. | |
254 | $params = [ | |
255 | 'courseid' => $this->course->id, | |
256 | 'timeaccess' => 0, | |
257 | ]; | |
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); | |
261 | } else { | |
262 | // Front page. | |
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); | |
267 | } | |
268 | ||
269 | $now = usergetmidnight(time()); | |
270 | $timeoptions = []; | |
271 | $criteria = get_string('usersnoaccesssince'); | |
272 | ||
273 | $getoptions = function(int $count, string $singletype, string $type) use ($now, $minlastaccess): array { | |
274 | $values = []; | |
275 | for ($i = 1; $i <= $count; $i++) { | |
276 | $timestamp = strtotime("-{$i} {$type}", $now); | |
277 | if ($timestamp < $minlastaccess) { | |
278 | break; | |
279 | } | |
280 | ||
281 | if ($i === 1) { | |
282 | $title = get_string("num{$singletype}", 'moodle', $i); | |
283 | } else { | |
284 | $title = get_string("num{$type}", 'moodle', $i); | |
285 | } | |
286 | ||
287 | $values[] = [ | |
288 | 'value' => $timestamp, | |
289 | 'title' => $title, | |
290 | ]; | |
291 | } | |
292 | ||
293 | return $values; | |
294 | }; | |
295 | ||
296 | $values = array_merge( | |
297 | $getoptions(6, 'day', 'days'), | |
298 | $getoptions(10, 'week', 'weeks'), | |
299 | $getoptions(11, 'month', 'months'), | |
300 | $getoptions(1, 'year', 'years') | |
301 | ); | |
302 | ||
303 | if ($lastaccess0exists) { | |
304 | $values[] = [ | |
305 | 'value' => time(), | |
306 | 'title' => get_string('never', 'moodle'), | |
307 | ]; | |
308 | } | |
309 | ||
310 | if (count($values) <= 1) { | |
311 | // Nothing to show. | |
312 | return null; | |
313 | } | |
314 | ||
315 | return $this->get_filter_object( | |
316 | 'accesssince', | |
317 | get_string('usersnoaccesssince'), | |
318 | false, | |
319 | false, | |
320 | null, | |
321 | $values | |
322 | ); | |
323 | } | |
324 | ||
028ec17c AN |
325 | /** |
326 | * Get data for the keywords filter. | |
327 | * | |
328 | * @return stdClass|null | |
329 | */ | |
330 | protected function get_keyword_filter(): ?stdClass { | |
331 | return $this->get_filter_object( | |
332 | 'keywords', | |
333 | get_string('filterbykeyword', 'core_user'), | |
334 | true, | |
335 | true, | |
336 | 'core_user/local/participantsfilter/filtertypes/keyword', | |
337 | [], | |
338 | true | |
339 | ); | |
340 | } | |
341 | ||
77ba77f1 AN |
342 | /** |
343 | * Export the renderer data in a mustache template friendly format. | |
344 | * | |
345 | * @param renderer_base $output Unused. | |
346 | * @return stdClass Data in a format compatible with a mustache template. | |
347 | */ | |
348 | public function export_for_template(renderer_base $output): stdClass { | |
349 | return (object) [ | |
350 | 'tableregionid' => $this->tableregionid, | |
351 | 'courseid' => $this->context->instanceid, | |
352 | 'filtertypes' => $this->get_filtertypes(), | |
3d60881d | 353 | 'rownumber' => 1, |
77ba77f1 AN |
354 | ]; |
355 | ||
356 | return $data; | |
357 | } | |
358 | ||
359 | /** | |
360 | * Get a standardised filter object. | |
361 | * | |
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 | |
028ec17c | 368 | * @param bool $allowempty |
77ba77f1 AN |
369 | * @return stdClass|null |
370 | */ | |
371 | protected function get_filter_object( | |
372 | string $name, | |
373 | string $title, | |
374 | bool $custom, | |
375 | bool $multiple, | |
376 | ?string $filterclass, | |
028ec17c AN |
377 | array $values, |
378 | bool $allowempty = false | |
77ba77f1 | 379 | ): ?stdClass { |
028ec17c AN |
380 | |
381 | if (!$allowempty && empty($values)) { | |
77ba77f1 AN |
382 | // Do not show empty filters. |
383 | return null; | |
384 | } | |
385 | ||
386 | return (object) [ | |
387 | 'name' => $name, | |
388 | 'title' => $title, | |
389 | 'allowcustom' => $custom, | |
390 | 'allowmultiple' => $multiple, | |
391 | 'filtertypeclass' => $filterclass, | |
392 | 'values' => $values, | |
393 | ]; | |
394 | } | |
395 | } |