Merge branch 'MDL-29464' of git://github.com/timhunt/moodle
[moodle.git] / lib / enrollib.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 library includes the basic parts of enrol api.
20  * It is available on each page.
21  *
22  * @package    core
23  * @subpackage enrol
24  * @copyright  2010 Petr Skoda {@link http://skodak.org}
25  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
26  */
28 defined('MOODLE_INTERNAL') || die();
30 /** Course enrol instance enabled. (used in enrol->status) */
31 define('ENROL_INSTANCE_ENABLED', 0);
33 /** Course enrol instance disabled, user may enter course if other enrol instance enabled. (used in enrol->status)*/
34 define('ENROL_INSTANCE_DISABLED', 1);
36 /** User is active participant (used in user_enrolments->status)*/
37 define('ENROL_USER_ACTIVE', 0);
39 /** User participation in course is suspended (used in user_enrolments->status) */
40 define('ENROL_USER_SUSPENDED', 1);
42 /** Enrol info is cached for this number of seconds in require_login() */
43 define('ENROL_REQUIRE_LOGIN_CACHE_PERIOD', 1800);
45 /** When user disappears from external source, the enrolment is completely removed */
46 define('ENROL_EXT_REMOVED_UNENROL', 0);
48 /** When user disappears from external source, the enrolment is kept as is - one way sync */
49 define('ENROL_EXT_REMOVED_KEEP', 1);
51 /** enrol plugin feature describing requested restore type */
52 define('ENROL_RESTORE_TYPE', 'enrolrestore');
53 /** User custom backup/restore class  stored in backup/moodle2/ subdirectory */
54 define('ENROL_RESTORE_CLASS', 'class');
55 /** Restore all custom fields from enrol table without any changes and all user_enrolments records */
56 define('ENROL_RESTORE_EXACT', 'exact');
57 /** Restore enrol record like ENROL_RESTORE_EXACT, but no user enrolments */
58 define('ENROL_RESTORE_NOUSERS', 'nousers');
60 /**
61  * When user disappears from external source, user enrolment is suspended, roles are kept as is.
62  * In some cases user needs a role with some capability to be visible in UI - suc has in gradebook,
63  * assignments, etc.
64  */
65 define('ENROL_EXT_REMOVED_SUSPEND', 2);
67 /**
68  * When user disappears from external source, the enrolment is suspended and roles assigned
69  * by enrol instance are removed. Please note that user may "disappear" from gradebook and other areas.
70  * */
71 define('ENROL_EXT_REMOVED_SUSPENDNOROLES', 3);
73 /**
74  * Returns instances of enrol plugins
75  * @param bool $enable return enabled only
76  * @return array of enrol plugins name=>instance
77  */
78 function enrol_get_plugins($enabled) {
79     global $CFG;
81     $result = array();
83     if ($enabled) {
84         // sorted by enabled plugin order
85         $enabled = explode(',', $CFG->enrol_plugins_enabled);
86         $plugins = array();
87         foreach ($enabled as $plugin) {
88             $plugins[$plugin] = "$CFG->dirroot/enrol/$plugin";
89         }
90     } else {
91         // sorted alphabetically
92         $plugins = get_plugin_list('enrol');
93         ksort($plugins);
94     }
96     foreach ($plugins as $plugin=>$location) {
97         if (!file_exists("$location/lib.php")) {
98             continue;
99         }
100         include_once("$location/lib.php");
101         $class = "enrol_{$plugin}_plugin";
102         if (!class_exists($class)) {
103             continue;
104         }
106         $result[$plugin] = new $class();
107     }
109     return $result;
112 /**
113  * Returns instance of enrol plugin
114  * @param  string $name name of enrol plugin ('manual', 'guest', ...)
115  * @return enrol_plugin
116  */
117 function enrol_get_plugin($name) {
118     global $CFG;
120     if ($name !== clean_param($name, PARAM_SAFEDIR)) {
121         // ignore malformed plugin names completely
122         return null;
123     }
125     $location = "$CFG->dirroot/enrol/$name";
127     if (!file_exists("$location/lib.php")) {
128         return null;
129     }
130     include_once("$location/lib.php");
131     $class = "enrol_{$name}_plugin";
132     if (!class_exists($class)) {
133         return null;
134     }
136     return new $class();
139 /**
140  * Returns enrolment instances in given course.
141  * @param int $courseid
142  * @param bool $enabled
143  * @return array of enrol instances
144  */
145 function enrol_get_instances($courseid, $enabled) {
146     global $DB, $CFG;
148     if (!$enabled) {
149         return $DB->get_records('enrol', array('courseid'=>$courseid), 'sortorder,id');
150     }
152     $result = $DB->get_records('enrol', array('courseid'=>$courseid, 'status'=>ENROL_INSTANCE_ENABLED), 'sortorder,id');
154     $enabled = explode(',', $CFG->enrol_plugins_enabled);
155     foreach ($result as $key=>$instance) {
156         if (!in_array($instance->enrol, $enabled)) {
157             unset($result[$key]);
158             continue;
159         }
160         if (!file_exists("$CFG->dirroot/enrol/$instance->enrol/lib.php")) {
161             // broken plugin
162             unset($result[$key]);
163             continue;
164         }
165     }
167     return $result;
170 /**
171  * Checks if a given plugin is in the list of enabled enrolment plugins.
172  *
173  * @param string $enrol Enrolment plugin name
174  * @return boolean Whether the plugin is enabled
175  */
176 function enrol_is_enabled($enrol) {
177     global $CFG;
179     if (empty($CFG->enrol_plugins_enabled)) {
180         return false;
181     }
182     return in_array($enrol, explode(',', $CFG->enrol_plugins_enabled));
185 /**
186  * Check all the login enrolment information for the given user object
187  * by querying the enrolment plugins
188  *
189  * @param object $user
190  * @return void
191  */
192 function enrol_check_plugins($user) {
193     global $CFG;
195     if (empty($user->id) or isguestuser($user)) {
196         // shortcut - there is no enrolment work for guests and not-logged-in users
197         return;
198     }
200     if (is_siteadmin()) {
201         // no sync for admin user, please use admin accounts only for admin tasks like the unix root user!
202         // if plugin fails on sync admins need to be able to log in
203         return;
204     }
206     static $inprogress = array();  // To prevent this function being called more than once in an invocation
208     if (!empty($inprogress[$user->id])) {
209         return;
210     }
212     $inprogress[$user->id] = true;  // Set the flag
214     $enabled = enrol_get_plugins(true);
216     foreach($enabled as $enrol) {
217         $enrol->sync_user_enrolments($user);
218     }
220     unset($inprogress[$user->id]);  // Unset the flag
223 /**
224  * Do these two students share any course?
225  *
226  * The courses has to be visible and enrolments has to be active,
227  * timestart and timeend restrictions are ignored.
228  *
229  * @param stdClass|int $user1
230  * @param stdClass|int $user2
231  * @return bool
232  */
233 function enrol_sharing_course($user1, $user2) {
234     global $DB, $CFG;
236     $user1 = !empty($user1->id) ? $user1->id : $user1;
237     $user2 = !empty($user2->id) ? $user2->id : $user2;
239     if (empty($user1) or empty($user2)) {
240         return false;
241     }
243     if (!$plugins = explode(',', $CFG->enrol_plugins_enabled)) {
244         return false;
245     }
247     list($plugins, $params) = $DB->get_in_or_equal($plugins, SQL_PARAMS_NAMED, 'ee');
248     $params['enabled'] = ENROL_INSTANCE_ENABLED;
249     $params['active1'] = ENROL_USER_ACTIVE;
250     $params['active2'] = ENROL_USER_ACTIVE;
251     $params['user1']   = $user1;
252     $params['user2']   = $user2;
254     $sql = "SELECT DISTINCT 'x'
255               FROM {enrol} e
256               JOIN {user_enrolments} ue1 ON (ue1.enrolid = e.id AND ue1.status = :active1 AND ue1.userid = :user1)
257               JOIN {user_enrolments} ue2 ON (ue1.enrolid = e.id AND ue1.status = :active2 AND ue2.userid = :user2)
258               JOIN {course} c ON (c.id = e.courseid AND c.visible = 1)
259              WHERE e.status = :enabled AND e.enrol $plugins";
261     return $DB->record_exists_sql($sql, $params);
264 /**
265  * This function adds necessary enrol plugins UI into the course edit form.
266  *
267  * @param MoodleQuickForm $mform
268  * @param object $data course edit form data
269  * @param object $context context of existing course or parent category if course does not exist
270  * @return void
271  */
272 function enrol_course_edit_form(MoodleQuickForm $mform, $data, $context) {
273     $plugins = enrol_get_plugins(true);
274     if (!empty($data->id)) {
275         $instances = enrol_get_instances($data->id, false);
276         foreach ($instances as $instance) {
277             if (!isset($plugins[$instance->enrol])) {
278                 continue;
279             }
280             $plugin = $plugins[$instance->enrol];
281             $plugin->course_edit_form($instance, $mform, $data, $context);
282         }
283     } else {
284         foreach ($plugins as $plugin) {
285             $plugin->course_edit_form(NULL, $mform, $data, $context);
286         }
287     }
290 /**
291  * Validate course edit form data
292  *
293  * @param array $data raw form data
294  * @param object $context context of existing course or parent category if course does not exist
295  * @return array errors array
296  */
297 function enrol_course_edit_validation(array $data, $context) {
298     $errors = array();
299     $plugins = enrol_get_plugins(true);
301     if (!empty($data['id'])) {
302         $instances = enrol_get_instances($data['id'], false);
303         foreach ($instances as $instance) {
304             if (!isset($plugins[$instance->enrol])) {
305                 continue;
306             }
307             $plugin = $plugins[$instance->enrol];
308             $errors = array_merge($errors, $plugin->course_edit_validation($instance, $data, $context));
309         }
310     } else {
311         foreach ($plugins as $plugin) {
312             $errors = array_merge($errors, $plugin->course_edit_validation(NULL, $data, $context));
313         }
314     }
316     return $errors;
319 /**
320  * Update enrol instances after course edit form submission
321  * @param bool $inserted true means new course added, false course already existed
322  * @param object $course
323  * @param object $data form data
324  * @return void
325  */
326 function enrol_course_updated($inserted, $course, $data) {
327     global $DB, $CFG;
329     $plugins = enrol_get_plugins(true);
331     foreach ($plugins as $plugin) {
332         $plugin->course_updated($inserted, $course, $data);
333     }
336 /**
337  * Add navigation nodes
338  * @param navigation_node $coursenode
339  * @param object $course
340  * @return void
341  */
342 function enrol_add_course_navigation(navigation_node $coursenode, $course) {
343     global $CFG;
345     $coursecontext = get_context_instance(CONTEXT_COURSE, $course->id);
347     $instances = enrol_get_instances($course->id, true);
348     $plugins   = enrol_get_plugins(true);
350     // we do not want to break all course pages if there is some borked enrol plugin, right?
351     foreach ($instances as $k=>$instance) {
352         if (!isset($plugins[$instance->enrol])) {
353             unset($instances[$k]);
354         }
355     }
357     $usersnode = $coursenode->add(get_string('users'), null, navigation_node::TYPE_CONTAINER, null, 'users');
359     if ($course->id != SITEID) {
360         // list all participants - allows assigning roles, groups, etc.
361         if (has_capability('moodle/course:enrolreview', $coursecontext)) {
362             $url = new moodle_url('/enrol/users.php', array('id'=>$course->id));
363             $usersnode->add(get_string('enrolledusers', 'enrol'), $url, navigation_node::TYPE_SETTING, null, 'review', new pix_icon('i/users', ''));
364         }
366         // manage enrol plugin instances
367         if (has_capability('moodle/course:enrolconfig', $coursecontext) or has_capability('moodle/course:enrolreview', $coursecontext)) {
368             $url = new moodle_url('/enrol/instances.php', array('id'=>$course->id));
369         } else {
370             $url = NULL;
371         }
372         $instancesnode = $usersnode->add(get_string('enrolmentinstances', 'enrol'), $url, navigation_node::TYPE_SETTING, null, 'manageinstances');
374         // each instance decides how to configure itself or how many other nav items are exposed
375         foreach ($instances as $instance) {
376             if (!isset($plugins[$instance->enrol])) {
377                 continue;
378             }
379             $plugins[$instance->enrol]->add_course_navigation($instancesnode, $instance);
380         }
382         if (!$url) {
383             $instancesnode->trim_if_empty();
384         }
385     }
387     // Manage groups in this course or even frontpage
388     if (($course->groupmode || !$course->groupmodeforce) && has_capability('moodle/course:managegroups', $coursecontext)) {
389         $url = new moodle_url('/group/index.php', array('id'=>$course->id));
390         $usersnode->add(get_string('groups'), $url, navigation_node::TYPE_SETTING, null, 'groups', new pix_icon('i/group', ''));
391     }
393      if (has_any_capability(array( 'moodle/role:assign', 'moodle/role:safeoverride','moodle/role:override', 'moodle/role:review'), $coursecontext)) {
394         // Override roles
395         if (has_capability('moodle/role:review', $coursecontext)) {
396             $url = new moodle_url('/admin/roles/permissions.php', array('contextid'=>$coursecontext->id));
397         } else {
398             $url = NULL;
399         }
400         $permissionsnode = $usersnode->add(get_string('permissions', 'role'), $url, navigation_node::TYPE_SETTING, null, 'override');
402         // Add assign or override roles if allowed
403         if ($course->id == SITEID or (!empty($CFG->adminsassignrolesincourse) and is_siteadmin())) {
404             if (has_capability('moodle/role:assign', $coursecontext)) {
405                 $url = new moodle_url('/admin/roles/assign.php', array('contextid'=>$coursecontext->id));
406                 $permissionsnode->add(get_string('assignedroles', 'role'), $url, navigation_node::TYPE_SETTING, null, 'roles', new pix_icon('i/roles', ''));
407             }
408         }
409         // Check role permissions
410         if (has_any_capability(array('moodle/role:assign', 'moodle/role:safeoverride','moodle/role:override', 'moodle/role:assign'), $coursecontext)) {
411             $url = new moodle_url('/admin/roles/check.php', array('contextid'=>$coursecontext->id));
412             $permissionsnode->add(get_string('checkpermissions', 'role'), $url, navigation_node::TYPE_SETTING, null, 'permissions', new pix_icon('i/checkpermissions', ''));
413         }
414      }
416      // Deal somehow with users that are not enrolled but still got a role somehow
417     if ($course->id != SITEID) {
418         //TODO, create some new UI for role assignments at course level
419         if (has_capability('moodle/role:assign', $coursecontext)) {
420             $url = new moodle_url('/enrol/otherusers.php', array('id'=>$course->id));
421             $usersnode->add(get_string('notenrolledusers', 'enrol'), $url, navigation_node::TYPE_SETTING, null, 'otherusers', new pix_icon('i/roles', ''));
422         }
423     }
425     // just in case nothing was actually added
426     $usersnode->trim_if_empty();
428     if ($course->id != SITEID) {
429         // Unenrol link
430         if (is_enrolled($coursecontext)) {
431             foreach ($instances as $instance) {
432                 if (!isset($plugins[$instance->enrol])) {
433                     continue;
434                 }
435                 $plugin = $plugins[$instance->enrol];
436                 if ($unenrollink = $plugin->get_unenrolself_link($instance)) {
437                     $shortname = format_string($course->shortname, true, array('context' => $coursecontext));
438                     $coursenode->add(get_string('unenrolme', 'core_enrol', $shortname), $unenrollink, navigation_node::TYPE_SETTING, null, 'unenrolself', new pix_icon('i/user', ''));
439                     break;
440                     //TODO. deal with multiple unenrol links - not likely case, but still...
441                 }
442             }
443         } else {
444             if (is_viewing($coursecontext)) {
445                 // better not show any enrol link, this is intended for managers and inspectors
446             } else {
447                 foreach ($instances as $instance) {
448                     if (!isset($plugins[$instance->enrol])) {
449                         continue;
450                     }
451                     $plugin = $plugins[$instance->enrol];
452                     if ($plugin->show_enrolme_link($instance)) {
453                         $url = new moodle_url('/enrol/index.php', array('id'=>$course->id));
454                         $shortname = format_string($course->shortname, true, array('context' => $coursecontext));
455                         $coursenode->add(get_string('enrolme', 'core_enrol', $shortname), $url, navigation_node::TYPE_SETTING, null, 'enrolself', new pix_icon('i/user', ''));
456                         break;
457                     }
458                 }
459             }
460         }
461     }
464 /**
465  * Returns list of courses current $USER is enrolled in and can access
466  *
467  * - $fields is an array of field names to ADD
468  *   so name the fields you really need, which will
469  *   be added and uniq'd
470  *
471  * @param string|array $fields
472  * @param string $sort
473  * @param int $limit max number of courses
474  * @return array
475  */
476 function enrol_get_my_courses($fields = NULL, $sort = 'visible DESC,sortorder ASC', $limit = 0) {
477     global $DB, $USER;
479     // Guest account does not have any courses
480     if (isguestuser() or !isloggedin()) {
481         return(array());
482     }
484     $basefields = array('id', 'category', 'sortorder',
485                         'shortname', 'fullname', 'idnumber',
486                         'startdate', 'visible',
487                         'groupmode', 'groupmodeforce');
489     if (empty($fields)) {
490         $fields = $basefields;
491     } else if (is_string($fields)) {
492         // turn the fields from a string to an array
493         $fields = explode(',', $fields);
494         $fields = array_map('trim', $fields);
495         $fields = array_unique(array_merge($basefields, $fields));
496     } else if (is_array($fields)) {
497         $fields = array_unique(array_merge($basefields, $fields));
498     } else {
499         throw new coding_exception('Invalid $fileds parameter in enrol_get_my_courses()');
500     }
501     if (in_array('*', $fields)) {
502         $fields = array('*');
503     }
505     $orderby = "";
506     $sort    = trim($sort);
507     if (!empty($sort)) {
508         $rawsorts = explode(',', $sort);
509         $sorts = array();
510         foreach ($rawsorts as $rawsort) {
511             $rawsort = trim($rawsort);
512             if (strpos($rawsort, 'c.') === 0) {
513                 $rawsort = substr($rawsort, 2);
514             }
515             $sorts[] = trim($rawsort);
516         }
517         $sort = 'c.'.implode(',c.', $sorts);
518         $orderby = "ORDER BY $sort";
519     }
521     $wheres = array("c.id <> :siteid");
522     $params = array('siteid'=>SITEID);
524     if (isset($USER->loginascontext) and $USER->loginascontext->contextlevel == CONTEXT_COURSE) {
525         // list _only_ this course - anything else is asking for trouble...
526         $wheres[] = "courseid = :loginas";
527         $params['loginas'] = $USER->loginascontext->instanceid;
528     }
530     $coursefields = 'c.' .join(',c.', $fields);
531     list($ccselect, $ccjoin) = context_instance_preload_sql('c.id', CONTEXT_COURSE, 'ctx');
532     $wheres = implode(" AND ", $wheres);
534     //note: we can not use DISTINCT + text fields due to Oracle and MS limitations, that is why we have the subselect there
535     $sql = "SELECT $coursefields $ccselect
536               FROM {course} c
537               JOIN (SELECT DISTINCT e.courseid
538                       FROM {enrol} e
539                       JOIN {user_enrolments} ue ON (ue.enrolid = e.id AND ue.userid = :userid)
540                      WHERE ue.status = :active AND e.status = :enabled AND ue.timestart < :now1 AND (ue.timeend = 0 OR ue.timeend > :now2)
541                    ) en ON (en.courseid = c.id)
542            $ccjoin
543              WHERE $wheres
544           $orderby";
545     $params['userid']  = $USER->id;
546     $params['active']  = ENROL_USER_ACTIVE;
547     $params['enabled'] = ENROL_INSTANCE_ENABLED;
548     $params['now1']    = round(time(), -2); // improves db caching
549     $params['now2']    = $params['now1'];
551     $courses = $DB->get_records_sql($sql, $params, 0, $limit);
553     // preload contexts and check visibility
554     foreach ($courses as $id=>$course) {
555         context_instance_preload($course);
556         if (!$course->visible) {
557             if (!$context = get_context_instance(CONTEXT_COURSE, $id)) {
558                 unset($courses[$id]);
559                 continue;
560             }
561             if (!has_capability('moodle/course:viewhiddencourses', $context)) {
562                 unset($courses[$id]);
563                 continue;
564             }
565         }
566         $courses[$id] = $course;
567     }
569     //wow! Is that really all? :-D
571     return $courses;
574 /**
575  * Returns course enrolment information icons.
576  *
577  * @param object $course
578  * @param array $instances enrol instances of this course, improves performance
579  * @return array of pix_icon
580  */
581 function enrol_get_course_info_icons($course, array $instances = NULL) {
582     $icons = array();
583     if (is_null($instances)) {
584         $instances = enrol_get_instances($course->id, true);
585     }
586     $plugins = enrol_get_plugins(true);
587     foreach ($plugins as $name => $plugin) {
588         $pis = array();
589         foreach ($instances as $instance) {
590             if ($instance->status != ENROL_INSTANCE_ENABLED or $instance->courseid != $course->id) {
591                 debugging('Invalid instances parameter submitted in enrol_get_info_icons()');
592                 continue;
593             }
594             if ($instance->enrol == $name) {
595                 $pis[$instance->id] = $instance;
596             }
597         }
598         if ($pis) {
599             $icons = array_merge($icons, $plugin->get_info_icons($pis));
600         }
601     }
602     return $icons;
605 /**
606  * Returns course enrolment detailed information.
607  *
608  * @param object $course
609  * @return array of html fragments - can be used to construct lists
610  */
611 function enrol_get_course_description_texts($course) {
612     $lines = array();
613     $instances = enrol_get_instances($course->id, true);
614     $plugins = enrol_get_plugins(true);
615     foreach ($instances as $instance) {
616         if (!isset($plugins[$instance->enrol])) {
617             //weird
618             continue;
619         }
620         $plugin = $plugins[$instance->enrol];
621         $text = $plugin->get_description_text($instance);
622         if ($text !== NULL) {
623             $lines[] = $text;
624         }
625     }
626     return $lines;
629 /**
630  * Returns list of courses user is enrolled into.
631  *
632  * - $fields is an array of fieldnames to ADD
633  *   so name the fields you really need, which will
634  *   be added and uniq'd
635  *
636  * @param int $userid
637  * @param bool $onlyactive return only active enrolments in courses user may see
638  * @param string|array $fields
639  * @param string $sort
640  * @return array
641  */
642 function enrol_get_users_courses($userid, $onlyactive = false, $fields = NULL, $sort = 'visible DESC,sortorder ASC') {
643     global $DB;
645     // Guest account does not have any courses
646     if (isguestuser($userid) or empty($userid)) {
647         return(array());
648     }
650     $basefields = array('id', 'category', 'sortorder',
651                         'shortname', 'fullname', 'idnumber',
652                         'startdate', 'visible',
653                         'groupmode', 'groupmodeforce');
655     if (empty($fields)) {
656         $fields = $basefields;
657     } else if (is_string($fields)) {
658         // turn the fields from a string to an array
659         $fields = explode(',', $fields);
660         $fields = array_map('trim', $fields);
661         $fields = array_unique(array_merge($basefields, $fields));
662     } else if (is_array($fields)) {
663         $fields = array_unique(array_merge($basefields, $fields));
664     } else {
665         throw new coding_exception('Invalid $fileds parameter in enrol_get_my_courses()');
666     }
667     if (in_array('*', $fields)) {
668         $fields = array('*');
669     }
671     $orderby = "";
672     $sort    = trim($sort);
673     if (!empty($sort)) {
674         $rawsorts = explode(',', $sort);
675         $sorts = array();
676         foreach ($rawsorts as $rawsort) {
677             $rawsort = trim($rawsort);
678             if (strpos($rawsort, 'c.') === 0) {
679                 $rawsort = substr($rawsort, 2);
680             }
681             $sorts[] = trim($rawsort);
682         }
683         $sort = 'c.'.implode(',c.', $sorts);
684         $orderby = "ORDER BY $sort";
685     }
687     $params = array('siteid'=>SITEID);
689     if ($onlyactive) {
690         $subwhere = "WHERE ue.status = :active AND e.status = :enabled AND ue.timestart < :now1 AND (ue.timeend = 0 OR ue.timeend > :now2)";
691         $params['now1']    = round(time(), -2); // improves db caching
692         $params['now2']    = $params['now1'];
693         $params['active']  = ENROL_USER_ACTIVE;
694         $params['enabled'] = ENROL_INSTANCE_ENABLED;
695     } else {
696         $subwhere = "";
697     }
699     $coursefields = 'c.' .join(',c.', $fields);
700     list($ccselect, $ccjoin) = context_instance_preload_sql('c.id', CONTEXT_COURSE, 'ctx');
702     //note: we can not use DISTINCT + text fields due to Oracle and MS limitations, that is why we have the subselect there
703     $sql = "SELECT $coursefields $ccselect
704               FROM {course} c
705               JOIN (SELECT DISTINCT e.courseid
706                       FROM {enrol} e
707                       JOIN {user_enrolments} ue ON (ue.enrolid = e.id AND ue.userid = :userid)
708                  $subwhere
709                    ) en ON (en.courseid = c.id)
710            $ccjoin
711              WHERE c.id <> :siteid
712           $orderby";
713     $params['userid']  = $userid;
715     $courses = $DB->get_records_sql($sql, $params);
717     // preload contexts and check visibility
718     foreach ($courses as $id=>$course) {
719         context_instance_preload($course);
720         if ($onlyactive) {
721             if (!$course->visible) {
722                 if (!$context = get_context_instance(CONTEXT_COURSE, $id)) {
723                     unset($courses[$id]);
724                     continue;
725                 }
726                 if (!has_capability('moodle/course:viewhiddencourses', $context, $userid)) {
727                     unset($courses[$id]);
728                     continue;
729                 }
730             }
731         }
732         $courses[$id] = $course;
733     }
735     //wow! Is that really all? :-D
737     return $courses;
741 /**
742  * Called when user is about to be deleted.
743  * @param object $user
744  * @return void
745  */
746 function enrol_user_delete($user) {
747     global $DB;
749     $plugins = enrol_get_plugins(true);
750     foreach ($plugins as $plugin) {
751         $plugin->user_delete($user);
752     }
754     // force cleanup of all broken enrolments
755     $DB->delete_records('user_enrolments', array('userid'=>$user->id));
758 /**
759  * Called when course is about to be deleted.
760  * @param stdClass $object
761  * @return void
762  */
763 function enrol_course_delete($course) {
764     global $DB;
766     $instances = enrol_get_instances($course->id, false);
767     $plugins = enrol_get_plugins(true);
768     foreach ($instances as $instance) {
769         if (isset($plugins[$instance->enrol])) {
770             $plugins[$instance->enrol]->delete_instance($instance);
771         }
772         // low level delete in case plugin did not do it
773         $DB->delete_records('user_enrolments', array('enrolid'=>$instance->id));
774         $DB->delete_records('role_assignments', array('itemid'=>$instance->id, 'component'=>'enrol_'.$instance->enrol));
775         $DB->delete_records('user_enrolments', array('enrolid'=>$instance->id));
776         $DB->delete_records('enrol', array('id'=>$instance->id));
777     }
780 /**
781  * Try to enrol user via default internal auth plugin.
782  *
783  * For now this is always using the manual enrol plugin...
784  *
785  * @param $courseid
786  * @param $userid
787  * @param $roleid
788  * @param $timestart
789  * @param $timeend
790  * @return bool success
791  */
792 function enrol_try_internal_enrol($courseid, $userid, $roleid = null, $timestart = 0, $timeend = 0) {
793     global $DB;
795     //note: this is hardcoded to manual plugin for now
797     if (!enrol_is_enabled('manual')) {
798         return false;
799     }
801     if (!$enrol = enrol_get_plugin('manual')) {
802         return false;
803     }
804     if (!$instances = $DB->get_records('enrol', array('enrol'=>'manual', 'courseid'=>$courseid, 'status'=>ENROL_INSTANCE_ENABLED), 'sortorder,id ASC')) {
805         return false;
806     }
807     $instance = reset($instances);
809     $enrol->enrol_user($instance, $userid, $roleid, $timestart, $timeend);
811     return true;
814 /**
815  * Is there a chance users might self enrol
816  * @param int $courseid
817  * @return bool
818  */
819 function enrol_selfenrol_available($courseid) {
820     $result = false;
822     $plugins = enrol_get_plugins(true);
823     $enrolinstances = enrol_get_instances($courseid, true);
824     foreach($enrolinstances as $instance) {
825         if (!isset($plugins[$instance->enrol])) {
826             continue;
827         }
828         if ($instance->enrol === 'guest') {
829             // blacklist known temporary guest plugins
830             continue;
831         }
832         if ($plugins[$instance->enrol]->show_enrolme_link($instance)) {
833             $result = true;
834             break;
835         }
836     }
838     return $result;
841 /**
842  * All enrol plugins should be based on this class,
843  * this is also the main source of documentation.
844  */
845 abstract class enrol_plugin {
846     protected $config = null;
848     /**
849      * Returns name of this enrol plugin
850      * @return string
851      */
852     public function get_name() {
853         // second word in class is always enrol name, sorry, no fancy plugin names with _
854         $words = explode('_', get_class($this));
855         return $words[1];
856     }
858     /**
859      * Returns localised name of enrol instance
860      *
861      * @param object $instance (null is accepted too)
862      * @return string
863      */
864     public function get_instance_name($instance) {
865         if (empty($instance->name)) {
866             $enrol = $this->get_name();
867             return get_string('pluginname', 'enrol_'.$enrol);
868         } else {
869             $context = get_context_instance(CONTEXT_COURSE, $instance->courseid);
870             return format_string($instance->name, true, array('context'=>$context));
871         }
872     }
874     /**
875      * Returns optional enrolment information icons.
876      *
877      * This is used in course list for quick overview of enrolment options.
878      *
879      * We are not using single instance parameter because sometimes
880      * we might want to prevent icon repetition when multiple instances
881      * of one type exist. One instance may also produce several icons.
882      *
883      * @param array $instances all enrol instances of this type in one course
884      * @return array of pix_icon
885      */
886     public function get_info_icons(array $instances) {
887         return array();
888     }
890     /**
891      * Returns optional enrolment instance description text.
892      *
893      * This is used in detailed course information.
894      *
895      *
896      * @param object $instance
897      * @return string short html text
898      */
899     public function get_description_text($instance) {
900         return null;
901     }
903     /**
904      * Makes sure config is loaded and cached.
905      * @return void
906      */
907     protected function load_config() {
908         if (!isset($this->config)) {
909             $name = $this->get_name();
910             if (!$config = get_config("enrol_$name")) {
911                 $config = new stdClass();
912             }
913             $this->config = $config;
914         }
915     }
917     /**
918      * Returns plugin config value
919      * @param  string $name
920      * @param  string $default value if config does not exist yet
921      * @return string value or default
922      */
923     public function get_config($name, $default = NULL) {
924         $this->load_config();
925         return isset($this->config->$name) ? $this->config->$name : $default;
926     }
928     /**
929      * Sets plugin config value
930      * @param  string $name name of config
931      * @param  string $value string config value, null means delete
932      * @return string value
933      */
934     public function set_config($name, $value) {
935         $pluginname = $this->get_name();
936         $this->load_config();
937         if ($value === NULL) {
938             unset($this->config->$name);
939         } else {
940             $this->config->$name = $value;
941         }
942         set_config($name, $value, "enrol_$pluginname");
943     }
945     /**
946      * Does this plugin assign protected roles are can they be manually removed?
947      * @return bool - false means anybody may tweak roles, it does not use itemid and component when assigning roles
948      */
949     public function roles_protected() {
950         return true;
951     }
953     /**
954      * Does this plugin allow manual enrolments?
955      *
956      * @param stdClass $instance course enrol instance
957      * All plugins allowing this must implement 'enrol/xxx:enrol' capability
958      *
959      * @return bool - true means user with 'enrol/xxx:enrol' may enrol others freely, trues means nobody may add more enrolments manually
960      */
961     public function allow_enrol(stdClass $instance) {
962         return false;
963     }
965     /**
966      * Does this plugin allow manual unenrolments?
967      *
968      * @param stdClass $instance course enrol instance
969      * All plugins allowing this must implement 'enrol/xxx:unenrol' capability
970      *
971      * @return bool - true means user with 'enrol/xxx:unenrol' may unenrol others freely, trues means nobody may touch user_enrolments
972      */
973     public function allow_unenrol(stdClass $instance) {
974         return false;
975     }
977     /**
978      * Does this plugin allow manual changes in user_enrolments table?
979      *
980      * All plugins allowing this must implement 'enrol/xxx:manage' capability
981      *
982      * @param stdClass $instance course enrol instance
983      * @return bool - true means it is possible to change enrol period and status in user_enrolments table
984      */
985     public function allow_manage(stdClass $instance) {
986         return false;
987     }
989     /**
990      * Does this plugin support some way to user to self enrol?
991      *
992      * @param stdClass $instance course enrol instance
993      *
994      * @return bool - true means show "Enrol me in this course" link in course UI
995      */
996     public function show_enrolme_link(stdClass $instance) {
997         return false;
998     }
1000     /**
1001      * Attempt to automatically enrol current user in course without any interaction,
1002      * calling code has to make sure the plugin and instance are active.
1003      *
1004      * This should return either a timestamp in the future or false.
1005      *
1006      * @param stdClass $instance course enrol instance
1007      * @param stdClass $user record
1008      * @return bool|int false means not enrolled, integer means timeend
1009      */
1010     public function try_autoenrol(stdClass $instance) {
1011         global $USER;
1013         return false;
1014     }
1016     /**
1017      * Attempt to automatically gain temporary guest access to course,
1018      * calling code has to make sure the plugin and instance are active.
1019      *
1020      * This should return either a timestamp in the future or false.
1021      *
1022      * @param stdClass $instance course enrol instance
1023      * @param stdClass $user record
1024      * @return bool|int false means no guest access, integer means timeend
1025      */
1026     public function try_guestaccess(stdClass $instance) {
1027         global $USER;
1029         return false;
1030     }
1032     /**
1033      * Enrol user into course via enrol instance.
1034      *
1035      * @param stdClass $instance
1036      * @param int $userid
1037      * @param int $roleid optional role id
1038      * @param int $timestart 0 means unknown
1039      * @param int $timeend 0 means forever
1040      * @param int $status default to ENROL_USER_ACTIVE for new enrolments, no change by default in updates
1041      * @return void
1042      */
1043     public function enrol_user(stdClass $instance, $userid, $roleid = NULL, $timestart = 0, $timeend = 0, $status = NULL) {
1044         global $DB, $USER, $CFG; // CFG necessary!!!
1046         if ($instance->courseid == SITEID) {
1047             throw new coding_exception('invalid attempt to enrol into frontpage course!');
1048         }
1050         $name = $this->get_name();
1051         $courseid = $instance->courseid;
1053         if ($instance->enrol !== $name) {
1054             throw new coding_exception('invalid enrol instance!');
1055         }
1056         $context = get_context_instance(CONTEXT_COURSE, $instance->courseid, MUST_EXIST);
1058         $inserted = false;
1059         if ($ue = $DB->get_record('user_enrolments', array('enrolid'=>$instance->id, 'userid'=>$userid))) {
1060             //only update if timestart or timeend or status are different.
1061             if ($ue->timestart != $timestart or $ue->timeend != $timeend or (!is_null($status) and $ue->status != $status)) {
1062                 $ue->timestart    = $timestart;
1063                 $ue->timeend      = $timeend;
1064                 if (!is_null($status)) {
1065                     $ue->status   = $status;
1066                 }
1067                 $ue->modifierid   = $USER->id;
1068                 $ue->timemodified = time();
1069                 $DB->update_record('user_enrolments', $ue);
1070             }
1071         } else {
1072             $ue = new stdClass();
1073             $ue->enrolid      = $instance->id;
1074             $ue->status       = is_null($status) ? ENROL_USER_ACTIVE : $status;
1075             $ue->userid       = $userid;
1076             $ue->timestart    = $timestart;
1077             $ue->timeend      = $timeend;
1078             $ue->modifierid   = $USER->id;
1079             $ue->timecreated  = time();
1080             $ue->timemodified = $ue->timecreated;
1081             $ue->id = $DB->insert_record('user_enrolments', $ue);
1083             $inserted = true;
1084         }
1086         if ($roleid) {
1087             if ($this->roles_protected()) {
1088                 role_assign($roleid, $userid, $context->id, 'enrol_'.$name, $instance->id);
1089             } else {
1090                 role_assign($roleid, $userid, $context->id);
1091             }
1092         }
1094         if ($inserted) {
1095             // add extra info and trigger event
1096             $ue->courseid  = $courseid;
1097             $ue->enrol     = $name;
1098             events_trigger('user_enrolled', $ue);
1099         }
1101         // reset primitive require_login() caching
1102         if ($userid == $USER->id) {
1103             if (isset($USER->enrol['enrolled'][$courseid])) {
1104                 unset($USER->enrol['enrolled'][$courseid]);
1105             }
1106             if (isset($USER->enrol['tempguest'][$courseid])) {
1107                 unset($USER->enrol['tempguest'][$courseid]);
1108                 $USER->access = remove_temp_roles($context, $USER->access);
1109             }
1110         }
1111     }
1113     /**
1114      * Store user_enrolments changes and trigger event.
1115      *
1116      * @param object $ue
1117      * @param int $user id
1118      * @param int $status
1119      * @param int $timestart
1120      * @param int $timeend
1121      * @return void
1122      */
1123     public function update_user_enrol(stdClass $instance, $userid, $status = NULL, $timestart = NULL, $timeend = NULL) {
1124         global $DB, $USER;
1126         $name = $this->get_name();
1128         if ($instance->enrol !== $name) {
1129             throw new coding_exception('invalid enrol instance!');
1130         }
1132         if (!$ue = $DB->get_record('user_enrolments', array('enrolid'=>$instance->id, 'userid'=>$userid))) {
1133             // weird, user not enrolled
1134             return;
1135         }
1137         $modified = false;
1138         if (isset($status) and $ue->status != $status) {
1139             $ue->status = $status;
1140             $modified = true;
1141         }
1142         if (isset($timestart) and $ue->timestart != $timestart) {
1143             $ue->timestart = $timestart;
1144             $modified = true;
1145         }
1146         if (isset($timeend) and $ue->timeend != $timeend) {
1147             $ue->timeend = $timeend;
1148             $modified = true;
1149         }
1151         if (!$modified) {
1152             // no change
1153             return;
1154         }
1156         $ue->modifierid = $USER->id;
1157         $DB->update_record('user_enrolments', $ue);
1159         // trigger event
1160         $ue->courseid  = $instance->courseid;
1161         $ue->enrol     = $instance->name;
1162         events_trigger('user_unenrol_modified', $ue);
1163     }
1165     /**
1166      * Unenrol user from course,
1167      * the last unenrolment removes all remaining roles.
1168      *
1169      * @param stdClass $instance
1170      * @param int $userid
1171      * @return void
1172      */
1173     public function unenrol_user(stdClass $instance, $userid) {
1174         global $CFG, $USER, $DB;
1176         $name = $this->get_name();
1177         $courseid = $instance->courseid;
1179         if ($instance->enrol !== $name) {
1180             throw new coding_exception('invalid enrol instance!');
1181         }
1182         $context = get_context_instance(CONTEXT_COURSE, $instance->courseid, MUST_EXIST);
1184         if (!$ue = $DB->get_record('user_enrolments', array('enrolid'=>$instance->id, 'userid'=>$userid))) {
1185             // weird, user not enrolled
1186             return;
1187         }
1189         role_unassign_all(array('userid'=>$userid, 'contextid'=>$context->id, 'component'=>'enrol_'.$name, 'itemid'=>$instance->id));
1190         $DB->delete_records('user_enrolments', array('id'=>$ue->id));
1192         // add extra info and trigger event
1193         $ue->courseid  = $courseid;
1194         $ue->enrol     = $name;
1196         $sql = "SELECT 'x'
1197                   FROM {user_enrolments} ue
1198                   JOIN {enrol} e ON (e.id = ue.enrolid)
1199                   WHERE ue.userid = :userid AND e.courseid = :courseid";
1200         if ($DB->record_exists_sql($sql, array('userid'=>$userid, 'courseid'=>$courseid))) {
1201             $ue->lastenrol = false;
1202             events_trigger('user_unenrolled', $ue);
1203             // user still has some enrolments, no big cleanup yet
1204         } else {
1205             // the big cleanup IS necessary!
1207             require_once("$CFG->dirroot/group/lib.php");
1208             require_once("$CFG->libdir/gradelib.php");
1210             // remove all remaining roles
1211             role_unassign_all(array('userid'=>$userid, 'contextid'=>$context->id), true, false);
1213             //clean up ALL invisible user data from course if this is the last enrolment - groups, grades, etc.
1214             groups_delete_group_members($courseid, $userid);
1216             grade_user_unenrol($courseid, $userid);
1218             $DB->delete_records('user_lastaccess', array('userid'=>$userid, 'courseid'=>$courseid));
1220             $ue->lastenrol = true; // means user not enrolled any more
1221             events_trigger('user_unenrolled', $ue);
1222         }
1223         // reset primitive require_login() caching
1224         if ($userid == $USER->id) {
1225             if (isset($USER->enrol['enrolled'][$courseid])) {
1226                 unset($USER->enrol['enrolled'][$courseid]);
1227             }
1228             if (isset($USER->enrol['tempguest'][$courseid])) {
1229                 unset($USER->enrol['tempguest'][$courseid]);
1230                 $USER->access = remove_temp_roles($context, $USER->access);
1231             }
1232         }
1233     }
1235     /**
1236      * Forces synchronisation of user enrolments.
1237      *
1238      * This is important especially for external enrol plugins,
1239      * this function is called for all enabled enrol plugins
1240      * right after every user login.
1241      *
1242      * @param object $user user record
1243      * @return void
1244      */
1245     public function sync_user_enrolments($user) {
1246         // override if necessary
1247     }
1249     /**
1250      * Returns link to page which may be used to add new instance of enrolment plugin in course.
1251      * @param int $courseid
1252      * @return moodle_url page url
1253      */
1254     public function get_newinstance_link($courseid) {
1255         // override for most plugins, check if instance already exists in cases only one instance is supported
1256         return NULL;
1257     }
1259     /**
1260      * Is it possible to delete enrol instance via standard UI?
1261      *
1262      * @param object $instance
1263      * @return bool
1264      */
1265     public function instance_deleteable($instance) {
1266         return true;
1267     }
1269     /**
1270      * Returns link to manual enrol UI if exists.
1271      * Does the access control tests automatically.
1272      *
1273      * @param object $instance
1274      * @return moodle_url
1275      */
1276     public function get_manual_enrol_link($instance) {
1277         return NULL;
1278     }
1280     /**
1281      * Returns list of unenrol links for all enrol instances in course.
1282      *
1283      * @param int $instance
1284      * @return moodle_url or NULL if self unenrolment not supported
1285      */
1286     public function get_unenrolself_link($instance) {
1287         global $USER, $CFG, $DB;
1289         $name = $this->get_name();
1290         if ($instance->enrol !== $name) {
1291             throw new coding_exception('invalid enrol instance!');
1292         }
1294         if ($instance->courseid == SITEID) {
1295             return NULL;
1296         }
1298         if (!enrol_is_enabled($name)) {
1299             return NULL;
1300         }
1302         if ($instance->status != ENROL_INSTANCE_ENABLED) {
1303             return NULL;
1304         }
1306         if (!file_exists("$CFG->dirroot/enrol/$name/unenrolself.php")) {
1307             return NULL;
1308         }
1310         $context = get_context_instance(CONTEXT_COURSE, $instance->courseid, MUST_EXIST);
1312         if (!has_capability("enrol/$name:unenrolself", $context)) {
1313             return NULL;
1314         }
1316         if (!$DB->record_exists('user_enrolments', array('enrolid'=>$instance->id, 'userid'=>$USER->id, 'status'=>ENROL_USER_ACTIVE))) {
1317             return NULL;
1318         }
1320         return new moodle_url("/enrol/$name/unenrolself.php", array('enrolid'=>$instance->id));;
1321     }
1323     /**
1324      * Adds enrol instance UI to course edit form
1325      *
1326      * @param object $instance enrol instance or null if does not exist yet
1327      * @param MoodleQuickForm $mform
1328      * @param object $data
1329      * @param object $context context of existing course or parent category if course does not exist
1330      * @return void
1331      */
1332     public function course_edit_form($instance, MoodleQuickForm $mform, $data, $context) {
1333         // override - usually at least enable/disable switch, has to add own form header
1334     }
1336     /**
1337      * Validates course edit form data
1338      *
1339      * @param object $instance enrol instance or null if does not exist yet
1340      * @param array $data
1341      * @param object $context context of existing course or parent category if course does not exist
1342      * @return array errors array
1343      */
1344     public function course_edit_validation($instance, array $data, $context) {
1345         return array();
1346     }
1348     /**
1349      * Called after updating/inserting course.
1350      *
1351      * @param bool $inserted true if course just inserted
1352      * @param object $course
1353      * @param object $data form data
1354      * @return void
1355      */
1356     public function course_updated($inserted, $course, $data) {
1357         if ($inserted) {
1358             if ($this->get_config('defaultenrol')) {
1359                 $this->add_default_instance($course);
1360             }
1361         }
1362     }
1364     /**
1365      * Add new instance of enrol plugin.
1366      * @param object $course
1367      * @param array instance fields
1368      * @return int id of new instance, null if can not be created
1369      */
1370     public function add_instance($course, array $fields = NULL) {
1371         global $DB;
1373         if ($course->id == SITEID) {
1374             throw new coding_exception('Invalid request to add enrol instance to frontpage.');
1375         }
1377         $instance = new stdClass();
1378         $instance->enrol          = $this->get_name();
1379         $instance->status         = ENROL_INSTANCE_ENABLED;
1380         $instance->courseid       = $course->id;
1381         $instance->enrolstartdate = 0;
1382         $instance->enrolenddate   = 0;
1383         $instance->timemodified   = time();
1384         $instance->timecreated    = $instance->timemodified;
1385         $instance->sortorder      = $DB->get_field('enrol', 'COALESCE(MAX(sortorder), -1) + 1', array('courseid'=>$course->id));
1387         $fields = (array)$fields;
1388         unset($fields['enrol']);
1389         unset($fields['courseid']);
1390         unset($fields['sortorder']);
1391         foreach($fields as $field=>$value) {
1392             $instance->$field = $value;
1393         }
1395         return $DB->insert_record('enrol', $instance);
1396     }
1398     /**
1399      * Add new instance of enrol plugin with default settings,
1400      * called when adding new instance manually or when adding new course.
1401      *
1402      * Not all plugins support this.
1403      *
1404      * @param object $course
1405      * @return int id of new instance or null if no default supported
1406      */
1407     public function add_default_instance($course) {
1408         return null;
1409     }
1411     /**
1412      * Delete course enrol plugin instance, unenrol all users.
1413      * @param object $instance
1414      * @return void
1415      */
1416     public function delete_instance($instance) {
1417         global $DB;
1419         $name = $this->get_name();
1420         if ($instance->enrol !== $name) {
1421             throw new coding_exception('invalid enrol instance!');
1422         }
1424         //first unenrol all users
1425         $participants = $DB->get_recordset('user_enrolments', array('enrolid'=>$instance->id));
1426         foreach ($participants as $participant) {
1427             $this->unenrol_user($instance, $participant->userid);
1428         }
1429         $participants->close();
1431         // now clean up all remainders that were not removed correctly
1432         $DB->delete_records('role_assignments', array('itemid'=>$instance->id, 'component'=>$name));
1433         $DB->delete_records('user_enrolments', array('enrolid'=>$instance->id));
1435         // finally drop the enrol row
1436         $DB->delete_records('enrol', array('id'=>$instance->id));
1437     }
1439     /**
1440      * Creates course enrol form, checks if form submitted
1441      * and enrols user if necessary. It can also redirect.
1442      *
1443      * @param stdClass $instance
1444      * @return string html text, usually a form in a text box
1445      */
1446     public function enrol_page_hook(stdClass $instance) {
1447         return null;
1448     }
1450     /**
1451      * Adds navigation links into course admin block.
1452      *
1453      * By defaults looks for manage links only.
1454      *
1455      * @param navigation_node $instancesnode
1456      * @param object $instance
1457      * @return void
1458      */
1459     public function add_course_navigation($instancesnode, stdClass $instance) {
1460         // usually adds manage users
1461     }
1463     /**
1464      * Returns edit icons for the page with list of instances
1465      * @param stdClass $instance
1466      * @return array
1467      */
1468     public function get_action_icons(stdClass $instance) {
1469         return array();
1470     }
1472     /**
1473      * Reads version.php and determines if it is necessary
1474      * to execute the cron job now.
1475      * @return bool
1476      */
1477     public function is_cron_required() {
1478         global $CFG;
1480         $name = $this->get_name();
1481         $versionfile = "$CFG->dirroot/enrol/$name/version.php";
1482         $plugin = new stdClass();
1483         include($versionfile);
1484         if (empty($plugin->cron)) {
1485             return false;
1486         }
1487         $lastexecuted = $this->get_config('lastcron', 0);
1488         if ($lastexecuted + $plugin->cron < time()) {
1489             return true;
1490         } else {
1491             return false;
1492         }
1493     }
1495     /**
1496      * Called for all enabled enrol plugins that returned true from is_cron_required().
1497      * @return void
1498      */
1499     public function cron() {
1500     }
1502     /**
1503      * Called when user is about to be deleted
1504      * @param object $user
1505      * @return void
1506      */
1507     public function user_delete($user) {
1508         global $DB;
1510         $sql = "SELECT e.*
1511                   FROM {enrol} e
1512                   JOIN {user_enrolments} ue ON (ue.enrolid = e.id)
1513                  WHERE e.enrol = :name AND ue.userid = :userid";
1514         $params = array('name'=>$this->get_name(), 'userid'=>$user->id);
1516         $rs = $DB->get_recordset_sql($sql, $params);
1517         foreach($rs as $instance) {
1518             $this->unenrol_user($instance, $user->id);
1519         }
1520         $rs->close();
1521     }
1523     /**
1524      * Returns an enrol_user_button that takes the user to a page where they are able to
1525      * enrol users into the managers course through this plugin.
1526      *
1527      * Optional: If the plugin supports manual enrolments it can choose to override this
1528      * otherwise it shouldn't
1529      *
1530      * @param course_enrolment_manager $manager
1531      * @return enrol_user_button|false
1532      */
1533     public function get_manual_enrol_button(course_enrolment_manager $manager) {
1534         return false;
1535     }
1537     /**
1538      * Gets an array of the user enrolment actions
1539      *
1540      * @param course_enrolment_manager $manager
1541      * @param stdClass $ue
1542      * @return array An array of user_enrolment_actions
1543      */
1544     public function get_user_enrolment_actions(course_enrolment_manager $manager, $ue) {
1545         return array();
1546     }
1548     /**
1549      * Returns true if the plugin has one or more bulk operations that can be performed on
1550      * user enrolments.
1551      *
1552      * @return bool
1553      */
1554     public function has_bulk_operations() {
1555        return false;
1556     }
1558     /**
1559      * Return an array of enrol_bulk_enrolment_operation objects that define
1560      * the bulk actions that can be performed on user enrolments by the plugin.
1561      *
1562      * @return array
1563      */
1564     public function get_bulk_operations() {
1565         return array();
1566     }