See Release Notes
Long Term Support Release
Differences Between: [Versions 39 and 310] [Versions 39 and 311] [Versions 39 and 400] [Versions 39 and 401] [Versions 39 and 402] [Versions 39 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 $bulkoperations = has_capability('moodle/course:bulkmessaging', $this->context); 128 if ($bulkoperations) { 129 $mastercheckbox = new \core\output\checkbox_toggleall('participants-table', true, [ 130 'id' => 'select-all-participants', 131 'name' => 'select-all-participants', 132 'label' => get_string('selectall'), 133 'labelclasses' => 'sr-only', 134 'classes' => 'm-1', 135 'checked' => false, 136 ]); 137 $headers[] = $OUTPUT->render($mastercheckbox); 138 $columns[] = 'select'; 139 } 140 141 $headers[] = get_string('fullname'); 142 $columns[] = 'fullname'; 143 144 $extrafields = get_extra_user_fields($this->context); 145 foreach ($extrafields as $field) { 146 $headers[] = get_user_field_name($field); 147 $columns[] = $field; 148 } 149 150 $headers[] = get_string('roles'); 151 $columns[] = 'roles'; 152 153 // Get the list of fields we have to hide. 154 $hiddenfields = array(); 155 if (!has_capability('moodle/course:viewhiddenuserfields', $this->context)) { 156 $hiddenfields = array_flip(explode(',', $CFG->hiddenuserfields)); 157 } 158 159 // Add column for groups if the user can view them. 160 $canseegroups = !isset($hiddenfields['groups']); 161 if ($canseegroups) { 162 $headers[] = get_string('groups'); 163 $columns[] = 'groups'; 164 } 165 166 // Do not show the columns if it exists in the hiddenfields array. 167 if (!isset($hiddenfields['lastaccess'])) { 168 if ($this->courseid == SITEID) { 169 $headers[] = get_string('lastsiteaccess'); 170 } else { 171 $headers[] = get_string('lastcourseaccess'); 172 } 173 $columns[] = 'lastaccess'; 174 } 175 176 $canreviewenrol = has_capability('moodle/course:enrolreview', $this->context); 177 if ($canreviewenrol && $this->courseid != SITEID) { 178 $columns[] = 'status'; 179 $headers[] = get_string('participationstatus', 'enrol'); 180 $this->no_sorting('status'); 181 }; 182 183 $this->define_columns($columns); 184 $this->define_headers($headers); 185 186 // The name column is a header. 187 $this->define_header_column('fullname'); 188 189 // Make this table sorted by last name by default. 190 $this->sortable(true, 'lastname'); 191 192 $this->no_sorting('select'); 193 $this->no_sorting('roles'); 194 if ($canseegroups) { 195 $this->no_sorting('groups'); 196 } 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 $this->allroles = role_fix_names(get_all_roles($this->context), $this->context); 206 $this->assignableroles = get_assignable_roles($this->context, ROLENAME_ALIAS, false); 207 $this->profileroles = get_profile_roles($this->context); 208 $this->viewableroles = get_viewable_roles($this->context); 209 210 parent::out($pagesize, $useinitialsbar, $downloadhelpbutton); 211 212 if (has_capability('moodle/course:enrolreview', $this->context)) { 213 $params = [ 214 'contextid' => $this->context->id, 215 'uniqueid' => $this->uniqueid, 216 ]; 217 $PAGE->requires->js_call_amd('core_user/status_field', 'init', [$params]); 218 } 219 } 220 221 /** 222 * Generate the select column. 223 * 224 * @param \stdClass $data 225 * @return string 226 */ 227 public function col_select($data) { 228 global $OUTPUT; 229 230 $checkbox = new \core\output\checkbox_toggleall('participants-table', false, [ 231 'classes' => 'usercheckbox m-1', 232 'id' => 'user' . $data->id, 233 'name' => 'user' . $data->id, 234 'checked' => false, 235 'label' => get_string('selectitem', 'moodle', fullname($data)), 236 'labelclasses' => 'accesshide', 237 ]); 238 239 return $OUTPUT->render($checkbox); 240 } 241 242 /** 243 * Generate the fullname column. 244 * 245 * @param \stdClass $data 246 * @return string 247 */ 248 public function col_fullname($data) { 249 global $OUTPUT; 250 251 return $OUTPUT->user_picture($data, array('size' => 35, 'courseid' => $this->course->id, 'includefullname' => true)); 252 } 253 254 /** 255 * User roles column. 256 * 257 * @param \stdClass $data 258 * @return string 259 */ 260 public function col_roles($data) { 261 global $OUTPUT; 262 263 $roles = isset($this->allroleassignments[$data->id]) ? $this->allroleassignments[$data->id] : []; 264 $editable = new \core_user\output\user_roles_editable($this->course, 265 $this->context, 266 $data, 267 $this->allroles, 268 $this->assignableroles, 269 $this->profileroles, 270 $roles, 271 $this->viewableroles); 272 273 return $OUTPUT->render_from_template('core/inplace_editable', $editable->export_for_template($OUTPUT)); 274 } 275 276 /** 277 * Generate the groups column. 278 * 279 * @param \stdClass $data 280 * @return string 281 */ 282 public function col_groups($data) { 283 global $OUTPUT; 284 285 $usergroups = []; 286 foreach ($this->groups as $coursegroup) { 287 if (isset($coursegroup->members[$data->id])) { 288 $usergroups[] = $coursegroup->id; 289 } 290 } 291 $editable = new \core_group\output\user_groups_editable($this->course, $this->context, $data, $this->groups, $usergroups); 292 return $OUTPUT->render_from_template('core/inplace_editable', $editable->export_for_template($OUTPUT)); 293 } 294 295 /** 296 * Generate the country column. 297 * 298 * @param \stdClass $data 299 * @return string 300 */ 301 public function col_country($data) { 302 if (!empty($this->countries[$data->country])) { 303 return $this->countries[$data->country]; 304 } 305 return ''; 306 } 307 308 /** 309 * Generate the last access column. 310 * 311 * @param \stdClass $data 312 * @return string 313 */ 314 public function col_lastaccess($data) { 315 if ($data->lastaccess) { 316 return format_time(time() - $data->lastaccess); 317 } 318 319 return get_string('never'); 320 } 321 322 /** 323 * Generate the status column. 324 * 325 * @param \stdClass $data The data object. 326 * @return string 327 */ 328 public function col_status($data) { 329 global $CFG, $OUTPUT, $PAGE; 330 331 $enrolstatusoutput = ''; 332 $canreviewenrol = has_capability('moodle/course:enrolreview', $this->context); 333 if ($canreviewenrol) { 334 $canviewfullnames = has_capability('moodle/site:viewfullnames', $this->context); 335 $fullname = fullname($data, $canviewfullnames); 336 $coursename = format_string($this->course->fullname, true, array('context' => $this->context)); 337 require_once($CFG->dirroot . '/enrol/locallib.php'); 338 $manager = new \course_enrolment_manager($PAGE, $this->course); 339 $userenrolments = $manager->get_user_enrolments($data->id); 340 foreach ($userenrolments as $ue) { 341 $timestart = $ue->timestart; 342 $timeend = $ue->timeend; 343 $timeenrolled = $ue->timecreated; 344 $actions = $ue->enrolmentplugin->get_user_enrolment_actions($manager, $ue); 345 $instancename = $ue->enrolmentinstancename; 346 347 // Default status field label and value. 348 $status = get_string('participationactive', 'enrol'); 349 $statusval = status_field::STATUS_ACTIVE; 350 switch ($ue->status) { 351 case ENROL_USER_ACTIVE: 352 $currentdate = new DateTime(); 353 $now = $currentdate->getTimestamp(); 354 $isexpired = $timestart > $now || ($timeend > 0 && $timeend < $now); 355 $enrolmentdisabled = $ue->enrolmentinstance->status == ENROL_INSTANCE_DISABLED; 356 // If user enrolment status has not yet started/already ended or the enrolment instance is disabled. 357 if ($isexpired || $enrolmentdisabled) { 358 $status = get_string('participationnotcurrent', 'enrol'); 359 $statusval = status_field::STATUS_NOT_CURRENT; 360 } 361 break; 362 case ENROL_USER_SUSPENDED: 363 $status = get_string('participationsuspended', 'enrol'); 364 $statusval = status_field::STATUS_SUSPENDED; 365 break; 366 } 367 368 $statusfield = new status_field($instancename, $coursename, $fullname, $status, $timestart, $timeend, 369 $actions, $timeenrolled); 370 $statusfielddata = $statusfield->set_status($statusval)->export_for_template($OUTPUT); 371 $enrolstatusoutput .= $OUTPUT->render_from_template('core_user/status_field', $statusfielddata); 372 } 373 } 374 return $enrolstatusoutput; 375 } 376 377 /** 378 * This function is used for the extra user fields. 379 * 380 * These are being dynamically added to the table so there are no functions 'col_<userfieldname>' as 381 * the list has the potential to increase in the future and we don't want to have to remember to add 382 * a new method to this class. We also don't want to pollute this class with unnecessary methods. 383 * 384 * @param string $colname The column name 385 * @param \stdClass $data 386 * @return string 387 */ 388 public function other_cols($colname, $data) { 389 // Do not process if it is not a part of the extra fields. 390 if (!in_array($colname, $this->extrafields)) { 391 return ''; 392 } 393 394 return s($data->{$colname}); 395 } 396 397 /** 398 * Query the database for results to display in the table. 399 * 400 * @param int $pagesize size of page for paginated displayed table. 401 * @param bool $useinitialsbar do you want to use the initials bar. 402 */ 403 public function query_db($pagesize, $useinitialsbar = true) { 404 list($twhere, $tparams) = $this->get_sql_where(); 405 $psearch = new participants_search($this->course, $this->context, $this->filterset); 406 407 $total = $psearch->get_total_participants_count($twhere, $tparams); 408 409 $this->pagesize($pagesize, $total); 410 411 $sort = $this->get_sql_sort(); 412 if ($sort) { 413 $sort = 'ORDER BY ' . $sort; 414 } 415 416 $rawdata = $psearch->get_participants($twhere, $tparams, $sort, $this->get_page_start(), $this->get_page_size()); 417 418 $this->rawdata = []; 419 foreach ($rawdata as $user) { 420 $this->rawdata[$user->id] = $user; 421 } 422 $rawdata->close(); 423 424 if ($this->rawdata) { 425 $this->allroleassignments = get_users_roles($this->context, array_keys($this->rawdata), 426 true, 'c.contextlevel DESC, r.sortorder ASC'); 427 } else { 428 $this->allroleassignments = []; 429 } 430 431 // Set initial bars. 432 if ($useinitialsbar) { 433 $this->initialbars(true); 434 } 435 } 436 437 /** 438 * Override the table show_hide_link to not show for select column. 439 * 440 * @param string $column the column name, index into various names. 441 * @param int $index numerical index of the column. 442 * @return string HTML fragment. 443 */ 444 protected function show_hide_link($column, $index) { 445 if ($index > 0) { 446 return parent::show_hide_link($column, $index); 447 } 448 return ''; 449 } 450 451 /** 452 * Set filters and build table structure. 453 * 454 * @param filterset $filterset The filterset object to get the filters from. 455 */ 456 public function set_filterset(filterset $filterset): void { 457 // Get the context. 458 $this->courseid = $filterset->get_filter('courseid')->current(); 459 $this->course = get_course($this->courseid); 460 $this->context = \context_course::instance($this->courseid, MUST_EXIST); 461 462 // Process the filterset. 463 parent::set_filterset($filterset); 464 } 465 466 /** 467 * Guess the base url for the participants table. 468 */ 469 public function guess_base_url(): void { 470 $this->baseurl = new moodle_url('/user/index.php', ['id' => $this->courseid]); 471 } 472 473 /** 474 * Get the context of the current table. 475 * 476 * Note: This function should not be called until after the filterset has been provided. 477 * 478 * @return context 479 */ 480 public function get_context(): context { 481 return $this->context; 482 } 483 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body