Merge branch 'MDL-31923-master-6' of git://git.luns.net.uk/moodle
[moodle.git] / enrol / locallib.php
1 <?php
3 // This file is part of Moodle - http://moodle.org/
4 //
5 // Moodle is free software: you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation, either version 3 of the License, or
8 // (at your option) any later version.
9 //
10 // Moodle is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
18 /**
19  * This file contains the course_enrolment_manager class which is used to interface
20  * with the functions that exist in enrollib.php in relation to a single course.
21  *
22  * @package    core
23  * @subpackage enrol
24  * @copyright  2010 Sam Hemelryk
25  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
26  */
28 defined('MOODLE_INTERNAL') || die();
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 {
41     /**
42      * The course context
43      * @var stdClass
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;
57     /**
58      * The total number of users enrolled in the course
59      * Populated by course_enrolment_manager::get_total_users
60      * @var int
61      */
62     protected $totalusers = null;
63     /**
64      * An array of users currently enrolled in the course
65      * Populated by course_enrolment_manager::get_users
66      * @var array
67      */
68     protected $users = array();
70     /**
71      * An array of users who have roles within this course but who have not
72      * been enrolled in the course
73      * @var array
74      */
75     protected $otherusers = array();
77     /**
78      * The total number of users who hold a role within the course but who
79      * arn't enrolled.
80      * @var int
81      */
82     protected $totalotherusers = null;
84     /**
85      * The current moodle_page object
86      * @var moodle_page
87      */
88     protected $moodlepage = null;
90     /**#@+
91      * These variables are used to cache the information this class uses
92      * please never use these directly instead use their get_ counterparts.
93      * @access private
94      * @var array
95      */
96     private $_instancessql = null;
97     private $_instances = null;
98     private $_inames = null;
99     private $_plugins = null;
100     private $_roles = null;
101     private $_assignableroles = null;
102     private $_assignablerolesothers = null;
103     private $_groups = null;
104     /**#@-*/
106     /**
107      * Constructs the course enrolment manager
108      *
109      * @param moodle_page $moodlepage
110      * @param stdClass $course
111      * @param string $instancefilter
112      */
113     public function __construct(moodle_page $moodlepage, $course, $instancefilter = null) {
114         $this->moodlepage = $moodlepage;
115         $this->context = get_context_instance(CONTEXT_COURSE, $course->id);
116         $this->course = $course;
117         $this->instancefilter = $instancefilter;
118     }
120     /**
121      * Returns the current moodle page
122      * @return moodle_page
123      */
124     public function get_moodlepage() {
125         return $this->moodlepage;
126     }
128     /**
129      * Returns the total number of enrolled users in the course.
130      *
131      * If a filter was specificed this will be the total number of users enrolled
132      * in this course by means of that instance.
133      *
134      * @global moodle_database $DB
135      * @return int
136      */
137     public function get_total_users() {
138         global $DB;
139         if ($this->totalusers === null) {
140             list($instancessql, $params, $filter) = $this->get_instance_sql();
141             $sqltotal = "SELECT COUNT(DISTINCT u.id)
142                            FROM {user} u
143                            JOIN {user_enrolments} ue ON (ue.userid = u.id  AND ue.enrolid $instancessql)
144                            JOIN {enrol} e ON (e.id = ue.enrolid)";
145             $this->totalusers = (int)$DB->count_records_sql($sqltotal, $params);
146         }
147         return $this->totalusers;
148     }
150     /**
151      * Returns the total number of enrolled users in the course.
152      *
153      * If a filter was specificed this will be the total number of users enrolled
154      * in this course by means of that instance.
155      *
156      * @global moodle_database $DB
157      * @return int
158      */
159     public function get_total_other_users() {
160         global $DB;
161         if ($this->totalotherusers === null) {
162             list($ctxcondition, $params) = $DB->get_in_or_equal(get_parent_contexts($this->context, true), SQL_PARAMS_NAMED, 'ctx');
163             $params['courseid'] = $this->course->id;
164             $sql = "SELECT COUNT(DISTINCT u.id)
165                       FROM {role_assignments} ra
166                       JOIN {user} u ON u.id = ra.userid
167                       JOIN {context} ctx ON ra.contextid = ctx.id
168                  LEFT JOIN (
169                            SELECT ue.id, ue.userid
170                              FROM {user_enrolments} ue
171                         LEFT JOIN {enrol} e ON e.id=ue.enrolid
172                             WHERE e.courseid = :courseid
173                          ) ue ON ue.userid=u.id
174                      WHERE ctx.id $ctxcondition AND
175                            ue.id IS NULL";
176             $this->totalotherusers = (int)$DB->count_records_sql($sql, $params);
177         }
178         return $this->totalotherusers;
179     }
181     /**
182      * Gets all of the users enrolled in this course.
183      *
184      * If a filter was specified this will be the users who were enrolled
185      * in this course by means of that instance.
186      *
187      * @global moodle_database $DB
188      * @param string $sort
189      * @param string $direction ASC or DESC
190      * @param int $page First page should be 0
191      * @param int $perpage Defaults to 25
192      * @return array
193      */
194     public function get_users($sort, $direction='ASC', $page=0, $perpage=25) {
195         global $DB;
196         if ($direction !== 'ASC') {
197             $direction = 'DESC';
198         }
199         $key = md5("$sort-$direction-$page-$perpage");
200         if (!array_key_exists($key, $this->users)) {
201             list($instancessql, $params, $filter) = $this->get_instance_sql();
202             $extrafields = get_extra_user_fields($this->get_context());
203             $extrafields[] = 'lastaccess';
204             $ufields = user_picture::fields('u', $extrafields);
205             $sql = "SELECT DISTINCT $ufields, ul.timeaccess AS lastseen
206                       FROM {user} u
207                       JOIN {user_enrolments} ue ON (ue.userid = u.id  AND ue.enrolid $instancessql)
208                       JOIN {enrol} e ON (e.id = ue.enrolid)
209                  LEFT JOIN {user_lastaccess} ul ON (ul.courseid = e.courseid AND ul.userid = u.id)";
210             if ($sort === 'firstname') {
211                 $sql .= " ORDER BY u.firstname $direction, u.lastname $direction";
212             } else if ($sort === 'lastname') {
213                 $sql .= " ORDER BY u.lastname $direction, u.firstname $direction";
214             } else if ($sort === 'email') {
215                 $sql .= " ORDER BY u.email $direction, u.lastname $direction, u.firstname $direction";
216             } else if ($sort === 'lastseen') {
217                 $sql .= " ORDER BY ul.timeaccess $direction, u.lastname $direction, u.firstname $direction";
218             }
219             $this->users[$key] = $DB->get_records_sql($sql, $params, $page*$perpage, $perpage);
220         }
221         return $this->users[$key];
222     }
224     /**
225      * Gets and array of other users.
226      *
227      * Other users are users who have been assigned roles or inherited roles
228      * within this course but who have not been enrolled in the course
229      *
230      * @global moodle_database $DB
231      * @param string $sort
232      * @param string $direction
233      * @param int $page
234      * @param int $perpage
235      * @return array
236      */
237     public function get_other_users($sort, $direction='ASC', $page=0, $perpage=25) {
238         global $DB;
239         if ($direction !== 'ASC') {
240             $direction = 'DESC';
241         }
242         $key = md5("$sort-$direction-$page-$perpage");
243         if (!array_key_exists($key, $this->otherusers)) {
244             list($ctxcondition, $params) = $DB->get_in_or_equal(get_parent_contexts($this->context, true), SQL_PARAMS_NAMED, 'ctx');
245             $params['courseid'] = $this->course->id;
246             $params['cid'] = $this->course->id;
247             $sql = "SELECT ra.id as raid, ra.contextid, ra.component, ctx.contextlevel, ra.roleid, u.*, ue.lastseen
248                     FROM {role_assignments} ra
249                     JOIN {user} u ON u.id = ra.userid
250                     JOIN {context} ctx ON ra.contextid = ctx.id
251                LEFT JOIN (
252                        SELECT ue.id, ue.userid, ul.timeaccess AS lastseen
253                          FROM {user_enrolments} ue
254                     LEFT JOIN {enrol} e ON e.id=ue.enrolid
255                     LEFT JOIN {user_lastaccess} ul ON (ul.courseid = e.courseid AND ul.userid = ue.userid)
256                         WHERE e.courseid = :courseid
257                        ) ue ON ue.userid=u.id
258                    WHERE ctx.id $ctxcondition AND
259                          ue.id IS NULL
260                 ORDER BY u.$sort $direction, ctx.depth DESC";
261             $this->otherusers[$key] = $DB->get_records_sql($sql, $params, $page*$perpage, $perpage);
262         }
263         return $this->otherusers[$key];
264     }
266     /**
267      * Gets an array of the users that can be enrolled in this course.
268      *
269      * @global moodle_database $DB
270      * @param int $enrolid
271      * @param string $search
272      * @param bool $searchanywhere
273      * @param int $page Defaults to 0
274      * @param int $perpage Defaults to 25
275      * @return array Array(totalusers => int, users => array)
276      */
277     public function get_potential_users($enrolid, $search='', $searchanywhere=false, $page=0, $perpage=25) {
278         global $DB, $CFG;
280         // Add some additional sensible conditions
281         $tests = array("id <> :guestid", 'u.deleted = 0', 'u.confirmed = 1');
282         $params = array('guestid' => $CFG->siteguest);
283         if (!empty($search)) {
284             $conditions = get_extra_user_fields($this->get_context());
285             $conditions[] = $DB->sql_concat('u.firstname', "' '", 'u.lastname');
286             if ($searchanywhere) {
287                 $searchparam = '%' . $search . '%';
288             } else {
289                 $searchparam = $search . '%';
290             }
291             $i = 0;
292             foreach ($conditions as $key=>$condition) {
293                 $conditions[$key] = $DB->sql_like($condition,":con{$i}00", false);
294                 $params["con{$i}00"] = $searchparam;
295                 $i++;
296             }
297             $tests[] = '(' . implode(' OR ', $conditions) . ')';
298         }
299         $wherecondition = implode(' AND ', $tests);
301         $extrafields = get_extra_user_fields($this->get_context(), array('username', 'lastaccess'));
302         $extrafields[] = 'username';
303         $extrafields[] = 'lastaccess';
304         $ufields = user_picture::fields('u', $extrafields);
306         $fields      = 'SELECT '.$ufields;
307         $countfields = 'SELECT COUNT(1)';
308         $sql = " FROM {user} u
309                 WHERE $wherecondition
310                       AND u.id NOT IN (SELECT ue.userid
311                                          FROM {user_enrolments} ue
312                                          JOIN {enrol} e ON (e.id = ue.enrolid AND e.id = :enrolid))";
313         $order = ' ORDER BY u.lastname ASC, u.firstname ASC';
314         $params['enrolid'] = $enrolid;
315         $totalusers = $DB->count_records_sql($countfields . $sql, $params);
316         $availableusers = $DB->get_records_sql($fields . $sql . $order, $params, $page*$perpage, $perpage);
317         return array('totalusers'=>$totalusers, 'users'=>$availableusers);
318     }
320     /**
321      * Searches other users and returns paginated results
322      *
323      * @global moodle_database $DB
324      * @param string $search
325      * @param bool $searchanywhere
326      * @param int $page Starting at 0
327      * @param int $perpage
328      * @return array
329      */
330     public function search_other_users($search='', $searchanywhere=false, $page=0, $perpage=25) {
331         global $DB, $CFG;
333         // Add some additional sensible conditions
334         $tests = array("u.id <> :guestid", 'u.deleted = 0', 'u.confirmed = 1');
335         $params = array('guestid'=>$CFG->siteguest);
336         if (!empty($search)) {
337             $conditions = array('u.firstname','u.lastname');
338             if ($searchanywhere) {
339                 $searchparam = '%' . $search . '%';
340             } else {
341                 $searchparam = $search . '%';
342             }
343             $i = 0;
344             foreach ($conditions as $key=>$condition) {
345                 $conditions[$key] = $DB->sql_like($condition, ":con{$i}00", false);
346                 $params["con{$i}00"] = $searchparam;
347                 $i++;
348             }
349             $tests[] = '(' . implode(' OR ', $conditions) . ')';
350         }
351         $wherecondition = implode(' AND ', $tests);
353         $fields      = 'SELECT '.user_picture::fields('u', array('username','lastaccess'));
354         $countfields = 'SELECT COUNT(u.id)';
355         $sql   = " FROM {user} u
356                   WHERE $wherecondition
357                     AND u.id NOT IN (
358                            SELECT u.id
359                              FROM {role_assignments} r, {user} u
360                             WHERE r.contextid = :contextid AND
361                                   u.id = r.userid)";
362         $order = ' ORDER BY lastname ASC, firstname ASC';
364         $params['contextid'] = $this->context->id;
365         $totalusers = $DB->count_records_sql($countfields . $sql, $params);
366         $availableusers = $DB->get_records_sql($fields . $sql . $order, $params, $page*$perpage, $perpage);
367         return array('totalusers'=>$totalusers, 'users'=>$availableusers);
368     }
370     /**
371      * Gets an array containing some SQL to user for when selecting, params for
372      * that SQL, and the filter that was used in constructing the sql.
373      *
374      * @global moodle_database $DB
375      * @return string
376      */
377     protected function get_instance_sql() {
378         global $DB;
379         if ($this->_instancessql === null) {
380             $instances = $this->get_enrolment_instances();
381             $filter = $this->get_enrolment_filter();
382             if ($filter && array_key_exists($filter, $instances)) {
383                 $sql = " = :ifilter";
384                 $params = array('ifilter'=>$filter);
385             } else {
386                 $filter = 0;
387                 if ($instances) {
388                     list($sql, $params) = $DB->get_in_or_equal(array_keys($this->get_enrolment_instances()), SQL_PARAMS_NAMED);
389                 } else {
390                     // no enabled instances, oops, we should probably say something
391                     $sql = "= :never";
392                     $params = array('never'=>-1);
393                 }
394             }
395             $this->instancefilter = $filter;
396             $this->_instancessql = array($sql, $params, $filter);
397         }
398         return $this->_instancessql;
399     }
401     /**
402      * Returns all of the enrolment instances for this course.
403      *
404      * @return array
405      */
406     public function get_enrolment_instances() {
407         if ($this->_instances === null) {
408             $this->_instances = enrol_get_instances($this->course->id, true);
409         }
410         return $this->_instances;
411     }
413     /**
414      * Returns the names for all of the enrolment instances for this course.
415      *
416      * @return array
417      */
418     public function get_enrolment_instance_names() {
419         if ($this->_inames === null) {
420             $instances = $this->get_enrolment_instances();
421             $plugins = $this->get_enrolment_plugins();
422             foreach ($instances as $key=>$instance) {
423                 if (!isset($plugins[$instance->enrol])) {
424                     // weird, some broken stuff in plugin
425                     unset($instances[$key]);
426                     continue;
427                 }
428                 $this->_inames[$key] = $plugins[$instance->enrol]->get_instance_name($instance);
429             }
430         }
431         return $this->_inames;
432     }
434     /**
435      * Gets all of the enrolment plugins that are active for this course.
436      *
437      * @return array
438      */
439     public function get_enrolment_plugins() {
440         if ($this->_plugins === null) {
441             $this->_plugins = enrol_get_plugins(true);
442         }
443         return $this->_plugins;
444     }
446     /**
447      * Gets all of the roles this course can contain.
448      *
449      * @return array
450      */
451     public function get_all_roles() {
452         if ($this->_roles === null) {
453             $this->_roles = role_fix_names(get_all_roles(), $this->context);
454         }
455         return $this->_roles;
456     }
458     /**
459      * Gets all of the assignable roles for this course.
460      *
461      * @return array
462      */
463     public function get_assignable_roles($otherusers = false) {
464         if ($this->_assignableroles === null) {
465             $this->_assignableroles = get_assignable_roles($this->context, ROLENAME_ALIAS, false); // verifies unassign access control too
466         }
468         if ($otherusers) {
469             if (!is_array($this->_assignablerolesothers)) {
470                 $this->_assignablerolesothers = array();
471                 list($courseviewroles, $ignored) = get_roles_with_cap_in_context($this->context, 'moodle/course:view');
472                 foreach ($this->_assignableroles as $roleid=>$role) {
473                     if (isset($courseviewroles[$roleid])) {
474                         $this->_assignablerolesothers[$roleid] = $role;
475                     }
476                 }
477             }
478             return $this->_assignablerolesothers;
479         } else {
480             return $this->_assignableroles;
481         }
482     }
484     /**
485      * Gets all of the groups for this course.
486      *
487      * @return array
488      */
489     public function get_all_groups() {
490         if ($this->_groups === null) {
491             $this->_groups = groups_get_all_groups($this->course->id);
492             foreach ($this->_groups as $gid=>$group) {
493                 $this->_groups[$gid]->name = format_string($group->name);
494             }
495         }
496         return $this->_groups;
497     }
499     /**
500      * Unenrols a user from the course given the users ue entry
501      *
502      * @global moodle_database $DB
503      * @param stdClass $ue
504      * @return bool
505      */
506     public function unenrol_user($ue) {
507         global $DB;
508         list ($instance, $plugin) = $this->get_user_enrolment_components($ue);
509         if ($instance && $plugin && $plugin->allow_unenrol_user($instance, $ue) && has_capability("enrol/$instance->enrol:unenrol", $this->context)) {
510             $plugin->unenrol_user($instance, $ue->userid);
511             return true;
512         }
513         return false;
514     }
516     /**
517      * Given a user enrolment record this method returns the plugin and enrolment
518      * instance that relate to it.
519      *
520      * @param stdClass|int $userenrolment
521      * @return array array($instance, $plugin)
522      */
523     public function get_user_enrolment_components($userenrolment) {
524         global $DB;
525         if (is_numeric($userenrolment)) {
526             $userenrolment = $DB->get_record('user_enrolments', array('id'=>(int)$userenrolment));
527         }
528         $instances = $this->get_enrolment_instances();
529         $plugins = $this->get_enrolment_plugins();
530         if (!$userenrolment || !isset($instances[$userenrolment->enrolid])) {
531             return array(false, false);
532         }
533         $instance = $instances[$userenrolment->enrolid];
534         $plugin = $plugins[$instance->enrol];
535         return array($instance, $plugin);
536     }
538     /**
539      * Removes an assigned role from a user.
540      *
541      * @global moodle_database $DB
542      * @param int $userid
543      * @param int $roleid
544      * @return bool
545      */
546     public function unassign_role_from_user($userid, $roleid) {
547         global $DB;
548         require_capability('moodle/role:assign', $this->context);
549         $user = $DB->get_record('user', array('id'=>$userid), '*', MUST_EXIST);
550         try {
551             role_unassign($roleid, $user->id, $this->context->id, '', NULL);
552         } catch (Exception $e) {
553             if (defined('AJAX_SCRIPT')) {
554                 throw $e;
555             }
556             return false;
557         }
558         return true;
559     }
561     /**
562      * Assigns a role to a user.
563      *
564      * @param int $roleid
565      * @param int $userid
566      * @return int|false
567      */
568     public function assign_role_to_user($roleid, $userid) {
569         require_capability('moodle/role:assign', $this->context);
570         if (!array_key_exists($roleid, $this->get_assignable_roles())) {
571             if (defined('AJAX_SCRIPT')) {
572                 throw new moodle_exception('invalidrole');
573             }
574             return false;
575         }
576         return role_assign($roleid, $userid, $this->context->id, '', NULL);
577     }
579     /**
580      * Adds a user to a group
581      *
582      * @param stdClass $user
583      * @param int $groupid
584      * @return bool
585      */
586     public function add_user_to_group($user, $groupid) {
587         require_capability('moodle/course:managegroups', $this->context);
588         $group = $this->get_group($groupid);
589         if (!$group) {
590             return false;
591         }
592         return groups_add_member($group->id, $user->id);
593     }
595     /**
596      * Removes a user from a group
597      *
598      * @global moodle_database $DB
599      * @param StdClass $user
600      * @param int $groupid
601      * @return bool
602      */
603     public function remove_user_from_group($user, $groupid) {
604         global $DB;
605         require_capability('moodle/course:managegroups', $this->context);
606         $group = $this->get_group($groupid);
607         if (!$group) {
608             return false;
609         }
610         return groups_remove_member($group, $user);
611     }
613     /**
614      * Gets the requested group
615      *
616      * @param int $groupid
617      * @return stdClass|int
618      */
619     public function get_group($groupid) {
620         $groups = $this->get_all_groups();
621         if (!array_key_exists($groupid, $groups)) {
622             return false;
623         }
624         return $groups[$groupid];
625     }
627     /**
628      * Edits an enrolment
629      *
630      * @param stdClass $userenrolment
631      * @param stdClass $data
632      * @return bool
633      */
634     public function edit_enrolment($userenrolment, $data) {
635         //Only allow editing if the user has the appropriate capability
636         //Already checked in /enrol/users.php but checking again in case this function is called from elsewhere
637         list($instance, $plugin) = $this->get_user_enrolment_components($userenrolment);
638         if ($instance && $plugin && $plugin->allow_manage($instance) && has_capability("enrol/$instance->enrol:manage", $this->context)) {
639             if (!isset($data->status)) {
640                 $data->status = $userenrolment->status;
641             }
642             $plugin->update_user_enrol($instance, $userenrolment->userid, $data->status, $data->timestart, $data->timeend);
643             return true;
644         }
645         return false;
646     }
648     /**
649      * Returns the current enrolment filter that is being applied by this class
650      * @return string
651      */
652     public function get_enrolment_filter() {
653         return $this->instancefilter;
654     }
656     /**
657      * Gets the roles assigned to this user that are applicable for this course.
658      *
659      * @param int $userid
660      * @return array
661      */
662     public function get_user_roles($userid) {
663         $roles = array();
664         $ras = get_user_roles($this->context, $userid, true, 'c.contextlevel DESC, r.sortorder ASC');
665         foreach ($ras as $ra) {
666             if ($ra->contextid != $this->context->id) {
667                 if (!array_key_exists($ra->roleid, $roles)) {
668                     $roles[$ra->roleid] = null;
669                 }
670                 // higher ras, course always takes precedence
671                 continue;
672             }
673             if (array_key_exists($ra->roleid, $roles) && $roles[$ra->roleid] === false) {
674                 continue;
675             }
676             $roles[$ra->roleid] = ($ra->itemid == 0 and $ra->component === '');
677         }
678         return $roles;
679     }
681     /**
682      * Gets the enrolments this user has in the course
683      *
684      * @global moodle_database $DB
685      * @param int $userid
686      * @return array
687      */
688     public function get_user_enrolments($userid) {
689         global $DB;
690         list($instancessql, $params, $filter) = $this->get_instance_sql();
691         $params['userid'] = $userid;
692         $userenrolments = $DB->get_records_select('user_enrolments', "enrolid $instancessql AND userid = :userid", $params);
693         $instances = $this->get_enrolment_instances();
694         $plugins = $this->get_enrolment_plugins();
695         $inames = $this->get_enrolment_instance_names();
696         foreach ($userenrolments as &$ue) {
697             $ue->enrolmentinstance     = $instances[$ue->enrolid];
698             $ue->enrolmentplugin       = $plugins[$ue->enrolmentinstance->enrol];
699             $ue->enrolmentinstancename = $inames[$ue->enrolmentinstance->id];
700         }
701         return $userenrolments;
702     }
704     /**
705      * Gets the groups this user belongs to
706      *
707      * @param int $userid
708      * @return array
709      */
710     public function get_user_groups($userid) {
711         return groups_get_all_groups($this->course->id, $userid, 0, 'g.id');
712     }
714     /**
715      * Retursn an array of params that would go into the URL to return to this
716      * exact page.
717      *
718      * @return array
719      */
720     public function get_url_params() {
721         $args = array(
722             'id' => $this->course->id
723         );
724         if (!empty($this->instancefilter)) {
725             $args['ifilter'] = $this->instancefilter;
726         }
727         return $args;
728     }
730     /**
731      * Returns the course this object is managing enrolments for
732      *
733      * @return stdClass
734      */
735     public function get_course() {
736         return $this->course;
737     }
739     /**
740      * Returns the course context
741      *
742      * @return stdClass
743      */
744     public function get_context() {
745         return $this->context;
746     }
748     /**
749      * Gets an array of other users in this course ready for display.
750      *
751      * Other users are users who have been assigned or inherited roles within this
752      * course but have not been enrolled.
753      *
754      * @param core_enrol_renderer $renderer
755      * @param moodle_url $pageurl
756      * @param string $sort
757      * @param string $direction ASC | DESC
758      * @param int $page Starting from 0
759      * @param int $perpage
760      * @return array
761      */
762     public function get_other_users_for_display(core_enrol_renderer $renderer, moodle_url $pageurl, $sort, $direction, $page, $perpage) {
764         $userroles = $this->get_other_users($sort, $direction, $page, $perpage);
765         $roles = $this->get_all_roles();
767         $context    = $this->get_context();
768         $now = time();
769         $extrafields = get_extra_user_fields($context);
771         $users = array();
772         foreach ($userroles as $userrole) {
773             if (!array_key_exists($userrole->id, $users)) {
774                 $users[$userrole->id] = $this->prepare_user_for_display($userrole, $extrafields, $now);
775             }
776             $a = new stdClass;
777             $a->role = $roles[$userrole->roleid]->localname;
778             $changeable = ($userrole->component == '');
779             if ($userrole->contextid == $this->context->id) {
780                 $roletext = get_string('rolefromthiscourse', 'enrol', $a);
781             } else {
782                 $changeable = false;
783                 switch ($userrole->contextlevel) {
784                     case CONTEXT_COURSE :
785                         // Meta course
786                         $roletext = get_string('rolefrommetacourse', 'enrol', $a);
787                         break;
788                     case CONTEXT_COURSECAT :
789                         $roletext = get_string('rolefromcategory', 'enrol', $a);
790                         break;
791                     case CONTEXT_SYSTEM:
792                     default:
793                         $roletext = get_string('rolefromsystem', 'enrol', $a);
794                         break;
795                 }
796             }
797             $users[$userrole->id]['roles'] = array();
798             $users[$userrole->id]['roles'][$userrole->roleid] = array(
799                 'text' => $roletext,
800                 'unchangeable' => !$changeable
801             );
802         }
803         return $users;
804     }
806     /**
807      * Gets an array of users for display, this includes minimal user information
808      * as well as minimal information on the users roles, groups, and enrolments.
809      *
810      * @param core_enrol_renderer $renderer
811      * @param moodle_url $pageurl
812      * @param int $sort
813      * @param string $direction ASC or DESC
814      * @param int $page
815      * @param int $perpage
816      * @return array
817      */
818     public function get_users_for_display(course_enrolment_manager $manager, $sort, $direction, $page, $perpage) {
819         $pageurl = $manager->get_moodlepage()->url;
820         $users = $this->get_users($sort, $direction, $page, $perpage);
822         $now = time();
823         $straddgroup = get_string('addgroup', 'group');
824         $strunenrol = get_string('unenrol', 'enrol');
825         $stredit = get_string('edit');
827         $allroles   = $this->get_all_roles();
828         $assignable = $this->get_assignable_roles();
829         $allgroups  = $this->get_all_groups();
830         $context    = $this->get_context();
831         $canmanagegroups = has_capability('moodle/course:managegroups', $context);
833         $url = new moodle_url($pageurl, $this->get_url_params());
834         $extrafields = get_extra_user_fields($context);
836         $userdetails = array();
837         foreach ($users as $user) {
838             $details = $this->prepare_user_for_display($user, $extrafields, $now);
840             // Roles
841             $details['roles'] = array();
842             foreach ($this->get_user_roles($user->id) as $rid=>$rassignable) {
843                 $details['roles'][$rid] = array('text'=>$allroles[$rid]->localname, 'unchangeable'=>(!$rassignable || !isset($assignable[$rid])));
844             }
846             // Users
847             $usergroups = $this->get_user_groups($user->id);
848             $details['groups'] = array();
849             foreach($usergroups as $gid=>$unused) {
850                 $details['groups'][$gid] = $allgroups[$gid]->name;
851             }
853             // Enrolments
854             $details['enrolments'] = array();
855             foreach ($this->get_user_enrolments($user->id) as $ue) {
856                 if ($ue->timestart and $ue->timeend) {
857                     $period = get_string('periodstartend', 'enrol', array('start'=>userdate($ue->timestart), 'end'=>userdate($ue->timeend)));
858                     $periodoutside = ($ue->timestart && $ue->timeend && $now < $ue->timestart && $now > $ue->timeend);
859                 } else if ($ue->timestart) {
860                     $period = get_string('periodstart', 'enrol', userdate($ue->timestart));
861                     $periodoutside = ($ue->timestart && $now < $ue->timestart);
862                 } else if ($ue->timeend) {
863                     $period = get_string('periodend', 'enrol', userdate($ue->timeend));
864                     $periodoutside = ($ue->timeend && $now > $ue->timeend);
865                 } else {
866                     $period = '';
867                     $periodoutside = false;
868                 }
869                 $details['enrolments'][$ue->id] = array(
870                     'text' => $ue->enrolmentinstancename,
871                     'period' => $period,
872                     'dimmed' =>  ($periodoutside || $ue->status != ENROL_USER_ACTIVE),
873                     'actions' => $ue->enrolmentplugin->get_user_enrolment_actions($manager, $ue)
874                 );
875             }
876             $userdetails[$user->id] = $details;
877         }
878         return $userdetails;
879     }
881     /**
882      * Prepare a user record for display
883      *
884      * This function is called by both {@link get_users_for_display} and {@link get_other_users_for_display} to correctly
885      * prepare user fields for display
886      *
887      * Please note that this function does not check capability for moodle/coures:viewhiddenuserfields
888      *
889      * @param object $user The user record
890      * @param array $extrafields The list of fields as returned from get_extra_user_fields used to determine which
891      * additional fields may be displayed
892      * @param int $now The time used for lastaccess calculation
893      * @return array The fields to be displayed including userid, courseid, picture, firstname, lastseen and any
894      * additional fields from $extrafields
895      */
896     private function prepare_user_for_display($user, $extrafields, $now) {
897         $details = array(
898             'userid'    => $user->id,
899             'courseid'  => $this->get_course()->id,
900             'picture'   => new user_picture($user),
901             'firstname' => fullname($user, has_capability('moodle/site:viewfullnames', $this->get_context())),
902             'lastseen'  => get_string('never'),
903         );
904         foreach ($extrafields as $field) {
905             $details[$field] = $user->{$field};
906         }
908         if ($user->lastaccess) {
909             $details['lastseen'] = format_time($now - $user->lastaccess);
910         }
911         return $details;
912     }
914     public function get_manual_enrol_buttons() {
915         $plugins = $this->get_enrolment_plugins();
916         $buttons = array();
917         foreach ($plugins as $plugin) {
918             $newbutton = $plugin->get_manual_enrol_button($this);
919             if (is_array($newbutton)) {
920                 $buttons += $newbutton;
921             } else if ($newbutton instanceof enrol_user_button) {
922                 $buttons[] = $newbutton;
923             }
924         }
925         return $buttons;
926     }
928     public function has_instance($enrolpluginname) {
929         // Make sure manual enrolments instance exists
930         foreach ($this->get_enrolment_instances() as $instance) {
931             if ($instance->enrol == $enrolpluginname) {
932                 return true;
933             }
934         }
935         return false;
936     }
938     /**
939      * Returns the enrolment plugin that the course manager was being filtered to.
940      *
941      * If no filter was being applied then this function returns false.
942      *
943      * @return enrol_plugin
944      */
945     public function get_filtered_enrolment_plugin() {
946         $instances = $this->get_enrolment_instances();
947         $plugins = $this->get_enrolment_plugins();
949         if (empty($this->instancefilter) || !array_key_exists($this->instancefilter, $instances)) {
950             return false;
951         }
953         $instance = $instances[$this->instancefilter];
954         return $plugins[$instance->enrol];
955     }
957     /**
958      * Returns and array of users + enrolment details.
959      *
960      * Given an array of user id's this function returns and array of user enrolments for those users
961      * as well as enough user information to display the users name and picture for each enrolment.
962      *
963      * @global moodle_database $DB
964      * @param array $userids
965      * @return array
966      */
967     public function get_users_enrolments(array $userids) {
968         global $DB;
970         $instances = $this->get_enrolment_instances();
971         $plugins = $this->get_enrolment_plugins();
973         if  (!empty($this->instancefilter)) {
974             $instancesql = ' = :instanceid';
975             $instanceparams = array('instanceid' => $this->instancefilter);
976         } else {
977             list($instancesql, $instanceparams) = $DB->get_in_or_equal(array_keys($instances), SQL_PARAMS_NAMED, 'instanceid0000');
978         }
980         $userfields = user_picture::fields('u');
981         list($idsql, $idparams) = $DB->get_in_or_equal($userids, SQL_PARAMS_NAMED, 'userid0000');
983         $sql = "SELECT ue.id AS ueid, ue.status, ue.enrolid, ue.userid, ue.timestart, ue.timeend, ue.modifierid, ue.timecreated, ue.timemodified, $userfields
984                   FROM {user_enrolments} ue
985              LEFT JOIN {user} u ON u.id = ue.userid
986                  WHERE ue.enrolid $instancesql AND
987                        u.id $idsql
988               ORDER BY u.firstname ASC, u.lastname ASC";
990         $rs = $DB->get_recordset_sql($sql, $idparams + $instanceparams);
991         $users = array();
992         foreach ($rs as $ue) {
993             $user = user_picture::unalias($ue);
994             $ue->id = $ue->ueid;
995             unset($ue->ueid);
996             if (!array_key_exists($user->id, $users)) {
997                 $user->enrolments = array();
998                 $users[$user->id] = $user;
999             }
1000             $ue->enrolmentinstance = $instances[$ue->enrolid];
1001             $ue->enrolmentplugin = $plugins[$ue->enrolmentinstance->enrol];
1002             $users[$user->id]->enrolments[$ue->id] = $ue;
1003         }
1004         $rs->close();
1005         return $users;
1006     }
1009 /**
1010  * A button that is used to enrol users in a course
1011  *
1012  * @copyright 2010 Sam Hemelryk
1013  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1014  */
1015 class enrol_user_button extends single_button {
1017     /**
1018      * An array containing JS YUI modules required by this button
1019      * @var array
1020      */
1021     protected $jsyuimodules = array();
1023     /**
1024      * An array containing JS initialisation calls required by this button
1025      * @var array
1026      */
1027     protected $jsinitcalls = array();
1029     /**
1030      * An array strings required by JS for this button
1031      * @var array
1032      */
1033     protected $jsstrings = array();
1035     /**
1036      * Initialises the new enrol_user_button
1037      *
1038      * @staticvar int $count The number of enrol user buttons already created
1039      * @param moodle_url $url
1040      * @param string $label The text to display in the button
1041      * @param string $method Either post or get
1042      */
1043     public function __construct(moodle_url $url, $label, $method = 'post') {
1044         static $count = 0;
1045         $count ++;
1046         parent::__construct($url, $label, $method);
1047         $this->class = 'singlebutton enrolusersbutton';
1048         $this->formid = 'enrolusersbutton-'.$count;
1049     }
1051     /**
1052      * Adds a YUI module call that will be added to the page when the button is used.
1053      *
1054      * @param string|array $modules One or more modules to require
1055      * @param string $function The JS function to call
1056      * @param array $arguments An array of arguments to pass to the function
1057      * @param string $galleryversion The YUI gallery version of any modules required
1058      * @param bool $ondomready If true the call is postponed until the DOM is finished loading
1059      */
1060     public function require_yui_module($modules, $function, array $arguments = null, $galleryversion = '2010.04.08-12-35', $ondomready = false) {
1061         $js = new stdClass;
1062         $js->modules = (array)$modules;
1063         $js->function = $function;
1064         $js->arguments = $arguments;
1065         $js->galleryversion = $galleryversion;
1066         $js->ondomready = $ondomready;
1067         $this->jsyuimodules[] = $js;
1068     }
1070     /**
1071      * Adds a JS initialisation call to the page when the button is used.
1072      *
1073      * @param string $function The function to call
1074      * @param array $extraarguments An array of arguments to pass to the function
1075      * @param bool $ondomready If true the call is postponed until the DOM is finished loading
1076      * @param array $module A module definition
1077      */
1078     public function require_js_init_call($function, array $extraarguments = null, $ondomready = false, array $module = null) {
1079         $js = new stdClass;
1080         $js->function = $function;
1081         $js->extraarguments = $extraarguments;
1082         $js->ondomready = $ondomready;
1083         $js->module = $module;
1084         $this->jsinitcalls[] = $js;
1085     }
1087     /**
1088      * Requires strings for JS that will be loaded when the button is used.
1089      *
1090      * @param type $identifiers
1091      * @param string $component
1092      * @param mixed $a
1093      */
1094     public function strings_for_js($identifiers, $component = 'moodle', $a = null) {
1095         $string = new stdClass;
1096         $string->identifiers = (array)$identifiers;
1097         $string->component = $component;
1098         $string->a = $a;
1099         $this->jsstrings[] = $string;
1100     }
1102     /**
1103      * Initialises the JS that is required by this button
1104      *
1105      * @param moodle_page $page
1106      */
1107     public function initialise_js(moodle_page $page) {
1108         foreach ($this->jsyuimodules as $js) {
1109             $page->requires->yui_module($js->modules, $js->function, $js->arguments, $js->galleryversion, $js->ondomready);
1110         }
1111         foreach ($this->jsinitcalls as $js) {
1112             $page->requires->js_init_call($js->function, $js->extraarguments, $js->ondomready, $js->module);
1113         }
1114         foreach ($this->jsstrings as $string) {
1115             $page->requires->strings_for_js($string->identifiers, $string->component, $string->a);
1116         }
1117     }
1120 /**
1121  * User enrolment action
1122  *
1123  * This class is used to manage a renderable ue action such as editing an user enrolment or deleting
1124  * a user enrolment.
1125  *
1126  * @copyright  2011 Sam Hemelryk
1127  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1128  */
1129 class user_enrolment_action implements renderable {
1131     /**
1132      * The icon to display for the action
1133      * @var pix_icon
1134      */
1135     protected $icon;
1137     /**
1138      * The title for the action
1139      * @var string
1140      */
1141     protected $title;
1143     /**
1144      * The URL to the action
1145      * @var moodle_url
1146      */
1147     protected $url;
1149     /**
1150      * An array of HTML attributes
1151      * @var array
1152      */
1153     protected $attributes = array();
1155     /**
1156      * Constructor
1157      * @param pix_icon $icon
1158      * @param string $title
1159      * @param moodle_url $url
1160      * @param array $attributes
1161      */
1162     public function __construct(pix_icon $icon, $title, $url, array $attributes = null) {
1163         $this->icon = $icon;
1164         $this->title = $title;
1165         $this->url = new moodle_url($url);
1166         if (!empty($attributes)) {
1167             $this->attributes = $attributes;
1168         }
1169         $this->attributes['title'] = $title;
1170     }
1172     /**
1173      * Returns the icon for this action
1174      * @return pix_icon
1175      */
1176     public function get_icon() {
1177         return $this->icon;
1178     }
1180     /**
1181      * Returns the title for this action
1182      * @return string
1183      */
1184     public function get_title() {
1185         return $this->title;
1186     }
1188     /**
1189      * Returns the URL for this action
1190      * @return moodle_url
1191      */
1192     public function get_url() {
1193         return $this->url;
1194     }
1196     /**
1197      * Returns the attributes to use for this action
1198      * @return array
1199      */
1200     public function get_attributes() {
1201         return $this->attributes;
1202     }
1205 class enrol_ajax_exception extends moodle_exception {
1206     /**
1207      * Constructor
1208      * @param string $errorcode The name of the string from error.php to print
1209      * @param string $module name of module
1210      * @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.
1211      * @param object $a Extra words and phrases that might be required in the error string
1212      * @param string $debuginfo optional debugging information
1213      */
1214     public function __construct($errorcode, $link = '', $a = NULL, $debuginfo = null) {
1215         parent::__construct($errorcode, 'enrol', $link, $a, $debuginfo);
1216     }
1219 /**
1220  * This class is used to manage a bulk operations for enrolment plugins.
1221  *
1222  * @copyright 2011 Sam Hemelryk
1223  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1224  */
1225 abstract class enrol_bulk_enrolment_operation {
1227     /**
1228      * The course enrolment manager
1229      * @var course_enrolment_manager
1230      */
1231     protected $manager;
1233     /**
1234      * The enrolment plugin to which this operation belongs
1235      * @var enrol_plugin
1236      */
1237     protected $plugin;
1239     /**
1240      * Contructor
1241      * @param course_enrolment_manager $manager
1242      * @param stdClass $plugin
1243      */
1244     public function __construct(course_enrolment_manager $manager, enrol_plugin $plugin = null) {
1245         $this->manager = $manager;
1246         $this->plugin = $plugin;
1247     }
1249     /**
1250      * Returns a moodleform used for this operation, or false if no form is required and the action
1251      * should be immediatly processed.
1252      *
1253      * @param moodle_url|string $defaultaction
1254      * @param mixed $defaultcustomdata
1255      * @return enrol_bulk_enrolment_change_form|moodleform|false
1256      */
1257     public function get_form($defaultaction = null, $defaultcustomdata = null) {
1258         return false;
1259     }
1261     /**
1262      * Returns the title to use for this bulk operation
1263      *
1264      * @return string
1265      */
1266     abstract public function get_title();
1268     /**
1269      * Returns the identifier for this bulk operation.
1270      * This should be the same identifier used by the plugins function when returning
1271      * all of its bulk operations.
1272      *
1273      * @return string
1274      */
1275     abstract public function get_identifier();
1277     /**
1278      * Processes the bulk operation on the given users
1279      *
1280      * @param course_enrolment_manager $manager
1281      * @param array $users
1282      * @param stdClass $properties
1283      */
1284     abstract public function process(course_enrolment_manager $manager, array $users, stdClass $properties);