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