[ Index ]

PHP Cross Reference of Unnamed Project

title

Body

[close]

/user/selector/ -> lib.php (source)

   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                              '&nbsp;&nbsp;&nbsp;&nbsp;' . s($user->infobelow) . '</option>';
 557                  }
 558              }
 559          } else {
 560              $output = '  <optgroup label="' . htmlspecialchars($groupname) . '">' . "\n";
 561              $output .= '    <option disabled="disabled">&nbsp;</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  }


Generated: Thu Aug 11 10:00:09 2016 Cross-referenced by PHPXref 0.7.1