[ Index ] |
PHP Cross Reference of Unnamed Project |
[Summary view] [Print] [Text view]
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 * Code for ajax user selectors. 19 * 20 * @package core_user 21 * @copyright 1999 onwards Martin Dougiamas http://dougiamas.com 22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 */ 24 25 /** 26 * The default size of a user selector. 27 */ 28 define('USER_SELECTOR_DEFAULT_ROWS', 20); 29 30 /** 31 * Base class for user selectors. 32 * 33 * In your theme, you must give each user-selector a defined width. If the 34 * user selector has name="myid", then the div myid_wrapper must have a width 35 * specified. 36 * 37 * @copyright 1999 onwards Martin Dougiamas http://dougiamas.com 38 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 39 */ 40 abstract class user_selector_base { 41 /** @var string The control name (and id) in the HTML. */ 42 protected $name; 43 /** @var array Extra fields to search on and return in addition to firstname and lastname. */ 44 protected $extrafields; 45 /** @var object Context used for capability checks regarding this selector (does 46 * not necessarily restrict user list) */ 47 protected $accesscontext; 48 /** @var boolean Whether the conrol should allow selection of many users, or just one. */ 49 protected $multiselect = true; 50 /** @var int The height this control should have, in rows. */ 51 protected $rows = USER_SELECTOR_DEFAULT_ROWS; 52 /** @var array A list of userids that should not be returned by this control. */ 53 protected $exclude = array(); 54 /** @var array|null A list of the users who are selected. */ 55 protected $selected = null; 56 /** @var boolean When the search changes, do we keep previously selected options that do 57 * not match the new search term? */ 58 protected $preserveselected = false; 59 /** @var boolean If only one user matches the search, should we select them automatically. */ 60 protected $autoselectunique = false; 61 /** @var boolean When searching, do we only match the starts of fields (better performance) 62 * or do we match occurrences anywhere? */ 63 protected $searchanywhere = false; 64 /** @var mixed This is used by get selected users */ 65 protected $validatinguserids = null; 66 67 /** @var boolean Used to ensure we only output the search options for one user selector on 68 * each page. */ 69 private static $searchoptionsoutput = false; 70 71 /** @var array JavaScript YUI3 Module definition */ 72 protected static $jsmodule = array( 73 'name' => 'user_selector', 74 'fullpath' => '/user/selector/module.js', 75 'requires' => array('node', 'event-custom', 'datasource', 'json', 'moodle-core-notification'), 76 'strings' => array( 77 array('previouslyselectedusers', 'moodle', '%%SEARCHTERM%%'), 78 array('nomatchingusers', 'moodle', '%%SEARCHTERM%%'), 79 array('none', 'moodle') 80 )); 81 82 /** @var int this is used to define maximum number of users visible in list */ 83 public $maxusersperpage = 100; 84 85 /** 86 * Constructor. Each subclass must have a constructor with this signature. 87 * 88 * @param string $name the control name/id for use in the HTML. 89 * @param array $options other options needed to construct this selector. 90 * You must be able to clone a userselector by doing new get_class($us)($us->get_name(), $us->get_options()); 91 */ 92 public function __construct($name, $options = array()) { 93 global $CFG, $PAGE; 94 95 // Initialise member variables from constructor arguments. 96 $this->name = $name; 97 98 // Use specified context for permission checks, system context if not specified. 99 if (isset($options['accesscontext'])) { 100 $this->accesscontext = $options['accesscontext']; 101 } else { 102 $this->accesscontext = context_system::instance(); 103 } 104 105 if (isset($options['extrafields'])) { 106 $this->extrafields = $options['extrafields']; 107 } else if (!empty($CFG->showuseridentity) && 108 has_capability('moodle/site:viewuseridentity', $this->accesscontext)) { 109 $this->extrafields = explode(',', $CFG->showuseridentity); 110 } else { 111 $this->extrafields = array(); 112 } 113 if (isset($options['exclude']) && is_array($options['exclude'])) { 114 $this->exclude = $options['exclude']; 115 } 116 if (isset($options['multiselect'])) { 117 $this->multiselect = $options['multiselect']; 118 } 119 120 // Read the user prefs / optional_params that we use. 121 $this->preserveselected = $this->initialise_option('userselector_preserveselected', $this->preserveselected); 122 $this->autoselectunique = $this->initialise_option('userselector_autoselectunique', $this->autoselectunique); 123 $this->searchanywhere = $this->initialise_option('userselector_searchanywhere', $this->searchanywhere); 124 125 if (!empty($CFG->maxusersperpage)) { 126 $this->maxusersperpage = $CFG->maxusersperpage; 127 } 128 } 129 130 /** 131 * All to the list of user ids that this control will not select. 132 * 133 * For example, on the role assign page, we do not list the users who already have the role in question. 134 * 135 * @param array $arrayofuserids the user ids to exclude. 136 */ 137 public function exclude($arrayofuserids) { 138 $this->exclude = array_unique(array_merge($this->exclude, $arrayofuserids)); 139 } 140 141 /** 142 * Clear the list of excluded user ids. 143 */ 144 public function clear_exclusions() { 145 $this->exclude = array(); 146 } 147 148 /** 149 * Returns the list of user ids that this control will not select. 150 * 151 * @return array the list of user ids that this control will not select. 152 */ 153 public function get_exclusions() { 154 return clone($this->exclude); 155 } 156 157 /** 158 * The users that were selected. 159 * 160 * This is a more sophisticated version of optional_param($this->name, array(), PARAM_INT) that validates the 161 * returned list of ids against the rules for this user selector. 162 * 163 * @return array of user objects. 164 */ 165 public function get_selected_users() { 166 // Do a lazy load. 167 if (is_null($this->selected)) { 168 $this->selected = $this->load_selected_users(); 169 } 170 return $this->selected; 171 } 172 173 /** 174 * Convenience method for when multiselect is false (throws an exception if not). 175 * 176 * @throws moodle_exception 177 * @return object the selected user object, or null if none. 178 */ 179 public function get_selected_user() { 180 if ($this->multiselect) { 181 throw new moodle_exception('cannotcallusgetselecteduser'); 182 } 183 $users = $this->get_selected_users(); 184 if (count($users) == 1) { 185 return reset($users); 186 } else if (count($users) == 0) { 187 return null; 188 } else { 189 throw new moodle_exception('userselectortoomany'); 190 } 191 } 192 193 /** 194 * Invalidates the list of selected users. 195 * 196 * If you update the database in such a way that it is likely to change the 197 * list of users that this component is allowed to select from, then you 198 * must call this method. For example, on the role assign page, after you have 199 * assigned some roles to some users, you should call this. 200 */ 201 public function invalidate_selected_users() { 202 $this->selected = null; 203 } 204 205 /** 206 * Output this user_selector as HTML. 207 * 208 * @param boolean $return if true, return the HTML as a string instead of outputting it. 209 * @return mixed if $return is true, returns the HTML as a string, otherwise returns nothing. 210 */ 211 public function display($return = false) { 212 global $PAGE; 213 214 // Get the list of requested users. 215 $search = optional_param($this->name . '_searchtext', '', PARAM_RAW); 216 if (optional_param($this->name . '_clearbutton', false, PARAM_BOOL)) { 217 $search = ''; 218 } 219 $groupedusers = $this->find_users($search); 220 221 // Output the select. 222 $name = $this->name; 223 $multiselect = ''; 224 if ($this->multiselect) { 225 $name .= '[]'; 226 $multiselect = 'multiple="multiple" '; 227 } 228 $output = '<div class="userselector" id="' . $this->name . '_wrapper">' . "\n" . 229 '<select name="' . $name . '" id="' . $this->name . '" ' . 230 $multiselect . 'size="' . $this->rows . '">' . "\n"; 231 232 // Populate the select. 233 $output .= $this->output_options($groupedusers, $search); 234 235 // Output the search controls. 236 $output .= "</select>\n<div>\n"; 237 $output .= '<input type="text" name="' . $this->name . '_searchtext" id="' . 238 $this->name . '_searchtext" size="15" value="' . s($search) . '" />'; 239 $output .= '<input type="submit" name="' . $this->name . '_searchbutton" id="' . 240 $this->name . '_searchbutton" value="' . $this->search_button_caption() . '" />'; 241 $output .= '<input type="submit" name="' . $this->name . '_clearbutton" id="' . 242 $this->name . '_clearbutton" value="' . get_string('clear') . '" />'; 243 244 // And the search options. 245 $optionsoutput = false; 246 if (!user_selector_base::$searchoptionsoutput) { 247 $output .= print_collapsible_region_start('', 'userselector_options', 248 get_string('searchoptions'), 'userselector_optionscollapsed', true, true); 249 $output .= $this->option_checkbox('preserveselected', $this->preserveselected, 250 get_string('userselectorpreserveselected')); 251 $output .= $this->option_checkbox('autoselectunique', $this->autoselectunique, 252 get_string('userselectorautoselectunique')); 253 $output .= $this->option_checkbox('searchanywhere', $this->searchanywhere, 254 get_string('userselectorsearchanywhere')); 255 $output .= print_collapsible_region_end(true); 256 257 $PAGE->requires->js_init_call('M.core_user.init_user_selector_options_tracker', array(), false, self::$jsmodule); 258 user_selector_base::$searchoptionsoutput = true; 259 } 260 $output .= "</div>\n</div>\n\n"; 261 262 // Initialise the ajax functionality. 263 $output .= $this->initialise_javascript($search); 264 265 // Return or output it. 266 if ($return) { 267 return $output; 268 } else { 269 echo $output; 270 } 271 } 272 273 /** 274 * The height this control will be displayed, in rows. 275 * 276 * @param integer $numrows the desired height. 277 */ 278 public function set_rows($numrows) { 279 $this->rows = $numrows; 280 } 281 282 /** 283 * Returns the number of rows to display in this control. 284 * 285 * @return integer the height this control will be displayed, in rows. 286 */ 287 public function get_rows() { 288 return $this->rows; 289 } 290 291 /** 292 * Whether this control will allow selection of many, or just one user. 293 * 294 * @param boolean $multiselect true = allow multiple selection. 295 */ 296 public function set_multiselect($multiselect) { 297 $this->multiselect = $multiselect; 298 } 299 300 /** 301 * Returns true is multiselect should be allowed. 302 * 303 * @return boolean whether this control will allow selection of more than one user. 304 */ 305 public function is_multiselect() { 306 return $this->multiselect; 307 } 308 309 /** 310 * Returns the id/name of this control. 311 * 312 * @return string the id/name that this control will have in the HTML. 313 */ 314 public function get_name() { 315 return $this->name; 316 } 317 318 /** 319 * Set the user fields that are displayed in the selector in addition to the user's name. 320 * 321 * @param array $fields a list of field names that exist in the user table. 322 */ 323 public function set_extra_fields($fields) { 324 $this->extrafields = $fields; 325 } 326 327 /** 328 * Search the database for users matching the $search string, and any other 329 * conditions that apply. The SQL for testing whether a user matches the 330 * search string should be obtained by calling the search_sql method. 331 * 332 * This method is used both when getting the list of choices to display to 333 * the user, and also when validating a list of users that was selected. 334 * 335 * When preparing a list of users to choose from ($this->is_validating() 336 * return false) you should probably have an maximum number of users you will 337 * return, and if more users than this match your search, you should instead 338 * return a message generated by the too_many_results() method. However, you 339 * should not do this when validating. 340 * 341 * If you are writing a new user_selector subclass, I strongly recommend you 342 * look at some of the subclasses later in this file and in admin/roles/lib.php. 343 * They should help you see exactly what you have to do. 344 * 345 * @param string $search the search string. 346 * @return array An array of arrays of users. The array keys of the outer 347 * array should be the string names of optgroups. The keys of the inner 348 * arrays should be userids, and the values should be user objects 349 * containing at least the list of fields returned by the method 350 * required_fields_sql(). If a user object has a ->disabled property 351 * that is true, then that option will be displayed greyed out, and 352 * will not be returned by get_selected_users. 353 */ 354 public abstract function find_users($search); 355 356 /** 357 * 358 * Note: this function must be implemented if you use the search ajax field 359 * (e.g. set $options['file'] = '/admin/filecontainingyourclass.php';) 360 * @return array the options needed to recreate this user_selector. 361 */ 362 protected function get_options() { 363 return array( 364 'class' => get_class($this), 365 'name' => $this->name, 366 'exclude' => $this->exclude, 367 'extrafields' => $this->extrafields, 368 'multiselect' => $this->multiselect, 369 'accesscontext' => $this->accesscontext, 370 ); 371 } 372 373 /** 374 * Returns true if this control is validating a list of users. 375 * 376 * @return boolean if true, we are validating a list of selected users, 377 * rather than preparing a list of uesrs to choose from. 378 */ 379 protected function is_validating() { 380 return !is_null($this->validatinguserids); 381 } 382 383 /** 384 * Get the list of users that were selected by doing optional_param then validating the result. 385 * 386 * @return array of user objects. 387 */ 388 protected function load_selected_users() { 389 // See if we got anything. 390 if ($this->multiselect) { 391 $userids = optional_param_array($this->name, array(), PARAM_INT); 392 } else if ($userid = optional_param($this->name, 0, PARAM_INT)) { 393 $userids = array($userid); 394 } 395 // If there are no users there is nobody to load. 396 if (empty($userids)) { 397 return array(); 398 } 399 400 // If we did, use the find_users method to validate the ids. 401 $this->validatinguserids = $userids; 402 $groupedusers = $this->find_users(''); 403 $this->validatinguserids = null; 404 405 // Aggregate the resulting list back into a single one. 406 $users = array(); 407 foreach ($groupedusers as $group) { 408 foreach ($group as $user) { 409 if (!isset($users[$user->id]) && empty($user->disabled) && in_array($user->id, $userids)) { 410 $users[$user->id] = $user; 411 } 412 } 413 } 414 415 // If we are only supposed to be selecting a single user, make sure we do. 416 if (!$this->multiselect && count($users) > 1) { 417 $users = array_slice($users, 0, 1); 418 } 419 420 return $users; 421 } 422 423 /** 424 * Returns SQL to select required fields. 425 * 426 * @param string $u the table alias for the user table in the query being 427 * built. May be ''. 428 * @return string fragment of SQL to go in the select list of the query. 429 */ 430 protected function required_fields_sql($u) { 431 // Raw list of fields. 432 $fields = array('id'); 433 // Add additional name fields. 434 $fields = array_merge($fields, get_all_user_name_fields(), $this->extrafields); 435 436 // Prepend the table alias. 437 if ($u) { 438 foreach ($fields as &$field) { 439 $field = $u . '.' . $field; 440 } 441 } 442 return implode(',', $fields); 443 } 444 445 /** 446 * Returns an array with SQL to perform a search and the params that go into it. 447 * 448 * @param string $search the text to search for. 449 * @param string $u the table alias for the user table in the query being 450 * built. May be ''. 451 * @return array an array with two elements, a fragment of SQL to go in the 452 * where clause the query, and an array containing any required parameters. 453 * this uses ? style placeholders. 454 */ 455 protected function search_sql($search, $u) { 456 return users_search_sql($search, $u, $this->searchanywhere, $this->extrafields, 457 $this->exclude, $this->validatinguserids); 458 } 459 460 /** 461 * Used to generate a nice message when there are too many users to show. 462 * 463 * The message includes the number of users that currently match, and the 464 * text of the message depends on whether the search term is non-blank. 465 * 466 * @param string $search the search term, as passed in to the find users method. 467 * @param int $count the number of users that currently match. 468 * @return array in the right format to return from the find_users method. 469 */ 470 protected function too_many_results($search, $count) { 471 if ($search) { 472 $a = new stdClass; 473 $a->count = $count; 474 $a->search = $search; 475 return array(get_string('toomanyusersmatchsearch', '', $a) => array(), 476 get_string('pleasesearchmore') => array()); 477 } else { 478 return array(get_string('toomanyuserstoshow', '', $count) => array(), 479 get_string('pleaseusesearch') => array()); 480 } 481 } 482 483 /** 484 * Output the list of <optgroup>s and <options>s that go inside the select. 485 * 486 * This method should do the same as the JavaScript method 487 * user_selector.prototype.handle_response. 488 * 489 * @param array $groupedusers an array, as returned by find_users. 490 * @param string $search 491 * @return string HTML code. 492 */ 493 protected function output_options($groupedusers, $search) { 494 $output = ''; 495 496 // Ensure that the list of previously selected users is up to date. 497 $this->get_selected_users(); 498 499 // If $groupedusers is empty, make a 'no matching users' group. If there is 500 // only one selected user, set a flag to select them if that option is turned on. 501 $select = false; 502 if (empty($groupedusers)) { 503 if (!empty($search)) { 504 $groupedusers = array(get_string('nomatchingusers', '', $search) => array()); 505 } else { 506 $groupedusers = array(get_string('none') => array()); 507 } 508 } else if ($this->autoselectunique && count($groupedusers) == 1 && 509 count(reset($groupedusers)) == 1) { 510 $select = true; 511 if (!$this->multiselect) { 512 $this->selected = array(); 513 } 514 } 515 516 // Output each optgroup. 517 foreach ($groupedusers as $groupname => $users) { 518 $output .= $this->output_optgroup($groupname, $users, $select); 519 } 520 521 // If there were previously selected users who do not match the search, show them too. 522 if ($this->preserveselected && !empty($this->selected)) { 523 $output .= $this->output_optgroup(get_string('previouslyselectedusers', '', $search), $this->selected, true); 524 } 525 526 // This method trashes $this->selected, so clear the cache so it is rebuilt before anyone tried to use it again. 527 $this->selected = null; 528 529 return $output; 530 } 531 532 /** 533 * Output one particular optgroup. Used by the preceding function output_options. 534 * 535 * @param string $groupname the label for this optgroup. 536 * @param array $users the users to put in this optgroup. 537 * @param boolean $select if true, select the users in this group. 538 * @return string HTML code. 539 */ 540 protected function output_optgroup($groupname, $users, $select) { 541 if (!empty($users)) { 542 $output = ' <optgroup label="' . htmlspecialchars($groupname) . ' (' . count($users) . ')">' . "\n"; 543 foreach ($users as $user) { 544 $attributes = ''; 545 if (!empty($user->disabled)) { 546 $attributes .= ' disabled="disabled"'; 547 } else if ($select || isset($this->selected[$user->id])) { 548 $attributes .= ' selected="selected"'; 549 } 550 unset($this->selected[$user->id]); 551 $output .= ' <option' . $attributes . ' value="' . $user->id . '">' . 552 $this->output_user($user) . "</option>\n"; 553 if (!empty($user->infobelow)) { 554 // Poor man's indent here is because CSS styles do not work in select options, except in Firefox. 555 $output .= ' <option disabled="disabled" class="userselector-infobelow">' . 556 ' ' . s($user->infobelow) . '</option>'; 557 } 558 } 559 } else { 560 $output = ' <optgroup label="' . htmlspecialchars($groupname) . '">' . "\n"; 561 $output .= ' <option disabled="disabled"> </option>' . "\n"; 562 } 563 $output .= " </optgroup>\n"; 564 return $output; 565 } 566 567 /** 568 * Convert a user object to a string suitable for displaying as an option in the list box. 569 * 570 * @param object $user the user to display. 571 * @return string a string representation of the user. 572 */ 573 public function output_user($user) { 574 $out = fullname($user); 575 if ($this->extrafields) { 576 $displayfields = array(); 577 foreach ($this->extrafields as $field) { 578 $displayfields[] = $user->{$field}; 579 } 580 $out .= ' (' . implode(', ', $displayfields) . ')'; 581 } 582 return $out; 583 } 584 585 /** 586 * Returns the string to use for the search button caption. 587 * 588 * @return string the caption for the search button. 589 */ 590 protected function search_button_caption() { 591 return get_string('search'); 592 } 593 594 /** 595 * Initialise one of the option checkboxes, either from the request, or failing that from the 596 * user_preferences table, or finally from the given default. 597 * 598 * @param string $name 599 * @param mixed $default 600 * @return mixed|null|string 601 */ 602 private function initialise_option($name, $default) { 603 $param = optional_param($name, null, PARAM_BOOL); 604 if (is_null($param)) { 605 return get_user_preferences($name, $default); 606 } else { 607 set_user_preference($name, $param); 608 return $param; 609 } 610 } 611 612 /** 613 * Output one of the options checkboxes. 614 * 615 * @param string $name 616 * @param string $on 617 * @param string $label 618 * @return string 619 */ 620 private function option_checkbox($name, $on, $label) { 621 if ($on) { 622 $checked = ' checked="checked"'; 623 } else { 624 $checked = ''; 625 } 626 $name = 'userselector_' . $name; 627 $output = '<p><input type="hidden" name="' . $name . '" value="0" />' . 628 // For the benefit of brain-dead IE, the id must be different from the name of the hidden form field above. 629 // It seems that document.getElementById('frog') in IE will return and element with name="frog". 630 '<input type="checkbox" id="' . $name . 'id" name="' . $name . '" value="1"' . $checked . ' /> ' . 631 '<label for="' . $name . 'id">' . $label . "</label></p>\n"; 632 user_preference_allow_ajax_update($name, PARAM_BOOL); 633 return $output; 634 } 635 636 /** 637 * Initialises JS for this control. 638 * 639 * @param string $search 640 * @return string any HTML needed here. 641 */ 642 protected function initialise_javascript($search) { 643 global $USER, $PAGE, $OUTPUT; 644 $output = ''; 645 646 // Put the options into the session, to allow search.php to respond to the ajax requests. 647 $options = $this->get_options(); 648 $hash = md5(serialize($options)); 649 $USER->userselectors[$hash] = $options; 650 651 // Initialise the selector. 652 $PAGE->requires->js_init_call( 653 'M.core_user.init_user_selector', 654 array($this->name, $hash, $this->extrafields, $search), 655 false, 656 self::$jsmodule 657 ); 658 return $output; 659 } 660 } 661 662 /** 663 * Base class to avoid duplicating code. 664 * 665 * @copyright 1999 onwards Martin Dougiamas http://dougiamas.com 666 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 667 */ 668 abstract class groups_user_selector_base extends user_selector_base { 669 /** @var int */ 670 protected $groupid; 671 /** @var int */ 672 protected $courseid; 673 674 /** 675 * Constructor. 676 * 677 * @param string $name control name 678 * @param array $options should have two elements with keys groupid and courseid. 679 */ 680 public function __construct($name, $options) { 681 global $CFG; 682 $options['accesscontext'] = context_course::instance($options['courseid']); 683 parent::__construct($name, $options); 684 $this->groupid = $options['groupid']; 685 $this->courseid = $options['courseid']; 686 require_once($CFG->dirroot . '/group/lib.php'); 687 } 688 689 /** 690 * Returns options for this selector. 691 * @return array 692 */ 693 protected function get_options() { 694 $options = parent::get_options(); 695 $options['groupid'] = $this->groupid; 696 $options['courseid'] = $this->courseid; 697 return $options; 698 } 699 700 /** 701 * Creates an organised array from given data. 702 * 703 * @param array $roles array in the format returned by groups_calculate_role_people. 704 * @param string $search 705 * @return array array in the format find_users is supposed to return. 706 */ 707 protected function convert_array_format($roles, $search) { 708 if (empty($roles)) { 709 $roles = array(); 710 } 711 $groupedusers = array(); 712 foreach ($roles as $role) { 713 if ($search) { 714 $a = new stdClass; 715 $a->role = $role->name; 716 $a->search = $search; 717 $groupname = get_string('matchingsearchandrole', '', $a); 718 } else { 719 $groupname = $role->name; 720 } 721 $groupedusers[$groupname] = $role->users; 722 foreach ($groupedusers[$groupname] as &$user) { 723 unset($user->roles); 724 $user->fullname = fullname($user); 725 if (!empty($user->component)) { 726 $user->infobelow = get_string('addedby', 'group', 727 get_string('pluginname', $user->component)); 728 } 729 } 730 } 731 return $groupedusers; 732 } 733 } 734 735 /** 736 * User selector subclass for the list of users who are in a certain group. 737 * 738 * Used on the add group memebers page. 739 * 740 * @copyright 1999 onwards Martin Dougiamas http://dougiamas.com 741 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 742 */ 743 class group_members_selector extends groups_user_selector_base { 744 745 /** 746 * Finds users to display in this control. 747 * @param string $search 748 * @return array 749 */ 750 public function find_users($search) { 751 list($wherecondition, $params) = $this->search_sql($search, 'u'); 752 753 list($sort, $sortparams) = users_order_by_sql('u', $search, $this->accesscontext); 754 755 $roles = groups_get_members_by_role($this->groupid, $this->courseid, 756 $this->required_fields_sql('u') . ', gm.component', 757 $sort, $wherecondition, array_merge($params, $sortparams)); 758 759 return $this->convert_array_format($roles, $search); 760 } 761 } 762 763 /** 764 * User selector subclass for the list of users who are not in a certain group. 765 * 766 * Used on the add group members page. 767 * 768 * @copyright 1999 onwards Martin Dougiamas http://dougiamas.com 769 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 770 */ 771 class group_non_members_selector extends groups_user_selector_base { 772 /** 773 * An array of user ids populated by find_users() used in print_user_summaries() 774 * @var array 775 */ 776 private $potentialmembersids = array(); 777 778 /** 779 * Output user. 780 * 781 * @param stdClass $user 782 * @return string 783 */ 784 public function output_user($user) { 785 return parent::output_user($user) . ' (' . $user->numgroups . ')'; 786 } 787 788 /** 789 * Returns the user selector JavaScript module 790 * @return array 791 */ 792 public function get_js_module() { 793 return self::$jsmodule; 794 } 795 796 /** 797 * Creates a global JS variable (userSummaries) that is used by the group selector 798 * to print related information when the user clicks on a user in the groups UI. 799 * 800 * Used by /group/clientlib.js 801 * 802 * @global moodle_database $DB 803 * @global moodle_page $PAGE 804 * @param int $courseid 805 */ 806 public function print_user_summaries($courseid) { 807 global $DB, $PAGE; 808 809 $usersummaries = array(); 810 811 // Get other groups user already belongs to. 812 $usergroups = array(); 813 $potentialmembersids = $this->potentialmembersids; 814 if (empty($potentialmembersids) == false) { 815 list($membersidsclause, $params) = $DB->get_in_or_equal($potentialmembersids, SQL_PARAMS_NAMED, 'pm'); 816 $sql = "SELECT u.id AS userid, g.* 817 FROM {user} u 818 JOIN {groups_members} gm ON u.id = gm.userid 819 JOIN {groups} g ON gm.groupid = g.id 820 WHERE u.id $membersidsclause AND g.courseid = :courseid "; 821 $params['courseid'] = $courseid; 822 $rs = $DB->get_recordset_sql($sql, $params); 823 foreach ($rs as $usergroup) { 824 $usergroups[$usergroup->userid][$usergroup->id] = $usergroup; 825 } 826 $rs->close(); 827 828 foreach ($potentialmembersids as $userid) { 829 if (isset($usergroups[$userid])) { 830 $usergrouplist = html_writer::start_tag('ul'); 831 foreach ($usergroups[$userid] as $groupitem) { 832 $usergrouplist .= html_writer::tag('li', format_string($groupitem->name)); 833 } 834 $usergrouplist .= html_writer::end_tag('ul'); 835 } else { 836 $usergrouplist = ''; 837 } 838 $usersummaries[] = $usergrouplist; 839 } 840 } 841 842 $PAGE->requires->data_for_js('userSummaries', $usersummaries); 843 } 844 845 /** 846 * Finds users to display in this control. 847 * 848 * @param string $search 849 * @return array 850 */ 851 public function find_users($search) { 852 global $DB; 853 854 // Get list of allowed roles. 855 $context = context_course::instance($this->courseid); 856 if ($validroleids = groups_get_possible_roles($context)) { 857 list($roleids, $roleparams) = $DB->get_in_or_equal($validroleids, SQL_PARAMS_NAMED, 'r'); 858 } else { 859 $roleids = " = -1"; 860 $roleparams = array(); 861 } 862 863 // We want to query both the current context and parent contexts. 864 list($relatedctxsql, $relatedctxparams) = 865 $DB->get_in_or_equal($context->get_parent_context_ids(true), SQL_PARAMS_NAMED, 'relatedctx'); 866 867 // Get the search condition. 868 list($searchcondition, $searchparams) = $this->search_sql($search, 'u'); 869 870 // Build the SQL. 871 list($enrolsql, $enrolparams) = get_enrolled_sql($context); 872 $fields = "SELECT r.id AS roleid, u.id AS userid, 873 " . $this->required_fields_sql('u') . ", 874 (SELECT count(igm.groupid) 875 FROM {groups_members} igm 876 JOIN {groups} ig ON igm.groupid = ig.id 877 WHERE igm.userid = u.id AND ig.courseid = :courseid) AS numgroups"; 878 $sql = " FROM {user} u 879 JOIN ($enrolsql) e ON e.id = u.id 880 LEFT JOIN {role_assignments} ra ON (ra.userid = u.id AND ra.contextid $relatedctxsql AND ra.roleid $roleids) 881 LEFT JOIN {role} r ON r.id = ra.roleid 882 LEFT JOIN {groups_members} gm ON (gm.userid = u.id AND gm.groupid = :groupid) 883 WHERE u.deleted = 0 884 AND gm.id IS NULL 885 AND $searchcondition"; 886 887 list($sort, $sortparams) = users_order_by_sql('u', $search, $this->accesscontext); 888 $orderby = ' ORDER BY ' . $sort; 889 890 $params = array_merge($searchparams, $roleparams, $enrolparams, $relatedctxparams); 891 $params['courseid'] = $this->courseid; 892 $params['groupid'] = $this->groupid; 893 894 if (!$this->is_validating()) { 895 $potentialmemberscount = $DB->count_records_sql("SELECT COUNT(DISTINCT u.id) $sql", $params); 896 if ($potentialmemberscount > $this->maxusersperpage) { 897 return $this->too_many_results($search, $potentialmemberscount); 898 } 899 } 900 901 $rs = $DB->get_recordset_sql("$fields $sql $orderby", array_merge($params, $sortparams)); 902 $roles = groups_calculate_role_people($rs, $context); 903 904 // Don't hold onto user IDs if we're doing validation. 905 if (empty($this->validatinguserids) ) { 906 if ($roles) { 907 foreach ($roles as $k => $v) { 908 if ($v) { 909 foreach ($v->users as $uid => $userobject) { 910 $this->potentialmembersids[] = $uid; 911 } 912 } 913 } 914 } 915 } 916 917 return $this->convert_array_format($roles, $search); 918 } 919 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Thu Aug 11 10:00:09 2016 | Cross-referenced by PHPXref 0.7.1 |