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