MDL-xxxx show appropriate instance status on user enrolments page
[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("u.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             LEFT JOIN {user_enrolments} ue ON (ue.userid = u.id AND ue.enrolid = :enrolid)
310                 WHERE $wherecondition
311                       AND ue.id IS NULL";
312         $order = ' ORDER BY u.lastname ASC, u.firstname ASC';
313         $params['enrolid'] = $enrolid;
314         $totalusers = $DB->count_records_sql($countfields . $sql, $params);
315         $availableusers = $DB->get_records_sql($fields . $sql . $order, $params, $page*$perpage, $perpage);
316         return array('totalusers'=>$totalusers, 'users'=>$availableusers);
317     }
319     /**
320      * Searches other users and returns paginated results
321      *
322      * @global moodle_database $DB
323      * @param string $search
324      * @param bool $searchanywhere
325      * @param int $page Starting at 0
326      * @param int $perpage
327      * @return array
328      */
329     public function search_other_users($search='', $searchanywhere=false, $page=0, $perpage=25) {
330         global $DB, $CFG;
332         // Add some additional sensible conditions
333         $tests = array("u.id <> :guestid", 'u.deleted = 0', 'u.confirmed = 1');
334         $params = array('guestid'=>$CFG->siteguest);
335         if (!empty($search)) {
336             $conditions = array('u.firstname','u.lastname');
337             if ($searchanywhere) {
338                 $searchparam = '%' . $search . '%';
339             } else {
340                 $searchparam = $search . '%';
341             }
342             $i = 0;
343             foreach ($conditions as $key=>$condition) {
344                 $conditions[$key] = $DB->sql_like($condition, ":con{$i}00", false);
345                 $params["con{$i}00"] = $searchparam;
346                 $i++;
347             }
348             $tests[] = '(' . implode(' OR ', $conditions) . ')';
349         }
350         $wherecondition = implode(' AND ', $tests);
352         $fields      = 'SELECT '.user_picture::fields('u', array('username','lastaccess'));
353         $countfields = 'SELECT COUNT(u.id)';
354         $sql   = " FROM {user} u
355               LEFT JOIN {role_assignments} ra ON (ra.userid = u.id AND ra.contextid = :contextid)
356                   WHERE $wherecondition
357                     AND ra.id IS NULL";
358         $order = ' ORDER BY lastname ASC, firstname ASC';
360         $params['contextid'] = $this->context->id;
361         $totalusers = $DB->count_records_sql($countfields . $sql, $params);
362         $availableusers = $DB->get_records_sql($fields . $sql . $order, $params, $page*$perpage, $perpage);
363         return array('totalusers'=>$totalusers, 'users'=>$availableusers);
364     }
366     /**
367      * Gets an array containing some SQL to user for when selecting, params for
368      * that SQL, and the filter that was used in constructing the sql.
369      *
370      * @global moodle_database $DB
371      * @return string
372      */
373     protected function get_instance_sql() {
374         global $DB;
375         if ($this->_instancessql === null) {
376             $instances = $this->get_enrolment_instances();
377             $filter = $this->get_enrolment_filter();
378             if ($filter && array_key_exists($filter, $instances)) {
379                 $sql = " = :ifilter";
380                 $params = array('ifilter'=>$filter);
381             } else {
382                 $filter = 0;
383                 if ($instances) {
384                     list($sql, $params) = $DB->get_in_or_equal(array_keys($this->get_enrolment_instances()), SQL_PARAMS_NAMED);
385                 } else {
386                     // no enabled instances, oops, we should probably say something
387                     $sql = "= :never";
388                     $params = array('never'=>-1);
389                 }
390             }
391             $this->instancefilter = $filter;
392             $this->_instancessql = array($sql, $params, $filter);
393         }
394         return $this->_instancessql;
395     }
397     /**
398      * Returns all of the enrolment instances for this course.
399      *
400      * @return array
401      */
402     public function get_enrolment_instances() {
403         if ($this->_instances === null) {
404             $this->_instances = enrol_get_instances($this->course->id, true);
405         }
406         return $this->_instances;
407     }
409     /**
410      * Returns the names for all of the enrolment instances for this course.
411      *
412      * @return array
413      */
414     public function get_enrolment_instance_names() {
415         if ($this->_inames === null) {
416             $instances = $this->get_enrolment_instances();
417             $plugins = $this->get_enrolment_plugins();
418             foreach ($instances as $key=>$instance) {
419                 if (!isset($plugins[$instance->enrol])) {
420                     // weird, some broken stuff in plugin
421                     unset($instances[$key]);
422                     continue;
423                 }
424                 $this->_inames[$key] = $plugins[$instance->enrol]->get_instance_name($instance);
425             }
426         }
427         return $this->_inames;
428     }
430     /**
431      * Gets all of the enrolment plugins that are active for this course.
432      *
433      * @return array
434      */
435     public function get_enrolment_plugins() {
436         if ($this->_plugins === null) {
437             $this->_plugins = enrol_get_plugins(true);
438         }
439         return $this->_plugins;
440     }
442     /**
443      * Gets all of the roles this course can contain.
444      *
445      * @return array
446      */
447     public function get_all_roles() {
448         if ($this->_roles === null) {
449             $this->_roles = role_fix_names(get_all_roles(), $this->context);
450         }
451         return $this->_roles;
452     }
454     /**
455      * Gets all of the assignable roles for this course.
456      *
457      * @return array
458      */
459     public function get_assignable_roles($otherusers = false) {
460         if ($this->_assignableroles === null) {
461             $this->_assignableroles = get_assignable_roles($this->context, ROLENAME_ALIAS, false); // verifies unassign access control too
462         }
464         if ($otherusers) {
465             if (!is_array($this->_assignablerolesothers)) {
466                 $this->_assignablerolesothers = array();
467                 list($courseviewroles, $ignored) = get_roles_with_cap_in_context($this->context, 'moodle/course:view');
468                 foreach ($this->_assignableroles as $roleid=>$role) {
469                     if (isset($courseviewroles[$roleid])) {
470                         $this->_assignablerolesothers[$roleid] = $role;
471                     }
472                 }
473             }
474             return $this->_assignablerolesothers;
475         } else {
476             return $this->_assignableroles;
477         }
478     }
480     /**
481      * Gets all of the groups for this course.
482      *
483      * @return array
484      */
485     public function get_all_groups() {
486         if ($this->_groups === null) {
487             $this->_groups = groups_get_all_groups($this->course->id);
488             foreach ($this->_groups as $gid=>$group) {
489                 $this->_groups[$gid]->name = format_string($group->name);
490             }
491         }
492         return $this->_groups;
493     }
495     /**
496      * Unenrols a user from the course given the users ue entry
497      *
498      * @global moodle_database $DB
499      * @param stdClass $ue
500      * @return bool
501      */
502     public function unenrol_user($ue) {
503         global $DB;
504         list ($instance, $plugin) = $this->get_user_enrolment_components($ue);
505         if ($instance && $plugin && $plugin->allow_unenrol_user($instance, $ue) && has_capability("enrol/$instance->enrol:unenrol", $this->context)) {
506             $plugin->unenrol_user($instance, $ue->userid);
507             return true;
508         }
509         return false;
510     }
512     /**
513      * Given a user enrolment record this method returns the plugin and enrolment
514      * instance that relate to it.
515      *
516      * @param stdClass|int $userenrolment
517      * @return array array($instance, $plugin)
518      */
519     public function get_user_enrolment_components($userenrolment) {
520         global $DB;
521         if (is_numeric($userenrolment)) {
522             $userenrolment = $DB->get_record('user_enrolments', array('id'=>(int)$userenrolment));
523         }
524         $instances = $this->get_enrolment_instances();
525         $plugins = $this->get_enrolment_plugins();
526         if (!$userenrolment || !isset($instances[$userenrolment->enrolid])) {
527             return array(false, false);
528         }
529         $instance = $instances[$userenrolment->enrolid];
530         $plugin = $plugins[$instance->enrol];
531         return array($instance, $plugin);
532     }
534     /**
535      * Removes an assigned role from a user.
536      *
537      * @global moodle_database $DB
538      * @param int $userid
539      * @param int $roleid
540      * @return bool
541      */
542     public function unassign_role_from_user($userid, $roleid) {
543         global $DB;
544         require_capability('moodle/role:assign', $this->context);
545         $user = $DB->get_record('user', array('id'=>$userid), '*', MUST_EXIST);
546         try {
547             role_unassign($roleid, $user->id, $this->context->id, '', NULL);
548         } catch (Exception $e) {
549             if (defined('AJAX_SCRIPT')) {
550                 throw $e;
551             }
552             return false;
553         }
554         return true;
555     }
557     /**
558      * Assigns a role to a user.
559      *
560      * @param int $roleid
561      * @param int $userid
562      * @return int|false
563      */
564     public function assign_role_to_user($roleid, $userid) {
565         require_capability('moodle/role:assign', $this->context);
566         if (!array_key_exists($roleid, $this->get_assignable_roles())) {
567             if (defined('AJAX_SCRIPT')) {
568                 throw new moodle_exception('invalidrole');
569             }
570             return false;
571         }
572         return role_assign($roleid, $userid, $this->context->id, '', NULL);
573     }
575     /**
576      * Adds a user to a group
577      *
578      * @param stdClass $user
579      * @param int $groupid
580      * @return bool
581      */
582     public function add_user_to_group($user, $groupid) {
583         require_capability('moodle/course:managegroups', $this->context);
584         $group = $this->get_group($groupid);
585         if (!$group) {
586             return false;
587         }
588         return groups_add_member($group->id, $user->id);
589     }
591     /**
592      * Removes a user from a group
593      *
594      * @global moodle_database $DB
595      * @param StdClass $user
596      * @param int $groupid
597      * @return bool
598      */
599     public function remove_user_from_group($user, $groupid) {
600         global $DB;
601         require_capability('moodle/course:managegroups', $this->context);
602         $group = $this->get_group($groupid);
603         if (!$group) {
604             return false;
605         }
606         return groups_remove_member($group, $user);
607     }
609     /**
610      * Gets the requested group
611      *
612      * @param int $groupid
613      * @return stdClass|int
614      */
615     public function get_group($groupid) {
616         $groups = $this->get_all_groups();
617         if (!array_key_exists($groupid, $groups)) {
618             return false;
619         }
620         return $groups[$groupid];
621     }
623     /**
624      * Edits an enrolment
625      *
626      * @param stdClass $userenrolment
627      * @param stdClass $data
628      * @return bool
629      */
630     public function edit_enrolment($userenrolment, $data) {
631         //Only allow editing if the user has the appropriate capability
632         //Already checked in /enrol/users.php but checking again in case this function is called from elsewhere
633         list($instance, $plugin) = $this->get_user_enrolment_components($userenrolment);
634         if ($instance && $plugin && $plugin->allow_manage($instance) && has_capability("enrol/$instance->enrol:manage", $this->context)) {
635             if (!isset($data->status)) {
636                 $data->status = $userenrolment->status;
637             }
638             $plugin->update_user_enrol($instance, $userenrolment->userid, $data->status, $data->timestart, $data->timeend);
639             return true;
640         }
641         return false;
642     }
644     /**
645      * Returns the current enrolment filter that is being applied by this class
646      * @return string
647      */
648     public function get_enrolment_filter() {
649         return $this->instancefilter;
650     }
652     /**
653      * Gets the roles assigned to this user that are applicable for this course.
654      *
655      * @param int $userid
656      * @return array
657      */
658     public function get_user_roles($userid) {
659         $roles = array();
660         $ras = get_user_roles($this->context, $userid, true, 'c.contextlevel DESC, r.sortorder ASC');
661         foreach ($ras as $ra) {
662             if ($ra->contextid != $this->context->id) {
663                 if (!array_key_exists($ra->roleid, $roles)) {
664                     $roles[$ra->roleid] = null;
665                 }
666                 // higher ras, course always takes precedence
667                 continue;
668             }
669             if (array_key_exists($ra->roleid, $roles) && $roles[$ra->roleid] === false) {
670                 continue;
671             }
672             $roles[$ra->roleid] = ($ra->itemid == 0 and $ra->component === '');
673         }
674         return $roles;
675     }
677     /**
678      * Gets the enrolments this user has in the course
679      *
680      * @global moodle_database $DB
681      * @param int $userid
682      * @return array
683      */
684     public function get_user_enrolments($userid) {
685         global $DB;
686         list($instancessql, $params, $filter) = $this->get_instance_sql();
687         $params['userid'] = $userid;
688         $userenrolments = $DB->get_records_select('user_enrolments', "enrolid $instancessql AND userid = :userid", $params);
689         $instances = $this->get_enrolment_instances();
690         $plugins = $this->get_enrolment_plugins();
691         $inames = $this->get_enrolment_instance_names();
692         foreach ($userenrolments as &$ue) {
693             $ue->enrolmentinstance     = $instances[$ue->enrolid];
694             $ue->enrolmentplugin       = $plugins[$ue->enrolmentinstance->enrol];
695             $ue->enrolmentinstancename = $inames[$ue->enrolmentinstance->id];
696         }
697         return $userenrolments;
698     }
700     /**
701      * Gets the groups this user belongs to
702      *
703      * @param int $userid
704      * @return array
705      */
706     public function get_user_groups($userid) {
707         return groups_get_all_groups($this->course->id, $userid, 0, 'g.id');
708     }
710     /**
711      * Retursn an array of params that would go into the URL to return to this
712      * exact page.
713      *
714      * @return array
715      */
716     public function get_url_params() {
717         $args = array(
718             'id' => $this->course->id
719         );
720         if (!empty($this->instancefilter)) {
721             $args['ifilter'] = $this->instancefilter;
722         }
723         return $args;
724     }
726     /**
727      * Returns the course this object is managing enrolments for
728      *
729      * @return stdClass
730      */
731     public function get_course() {
732         return $this->course;
733     }
735     /**
736      * Returns the course context
737      *
738      * @return stdClass
739      */
740     public function get_context() {
741         return $this->context;
742     }
744     /**
745      * Gets an array of other users in this course ready for display.
746      *
747      * Other users are users who have been assigned or inherited roles within this
748      * course but have not been enrolled.
749      *
750      * @param core_enrol_renderer $renderer
751      * @param moodle_url $pageurl
752      * @param string $sort
753      * @param string $direction ASC | DESC
754      * @param int $page Starting from 0
755      * @param int $perpage
756      * @return array
757      */
758     public function get_other_users_for_display(core_enrol_renderer $renderer, moodle_url $pageurl, $sort, $direction, $page, $perpage) {
760         $userroles = $this->get_other_users($sort, $direction, $page, $perpage);
761         $roles = $this->get_all_roles();
763         $context    = $this->get_context();
764         $now = time();
765         $extrafields = get_extra_user_fields($context);
767         $users = array();
768         foreach ($userroles as $userrole) {
769             $contextid = $userrole->contextid;
770             unset($userrole->contextid); // This would collide with user avatar.
771             if (!array_key_exists($userrole->id, $users)) {
772                 $users[$userrole->id] = $this->prepare_user_for_display($userrole, $extrafields, $now);
773             }
774             $a = new stdClass;
775             $a->role = $roles[$userrole->roleid]->localname;
776             $changeable = ($userrole->component == '');
777             if ($contextid == $this->context->id) {
778                 $roletext = get_string('rolefromthiscourse', 'enrol', $a);
779             } else {
780                 $changeable = false;
781                 switch ($userrole->contextlevel) {
782                     case CONTEXT_COURSE :
783                         // Meta course
784                         $roletext = get_string('rolefrommetacourse', 'enrol', $a);
785                         break;
786                     case CONTEXT_COURSECAT :
787                         $roletext = get_string('rolefromcategory', 'enrol', $a);
788                         break;
789                     case CONTEXT_SYSTEM:
790                     default:
791                         $roletext = get_string('rolefromsystem', 'enrol', $a);
792                         break;
793                 }
794             }
795             $users[$userrole->id]['roles'] = array();
796             $users[$userrole->id]['roles'][$userrole->roleid] = array(
797                 'text' => $roletext,
798                 'unchangeable' => !$changeable
799             );
800         }
801         return $users;
802     }
804     /**
805      * Gets an array of users for display, this includes minimal user information
806      * as well as minimal information on the users roles, groups, and enrolments.
807      *
808      * @param core_enrol_renderer $renderer
809      * @param moodle_url $pageurl
810      * @param int $sort
811      * @param string $direction ASC or DESC
812      * @param int $page
813      * @param int $perpage
814      * @return array
815      */
816     public function get_users_for_display(course_enrolment_manager $manager, $sort, $direction, $page, $perpage) {
817         $pageurl = $manager->get_moodlepage()->url;
818         $users = $this->get_users($sort, $direction, $page, $perpage);
820         $now = time();
821         $straddgroup = get_string('addgroup', 'group');
822         $strunenrol = get_string('unenrol', 'enrol');
823         $stredit = get_string('edit');
825         $allroles   = $this->get_all_roles();
826         $assignable = $this->get_assignable_roles();
827         $allgroups  = $this->get_all_groups();
828         $context    = $this->get_context();
829         $canmanagegroups = has_capability('moodle/course:managegroups', $context);
831         $url = new moodle_url($pageurl, $this->get_url_params());
832         $extrafields = get_extra_user_fields($context);
834         $userdetails = array();
835         foreach ($users as $user) {
836             $details = $this->prepare_user_for_display($user, $extrafields, $now);
838             // Roles
839             $details['roles'] = array();
840             foreach ($this->get_user_roles($user->id) as $rid=>$rassignable) {
841                 $details['roles'][$rid] = array('text'=>$allroles[$rid]->localname, 'unchangeable'=>(!$rassignable || !isset($assignable[$rid])));
842             }
844             // Users
845             $usergroups = $this->get_user_groups($user->id);
846             $details['groups'] = array();
847             foreach($usergroups as $gid=>$unused) {
848                 $details['groups'][$gid] = $allgroups[$gid]->name;
849             }
851             // Enrolments
852             $details['enrolments'] = array();
853             foreach ($this->get_user_enrolments($user->id) as $ue) {
854                 if ($ue->timestart and $ue->timeend) {
855                     $period = get_string('periodstartend', 'enrol', array('start'=>userdate($ue->timestart), 'end'=>userdate($ue->timeend)));
856                     $periodoutside = ($ue->timestart && $ue->timeend && $now < $ue->timestart && $now > $ue->timeend);
857                 } else if ($ue->timestart) {
858                     $period = get_string('periodstart', 'enrol', userdate($ue->timestart));
859                     $periodoutside = ($ue->timestart && $now < $ue->timestart);
860                 } else if ($ue->timeend) {
861                     $period = get_string('periodend', 'enrol', userdate($ue->timeend));
862                     $periodoutside = ($ue->timeend && $now > $ue->timeend);
863                 } else {
864                     $period = '';
865                     $periodoutside = false;
866                 }
867                 $details['enrolments'][$ue->id] = array(
868                     'text' => $ue->enrolmentinstancename,
869                     'period' => $period,
870                     'dimmed' =>  ($periodoutside or $ue->status != ENROL_USER_ACTIVE or $ue->enrolmentinstance->status != ENROL_INSTANCE_ENABLED),
871                     'actions' => $ue->enrolmentplugin->get_user_enrolment_actions($manager, $ue)
872                 );
873             }
874             $userdetails[$user->id] = $details;
875         }
876         return $userdetails;
877     }
879     /**
880      * Prepare a user record for display
881      *
882      * This function is called by both {@link get_users_for_display} and {@link get_other_users_for_display} to correctly
883      * prepare user fields for display
884      *
885      * Please note that this function does not check capability for moodle/coures:viewhiddenuserfields
886      *
887      * @param object $user The user record
888      * @param array $extrafields The list of fields as returned from get_extra_user_fields used to determine which
889      * additional fields may be displayed
890      * @param int $now The time used for lastaccess calculation
891      * @return array The fields to be displayed including userid, courseid, picture, firstname, lastseen and any
892      * additional fields from $extrafields
893      */
894     private function prepare_user_for_display($user, $extrafields, $now) {
895         $details = array(
896             'userid'    => $user->id,
897             'courseid'  => $this->get_course()->id,
898             'picture'   => new user_picture($user),
899             'firstname' => fullname($user, has_capability('moodle/site:viewfullnames', $this->get_context())),
900             'lastseen'  => get_string('never'),
901         );
902         foreach ($extrafields as $field) {
903             $details[$field] = $user->{$field};
904         }
906         if ($user->lastaccess) {
907             $details['lastseen'] = format_time($now - $user->lastaccess);
908         }
909         return $details;
910     }
912     public function get_manual_enrol_buttons() {
913         $plugins = $this->get_enrolment_plugins();
914         $buttons = array();
915         foreach ($plugins as $plugin) {
916             $newbutton = $plugin->get_manual_enrol_button($this);
917             if (is_array($newbutton)) {
918                 $buttons += $newbutton;
919             } else if ($newbutton instanceof enrol_user_button) {
920                 $buttons[] = $newbutton;
921             }
922         }
923         return $buttons;
924     }
926     public function has_instance($enrolpluginname) {
927         // Make sure manual enrolments instance exists
928         foreach ($this->get_enrolment_instances() as $instance) {
929             if ($instance->enrol == $enrolpluginname) {
930                 return true;
931             }
932         }
933         return false;
934     }
936     /**
937      * Returns the enrolment plugin that the course manager was being filtered to.
938      *
939      * If no filter was being applied then this function returns false.
940      *
941      * @return enrol_plugin
942      */
943     public function get_filtered_enrolment_plugin() {
944         $instances = $this->get_enrolment_instances();
945         $plugins = $this->get_enrolment_plugins();
947         if (empty($this->instancefilter) || !array_key_exists($this->instancefilter, $instances)) {
948             return false;
949         }
951         $instance = $instances[$this->instancefilter];
952         return $plugins[$instance->enrol];
953     }
955     /**
956      * Returns and array of users + enrolment details.
957      *
958      * Given an array of user id's this function returns and array of user enrolments for those users
959      * as well as enough user information to display the users name and picture for each enrolment.
960      *
961      * @global moodle_database $DB
962      * @param array $userids
963      * @return array
964      */
965     public function get_users_enrolments(array $userids) {
966         global $DB;
968         $instances = $this->get_enrolment_instances();
969         $plugins = $this->get_enrolment_plugins();
971         if  (!empty($this->instancefilter)) {
972             $instancesql = ' = :instanceid';
973             $instanceparams = array('instanceid' => $this->instancefilter);
974         } else {
975             list($instancesql, $instanceparams) = $DB->get_in_or_equal(array_keys($instances), SQL_PARAMS_NAMED, 'instanceid0000');
976         }
978         $userfields = user_picture::fields('u');
979         list($idsql, $idparams) = $DB->get_in_or_equal($userids, SQL_PARAMS_NAMED, 'userid0000');
981         $sql = "SELECT ue.id AS ueid, ue.status, ue.enrolid, ue.userid, ue.timestart, ue.timeend, ue.modifierid, ue.timecreated, ue.timemodified, $userfields
982                   FROM {user_enrolments} ue
983              LEFT JOIN {user} u ON u.id = ue.userid
984                  WHERE ue.enrolid $instancesql AND
985                        u.id $idsql
986               ORDER BY u.firstname ASC, u.lastname ASC";
988         $rs = $DB->get_recordset_sql($sql, $idparams + $instanceparams);
989         $users = array();
990         foreach ($rs as $ue) {
991             $user = user_picture::unalias($ue);
992             $ue->id = $ue->ueid;
993             unset($ue->ueid);
994             if (!array_key_exists($user->id, $users)) {
995                 $user->enrolments = array();
996                 $users[$user->id] = $user;
997             }
998             $ue->enrolmentinstance = $instances[$ue->enrolid];
999             $ue->enrolmentplugin = $plugins[$ue->enrolmentinstance->enrol];
1000             $users[$user->id]->enrolments[$ue->id] = $ue;
1001         }
1002         $rs->close();
1003         return $users;
1004     }
1007 /**
1008  * A button that is used to enrol users in a course
1009  *
1010  * @copyright 2010 Sam Hemelryk
1011  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1012  */
1013 class enrol_user_button extends single_button {
1015     /**
1016      * An array containing JS YUI modules required by this button
1017      * @var array
1018      */
1019     protected $jsyuimodules = array();
1021     /**
1022      * An array containing JS initialisation calls required by this button
1023      * @var array
1024      */
1025     protected $jsinitcalls = array();
1027     /**
1028      * An array strings required by JS for this button
1029      * @var array
1030      */
1031     protected $jsstrings = array();
1033     /**
1034      * Initialises the new enrol_user_button
1035      *
1036      * @staticvar int $count The number of enrol user buttons already created
1037      * @param moodle_url $url
1038      * @param string $label The text to display in the button
1039      * @param string $method Either post or get
1040      */
1041     public function __construct(moodle_url $url, $label, $method = 'post') {
1042         static $count = 0;
1043         $count ++;
1044         parent::__construct($url, $label, $method);
1045         $this->class = 'singlebutton enrolusersbutton';
1046         $this->formid = 'enrolusersbutton-'.$count;
1047     }
1049     /**
1050      * Adds a YUI module call that will be added to the page when the button is used.
1051      *
1052      * @param string|array $modules One or more modules to require
1053      * @param string $function The JS function to call
1054      * @param array $arguments An array of arguments to pass to the function
1055      * @param string $galleryversion The YUI gallery version of any modules required
1056      * @param bool $ondomready If true the call is postponed until the DOM is finished loading
1057      */
1058     public function require_yui_module($modules, $function, array $arguments = null, $galleryversion = '2010.04.08-12-35', $ondomready = false) {
1059         $js = new stdClass;
1060         $js->modules = (array)$modules;
1061         $js->function = $function;
1062         $js->arguments = $arguments;
1063         $js->galleryversion = $galleryversion;
1064         $js->ondomready = $ondomready;
1065         $this->jsyuimodules[] = $js;
1066     }
1068     /**
1069      * Adds a JS initialisation call to the page when the button is used.
1070      *
1071      * @param string $function The function to call
1072      * @param array $extraarguments An array of arguments to pass to the function
1073      * @param bool $ondomready If true the call is postponed until the DOM is finished loading
1074      * @param array $module A module definition
1075      */
1076     public function require_js_init_call($function, array $extraarguments = null, $ondomready = false, array $module = null) {
1077         $js = new stdClass;
1078         $js->function = $function;
1079         $js->extraarguments = $extraarguments;
1080         $js->ondomready = $ondomready;
1081         $js->module = $module;
1082         $this->jsinitcalls[] = $js;
1083     }
1085     /**
1086      * Requires strings for JS that will be loaded when the button is used.
1087      *
1088      * @param type $identifiers
1089      * @param string $component
1090      * @param mixed $a
1091      */
1092     public function strings_for_js($identifiers, $component = 'moodle', $a = null) {
1093         $string = new stdClass;
1094         $string->identifiers = (array)$identifiers;
1095         $string->component = $component;
1096         $string->a = $a;
1097         $this->jsstrings[] = $string;
1098     }
1100     /**
1101      * Initialises the JS that is required by this button
1102      *
1103      * @param moodle_page $page
1104      */
1105     public function initialise_js(moodle_page $page) {
1106         foreach ($this->jsyuimodules as $js) {
1107             $page->requires->yui_module($js->modules, $js->function, $js->arguments, $js->galleryversion, $js->ondomready);
1108         }
1109         foreach ($this->jsinitcalls as $js) {
1110             $page->requires->js_init_call($js->function, $js->extraarguments, $js->ondomready, $js->module);
1111         }
1112         foreach ($this->jsstrings as $string) {
1113             $page->requires->strings_for_js($string->identifiers, $string->component, $string->a);
1114         }
1115     }
1118 /**
1119  * User enrolment action
1120  *
1121  * This class is used to manage a renderable ue action such as editing an user enrolment or deleting
1122  * a user enrolment.
1123  *
1124  * @copyright  2011 Sam Hemelryk
1125  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1126  */
1127 class user_enrolment_action implements renderable {
1129     /**
1130      * The icon to display for the action
1131      * @var pix_icon
1132      */
1133     protected $icon;
1135     /**
1136      * The title for the action
1137      * @var string
1138      */
1139     protected $title;
1141     /**
1142      * The URL to the action
1143      * @var moodle_url
1144      */
1145     protected $url;
1147     /**
1148      * An array of HTML attributes
1149      * @var array
1150      */
1151     protected $attributes = array();
1153     /**
1154      * Constructor
1155      * @param pix_icon $icon
1156      * @param string $title
1157      * @param moodle_url $url
1158      * @param array $attributes
1159      */
1160     public function __construct(pix_icon $icon, $title, $url, array $attributes = null) {
1161         $this->icon = $icon;
1162         $this->title = $title;
1163         $this->url = new moodle_url($url);
1164         if (!empty($attributes)) {
1165             $this->attributes = $attributes;
1166         }
1167         $this->attributes['title'] = $title;
1168     }
1170     /**
1171      * Returns the icon for this action
1172      * @return pix_icon
1173      */
1174     public function get_icon() {
1175         return $this->icon;
1176     }
1178     /**
1179      * Returns the title for this action
1180      * @return string
1181      */
1182     public function get_title() {
1183         return $this->title;
1184     }
1186     /**
1187      * Returns the URL for this action
1188      * @return moodle_url
1189      */
1190     public function get_url() {
1191         return $this->url;
1192     }
1194     /**
1195      * Returns the attributes to use for this action
1196      * @return array
1197      */
1198     public function get_attributes() {
1199         return $this->attributes;
1200     }
1203 class enrol_ajax_exception extends moodle_exception {
1204     /**
1205      * Constructor
1206      * @param string $errorcode The name of the string from error.php to print
1207      * @param string $module name of module
1208      * @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.
1209      * @param object $a Extra words and phrases that might be required in the error string
1210      * @param string $debuginfo optional debugging information
1211      */
1212     public function __construct($errorcode, $link = '', $a = NULL, $debuginfo = null) {
1213         parent::__construct($errorcode, 'enrol', $link, $a, $debuginfo);
1214     }
1217 /**
1218  * This class is used to manage a bulk operations for enrolment plugins.
1219  *
1220  * @copyright 2011 Sam Hemelryk
1221  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1222  */
1223 abstract class enrol_bulk_enrolment_operation {
1225     /**
1226      * The course enrolment manager
1227      * @var course_enrolment_manager
1228      */
1229     protected $manager;
1231     /**
1232      * The enrolment plugin to which this operation belongs
1233      * @var enrol_plugin
1234      */
1235     protected $plugin;
1237     /**
1238      * Contructor
1239      * @param course_enrolment_manager $manager
1240      * @param stdClass $plugin
1241      */
1242     public function __construct(course_enrolment_manager $manager, enrol_plugin $plugin = null) {
1243         $this->manager = $manager;
1244         $this->plugin = $plugin;
1245     }
1247     /**
1248      * Returns a moodleform used for this operation, or false if no form is required and the action
1249      * should be immediatly processed.
1250      *
1251      * @param moodle_url|string $defaultaction
1252      * @param mixed $defaultcustomdata
1253      * @return enrol_bulk_enrolment_change_form|moodleform|false
1254      */
1255     public function get_form($defaultaction = null, $defaultcustomdata = null) {
1256         return false;
1257     }
1259     /**
1260      * Returns the title to use for this bulk operation
1261      *
1262      * @return string
1263      */
1264     abstract public function get_title();
1266     /**
1267      * Returns the identifier for this bulk operation.
1268      * This should be the same identifier used by the plugins function when returning
1269      * all of its bulk operations.
1270      *
1271      * @return string
1272      */
1273     abstract public function get_identifier();
1275     /**
1276      * Processes the bulk operation on the given users
1277      *
1278      * @param course_enrolment_manager $manager
1279      * @param array $users
1280      * @param stdClass $properties
1281      */
1282     abstract public function process(course_enrolment_manager $manager, array $users, stdClass $properties);