Merge branch 'wip-MDL-29487-master' of git://github.com/samhemelryk/moodle
[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
120 if ($name !== clean_param($name, PARAM_SAFEDIR)) {
121 // ignore malformed plugin names completely
122 return null;
123 }
124
125 $location = "$CFG->dirroot/enrol/$name";
126
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 }
135
136 return new $class();
137}
138
139/**
140 * Returns enrolment instances in given course.
141 * @param int $courseid
142 * @param bool $enabled
143 * @return array of enrol instances
144 */
145function enrol_get_instances($courseid, $enabled) {
146 global $DB, $CFG;
147
148 if (!$enabled) {
149 return $DB->get_records('enrol', array('courseid'=>$courseid), 'sortorder,id');
150 }
151
152 $result = $DB->get_records('enrol', array('courseid'=>$courseid, 'status'=>ENROL_INSTANCE_ENABLED), 'sortorder,id');
153
79721bd3 154 $enabled = explode(',', $CFG->enrol_plugins_enabled);
df997f84
PS
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 }
166
167 return $result;
168}
169
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 */
176function enrol_is_enabled($enrol) {
177 global $CFG;
178
179 if (empty($CFG->enrol_plugins_enabled)) {
180 return false;
181 }
182 return in_array($enrol, explode(',', $CFG->enrol_plugins_enabled));
183}
184
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 */
192function enrol_check_plugins($user) {
193 global $CFG;
194
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 }
199
e384d2dc
PS
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 }
205
df997f84
PS
206 static $inprogress = array(); // To prevent this function being called more than once in an invocation
207
208 if (!empty($inprogress[$user->id])) {
209 return;
210 }
211
212 $inprogress[$user->id] = true; // Set the flag
213
214 $enabled = enrol_get_plugins(true);
215
216 foreach($enabled as $enrol) {
217 $enrol->sync_user_enrolments($user);
218 }
219
220 unset($inprogress[$user->id]); // Unset the flag
221}
222
181991e7
PS
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 */
233function enrol_sharing_course($user1, $user2) {
234 global $DB, $CFG;
235
236 $user1 = !empty($user1->id) ? $user1->id : $user1;
237 $user2 = !empty($user2->id) ? $user2->id : $user2;
238
239 if (empty($user1) or empty($user2)) {
240 return false;
241 }
242
243 if (!$plugins = explode(',', $CFG->enrol_plugins_enabled)) {
244 return false;
245 }
246
cf717dc2 247 list($plugins, $params) = $DB->get_in_or_equal($plugins, SQL_PARAMS_NAMED, 'ee');
181991e7
PS
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;
253
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)
9d796fba 257 JOIN {user_enrolments} ue2 ON (ue2.enrolid = e.id AND ue2.status = :active2 AND ue2.userid = :user2)
181991e7
PS
258 JOIN {course} c ON (c.id = e.courseid AND c.visible = 1)
259 WHERE e.status = :enabled AND e.enrol $plugins";
260
261 return $DB->record_exists_sql($sql, $params);
262}
263
df997f84
PS
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 */
272function 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 }
288}
289
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 */
297function enrol_course_edit_validation(array $data, $context) {
298 $errors = array();
299 $plugins = enrol_get_plugins(true);
300
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 }
315
316 return $errors;
317}
318
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 */
326function enrol_course_updated($inserted, $course, $data) {
327 global $DB, $CFG;
328
329 $plugins = enrol_get_plugins(true);
330
331 foreach ($plugins as $plugin) {
332 $plugin->course_updated($inserted, $course, $data);
333 }
334}
335
336/**
337 * Add navigation nodes
338 * @param navigation_node $coursenode
339 * @param object $course
340 * @return void
341 */
342function enrol_add_course_navigation(navigation_node $coursenode, $course) {
6e4c374d 343 global $CFG;
df997f84
PS
344
345 $coursecontext = get_context_instance(CONTEXT_COURSE, $course->id);
346
347 $instances = enrol_get_instances($course->id, true);
348 $plugins = enrol_get_plugins(true);
349
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 }
356
f5ce6b71 357 $usersnode = $coursenode->add(get_string('users'), null, navigation_node::TYPE_CONTAINER, null, 'users');
df997f84
PS
358
359 if ($course->id != SITEID) {
f5ce6b71 360 // list all participants - allows assigning roles, groups, etc.
df997f84
PS
361 if (has_capability('moodle/course:enrolreview', $coursecontext)) {
362 $url = new moodle_url('/enrol/users.php', array('id'=>$course->id));
f5ce6b71 363 $usersnode->add(get_string('enrolledusers', 'enrol'), $url, navigation_node::TYPE_SETTING, null, 'review', new pix_icon('i/users', ''));
df997f84
PS
364 }
365
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 }
f5ce6b71 372 $instancesnode = $usersnode->add(get_string('enrolmentinstances', 'enrol'), $url, navigation_node::TYPE_SETTING, null, 'manageinstances');
df997f84
PS
373
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 }
381
382 if (!$url) {
383 $instancesnode->trim_if_empty();
384 }
385 }
386
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 }
392
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 }
f5ce6b71 400 $permissionsnode = $usersnode->add(get_string('permissions', 'role'), $url, navigation_node::TYPE_SETTING, null, 'override');
df997f84
PS
401
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));
f5ce6b71 406 $permissionsnode->add(get_string('assignedroles', 'role'), $url, navigation_node::TYPE_SETTING, null, 'roles', new pix_icon('i/roles', ''));
df997f84
PS
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));
f5ce6b71 412 $permissionsnode->add(get_string('checkpermissions', 'role'), $url, navigation_node::TYPE_SETTING, null, 'permissions', new pix_icon('i/checkpermissions', ''));
df997f84
PS
413 }
414 }
415
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));
f5ce6b71 421 $usersnode->add(get_string('notenrolledusers', 'enrol'), $url, navigation_node::TYPE_SETTING, null, 'otherusers', new pix_icon('i/roles', ''));
df997f84
PS
422 }
423 }
424
425 // just in case nothing was actually added
426 $usersnode->trim_if_empty();
427
428 if ($course->id != SITEID) {
429 // Unenrol link
217d0397
PS
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)) {
8ebbb06a
SH
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', ''));
217d0397
PS
439 break;
440 //TODO. deal with multiple unenrol links - not likely case, but still...
441 }
df997f84 442 }
217d0397
PS
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));
8ebbb06a
SH
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', ''));
217d0397
PS
456 break;
457 }
458 }
df997f84
PS
459 }
460 }
df997f84
PS
461 }
462}
463
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 *
eb6f592a 471 * @param string|array $fields
df997f84
PS
472 * @param string $sort
473 * @param int $limit max number of courses
474 * @return array
475 */
476function enrol_get_my_courses($fields = NULL, $sort = 'visible DESC,sortorder ASC', $limit = 0) {
477 global $DB, $USER;
478
479 // Guest account does not have any courses
480 if (isguestuser() or !isloggedin()) {
481 return(array());
482 }
483
484 $basefields = array('id', 'category', 'sortorder',
485 'shortname', 'fullname', 'idnumber',
486 'startdate', 'visible',
487 'groupmode', 'groupmodeforce');
488
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 }
504
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 }
520
521 $wheres = array("c.id <> :siteid");
522 $params = array('siteid'=>SITEID);
523
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 }
529
530 $coursefields = 'c.' .join(',c.', $fields);
531 list($ccselect, $ccjoin) = context_instance_preload_sql('c.id', CONTEXT_COURSE, 'ctx');
4129338c 532 $wheres = implode(" AND ", $wheres);
df997f84 533
4129338c
PS
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
df997f84 536 FROM {course} c
4129338c
PS
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)
df997f84 542 $ccjoin
4129338c 543 WHERE $wheres
df997f84
PS
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'];
550
551 $courses = $DB->get_records_sql($sql, $params, 0, $limit);
552
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)) {
55880bdd 558 unset($courses[$id]);
df997f84
PS
559 continue;
560 }
561 if (!has_capability('moodle/course:viewhiddencourses', $context)) {
55880bdd 562 unset($courses[$id]);
df997f84
PS
563 continue;
564 }
565 }
566 $courses[$id] = $course;
567 }
568
569 //wow! Is that really all? :-D
570
571 return $courses;
572}
573
bf423bb1
PS
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 */
581function 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;
603}
604
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 */
611function 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) {
64942a9d 616 if (!isset($plugins[$instance->enrol])) {
bf423bb1
PS
617 //weird
618 continue;
619 }
64942a9d 620 $plugin = $plugins[$instance->enrol];
bf423bb1
PS
621 $text = $plugin->get_description_text($instance);
622 if ($text !== NULL) {
623 $lines[] = $text;
624 }
625 }
626 return $lines;
627}
628
df997f84
PS
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
eb6f592a 638 * @param string|array $fields
df997f84
PS
639 * @param string $sort
640 * @return array
641 */
642function enrol_get_users_courses($userid, $onlyactive = false, $fields = NULL, $sort = 'visible DESC,sortorder ASC') {
643 global $DB;
644
645 // Guest account does not have any courses
87163782 646 if (isguestuser($userid) or empty($userid)) {
df997f84
PS
647 return(array());
648 }
649
650 $basefields = array('id', 'category', 'sortorder',
651 'shortname', 'fullname', 'idnumber',
652 'startdate', 'visible',
653 'groupmode', 'groupmodeforce');
654
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 }
670
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 }
686
df997f84
PS
687 $params = array('siteid'=>SITEID);
688
689 if ($onlyactive) {
4129338c 690 $subwhere = "WHERE ue.status = :active AND e.status = :enabled AND ue.timestart < :now1 AND (ue.timeend = 0 OR ue.timeend > :now2)";
df997f84
PS
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;
4129338c
PS
695 } else {
696 $subwhere = "";
df997f84
PS
697 }
698
699 $coursefields = 'c.' .join(',c.', $fields);
700 list($ccselect, $ccjoin) = context_instance_preload_sql('c.id', CONTEXT_COURSE, 'ctx');
df997f84 701
4129338c
PS
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
df997f84 704 FROM {course} c
4129338c
PS
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)
df997f84 710 $ccjoin
4129338c 711 WHERE c.id <> :siteid
df997f84 712 $orderby";
87163782 713 $params['userid'] = $userid;
df997f84
PS
714
715 $courses = $DB->get_records_sql($sql, $params);
716
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)) {
4a5aba7c 723 unset($courses[$id]);
df997f84
PS
724 continue;
725 }
726 if (!has_capability('moodle/course:viewhiddencourses', $context, $userid)) {
4a5aba7c 727 unset($courses[$id]);
df997f84
PS
728 continue;
729 }
730 }
731 }
732 $courses[$id] = $course;
733 }
734
735 //wow! Is that really all? :-D
736
737 return $courses;
738
739}
740
741/**
742 * Called when user is about to be deleted.
743 * @param object $user
744 * @return void
745 */
746function enrol_user_delete($user) {
747 global $DB;
748
749 $plugins = enrol_get_plugins(true);
750 foreach ($plugins as $plugin) {
751 $plugin->user_delete($user);
752 }
753
754 // force cleanup of all broken enrolments
755 $DB->delete_records('user_enrolments', array('userid'=>$user->id));
756}
757
582bae08
PS
758/**
759 * Called when course is about to be deleted.
760 * @param stdClass $object
761 * @return void
762 */
763function enrol_course_delete($course) {
764 global $DB;
765
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 }
778}
779
df997f84
PS
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 */
792function enrol_try_internal_enrol($courseid, $userid, $roleid = null, $timestart = 0, $timeend = 0) {
793 global $DB;
794
795 //note: this is hardcoded to manual plugin for now
796
797 if (!enrol_is_enabled('manual')) {
798 return false;
799 }
800
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);
808
809 $enrol->enrol_user($instance, $userid, $roleid, $timestart, $timeend);
810
811 return true;
812}
813
45ff8a80
PS
814/**
815 * Is there a chance users might self enrol
816 * @param int $courseid
817 * @return bool
818 */
819function enrol_selfenrol_available($courseid) {
820 $result = false;
821
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 }
837
838 return $result;
839}
840
df997f84
PS
841/**
842 * All enrol plugins should be based on this class,
843 * this is also the main source of documentation.
844 */
845abstract class enrol_plugin {
846 protected $config = null;
847
848 /**
849 * Returns name of this enrol plugin
850 * @return string
851 */
852 public function get_name() {
bf423bb1 853 // second word in class is always enrol name, sorry, no fancy plugin names with _
df997f84
PS
854 $words = explode('_', get_class($this));
855 return $words[1];
856 }
857
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 {
54475ccb
PS
869 $context = get_context_instance(CONTEXT_COURSE, $instance->courseid);
870 return format_string($instance->name, true, array('context'=>$context));
df997f84
PS
871 }
872 }
873
bf423bb1
PS
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 }
889
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 }
902
df997f84
PS
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")) {
365a5941 911 $config = new stdClass();
df997f84
PS
912 }
913 $this->config = $config;
914 }
915 }
916
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 }
927
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) {
47811589 935 $pluginname = $this->get_name();
df997f84
PS
936 $this->load_config();
937 if ($value === NULL) {
938 unset($this->config->$name);
939 } else {
940 $this->config->$name = $value;
941 }
47811589 942 set_config($name, $value, "enrol_$pluginname");
df997f84
PS
943 }
944
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 }
952
91b99e80
PS
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 }
964
df997f84
PS
965 /**
966 * Does this plugin allow manual unenrolments?
967 *
968 * @param stdClass $instance course enrol instance
91b99e80 969 * All plugins allowing this must implement 'enrol/xxx:unenrol' capability
df997f84 970 *
91b99e80 971 * @return bool - true means user with 'enrol/xxx:unenrol' may unenrol others freely, trues means nobody may touch user_enrolments
df997f84
PS
972 */
973 public function allow_unenrol(stdClass $instance) {
974 return false;
975 }
976
977 /**
978 * Does this plugin allow manual changes in user_enrolments table?
979 *
91b99e80 980 * All plugins allowing this must implement 'enrol/xxx:manage' capability
df997f84
PS
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 }
988
217d0397
PS
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 }
999
df997f84
PS
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 *
ed1d72ea
SH
1004 * This should return either a timestamp in the future or false.
1005 *
df997f84
PS
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;
1012
1013 return false;
1014 }
1015
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 *
ed1d72ea
SH
1020 * This should return either a timestamp in the future or false.
1021 *
df997f84
PS
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;
1028
1029 return false;
1030 }
1031
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
2a6dcb72
PS
1038 * @param int $timestart 0 means unknown
1039 * @param int $timeend 0 means forever
f2a9be5f 1040 * @param int $status default to ENROL_USER_ACTIVE for new enrolments, no change by default in updates
df997f84
PS
1041 * @return void
1042 */
f2a9be5f 1043 public function enrol_user(stdClass $instance, $userid, $roleid = NULL, $timestart = 0, $timeend = 0, $status = NULL) {
df997f84
PS
1044 global $DB, $USER, $CFG; // CFG necessary!!!
1045
1046 if ($instance->courseid == SITEID) {
1047 throw new coding_exception('invalid attempt to enrol into frontpage course!');
1048 }
1049
1050 $name = $this->get_name();
1051 $courseid = $instance->courseid;
1052
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);
1057
1058 $inserted = false;
1059 if ($ue = $DB->get_record('user_enrolments', array('enrolid'=>$instance->id, 'userid'=>$userid))) {
d587f077 1060 //only update if timestart or timeend or status are different.
ae8c1f3d 1061 if ($ue->timestart != $timestart or $ue->timeend != $timeend or (!is_null($status) and $ue->status != $status)) {
df997f84
PS
1062 $ue->timestart = $timestart;
1063 $ue->timeend = $timeend;
d587f077 1064 if (!is_null($status)) {
ae8c1f3d 1065 $ue->status = $status;
d587f077 1066 }
6006774c 1067 $ue->modifierid = $USER->id;
df997f84
PS
1068 $ue->timemodified = time();
1069 $DB->update_record('user_enrolments', $ue);
1070 }
1071 } else {
365a5941 1072 $ue = new stdClass();
df997f84 1073 $ue->enrolid = $instance->id;
ae8c1f3d 1074 $ue->status = is_null($status) ? ENROL_USER_ACTIVE : $status;
df997f84
PS
1075 $ue->userid = $userid;
1076 $ue->timestart = $timestart;
1077 $ue->timeend = $timeend;
6006774c 1078 $ue->modifierid = $USER->id;
2a6dcb72
PS
1079 $ue->timecreated = time();
1080 $ue->timemodified = $ue->timecreated;
df997f84
PS
1081 $ue->id = $DB->insert_record('user_enrolments', $ue);
1082
1083 $inserted = true;
1084 }
1085
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 }
1093
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 }
1100
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 }
1112
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;
1125
1126 $name = $this->get_name();
1127
1128 if ($instance->enrol !== $name) {
1129 throw new coding_exception('invalid enrol instance!');
1130 }
1131
1132 if (!$ue = $DB->get_record('user_enrolments', array('enrolid'=>$instance->id, 'userid'=>$userid))) {
1133 // weird, user not enrolled
1134 return;
1135 }
1136
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 }
1150
1151 if (!$modified) {
1152 // no change
1153 return;
1154 }
1155
1156 $ue->modifierid = $USER->id;
1157 $DB->update_record('user_enrolments', $ue);
1158
1159 // trigger event
1160 $ue->courseid = $instance->courseid;
1161 $ue->enrol = $instance->name;
1162 events_trigger('user_unenrol_modified', $ue);
1163 }
1164
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;
1175
1176 $name = $this->get_name();
1177 $courseid = $instance->courseid;
1178
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);
1183
1184 if (!$ue = $DB->get_record('user_enrolments', array('enrolid'=>$instance->id, 'userid'=>$userid))) {
1185 // weird, user not enrolled
1186 return;
1187 }
1188
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));
1191
1192 // add extra info and trigger event
1193 $ue->courseid = $courseid;
1194 $ue->enrol = $name;
1195
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!
1206
1207 require_once("$CFG->dirroot/group/lib.php");
1208 require_once("$CFG->libdir/gradelib.php");
1209
1210 // remove all remaining roles
1211 role_unassign_all(array('userid'=>$userid, 'contextid'=>$context->id), true, false);
1212
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);
1215
1216 grade_user_unenrol($courseid, $userid);
1217
1218 $DB->delete_records('user_lastaccess', array('userid'=>$userid, 'courseid'=>$courseid));
1219
7d2800fb 1220 $ue->lastenrol = true; // means user not enrolled any more
df997f84
PS
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 }
1234
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 }
1248
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 */
e25f2466 1254 public function get_newinstance_link($courseid) {
df997f84
PS
1255 // override for most plugins, check if instance already exists in cases only one instance is supported
1256 return NULL;
1257 }
1258
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 }
1268
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 }
1279
1280 /**
1281 * Returns list of unenrol links for all enrol instances in course.
1282 *
217d0397 1283 * @param int $instance
bf423bb1 1284 * @return moodle_url or NULL if self unenrolment not supported
df997f84
PS
1285 */
1286 public function get_unenrolself_link($instance) {
1287 global $USER, $CFG, $DB;
1288
1289 $name = $this->get_name();
1290 if ($instance->enrol !== $name) {
1291 throw new coding_exception('invalid enrol instance!');
1292 }
1293
1294 if ($instance->courseid == SITEID) {
1295 return NULL;
1296 }
1297
1298 if (!enrol_is_enabled($name)) {
1299 return NULL;
1300 }
1301
1302 if ($instance->status != ENROL_INSTANCE_ENABLED) {
1303 return NULL;
1304 }
1305
df997f84
PS
1306 if (!file_exists("$CFG->dirroot/enrol/$name/unenrolself.php")) {
1307 return NULL;
1308 }
1309
217d0397
PS
1310 $context = get_context_instance(CONTEXT_COURSE, $instance->courseid, MUST_EXIST);
1311
df997f84
PS
1312 if (!has_capability("enrol/$name:unenrolself", $context)) {
1313 return NULL;
1314 }
1315
1316 if (!$DB->record_exists('user_enrolments', array('enrolid'=>$instance->id, 'userid'=>$USER->id, 'status'=>ENROL_USER_ACTIVE))) {
1317 return NULL;
1318 }
1319
1320 return new moodle_url("/enrol/$name/unenrolself.php", array('enrolid'=>$instance->id));;
1321 }
1322
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 }
1335
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 }
1347
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) {
eafb7a72
PS
1357 if ($inserted) {
1358 if ($this->get_config('defaultenrol')) {
1359 $this->add_default_instance($course);
1360 }
1361 }
df997f84
PS
1362 }
1363
1364 /**
eafb7a72 1365 * Add new instance of enrol plugin.
df997f84
PS
1366 * @param object $course
1367 * @param array instance fields
0848a196 1368 * @return int id of new instance, null if can not be created
df997f84
PS
1369 */
1370 public function add_instance($course, array $fields = NULL) {
1371 global $DB;
1372
1373 if ($course->id == SITEID) {
1374 throw new coding_exception('Invalid request to add enrol instance to frontpage.');
1375 }
1376
365a5941 1377 $instance = new stdClass();
df997f84
PS
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));
1386
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 }
1394
1395 return $DB->insert_record('enrol', $instance);
1396 }
1397
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 }
1410
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;
1418
1419 $name = $this->get_name();
1420 if ($instance->enrol !== $name) {
1421 throw new coding_exception('invalid enrol instance!');
1422 }
1423
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();
1430
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));
1434
1435 // finally drop the enrol row
1436 $DB->delete_records('enrol', array('id'=>$instance->id));
1437 }
1438
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 }
1449
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
2d4b1f3e 1457 * @return void
df997f84
PS
1458 */
1459 public function add_course_navigation($instancesnode, stdClass $instance) {
2d4b1f3e 1460 // usually adds manage users
df997f84
PS
1461 }
1462
1463 /**
2d4b1f3e
PS
1464 * Returns edit icons for the page with list of instances
1465 * @param stdClass $instance
1466 * @return array
df997f84 1467 */
2d4b1f3e
PS
1468 public function get_action_icons(stdClass $instance) {
1469 return array();
df997f84
PS
1470 }
1471
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;
1479
1480 $name = $this->get_name();
1481 $versionfile = "$CFG->dirroot/enrol/$name/version.php";
365a5941 1482 $plugin = new stdClass();
df997f84
PS
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 }
1494
1495 /**
1496 * Called for all enabled enrol plugins that returned true from is_cron_required().
1497 * @return void
1498 */
1499 public function cron() {
1500 }
1501
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;
1509
1510 $sql = "SELECT e.*
1511 FROM {enrol} e
45fb2cf8
PS
1512 JOIN {user_enrolments} ue ON (ue.enrolid = e.id)
1513 WHERE e.enrol = :name AND ue.userid = :userid";
df997f84
PS
1514 $params = array('name'=>$this->get_name(), 'userid'=>$user->id);
1515
45fb2cf8 1516 $rs = $DB->get_recordset_sql($sql, $params);
df997f84
PS
1517 foreach($rs as $instance) {
1518 $this->unenrol_user($instance, $user->id);
1519 }
1520 $rs->close();
1521 }
df997f84 1522
b69ca6be
SH
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 }
291215f4
SH
1536
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) {
12a52d7c 1545 return array();
291215f4 1546 }
75ee207b
SH
1547
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 }
1557
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 }
291215f4 1567}