See Release Notes
Long Term Support Release
Differences Between: [Versions 310 and 401] [Versions 311 and 401] [Versions 39 and 401] [Versions 400 and 401] [Versions 401 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 * Contains the class used for the displaying the participants table. 19 * 20 * @package core_user 21 * @copyright 2017 Mark Nelson <markn@moodle.com> 22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 */ 24 declare(strict_types=1); 25 26 namespace core_user\table; 27 28 use DateTime; 29 use context; 30 use core_table\dynamic as dynamic_table; 31 use core_table\local\filter\filterset; 32 use core_user\output\status_field; 33 use core_user\table\participants_search; 34 use moodle_url; 35 36 defined('MOODLE_INTERNAL') || die; 37 38 global $CFG; 39 40 require_once($CFG->libdir . '/tablelib.php'); 41 require_once($CFG->dirroot . '/user/lib.php'); 42 43 /** 44 * Class for the displaying the participants table. 45 * 46 * @package core_user 47 * @copyright 2017 Mark Nelson <markn@moodle.com> 48 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 49 */ 50 class participants extends \table_sql implements dynamic_table { 51 52 /** 53 * @var int $courseid The course id 54 */ 55 protected $courseid; 56 57 /** 58 * @var string[] The list of countries. 59 */ 60 protected $countries; 61 62 /** 63 * @var \stdClass[] The list of groups with membership info for the course. 64 */ 65 protected $groups; 66 67 /** 68 * @var string[] Extra fields to display. 69 */ 70 protected $extrafields; 71 72 /** 73 * @var \stdClass $course The course details. 74 */ 75 protected $course; 76 77 /** 78 * @var context $context The course context. 79 */ 80 protected $context; 81 82 /** 83 * @var \stdClass[] List of roles indexed by roleid. 84 */ 85 protected $allroles; 86 87 /** 88 * @var \stdClass[] List of roles indexed by roleid. 89 */ 90 protected $allroleassignments; 91 92 /** 93 * @var \stdClass[] Assignable roles in this course. 94 */ 95 protected $assignableroles; 96 97 /** 98 * @var \stdClass[] Profile roles in this course. 99 */ 100 protected $profileroles; 101 102 /** 103 * @var filterset Filterset describing which participants to include. 104 */ 105 protected $filterset; 106 107 /** @var \stdClass[] $viewableroles */ 108 private $viewableroles; 109 110 /** @var moodle_url $baseurl The base URL for the report. */ 111 public $baseurl; 112 113 /** 114 * Render the participants table. 115 * 116 * @param int $pagesize Size of page for paginated displayed table. 117 * @param bool $useinitialsbar Whether to use the initials bar which will only be used if there is a fullname column defined. 118 * @param string $downloadhelpbutton 119 */ 120 public function out($pagesize, $useinitialsbar, $downloadhelpbutton = '') { 121 global $CFG, $OUTPUT, $PAGE; 122 123 // Define the headers and columns. 124 $headers = []; 125 $columns = []; 126 127 // At the very least, the user viewing this table will be able to use bulk actions to export it, so add 'select' column. 128 $mastercheckbox = new \core\output\checkbox_toggleall('participants-table', true, [ 129 'id' => 'select-all-participants', 130 'name' => 'select-all-participants', 131 'label' => get_string('selectall'), 132 'labelclasses' => 'sr-only', 133 'classes' => 'm-1', 134 'checked' => false, 135 ]); 136 $headers[] = $OUTPUT->render($mastercheckbox); 137 $columns[] = 'select'; 138 139 $headers[] = get_string('fullname'); 140 $columns[] = 'fullname'; 141 142 $extrafields = \core_user\fields::get_identity_fields($this->context); 143 foreach ($extrafields as $field) { 144 $headers[] = \core_user\fields::get_display_name($field); 145 $columns[] = $field; 146 } 147 148 $headers[] = get_string('roles'); 149 $columns[] = 'roles'; 150 151 // Get the list of fields we have to hide. 152 $hiddenfields = array(); 153 if (!has_capability('moodle/course:viewhiddenuserfields', $this->context)) { 154 $hiddenfields = array_flip(explode(',', $CFG->hiddenuserfields)); 155 } 156 157 // Add column for groups if the user can view them. 158 $canseegroups = !isset($hiddenfields['groups']); 159 if ($canseegroups) { 160 $headers[] = get_string('groups'); 161 $columns[] = 'groups'; 162 } 163 164 // Do not show the columns if it exists in the hiddenfields array. 165 if (!isset($hiddenfields['lastaccess'])) { 166 if ($this->courseid == SITEID) { 167 $headers[] = get_string('lastsiteaccess'); 168 } else { 169 $headers[] = get_string('lastcourseaccess'); 170 } 171 $columns[] = 'lastaccess'; 172 } 173 174 $canreviewenrol = has_capability('moodle/course:enrolreview', $this->context); 175 if ($canreviewenrol && $this->courseid != SITEID) { 176 $columns[] = 'status'; 177 $headers[] = get_string('participationstatus', 'enrol'); 178 $this->no_sorting('status'); 179 }; 180 181 $this->define_columns($columns); 182 $this->define_headers($headers); 183 184 // The name column is a header. 185 $this->define_header_column('fullname'); 186 187 // Make this table sorted by last name by default. 188 $this->sortable(true, 'lastname'); 189 190 $this->no_sorting('select'); 191 $this->no_sorting('roles'); 192 if ($canseegroups) { 193 $this->no_sorting('groups'); 194 } 195 196 $this->set_default_per_page(20); 197 198 $this->set_attribute('id', 'participants'); 199 200 $this->countries = get_string_manager()->get_list_of_countries(true); 201 $this->extrafields = $extrafields; 202 if ($canseegroups) { 203 $this->groups = groups_get_all_groups($this->courseid, 0, 0, 'g.*', true); 204 } 205 206 // If user has capability to review enrol, show them both role names. 207 $allrolesnamedisplay = ($canreviewenrol ? ROLENAME_BOTH : ROLENAME_ALIAS); 208 $this->allroles = role_fix_names(get_all_roles($this->context), $this->context, $allrolesnamedisplay); 209 $this->assignableroles = get_assignable_roles($this->context, ROLENAME_BOTH, false); 210 $this->profileroles = get_profile_roles($this->context); 211 $this->viewableroles = get_viewable_roles($this->context); 212 213 parent::out($pagesize, $useinitialsbar, $downloadhelpbutton); 214 215 if (has_capability('moodle/course:enrolreview', $this->context)) { 216 $params = [ 217 'contextid' => $this->context->id, 218 'uniqueid' => $this->uniqueid, 219 ]; 220 $PAGE->requires->js_call_amd('core_user/status_field', 'init', [$params]); 221 } 222 } 223 224 /** 225 * Generate the select column. 226 * 227 * @param \stdClass $data 228 * @return string 229 */ 230 public function col_select($data) { 231 global $OUTPUT; 232 233 $checkbox = new \core\output\checkbox_toggleall('participants-table', false, [ 234 'classes' => 'usercheckbox m-1', 235 'id' => 'user' . $data->id, 236 'name' => 'user' . $data->id, 237 'checked' => false, 238 'label' => get_string('selectitem', 'moodle', fullname($data)), 239 'labelclasses' => 'accesshide', 240 ]); 241 242 return $OUTPUT->render($checkbox); 243 } 244 245 /** 246 * Generate the fullname column. 247 * 248 * @param \stdClass $data 249 * @return string 250 */ 251 public function col_fullname($data) { 252 global $OUTPUT; 253 254 return $OUTPUT->user_picture($data, array('size' => 35, 'courseid' => $this->course->id, 'includefullname' => true)); 255 } 256 257 /** 258 * User roles column. 259 * 260 * @param \stdClass $data 261 * @return string 262 */ 263 public function col_roles($data) { 264 global $OUTPUT; 265 266 $roles = isset($this->allroleassignments[$data->id]) ? $this->allroleassignments[$data->id] : []; 267 $editable = new \core_user\output\user_roles_editable($this->course, 268 $this->context, 269 $data, 270 $this->allroles, 271 $this->assignableroles, 272 $this->profileroles, 273 $roles, 274 $this->viewableroles); 275 276 return $OUTPUT->render_from_template('core/inplace_editable', $editable->export_for_template($OUTPUT)); 277 } 278 279 /** 280 * Generate the groups column. 281 * 282 * @param \stdClass $data 283 * @return string 284 */ 285 public function col_groups($data) { 286 global $OUTPUT; 287 288 $usergroups = []; 289 foreach ($this->groups as $coursegroup) { 290 if (isset($coursegroup->members[$data->id])) { 291 $usergroups[] = $coursegroup->id; 292 } 293 } 294 $editable = new \core_group\output\user_groups_editable($this->course, $this->context, $data, $this->groups, $usergroups); 295 return $OUTPUT->render_from_template('core/inplace_editable', $editable->export_for_template($OUTPUT)); 296 } 297 298 /** 299 * Generate the country column. 300 * 301 * @param \stdClass $data 302 * @return string 303 */ 304 public function col_country($data) { 305 if (!empty($this->countries[$data->country])) { 306 return $this->countries[$data->country]; 307 } 308 return ''; 309 } 310 311 /** 312 * Generate the last access column. 313 * 314 * @param \stdClass $data 315 * @return string 316 */ 317 public function col_lastaccess($data) { 318 if ($data->lastaccess) { 319 return format_time(time() - $data->lastaccess); 320 } 321 322 return get_string('never'); 323 } 324 325 /** 326 * Generate the status column. 327 * 328 * @param \stdClass $data The data object. 329 * @return string 330 */ 331 public function col_status($data) { 332 global $CFG, $OUTPUT, $PAGE; 333 334 $enrolstatusoutput = ''; 335 $canreviewenrol = has_capability('moodle/course:enrolreview', $this->context); 336 if ($canreviewenrol) { 337 $canviewfullnames = has_capability('moodle/site:viewfullnames', $this->context); 338 $fullname = fullname($data, $canviewfullnames); 339 $coursename = format_string($this->course->fullname, true, array('context' => $this->context)); 340 require_once($CFG->dirroot . '/enrol/locallib.php'); 341 $manager = new \course_enrolment_manager($PAGE, $this->course); 342 $userenrolments = $manager->get_user_enrolments($data->id); 343 foreach ($userenrolments as $ue) { 344 $timestart = $ue->timestart; 345 $timeend = $ue->timeend; 346 $timeenrolled = $ue->timecreated; 347 $actions = $ue->enrolmentplugin->get_user_enrolment_actions($manager, $ue); 348 $instancename = $ue->enrolmentinstancename; 349 350 // Default status field label and value. 351 $status = get_string('participationactive', 'enrol'); 352 $statusval = status_field::STATUS_ACTIVE; 353 switch ($ue->status) { 354 case ENROL_USER_ACTIVE: 355 $currentdate = new DateTime(); 356 $now = $currentdate->getTimestamp(); 357 $isexpired = $timestart > $now || ($timeend > 0 && $timeend < $now); 358 $enrolmentdisabled = $ue->enrolmentinstance->status == ENROL_INSTANCE_DISABLED; 359 // If user enrolment status has not yet started/already ended or the enrolment instance is disabled. 360 if ($isexpired || $enrolmentdisabled) { 361 $status = get_string('participationnotcurrent', 'enrol'); 362 $statusval = status_field::STATUS_NOT_CURRENT; 363 } 364 break; 365 case ENROL_USER_SUSPENDED: 366 $status = get_string('participationsuspended', 'enrol'); 367 $statusval = status_field::STATUS_SUSPENDED; 368 break; 369 } 370 371 $statusfield = new status_field($instancename, $coursename, $fullname, $status, $timestart, $timeend, 372 $actions, $timeenrolled); 373 $statusfielddata = $statusfield->set_status($statusval)->export_for_template($OUTPUT); 374 $enrolstatusoutput .= $OUTPUT->render_from_template('core_user/status_field', $statusfielddata); 375 } 376 } 377 return $enrolstatusoutput; 378 } 379 380 /** 381 * This function is used for the extra user fields. 382 * 383 * These are being dynamically added to the table so there are no functions 'col_<userfieldname>' as 384 * the list has the potential to increase in the future and we don't want to have to remember to add 385 * a new method to this class. We also don't want to pollute this class with unnecessary methods. 386 * 387 * @param string $colname The column name 388 * @param \stdClass $data 389 * @return string 390 */ 391 public function other_cols($colname, $data) { 392 // Do not process if it is not a part of the extra fields. 393 if (!in_array($colname, $this->extrafields)) { 394 return ''; 395 } 396 397 return s($data->{$colname}); 398 } 399 400 /** 401 * Query the database for results to display in the table. 402 * 403 * @param int $pagesize size of page for paginated displayed table. 404 * @param bool $useinitialsbar do you want to use the initials bar. 405 */ 406 public function query_db($pagesize, $useinitialsbar = true) { 407 list($twhere, $tparams) = $this->get_sql_where(); 408 $psearch = new participants_search($this->course, $this->context, $this->filterset); 409 410 $total = $psearch->get_total_participants_count($twhere, $tparams); 411 412 $this->pagesize($pagesize, $total); 413 414 $sort = $this->get_sql_sort(); 415 if ($sort) { 416 $sort = 'ORDER BY ' . $sort; 417 } 418 419 $rawdata = $psearch->get_participants($twhere, $tparams, $sort, $this->get_page_start(), $this->get_page_size()); 420 421 $this->rawdata = []; 422 foreach ($rawdata as $user) { 423 $this->rawdata[$user->id] = $user; 424 } 425 $rawdata->close(); 426 427 if ($this->rawdata) { 428 $this->allroleassignments = get_users_roles($this->context, array_keys($this->rawdata), 429 true, 'c.contextlevel DESC, r.sortorder ASC'); 430 } else { 431 $this->allroleassignments = []; 432 } 433 434 // Set initial bars. 435 if ($useinitialsbar) { 436 $this->initialbars(true); 437 } 438 } 439 440 /** 441 * Override the table show_hide_link to not show for select column. 442 * 443 * @param string $column the column name, index into various names. 444 * @param int $index numerical index of the column. 445 * @return string HTML fragment. 446 */ 447 protected function show_hide_link($column, $index) { 448 if ($index > 0) { 449 return parent::show_hide_link($column, $index); 450 } 451 return ''; 452 } 453 454 /** 455 * Set filters and build table structure. 456 * 457 * @param filterset $filterset The filterset object to get the filters from. 458 */ 459 public function set_filterset(filterset $filterset): void { 460 // Get the context. 461 $this->courseid = $filterset->get_filter('courseid')->current(); 462 $this->course = get_course($this->courseid); 463 $this->context = \context_course::instance($this->courseid, MUST_EXIST); 464 465 // Process the filterset. 466 parent::set_filterset($filterset); 467 } 468 469 /** 470 * Guess the base url for the participants table. 471 */ 472 public function guess_base_url(): void { 473 $this->baseurl = new moodle_url('/user/index.php', ['id' => $this->courseid]); 474 } 475 476 /** 477 * Get the context of the current table. 478 * 479 * Note: This function should not be called until after the filterset has been provided. 480 * 481 * @return context 482 */ 483 public function get_context(): context { 484 return $this->context; 485 } 486 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body