Search moodle.org's
Developer Documentation

  • Bug fixes for general core bugs in 3.11.x will end 9 May 2022 (12 months).
  • Bug fixes for security issues in 3.11.x will end 14 November 2022 (18 months).
  • PHP version: minimum PHP 7.3.0 Note: minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is supported too.
  • /enrol/ -> locallib.php (source)

    Differences Between: [Versions 310 and 311] [Versions 35 and 311] [Versions 36 and 311] [Versions 37 and 311] [Versions 38 and 311] [Versions 39 and 311]

       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   * This file contains the course_enrolment_manager class which is used to interface
      19   * with the functions that exist in enrollib.php in relation to a single course.
      20   *
      21   * @package    core_enrol
      22   * @copyright  2010 Sam Hemelryk
      23   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
      24   */
      25  
      26  use core_user\fields;
      27  
      28  defined('MOODLE_INTERNAL') || die();
      29  
      30  /**
      31   * This class provides a targeted tied together means of interfacing the enrolment
      32   * tasks together with a course.
      33   *
      34   * It is provided as a convenience more than anything else.
      35   *
      36   * @copyright 2010 Sam Hemelryk
      37   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
      38   */
      39  class course_enrolment_manager {
      40  
      41      /**
      42       * The course context
      43       * @var context
      44       */
      45      protected $context;
      46      /**
      47       * The course we are managing enrolments for
      48       * @var stdClass
      49       */
      50      protected $course = null;
      51      /**
      52       * Limits the focus of the manager to one enrolment plugin instance
      53       * @var string
      54       */
      55      protected $instancefilter = null;
      56      /**
      57       * Limits the focus of the manager to users with specified role
      58       * @var int
      59       */
      60      protected $rolefilter = 0;
      61      /**
      62       * Limits the focus of the manager to users who match search string
      63       * @var string
      64       */
      65      protected $searchfilter = '';
      66      /**
      67       * Limits the focus of the manager to users in specified group
      68       * @var int
      69       */
      70      protected $groupfilter = 0;
      71      /**
      72       * Limits the focus of the manager to users who match status active/inactive
      73       * @var int
      74       */
      75      protected $statusfilter = -1;
      76  
      77      /**
      78       * The total number of users enrolled in the course
      79       * Populated by course_enrolment_manager::get_total_users
      80       * @var int
      81       */
      82      protected $totalusers = null;
      83      /**
      84       * An array of users currently enrolled in the course
      85       * Populated by course_enrolment_manager::get_users
      86       * @var array
      87       */
      88      protected $users = array();
      89  
      90      /**
      91       * An array of users who have roles within this course but who have not
      92       * been enrolled in the course
      93       * @var array
      94       */
      95      protected $otherusers = array();
      96  
      97      /**
      98       * The total number of users who hold a role within the course but who
      99       * arn't enrolled.
     100       * @var int
     101       */
     102      protected $totalotherusers = null;
     103  
     104      /**
     105       * The current moodle_page object
     106       * @var moodle_page
     107       */
     108      protected $moodlepage = null;
     109  
     110      /**#@+
     111       * These variables are used to cache the information this class uses
     112       * please never use these directly instead use their get_ counterparts.
     113       * @access private
     114       * @var array
     115       */
     116      private $_instancessql = null;
     117      private $_instances = null;
     118      private $_inames = null;
     119      private $_plugins = null;
     120      private $_allplugins = null;
     121      private $_roles = null;
     122      private $_visibleroles = null;
     123      private $_assignableroles = null;
     124      private $_assignablerolesothers = null;
     125      private $_groups = null;
     126      /**#@-*/
     127  
     128      /**
     129       * Constructs the course enrolment manager
     130       *
     131       * @param moodle_page $moodlepage
     132       * @param stdClass $course
     133       * @param string $instancefilter
     134       * @param int $rolefilter If non-zero, filters to users with specified role
     135       * @param string $searchfilter If non-blank, filters to users with search text
     136       * @param int $groupfilter if non-zero, filter users with specified group
     137       * @param int $statusfilter if not -1, filter users with active/inactive enrollment.
     138       */
     139      public function __construct(moodle_page $moodlepage, $course, $instancefilter = null,
     140              $rolefilter = 0, $searchfilter = '', $groupfilter = 0, $statusfilter = -1) {
     141          $this->moodlepage = $moodlepage;
     142          $this->context = context_course::instance($course->id);
     143          $this->course = $course;
     144          $this->instancefilter = $instancefilter;
     145          $this->rolefilter = $rolefilter;
     146          $this->searchfilter = $searchfilter;
     147          $this->groupfilter = $groupfilter;
     148          $this->statusfilter = $statusfilter;
     149      }
     150  
     151      /**
     152       * Returns the current moodle page
     153       * @return moodle_page
     154       */
     155      public function get_moodlepage() {
     156          return $this->moodlepage;
     157      }
     158  
     159      /**
     160       * Returns the total number of enrolled users in the course.
     161       *
     162       * If a filter was specificed this will be the total number of users enrolled
     163       * in this course by means of that instance.
     164       *
     165       * @global moodle_database $DB
     166       * @return int
     167       */
     168      public function get_total_users() {
     169          global $DB;
     170          if ($this->totalusers === null) {
     171              list($instancessql, $params, $filter) = $this->get_instance_sql();
     172              list($filtersql, $moreparams) = $this->get_filter_sql();
     173              $params += $moreparams;
     174              $sqltotal = "SELECT COUNT(DISTINCT u.id)
     175                             FROM {user} u
     176                             JOIN {user_enrolments} ue ON (ue.userid = u.id  AND ue.enrolid $instancessql)
     177                             JOIN {enrol} e ON (e.id = ue.enrolid)";
     178              if ($this->groupfilter) {
     179                  $sqltotal .= " LEFT JOIN ({groups_members} gm JOIN {groups} g ON (g.id = gm.groupid))
     180                                           ON (u.id = gm.userid AND g.courseid = e.courseid)";
     181              }
     182              $sqltotal .= "WHERE $filtersql";
     183              $this->totalusers = (int)$DB->count_records_sql($sqltotal, $params);
     184          }
     185          return $this->totalusers;
     186      }
     187  
     188      /**
     189       * Returns the total number of enrolled users in the course.
     190       *
     191       * If a filter was specificed this will be the total number of users enrolled
     192       * in this course by means of that instance.
     193       *
     194       * @global moodle_database $DB
     195       * @return int
     196       */
     197      public function get_total_other_users() {
     198          global $DB;
     199          if ($this->totalotherusers === null) {
     200              list($ctxcondition, $params) = $DB->get_in_or_equal($this->context->get_parent_context_ids(true), SQL_PARAMS_NAMED, 'ctx');
     201              $params['courseid'] = $this->course->id;
     202              $sql = "SELECT COUNT(DISTINCT u.id)
     203                        FROM {role_assignments} ra
     204                        JOIN {user} u ON u.id = ra.userid
     205                        JOIN {context} ctx ON ra.contextid = ctx.id
     206                   LEFT JOIN (
     207                             SELECT ue.id, ue.userid
     208                               FROM {user_enrolments} ue
     209                          LEFT JOIN {enrol} e ON e.id=ue.enrolid
     210                              WHERE e.courseid = :courseid
     211                           ) ue ON ue.userid=u.id
     212                       WHERE ctx.id $ctxcondition AND
     213                             ue.id IS NULL";
     214              $this->totalotherusers = (int)$DB->count_records_sql($sql, $params);
     215          }
     216          return $this->totalotherusers;
     217      }
     218  
     219      /**
     220       * Gets all of the users enrolled in this course.
     221       *
     222       * If a filter was specified this will be the users who were enrolled
     223       * in this course by means of that instance. If role or search filters were
     224       * specified then these will also be applied.
     225       *
     226       * @global moodle_database $DB
     227       * @param string $sort
     228       * @param string $direction ASC or DESC
     229       * @param int $page First page should be 0
     230       * @param int $perpage Defaults to 25
     231       * @return array
     232       */
     233      public function get_users($sort, $direction='ASC', $page=0, $perpage=25) {
     234          global $DB;
     235          if ($direction !== 'ASC') {
     236              $direction = 'DESC';
     237          }
     238          $key = md5("$sort-$direction-$page-$perpage");
     239          if (!array_key_exists($key, $this->users)) {
     240              list($instancessql, $params, $filter) = $this->get_instance_sql();
     241              list($filtersql, $moreparams) = $this->get_filter_sql();
     242              $params += $moreparams;
     243              $userfields = fields::for_identity($this->get_context())->with_userpic()->excluding('lastaccess');
     244              ['selects' => $fieldselect, 'joins' => $fieldjoin, 'params' => $fieldjoinparams] =
     245                      (array)$userfields->get_sql('u', true, '', '', false);
     246              $params += $fieldjoinparams;
     247              $sql = "SELECT DISTINCT $fieldselect, COALESCE(ul.timeaccess, 0) AS lastcourseaccess
     248                        FROM {user} u
     249                        JOIN {user_enrolments} ue ON (ue.userid = u.id  AND ue.enrolid $instancessql)
     250                        JOIN {enrol} e ON (e.id = ue.enrolid)
     251                             $fieldjoin
     252                   LEFT JOIN {user_lastaccess} ul ON (ul.courseid = e.courseid AND ul.userid = u.id)";
     253              if ($this->groupfilter) {
     254                  $sql .= " LEFT JOIN ({groups_members} gm JOIN {groups} g ON (g.id = gm.groupid))
     255                                      ON (u.id = gm.userid AND g.courseid = e.courseid)";
     256              }
     257              $sql .= "WHERE $filtersql
     258                    ORDER BY $sort $direction";
     259              $this->users[$key] = $DB->get_records_sql($sql, $params, $page*$perpage, $perpage);
     260          }
     261          return $this->users[$key];
     262      }
     263  
     264      /**
     265       * Obtains WHERE clause to filter results by defined search and role filter
     266       * (instance filter is handled separately in JOIN clause, see
     267       * get_instance_sql).
     268       *
     269       * @return array Two-element array with SQL and params for WHERE clause
     270       */
     271      protected function get_filter_sql() {
     272          global $DB;
     273  
     274          // Search condition.
     275          // TODO Does not support custom user profile fields (MDL-70456).
     276          $extrafields = fields::get_identity_fields($this->get_context(), false);
     277          list($sql, $params) = users_search_sql($this->searchfilter, 'u', true, $extrafields);
     278  
     279          // Role condition.
     280          if ($this->rolefilter) {
     281              // Get context SQL.
     282              $contextids = $this->context->get_parent_context_ids();
     283              $contextids[] = $this->context->id;
     284              list($contextsql, $contextparams) = $DB->get_in_or_equal($contextids, SQL_PARAMS_NAMED);
     285              $params += $contextparams;
     286  
     287              // Role check condition.
     288              $sql .= " AND (SELECT COUNT(1) FROM {role_assignments} ra WHERE ra.userid = u.id " .
     289                      "AND ra.roleid = :roleid AND ra.contextid $contextsql) > 0";
     290              $params['roleid'] = $this->rolefilter;
     291          }
     292  
     293          // Group condition.
     294          if ($this->groupfilter) {
     295              if ($this->groupfilter < 0) {
     296                  // Show users who are not in any group.
     297                  $sql .= " AND gm.groupid IS NULL";
     298              } else {
     299                  $sql .= " AND gm.groupid = :groupid";
     300                  $params['groupid'] = $this->groupfilter;
     301              }
     302          }
     303  
     304          // Status condition.
     305          if ($this->statusfilter === ENROL_USER_ACTIVE) {
     306              $sql .= " AND ue.status = :active AND e.status = :enabled AND ue.timestart < :now1
     307                      AND (ue.timeend = 0 OR ue.timeend > :now2)";
     308              $now = round(time(), -2); // rounding helps caching in DB
     309              $params += array('enabled' => ENROL_INSTANCE_ENABLED,
     310                               'active' => ENROL_USER_ACTIVE,
     311                               'now1' => $now,
     312                               'now2' => $now);
     313          } else if ($this->statusfilter === ENROL_USER_SUSPENDED) {
     314              $sql .= " AND (ue.status = :inactive OR e.status = :disabled OR ue.timestart > :now1
     315                      OR (ue.timeend <> 0 AND ue.timeend < :now2))";
     316              $now = round(time(), -2); // rounding helps caching in DB
     317              $params += array('disabled' => ENROL_INSTANCE_DISABLED,
     318                               'inactive' => ENROL_USER_SUSPENDED,
     319                               'now1' => $now,
     320                               'now2' => $now);
     321          }
     322  
     323          return array($sql, $params);
     324      }
     325  
     326      /**
     327       * Gets and array of other users.
     328       *
     329       * Other users are users who have been assigned roles or inherited roles
     330       * within this course but who have not been enrolled in the course
     331       *
     332       * @global moodle_database $DB
     333       * @param string $sort
     334       * @param string $direction
     335       * @param int $page
     336       * @param int $perpage
     337       * @return array
     338       */
     339      public function get_other_users($sort, $direction='ASC', $page=0, $perpage=25) {
     340          global $DB;
     341          if ($direction !== 'ASC') {
     342              $direction = 'DESC';
     343          }
     344          $key = md5("$sort-$direction-$page-$perpage");
     345          if (!array_key_exists($key, $this->otherusers)) {
     346              list($ctxcondition, $params) = $DB->get_in_or_equal($this->context->get_parent_context_ids(true), SQL_PARAMS_NAMED, 'ctx');
     347              $params['courseid'] = $this->course->id;
     348              $params['cid'] = $this->course->id;
     349              $userfields = fields::for_identity($this->get_context())->with_userpic();
     350              ['selects' => $fieldselect, 'joins' => $fieldjoin, 'params' => $fieldjoinparams] =
     351                      (array)$userfields->get_sql('u', true);
     352              $params += $fieldjoinparams;
     353              $sql = "SELECT ra.id as raid, ra.contextid, ra.component, ctx.contextlevel, ra.roleid,
     354                             coalesce(u.lastaccess,0) AS lastaccess
     355                             $fieldselect
     356                        FROM {role_assignments} ra
     357                        JOIN {user} u ON u.id = ra.userid
     358                        JOIN {context} ctx ON ra.contextid = ctx.id
     359                             $fieldjoin
     360                   LEFT JOIN (
     361                         SELECT ue.id, ue.userid
     362                           FROM {user_enrolments} ue
     363                           JOIN {enrol} e ON e.id = ue.enrolid
     364                          WHERE e.courseid = :courseid
     365                         ) ue ON ue.userid=u.id
     366                       WHERE ctx.id $ctxcondition AND
     367                             ue.id IS NULL
     368                    ORDER BY $sort $direction, ctx.depth DESC";
     369              $this->otherusers[$key] = $DB->get_records_sql($sql, $params, $page*$perpage, $perpage);
     370          }
     371          return $this->otherusers[$key];
     372      }
     373  
     374      /**
     375       * Helper method used by {@link get_potential_users()} and {@link search_other_users()}.
     376       *
     377       * @param string $search the search term, if any.
     378       * @param bool $searchanywhere Can the search term be anywhere, or must it be at the start.
     379       * @return array with three elements:
     380       *     string list of fields to SELECT,
     381       *     string possible database joins for user fields
     382       *     string contents of SQL WHERE clause,
     383       *     array query params. Note that the SQL snippets use named parameters.
     384       */
     385      protected function get_basic_search_conditions($search, $searchanywhere) {
     386          global $DB, $CFG;
     387  
     388          // Get custom user field SQL used for querying all the fields we need (identity, name, and
     389          // user picture).
     390          $userfields = fields::for_identity($this->context)->with_name()->with_userpic()
     391                  ->excluding('username', 'lastaccess', 'maildisplay');
     392          ['selects' => $fieldselects, 'joins' => $fieldjoins, 'params' => $params, 'mappings' => $mappings] =
     393                  (array)$userfields->get_sql('u', true, '', '', false);
     394  
     395          // Searchable fields are only the identity and name ones (not userpic).
     396          $searchable = array_fill_keys($userfields->get_required_fields(
     397                  [fields::PURPOSE_IDENTITY, fields::PURPOSE_NAME]), true);
     398  
     399          // Add some additional sensible conditions
     400          $tests = array("u.id <> :guestid", 'u.deleted = 0', 'u.confirmed = 1');
     401          $params['guestid'] = $CFG->siteguest;
     402          if (!empty($search)) {
     403              // Include identity and name fields as conditions.
     404              foreach ($mappings as $fieldname => $fieldsql) {
     405                  if (array_key_exists($fieldname, $searchable)) {
     406                      $conditions[] = $fieldsql;
     407                  }
     408              }
     409              $conditions[] = $DB->sql_fullname('u.firstname', 'u.lastname');
     410              if ($searchanywhere) {
     411                  $searchparam = '%' . $search . '%';
     412              } else {
     413                  $searchparam = $search . '%';
     414              }
     415              $i = 0;
     416              foreach ($conditions as $key => $condition) {
     417                  $conditions[$key] = $DB->sql_like($condition, ":con{$i}00", false);
     418                  $params["con{$i}00"] = $searchparam;
     419                  $i++;
     420              }
     421              $tests[] = '(' . implode(' OR ', $conditions) . ')';
     422          }
     423          $wherecondition = implode(' AND ', $tests);
     424  
     425          $selects = $fieldselects . ', u.username, u.lastaccess, u.maildisplay';
     426          return [$selects, $fieldjoins, $params, $wherecondition];
     427      }
     428  
     429      /**
     430       * Helper method used by {@link get_potential_users()} and {@link search_other_users()}.
     431       *
     432       * @param string $search the search string, if any.
     433       * @param string $fields the first bit of the SQL when returning some users.
     434       * @param string $countfields fhe first bit of the SQL when counting the users.
     435       * @param string $sql the bulk of the SQL statement.
     436       * @param array $params query parameters.
     437       * @param int $page which page number of the results to show.
     438       * @param int $perpage number of users per page.
     439       * @param int $addedenrollment number of users added to enrollment.
     440       * @param bool $returnexactcount Return the exact total users using count_record or not.
     441       * @return array with two or three elements:
     442       *      int totalusers Number users matching the search. (This element only exist if $returnexactcount was set to true)
     443       *      array users List of user objects returned by the query.
     444       *      boolean moreusers True if there are still more users, otherwise is False.
     445       * @throws dml_exception
     446       */
     447      protected function execute_search_queries($search, $fields, $countfields, $sql, array $params, $page, $perpage,
     448              $addedenrollment = 0, $returnexactcount = false) {
     449          global $DB, $CFG;
     450  
     451          list($sort, $sortparams) = users_order_by_sql('u', $search, $this->get_context());
     452          $order = ' ORDER BY ' . $sort;
     453  
     454          $totalusers = 0;
     455          $moreusers = false;
     456          $results = [];
     457  
     458          $availableusers = $DB->get_records_sql($fields . $sql . $order,
     459                  array_merge($params, $sortparams), ($page * $perpage) - $addedenrollment, $perpage + 1);
     460          if ($availableusers) {
     461              $totalusers = count($availableusers);
     462              $moreusers = $totalusers > $perpage;
     463  
     464              if ($moreusers) {
     465                  // We need to discard the last record.
     466                  array_pop($availableusers);
     467              }
     468  
     469              if ($returnexactcount && $moreusers) {
     470                  // There is more data. We need to do the exact count.
     471                  $totalusers = $DB->count_records_sql($countfields . $sql, $params);
     472              }
     473          }
     474  
     475          $results['users'] = $availableusers;
     476          $results['moreusers'] = $moreusers;
     477  
     478          if ($returnexactcount) {
     479              // Include totalusers in result if $returnexactcount flag is true.
     480              $results['totalusers'] = $totalusers;
     481          }
     482  
     483          return $results;
     484      }
     485  
     486      /**
     487       * Gets an array of the users that can be enrolled in this course.
     488       *
     489       * @global moodle_database $DB
     490       * @param int $enrolid
     491       * @param string $search
     492       * @param bool $searchanywhere
     493       * @param int $page Defaults to 0
     494       * @param int $perpage Defaults to 25
     495       * @param int $addedenrollment Defaults to 0
     496       * @param bool $returnexactcount Return the exact total users using count_record or not.
     497       * @return array with two or three elements:
     498       *      int totalusers Number users matching the search. (This element only exist if $returnexactcount was set to true)
     499       *      array users List of user objects returned by the query.
     500       *      boolean moreusers True if there are still more users, otherwise is False.
     501       * @throws dml_exception
     502       */
     503      public function get_potential_users($enrolid, $search = '', $searchanywhere = false, $page = 0, $perpage = 25,
     504              $addedenrollment = 0, $returnexactcount = false) {
     505          global $DB;
     506  
     507          [$ufields, $joins, $params, $wherecondition] = $this->get_basic_search_conditions($search, $searchanywhere);
     508  
     509          $fields      = 'SELECT '.$ufields;
     510          $countfields = 'SELECT COUNT(1)';
     511          $sql = " FROM {user} u
     512                        $joins
     513              LEFT JOIN {user_enrolments} ue ON (ue.userid = u.id AND ue.enrolid = :enrolid)
     514                  WHERE $wherecondition
     515                        AND ue.id IS NULL";
     516          $params['enrolid'] = $enrolid;
     517  
     518          return $this->execute_search_queries($search, $fields, $countfields, $sql, $params, $page, $perpage, $addedenrollment,
     519                  $returnexactcount);
     520      }
     521  
     522      /**
     523       * Searches other users and returns paginated results
     524       *
     525       * @global moodle_database $DB
     526       * @param string $search
     527       * @param bool $searchanywhere
     528       * @param int $page Starting at 0
     529       * @param int $perpage
     530       * @param bool $returnexactcount Return the exact total users using count_record or not.
     531       * @return array with two or three elements:
     532       *      int totalusers Number users matching the search. (This element only exist if $returnexactcount was set to true)
     533       *      array users List of user objects returned by the query.
     534       *      boolean moreusers True if there are still more users, otherwise is False.
     535       * @throws dml_exception
     536       */
     537      public function search_other_users($search = '', $searchanywhere = false, $page = 0, $perpage = 25, $returnexactcount = false) {
     538          global $DB, $CFG;
     539  
     540          [$ufields, $joins, $params, $wherecondition] = $this->get_basic_search_conditions($search, $searchanywhere);
     541  
     542          $fields      = 'SELECT ' . $ufields;
     543          $countfields = 'SELECT COUNT(u.id)';
     544          $sql   = " FROM {user} u
     545                          $joins
     546                LEFT JOIN {role_assignments} ra ON (ra.userid = u.id AND ra.contextid = :contextid)
     547                    WHERE $wherecondition
     548                      AND ra.id IS NULL";
     549          $params['contextid'] = $this->context->id;
     550  
     551          return $this->execute_search_queries($search, $fields, $countfields, $sql, $params, $page, $perpage, 0, $returnexactcount);
     552      }
     553  
     554      /**
     555       * Searches through the enrolled users in this course.
     556       *
     557       * @param string $search The search term.
     558       * @param bool $searchanywhere Can the search term be anywhere, or must it be at the start.
     559       * @param int $page Starting at 0.
     560       * @param int $perpage Number of users returned per page.
     561       * @param bool $returnexactcount Return the exact total users using count_record or not.
     562       * @return array with two or three elements:
     563       *      int totalusers Number users matching the search. (This element only exist if $returnexactcount was set to true)
     564       *      array users List of user objects returned by the query.
     565       *      boolean moreusers True if there are still more users, otherwise is False.
     566       */
     567      public function search_users(string $search = '', bool $searchanywhere = false, int $page = 0, int $perpage = 25,
     568              bool $returnexactcount = false) {
     569          [$ufields, $joins, $params, $wherecondition] = $this->get_basic_search_conditions($search, $searchanywhere);
     570  
     571          $fields      = 'SELECT ' . $ufields;
     572          $countfields = 'SELECT COUNT(u.id)';
     573          $sql = " FROM {user} u
     574                        $joins
     575                   JOIN {user_enrolments} ue ON ue.userid = u.id
     576                   JOIN {enrol} e ON ue.enrolid = e.id
     577                  WHERE $wherecondition
     578                    AND e.courseid = :courseid";
     579          $params['courseid'] = $this->course->id;
     580  
     581          return $this->execute_search_queries($search, $fields, $countfields, $sql, $params, $page, $perpage, 0, $returnexactcount);
     582      }
     583  
     584      /**
     585       * Gets an array containing some SQL to user for when selecting, params for
     586       * that SQL, and the filter that was used in constructing the sql.
     587       *
     588       * @global moodle_database $DB
     589       * @return string
     590       */
     591      protected function get_instance_sql() {
     592          global $DB;
     593          if ($this->_instancessql === null) {
     594              $instances = $this->get_enrolment_instances();
     595              $filter = $this->get_enrolment_filter();
     596              if ($filter && array_key_exists($filter, $instances)) {
     597                  $sql = " = :ifilter";
     598                  $params = array('ifilter'=>$filter);
     599              } else {
     600                  $filter = 0;
     601                  if ($instances) {
     602                      list($sql, $params) = $DB->get_in_or_equal(array_keys($this->get_enrolment_instances()), SQL_PARAMS_NAMED);
     603                  } else {
     604                      // no enabled instances, oops, we should probably say something
     605                      $sql = "= :never";
     606                      $params = array('never'=>-1);
     607                  }
     608              }
     609              $this->instancefilter = $filter;
     610              $this->_instancessql = array($sql, $params, $filter);
     611          }
     612          return $this->_instancessql;
     613      }
     614  
     615      /**
     616       * Returns all of the enrolment instances for this course.
     617       *
     618       * @param bool $onlyenabled Whether to return data from enabled enrolment instance names only.
     619       * @return array
     620       */
     621      public function get_enrolment_instances($onlyenabled = false) {
     622          if ($this->_instances === null) {
     623              $this->_instances = enrol_get_instances($this->course->id, $onlyenabled);
     624          }
     625          return $this->_instances;
     626      }
     627  
     628      /**
     629       * Returns the names for all of the enrolment instances for this course.
     630       *
     631       * @param bool $onlyenabled Whether to return data from enabled enrolment instance names only.
     632       * @return array
     633       */
     634      public function get_enrolment_instance_names($onlyenabled = false) {
     635          if ($this->_inames === null) {
     636              $instances = $this->get_enrolment_instances($onlyenabled);
     637              $plugins = $this->get_enrolment_plugins(false);
     638              foreach ($instances as $key=>$instance) {
     639                  if (!isset($plugins[$instance->enrol])) {
     640                      // weird, some broken stuff in plugin
     641                      unset($instances[$key]);
     642                      continue;
     643                  }
     644                  $this->_inames[$key] = $plugins[$instance->enrol]->get_instance_name($instance);
     645              }
     646          }
     647          return $this->_inames;
     648      }
     649  
     650      /**
     651       * Gets all of the enrolment plugins that are available for this course.
     652       *
     653       * @param bool $onlyenabled return only enabled enrol plugins
     654       * @return array
     655       */
     656      public function get_enrolment_plugins($onlyenabled = true) {
     657          if ($this->_plugins === null) {
     658              $this->_plugins = enrol_get_plugins(true);
     659          }
     660  
     661          if ($onlyenabled) {
     662              return $this->_plugins;
     663          }
     664  
     665          if ($this->_allplugins === null) {
     666              // Make sure we have the same objects in _allplugins and _plugins.
     667              $this->_allplugins = $this->_plugins;
     668              foreach (enrol_get_plugins(false) as $name=>$plugin) {
     669                  if (!isset($this->_allplugins[$name])) {
     670                      $this->_allplugins[$name] = $plugin;
     671                  }
     672              }
     673          }
     674  
     675          return $this->_allplugins;
     676      }
     677  
     678      /**
     679       * Gets all of the roles this course can contain.
     680       *
     681       * @return array
     682       */
     683      public function get_all_roles() {
     684          if ($this->_roles === null) {
     685              $this->_roles = role_fix_names(get_all_roles($this->context), $this->context);
     686          }
     687          return $this->_roles;
     688      }
     689  
     690      /**
     691       * Gets all of the roles this course can contain.
     692       *
     693       * @return array
     694       */
     695      public function get_viewable_roles() {
     696          if ($this->_visibleroles === null) {
     697              $this->_visibleroles = get_viewable_roles($this->context);
     698          }
     699          return $this->_visibleroles;
     700      }
     701  
     702      /**
     703       * Gets all of the assignable roles for this course.
     704       *
     705       * @return array
     706       */
     707      public function get_assignable_roles($otherusers = false) {
     708          if ($this->_assignableroles === null) {
     709              $this->_assignableroles = get_assignable_roles($this->context, ROLENAME_ALIAS, false); // verifies unassign access control too
     710          }
     711  
     712          if ($otherusers) {
     713              if (!is_array($this->_assignablerolesothers)) {
     714                  $this->_assignablerolesothers = array();
     715                  list($courseviewroles, $ignored) = get_roles_with_cap_in_context($this->context, 'moodle/course:view');
     716                  foreach ($this->_assignableroles as $roleid=>$role) {
     717                      if (isset($courseviewroles[$roleid])) {
     718                          $this->_assignablerolesothers[$roleid] = $role;
     719                      }
     720                  }
     721              }
     722              return $this->_assignablerolesothers;
     723          } else {
     724              return $this->_assignableroles;
     725          }
     726      }
     727  
     728      /**
     729       * Gets all of the assignable roles for this course, wrapped in an array to ensure
     730       * role sort order is not lost during json deserialisation.
     731       *
     732       * @param boolean $otherusers whether to include the assignable roles for other users
     733       * @return array
     734       */
     735      public function get_assignable_roles_for_json($otherusers = false) {
     736          $rolesarray = array();
     737          $assignable = $this->get_assignable_roles($otherusers);
     738          foreach ($assignable as $id => $role) {
     739              $rolesarray[] = array('id' => $id, 'name' => $role);
     740          }
     741          return $rolesarray;
     742      }
     743  
     744      /**
     745       * Gets all of the groups for this course.
     746       *
     747       * @return array
     748       */
     749      public function get_all_groups() {
     750          if ($this->_groups === null) {
     751              $this->_groups = groups_get_all_groups($this->course->id);
     752              foreach ($this->_groups as $gid=>$group) {
     753                  $this->_groups[$gid]->name = format_string($group->name);
     754              }
     755          }
     756          return $this->_groups;
     757      }
     758  
     759      /**
     760       * Unenrols a user from the course given the users ue entry
     761       *
     762       * @global moodle_database $DB
     763       * @param stdClass $ue
     764       * @return bool
     765       */
     766      public function unenrol_user($ue) {
     767          global $DB;
     768          list ($instance, $plugin) = $this->get_user_enrolment_components($ue);
     769          if ($instance && $plugin && $plugin->allow_unenrol_user($instance, $ue) && has_capability("enrol/$instance->enrol:unenrol", $this->context)) {
     770              $plugin->unenrol_user($instance, $ue->userid);
     771              return true;
     772          }
     773          return false;
     774      }
     775  
     776      /**
     777       * Given a user enrolment record this method returns the plugin and enrolment
     778       * instance that relate to it.
     779       *
     780       * @param stdClass|int $userenrolment
     781       * @return array array($instance, $plugin)
     782       */
     783      public function get_user_enrolment_components($userenrolment) {
     784          global $DB;
     785          if (is_numeric($userenrolment)) {
     786              $userenrolment = $DB->get_record('user_enrolments', array('id'=>(int)$userenrolment));
     787          }
     788          $instances = $this->get_enrolment_instances();
     789          $plugins = $this->get_enrolment_plugins(false);
     790          if (!$userenrolment || !isset($instances[$userenrolment->enrolid])) {
     791              return array(false, false);
     792          }
     793          $instance = $instances[$userenrolment->enrolid];
     794          $plugin = $plugins[$instance->enrol];
     795          return array($instance, $plugin);
     796      }
     797  
     798      /**
     799       * Removes an assigned role from a user.
     800       *
     801       * @global moodle_database $DB
     802       * @param int $userid
     803       * @param int $roleid
     804       * @return bool
     805       */
     806      public function unassign_role_from_user($userid, $roleid) {
     807          global $DB;
     808          // Admins may unassign any role, others only those they could assign.
     809          if (!is_siteadmin() and !array_key_exists($roleid, $this->get_assignable_roles())) {
     810              if (defined('AJAX_SCRIPT')) {
     811                  throw new moodle_exception('invalidrole');
     812              }
     813              return false;
     814          }
     815          $user = $DB->get_record('user', array('id'=>$userid), '*', MUST_EXIST);
     816          $ras = $DB->get_records('role_assignments', array('contextid'=>$this->context->id, 'userid'=>$user->id, 'roleid'=>$roleid));
     817          foreach ($ras as $ra) {
     818              if ($ra->component) {
     819                  if (strpos($ra->component, 'enrol_') !== 0) {
     820                      continue;
     821                  }
     822                  if (!$plugin = enrol_get_plugin(substr($ra->component, 6))) {
     823                      continue;
     824                  }
     825                  if ($plugin->roles_protected()) {
     826                      continue;
     827                  }
     828              }
     829              role_unassign($ra->roleid, $ra->userid, $ra->contextid, $ra->component, $ra->itemid);
     830          }
     831          return true;
     832      }
     833  
     834      /**
     835       * Assigns a role to a user.
     836       *
     837       * @param int $roleid
     838       * @param int $userid
     839       * @return int|false
     840       */
     841      public function assign_role_to_user($roleid, $userid) {
     842          require_capability('moodle/role:assign', $this->context);
     843          if (!array_key_exists($roleid, $this->get_assignable_roles())) {
     844              if (defined('AJAX_SCRIPT')) {
     845                  throw new moodle_exception('invalidrole');
     846              }
     847              return false;
     848          }
     849          return role_assign($roleid, $userid, $this->context->id, '', NULL);
     850      }
     851  
     852      /**
     853       * Adds a user to a group
     854       *
     855       * @param stdClass $user
     856       * @param int $groupid
     857       * @return bool
     858       */
     859      public function add_user_to_group($user, $groupid) {
     860          require_capability('moodle/course:managegroups', $this->context);
     861          $group = $this->get_group($groupid);
     862          if (!$group) {
     863              return false;
     864          }
     865          return groups_add_member($group->id, $user->id);
     866      }
     867  
     868      /**
     869       * Removes a user from a group
     870       *
     871       * @global moodle_database $DB
     872       * @param StdClass $user
     873       * @param int $groupid
     874       * @return bool
     875       */
     876      public function remove_user_from_group($user, $groupid) {
     877          global $DB;
     878          require_capability('moodle/course:managegroups', $this->context);
     879          $group = $this->get_group($groupid);
     880          if (!groups_remove_member_allowed($group, $user)) {
     881              return false;
     882          }
     883          if (!$group) {
     884              return false;
     885          }
     886          return groups_remove_member($group, $user);
     887      }
     888  
     889      /**
     890       * Gets the requested group
     891       *
     892       * @param int $groupid
     893       * @return stdClass|int
     894       */
     895      public function get_group($groupid) {
     896          $groups = $this->get_all_groups();
     897          if (!array_key_exists($groupid, $groups)) {
     898              return false;
     899          }
     900          return $groups[$groupid];
     901      }
     902  
     903      /**
     904       * Edits an enrolment
     905       *
     906       * @param stdClass $userenrolment
     907       * @param stdClass $data
     908       * @return bool
     909       */
     910      public function edit_enrolment($userenrolment, $data) {
     911          //Only allow editing if the user has the appropriate capability
     912          //Already checked in /user/index.php but checking again in case this function is called from elsewhere
     913          list($instance, $plugin) = $this->get_user_enrolment_components($userenrolment);
     914          if ($instance && $plugin && $plugin->allow_manage($instance) && has_capability("enrol/$instance->enrol:manage", $this->context)) {
     915              if (!isset($data->status)) {
     916                  $data->status = $userenrolment->status;
     917              }
     918              $plugin->update_user_enrol($instance, $userenrolment->userid, $data->status, $data->timestart, $data->timeend);
     919              return true;
     920          }
     921          return false;
     922      }
     923  
     924      /**
     925       * Returns the current enrolment filter that is being applied by this class
     926       * @return string
     927       */
     928      public function get_enrolment_filter() {
     929          return $this->instancefilter;
     930      }
     931  
     932      /**
     933       * Gets the roles assigned to this user that are applicable for this course.
     934       *
     935       * @param int $userid
     936       * @return array
     937       */
     938      public function get_user_roles($userid) {
     939          $roles = array();
     940          $ras = get_user_roles($this->context, $userid, true, 'c.contextlevel DESC, r.sortorder ASC');
     941          $plugins = $this->get_enrolment_plugins(false);
     942          foreach ($ras as $ra) {
     943              if ($ra->contextid != $this->context->id) {
     944                  if (!array_key_exists($ra->roleid, $roles)) {
     945                      $roles[$ra->roleid] = null;
     946                  }
     947                  // higher ras, course always takes precedence
     948                  continue;
     949              }
     950              if (array_key_exists($ra->roleid, $roles) && $roles[$ra->roleid] === false) {
     951                  continue;
     952              }
     953              $changeable = true;
     954              if ($ra->component) {
     955                  $changeable = false;
     956                  if (strpos($ra->component, 'enrol_') === 0) {
     957                      $plugin = substr($ra->component, 6);
     958                      if (isset($plugins[$plugin])) {
     959                          $changeable = !$plugins[$plugin]->roles_protected();
     960                      }
     961                  }
     962              }
     963  
     964              $roles[$ra->roleid] = $changeable;
     965          }
     966          return $roles;
     967      }
     968  
     969      /**
     970       * Gets the enrolments this user has in the course - including all suspended plugins and instances.
     971       *
     972       * @global moodle_database $DB
     973       * @param int $userid
     974       * @return array
     975       */
     976      public function get_user_enrolments($userid) {
     977          global $DB;
     978          list($instancessql, $params, $filter) = $this->get_instance_sql();
     979          $params['userid'] = $userid;
     980          $userenrolments = $DB->get_records_select('user_enrolments', "enrolid $instancessql AND userid = :userid", $params);
     981          $instances = $this->get_enrolment_instances();
     982          $plugins = $this->get_enrolment_plugins(false);
     983          $inames = $this->get_enrolment_instance_names();
     984          foreach ($userenrolments as &$ue) {
     985              $ue->enrolmentinstance     = $instances[$ue->enrolid];
     986              $ue->enrolmentplugin       = $plugins[$ue->enrolmentinstance->enrol];
     987              $ue->enrolmentinstancename = $inames[$ue->enrolmentinstance->id];
     988          }
     989          return $userenrolments;
     990      }
     991  
     992      /**
     993       * Gets the groups this user belongs to
     994       *
     995       * @param int $userid
     996       * @return array
     997       */
     998      public function get_user_groups($userid) {
     999          return groups_get_all_groups($this->course->id, $userid, 0, 'g.id');
    1000      }
    1001  
    1002      /**
    1003       * Retursn an array of params that would go into the URL to return to this
    1004       * exact page.
    1005       *
    1006       * @return array
    1007       */
    1008      public function get_url_params() {
    1009          $args = array(
    1010              'id' => $this->course->id
    1011          );
    1012          if (!empty($this->instancefilter)) {
    1013              $args['ifilter'] = $this->instancefilter;
    1014          }
    1015          if (!empty($this->rolefilter)) {
    1016              $args['role'] = $this->rolefilter;
    1017          }
    1018          if ($this->searchfilter !== '') {
    1019              $args['search'] = $this->searchfilter;
    1020          }
    1021          if (!empty($this->groupfilter)) {
    1022              $args['filtergroup'] = $this->groupfilter;
    1023          }
    1024          if ($this->statusfilter !== -1) {
    1025              $args['status'] = $this->statusfilter;
    1026          }
    1027          return $args;
    1028      }
    1029  
    1030      /**
    1031       * Returns the course this object is managing enrolments for
    1032       *
    1033       * @return stdClass
    1034       */
    1035      public function get_course() {
    1036          return $this->course;
    1037      }
    1038  
    1039      /**
    1040       * Returns the course context
    1041       *
    1042       * @return context
    1043       */
    1044      public function get_context() {
    1045          return $this->context;
    1046      }
    1047  
    1048      /**
    1049       * Gets an array of other users in this course ready for display.
    1050       *
    1051       * Other users are users who have been assigned or inherited roles within this
    1052       * course but have not been enrolled.
    1053       *
    1054       * @param core_enrol_renderer $renderer
    1055       * @param moodle_url $pageurl
    1056       * @param string $sort
    1057       * @param string $direction ASC | DESC
    1058       * @param int $page Starting from 0
    1059       * @param int $perpage
    1060       * @return array
    1061       */
    1062      public function get_other_users_for_display(core_enrol_renderer $renderer, moodle_url $pageurl, $sort, $direction, $page, $perpage) {
    1063  
    1064          $userroles = $this->get_other_users($sort, $direction, $page, $perpage);
    1065          $roles = $this->get_all_roles();
    1066          $plugins = $this->get_enrolment_plugins(false);
    1067  
    1068          $context    = $this->get_context();
    1069          $now = time();
    1070          // TODO Does not support custom user profile fields (MDL-70456).
    1071          $extrafields = fields::get_identity_fields($context, false);
    1072  
    1073          $users = array();
    1074          foreach ($userroles as $userrole) {
    1075              $contextid = $userrole->contextid;
    1076              unset($userrole->contextid); // This would collide with user avatar.
    1077              if (!array_key_exists($userrole->id, $users)) {
    1078                  $users[$userrole->id] = $this->prepare_user_for_display($userrole, $extrafields, $now);
    1079              }
    1080              $a = new stdClass;
    1081              $a->role = $roles[$userrole->roleid]->localname;
    1082              if ($contextid == $this->context->id) {
    1083                  $changeable = true;
    1084                  if ($userrole->component) {
    1085                      $changeable = false;
    1086                      if (strpos($userrole->component, 'enrol_') === 0) {
    1087                          $plugin = substr($userrole->component, 6);
    1088                          if (isset($plugins[$plugin])) {
    1089                              $changeable = !$plugins[$plugin]->roles_protected();
    1090                          }
    1091                      }
    1092                  }
    1093                  $roletext = get_string('rolefromthiscourse', 'enrol', $a);
    1094              } else {
    1095                  $changeable = false;
    1096                  switch ($userrole->contextlevel) {
    1097                      case CONTEXT_COURSE :
    1098                          // Meta course
    1099                          $roletext = get_string('rolefrommetacourse', 'enrol', $a);
    1100                          break;
    1101                      case CONTEXT_COURSECAT :
    1102                          $roletext = get_string('rolefromcategory', 'enrol', $a);
    1103                          break;
    1104                      case CONTEXT_SYSTEM:
    1105                      default:
    1106                          $roletext = get_string('rolefromsystem', 'enrol', $a);
    1107                          break;
    1108                  }
    1109              }
    1110              if (!isset($users[$userrole->id]['roles'])) {
    1111                  $users[$userrole->id]['roles'] = array();
    1112              }
    1113              $users[$userrole->id]['roles'][$userrole->roleid] = array(
    1114                  'text' => $roletext,
    1115                  'unchangeable' => !$changeable
    1116              );
    1117          }
    1118          return $users;
    1119      }
    1120  
    1121      /**
    1122       * Gets an array of users for display, this includes minimal user information
    1123       * as well as minimal information on the users roles, groups, and enrolments.
    1124       *
    1125       * @param core_enrol_renderer $renderer
    1126       * @param moodle_url $pageurl
    1127       * @param int $sort
    1128       * @param string $direction ASC or DESC
    1129       * @param int $page
    1130       * @param int $perpage
    1131       * @return array
    1132       */
    1133      public function get_users_for_display(course_enrolment_manager $manager, $sort, $direction, $page, $perpage) {
    1134          $pageurl = $manager->get_moodlepage()->url;
    1135          $users = $this->get_users($sort, $direction, $page, $perpage);
    1136  
    1137          $now = time();
    1138          $straddgroup = get_string('addgroup', 'group');
    1139          $strunenrol = get_string('unenrol', 'enrol');
    1140          $stredit = get_string('edit');
    1141  
    1142          $visibleroles   = $this->get_viewable_roles();
    1143          $assignable = $this->get_assignable_roles();
    1144          $allgroups  = $this->get_all_groups();
    1145          $context    = $this->get_context();
    1146          $canmanagegroups = has_capability('moodle/course:managegroups', $context);
    1147  
    1148          $url = new moodle_url($pageurl, $this->get_url_params());
    1149          // TODO Does not support custom user profile fields (MDL-70456).
    1150          $extrafields = fields::get_identity_fields($context, false);
    1151  
    1152          $enabledplugins = $this->get_enrolment_plugins(true);
    1153  
    1154          $userdetails = array();
    1155          foreach ($users as $user) {
    1156              $details = $this->prepare_user_for_display($user, $extrafields, $now);
    1157  
    1158              // Roles
    1159              $details['roles'] = array();
    1160              foreach ($this->get_user_roles($user->id) as $rid=>$rassignable) {
    1161                  $unchangeable = !$rassignable;
    1162                  if (!is_siteadmin() and !isset($assignable[$rid])) {
    1163                      $unchangeable = true;
    1164                  }
    1165  
    1166                  if (isset($visibleroles[$rid])) {
    1167                      $label = $visibleroles[$rid];
    1168                  } else {
    1169                      $label = get_string('novisibleroles', 'role');
    1170                      $unchangeable = true;
    1171                  }
    1172  
    1173                  $details['roles'][$rid] = array('text' => $label, 'unchangeable' => $unchangeable);
    1174              }
    1175  
    1176              // Users
    1177              $usergroups = $this->get_user_groups($user->id);
    1178              $details['groups'] = array();
    1179              foreach($usergroups as $gid=>$unused) {
    1180                  $details['groups'][$gid] = $allgroups[$gid]->name;
    1181              }
    1182  
    1183              // Enrolments
    1184              $details['enrolments'] = array();
    1185              foreach ($this->get_user_enrolments($user->id) as $ue) {
    1186                  if (!isset($enabledplugins[$ue->enrolmentinstance->enrol])) {
    1187                      $details['enrolments'][$ue->id] = array(
    1188                          'text' => $ue->enrolmentinstancename,
    1189                          'period' => null,
    1190                          'dimmed' =>  true,
    1191                          'actions' => array()
    1192                      );
    1193                      continue;
    1194                  } else if ($ue->timestart and $ue->timeend) {
    1195                      $period = get_string('periodstartend', 'enrol', array('start'=>userdate($ue->timestart), 'end'=>userdate($ue->timeend)));
    1196                      $periodoutside = ($ue->timestart && $ue->timeend && ($now < $ue->timestart || $now > $ue->timeend));
    1197                  } else if ($ue->timestart) {
    1198                      $period = get_string('periodstart', 'enrol', userdate($ue->timestart));
    1199                      $periodoutside = ($ue->timestart && $now < $ue->timestart);
    1200                  } else if ($ue->timeend) {
    1201                      $period = get_string('periodend', 'enrol', userdate($ue->timeend));
    1202                      $periodoutside = ($ue->timeend && $now > $ue->timeend);
    1203                  } else {
    1204                      // If there is no start or end show when user was enrolled.
    1205                      $period = get_string('periodnone', 'enrol', userdate($ue->timecreated));
    1206                      $periodoutside = false;
    1207                  }
    1208                  $details['enrolments'][$ue->id] = array(
    1209                      'text' => $ue->enrolmentinstancename,
    1210                      'period' => $period,
    1211                      'dimmed' =>  ($periodoutside or $ue->status != ENROL_USER_ACTIVE or $ue->enrolmentinstance->status != ENROL_INSTANCE_ENABLED),
    1212                      'actions' => $ue->enrolmentplugin->get_user_enrolment_actions($manager, $ue)
    1213                  );
    1214              }
    1215              $userdetails[$user->id] = $details;
    1216          }
    1217          return $userdetails;
    1218      }
    1219  
    1220      /**
    1221       * Prepare a user record for display
    1222       *
    1223       * This function is called by both {@link get_users_for_display} and {@link get_other_users_for_display} to correctly
    1224       * prepare user fields for display
    1225       *
    1226       * Please note that this function does not check capability for moodle/coures:viewhiddenuserfields
    1227       *
    1228       * @param object $user The user record
    1229       * @param array $extrafields The list of fields as returned from \core_user\fields::get_identity_fields used to determine which
    1230       * additional fields may be displayed
    1231       * @param int $now The time used for lastaccess calculation
    1232       * @return array The fields to be displayed including userid, courseid, picture, firstname, lastcourseaccess, lastaccess and any
    1233       * additional fields from $extrafields
    1234       */
    1235      private function prepare_user_for_display($user, $extrafields, $now) {
    1236          $details = array(
    1237              'userid'              => $user->id,
    1238              'courseid'            => $this->get_course()->id,
    1239              'picture'             => new user_picture($user),
    1240              'userfullnamedisplay' => fullname($user, has_capability('moodle/site:viewfullnames', $this->get_context())),
    1241              'lastaccess'          => get_string('never'),
    1242              'lastcourseaccess'    => get_string('never'),
    1243          );
    1244  
    1245          foreach ($extrafields as $field) {
    1246              $details[$field] = s($user->{$field});
    1247          }
    1248  
    1249          // Last time user has accessed the site.
    1250          if (!empty($user->lastaccess)) {
    1251              $details['lastaccess'] = format_time($now - $user->lastaccess);
    1252          }
    1253  
    1254          // Last time user has accessed the course.
    1255          if (!empty($user->lastcourseaccess)) {
    1256              $details['lastcourseaccess'] = format_time($now - $user->lastcourseaccess);
    1257          }
    1258          return $details;
    1259      }
    1260  
    1261      public function get_manual_enrol_buttons() {
    1262          $plugins = $this->get_enrolment_plugins(true); // Skip disabled plugins.
    1263          $buttons = array();
    1264          foreach ($plugins as $plugin) {
    1265              $newbutton = $plugin->get_manual_enrol_button($this);
    1266              if (is_array($newbutton)) {
    1267                  $buttons += $newbutton;
    1268              } else if ($newbutton instanceof enrol_user_button) {
    1269                  $buttons[] = $newbutton;
    1270              }
    1271          }
    1272          return $buttons;
    1273      }
    1274  
    1275      public function has_instance($enrolpluginname) {
    1276          // Make sure manual enrolments instance exists
    1277          foreach ($this->get_enrolment_instances() as $instance) {
    1278              if ($instance->enrol == $enrolpluginname) {
    1279                  return true;
    1280              }
    1281          }
    1282          return false;
    1283      }
    1284  
    1285      /**
    1286       * Returns the enrolment plugin that the course manager was being filtered to.
    1287       *
    1288       * If no filter was being applied then this function returns false.
    1289       *
    1290       * @return enrol_plugin
    1291       */
    1292      public function get_filtered_enrolment_plugin() {
    1293          $instances = $this->get_enrolment_instances();
    1294          $plugins = $this->get_enrolment_plugins(false);
    1295  
    1296          if (empty($this->instancefilter) || !array_key_exists($this->instancefilter, $instances)) {
    1297              return false;
    1298          }
    1299  
    1300          $instance = $instances[$this->instancefilter];
    1301          return $plugins[$instance->enrol];
    1302      }
    1303  
    1304      /**
    1305       * Returns and array of users + enrolment details.
    1306       *
    1307       * Given an array of user id's this function returns and array of user enrolments for those users
    1308       * as well as enough user information to display the users name and picture for each enrolment.
    1309       *
    1310       * @global moodle_database $DB
    1311       * @param array $userids
    1312       * @return array
    1313       */
    1314      public function get_users_enrolments(array $userids) {
    1315          global $DB;
    1316  
    1317          $instances = $this->get_enrolment_instances();
    1318          $plugins = $this->get_enrolment_plugins(false);
    1319  
    1320          if  (!empty($this->instancefilter)) {
    1321              $instancesql = ' = :instanceid';
    1322              $instanceparams = array('instanceid' => $this->instancefilter);
    1323          } else {
    1324              list($instancesql, $instanceparams) = $DB->get_in_or_equal(array_keys($instances), SQL_PARAMS_NAMED, 'instanceid0000');
    1325          }
    1326  
    1327          $userfieldsapi = \core_user\fields::for_userpic();
    1328          $userfields = $userfieldsapi->get_sql('u', false, '', '', false)->selects;
    1329          list($idsql, $idparams) = $DB->get_in_or_equal($userids, SQL_PARAMS_NAMED, 'userid0000');
    1330  
    1331          list($sort, $sortparams) = users_order_by_sql('u');
    1332  
    1333          $sql = "SELECT ue.id AS ueid, ue.status, ue.enrolid, ue.userid, ue.timestart, ue.timeend, ue.modifierid, ue.timecreated, ue.timemodified, $userfields
    1334                    FROM {user_enrolments} ue
    1335               LEFT JOIN {user} u ON u.id = ue.userid
    1336                   WHERE ue.enrolid $instancesql AND
    1337                         u.id $idsql
    1338                ORDER BY $sort";
    1339  
    1340          $rs = $DB->get_recordset_sql($sql, $idparams + $instanceparams + $sortparams);
    1341          $users = array();
    1342          foreach ($rs as $ue) {
    1343              $user = user_picture::unalias($ue);
    1344              $ue->id = $ue->ueid;
    1345              unset($ue->ueid);
    1346              if (!array_key_exists($user->id, $users)) {
    1347                  $user->enrolments = array();
    1348                  $users[$user->id] = $user;
    1349              }
    1350              $ue->enrolmentinstance = $instances[$ue->enrolid];
    1351              $ue->enrolmentplugin = $plugins[$ue->enrolmentinstance->enrol];
    1352              $users[$user->id]->enrolments[$ue->id] = $ue;
    1353          }
    1354          $rs->close();
    1355          return $users;
    1356      }
    1357  }
    1358  
    1359  /**
    1360   * A button that is used to enrol users in a course
    1361   *
    1362   * @copyright 2010 Sam Hemelryk
    1363   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
    1364   */
    1365  class enrol_user_button extends single_button {
    1366  
    1367      /**
    1368       * An array containing JS YUI modules required by this button
    1369       * @var array
    1370       */
    1371      protected $jsyuimodules = array();
    1372  
    1373      /**
    1374       * An array containing JS initialisation calls required by this button
    1375       * @var array
    1376       */
    1377      protected $jsinitcalls = array();
    1378  
    1379      /**
    1380       * An array strings required by JS for this button
    1381       * @var array
    1382       */
    1383      protected $jsstrings = array();
    1384  
    1385      /**
    1386       * Initialises the new enrol_user_button
    1387       *
    1388       * @staticvar int $count The number of enrol user buttons already created
    1389       * @param moodle_url $url
    1390       * @param string $label The text to display in the button
    1391       * @param string $method Either post or get
    1392       */
    1393      public function __construct(moodle_url $url, $label, $method = 'post') {
    1394          static $count = 0;
    1395          $count ++;
    1396          parent::__construct($url, $label, $method);
    1397          $this->class = 'singlebutton enrolusersbutton';
    1398          $this->formid = 'enrolusersbutton-'.$count;
    1399      }
    1400  
    1401      /**
    1402       * Adds a YUI module call that will be added to the page when the button is used.
    1403       *
    1404       * @param string|array $modules One or more modules to require
    1405       * @param string $function The JS function to call
    1406       * @param array $arguments An array of arguments to pass to the function
    1407       * @param string $galleryversion Deprecated: The gallery version to use
    1408       * @param bool $ondomready If true the call is postponed until the DOM is finished loading
    1409       */
    1410      public function require_yui_module($modules, $function, array $arguments = null, $galleryversion = null, $ondomready = false) {
    1411          if ($galleryversion != null) {
    1412              debugging('The galleryversion parameter to yui_module has been deprecated since Moodle 2.3.', DEBUG_DEVELOPER);
    1413          }
    1414  
    1415          $js = new stdClass;
    1416          $js->modules = (array)$modules;
    1417          $js->function = $function;
    1418          $js->arguments = $arguments;
    1419          $js->ondomready = $ondomready;
    1420          $this->jsyuimodules[] = $js;
    1421      }
    1422  
    1423      /**
    1424       * Adds a JS initialisation call to the page when the button is used.
    1425       *
    1426       * @param string $function The function to call
    1427       * @param array $extraarguments An array of arguments to pass to the function
    1428       * @param bool $ondomready If true the call is postponed until the DOM is finished loading
    1429       * @param array $module A module definition
    1430       */
    1431      public function require_js_init_call($function, array $extraarguments = null, $ondomready = false, array $module = null) {
    1432          $js = new stdClass;
    1433          $js->function = $function;
    1434          $js->extraarguments = $extraarguments;
    1435          $js->ondomready = $ondomready;
    1436          $js->module = $module;
    1437          $this->jsinitcalls[] = $js;
    1438      }
    1439  
    1440      /**
    1441       * Requires strings for JS that will be loaded when the button is used.
    1442       *
    1443       * @param type $identifiers
    1444       * @param string $component
    1445       * @param mixed $a
    1446       */
    1447      public function strings_for_js($identifiers, $component = 'moodle', $a = null) {
    1448          $string = new stdClass;
    1449          $string->identifiers = (array)$identifiers;
    1450          $string->component = $component;
    1451          $string->a = $a;
    1452          $this->jsstrings[] = $string;
    1453      }
    1454  
    1455      /**
    1456       * Initialises the JS that is required by this button
    1457       *
    1458       * @param moodle_page $page
    1459       */
    1460      public function initialise_js(moodle_page $page) {
    1461          foreach ($this->jsyuimodules as $js) {
    1462              $page->requires->yui_module($js->modules, $js->function, $js->arguments, null, $js->ondomready);
    1463          }
    1464          foreach ($this->jsinitcalls as $js) {
    1465              $page->requires->js_init_call($js->function, $js->extraarguments, $js->ondomready, $js->module);
    1466          }
    1467          foreach ($this->jsstrings as $string) {
    1468              $page->requires->strings_for_js($string->identifiers, $string->component, $string->a);
    1469          }
    1470      }
    1471  }
    1472  
    1473  /**
    1474   * User enrolment action
    1475   *
    1476   * This class is used to manage a renderable ue action such as editing an user enrolment or deleting
    1477   * a user enrolment.
    1478   *
    1479   * @copyright  2011 Sam Hemelryk
    1480   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
    1481   */
    1482  class user_enrolment_action implements renderable {
    1483  
    1484      /**
    1485       * The icon to display for the action
    1486       * @var pix_icon
    1487       */
    1488      protected $icon;
    1489  
    1490      /**
    1491       * The title for the action
    1492       * @var string
    1493       */
    1494      protected $title;
    1495  
    1496      /**
    1497       * The URL to the action
    1498       * @var moodle_url
    1499       */
    1500      protected $url;
    1501  
    1502      /**
    1503       * An array of HTML attributes
    1504       * @var array
    1505       */
    1506      protected $attributes = array();
    1507  
    1508      /**
    1509       * Constructor
    1510       * @param pix_icon $icon
    1511       * @param string $title
    1512       * @param moodle_url $url
    1513       * @param array $attributes
    1514       */
    1515      public function __construct(pix_icon $icon, $title, $url, array $attributes = null) {
    1516          $this->icon = $icon;
    1517          $this->title = $title;
    1518          $this->url = new moodle_url($url);
    1519          if (!empty($attributes)) {
    1520              $this->attributes = $attributes;
    1521          }
    1522          $this->attributes['title'] = $title;
    1523      }
    1524  
    1525      /**
    1526       * Returns the icon for this action
    1527       * @return pix_icon
    1528       */
    1529      public function get_icon() {
    1530          return $this->icon;
    1531      }
    1532  
    1533      /**
    1534       * Returns the title for this action
    1535       * @return string
    1536       */
    1537      public function get_title() {
    1538          return $this->title;
    1539      }
    1540  
    1541      /**
    1542       * Returns the URL for this action
    1543       * @return moodle_url
    1544       */
    1545      public function get_url() {
    1546          return $this->url;
    1547      }
    1548  
    1549      /**
    1550       * Returns the attributes to use for this action
    1551       * @return array
    1552       */
    1553      public function get_attributes() {
    1554          return $this->attributes;
    1555      }
    1556  }
    1557  
    1558  class enrol_ajax_exception extends moodle_exception {
    1559      /**
    1560       * Constructor
    1561       * @param string $errorcode The name of the string from error.php to print
    1562       * @param string $module name of module
    1563       * @param string $link The url where the user will be prompted to continue. If no url is provided the user will be directed to the site index page.
    1564       * @param object $a Extra words and phrases that might be required in the error string
    1565       * @param string $debuginfo optional debugging information
    1566       */
    1567      public function __construct($errorcode, $link = '', $a = NULL, $debuginfo = null) {
    1568          parent::__construct($errorcode, 'enrol', $link, $a, $debuginfo);
    1569      }
    1570  }
    1571  
    1572  /**
    1573   * This class is used to manage a bulk operations for enrolment plugins.
    1574   *
    1575   * @copyright 2011 Sam Hemelryk
    1576   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
    1577   */
    1578  abstract class enrol_bulk_enrolment_operation {
    1579  
    1580      /**
    1581       * The course enrolment manager
    1582       * @var course_enrolment_manager
    1583       */
    1584      protected $manager;
    1585  
    1586      /**
    1587       * The enrolment plugin to which this operation belongs
    1588       * @var enrol_plugin
    1589       */
    1590      protected $plugin;
    1591  
    1592      /**
    1593       * Contructor
    1594       * @param course_enrolment_manager $manager
    1595       * @param stdClass $plugin
    1596       */
    1597      public function __construct(course_enrolment_manager $manager, enrol_plugin $plugin = null) {
    1598          $this->manager = $manager;
    1599          $this->plugin = $plugin;
    1600      }
    1601  
    1602      /**
    1603       * Returns a moodleform used for this operation, or false if no form is required and the action
    1604       * should be immediatly processed.
    1605       *
    1606       * @param moodle_url|string $defaultaction
    1607       * @param mixed $defaultcustomdata
    1608       * @return enrol_bulk_enrolment_change_form|moodleform|false
    1609       */
    1610      public function get_form($defaultaction = null, $defaultcustomdata = null) {
    1611          return false;
    1612      }
    1613  
    1614      /**
    1615       * Returns the title to use for this bulk operation
    1616       *
    1617       * @return string
    1618       */
    1619      abstract public function get_title();
    1620  
    1621      /**
    1622       * Returns the identifier for this bulk operation.
    1623       * This should be the same identifier used by the plugins function when returning
    1624       * all of its bulk operations.
    1625       *
    1626       * @return string
    1627       */
    1628      abstract public function get_identifier();
    1629  
    1630      /**
    1631       * Processes the bulk operation on the given users
    1632       *
    1633       * @param course_enrolment_manager $manager
    1634       * @param array $users
    1635       * @param stdClass $properties
    1636       */
    1637      abstract public function process(course_enrolment_manager $manager, array $users, stdClass $properties);
    1638  }