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