Commit | Line | Data |
---|---|---|
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 | 28 | defined('MOODLE_INTERNAL') || die(); |
df997f84 PS |
29 | |
30 | /** Course enrol instance enabled. (used in enrol->status) */ | |
31 | define('ENROL_INSTANCE_ENABLED', 0); | |
32 | ||
33 | /** Course enrol instance disabled, user may enter course if other enrol instance enabled. (used in enrol->status)*/ | |
34 | define('ENROL_INSTANCE_DISABLED', 1); | |
35 | ||
36 | /** User is active participant (used in user_enrolments->status)*/ | |
37 | define('ENROL_USER_ACTIVE', 0); | |
38 | ||
39 | /** User participation in course is suspended (used in user_enrolments->status) */ | |
40 | define('ENROL_USER_SUSPENDED', 1); | |
41 | ||
42 | /** Enrol info is cached for this number of seconds in require_login() */ | |
43 | define('ENROL_REQUIRE_LOGIN_CACHE_PERIOD', 1800); | |
44 | ||
34121765 PS |
45 | /** When user disappears from external source, the enrolment is completely removed */ |
46 | define('ENROL_EXT_REMOVED_UNENROL', 0); | |
47 | ||
48 | /** When user disappears from external source, the enrolment is kept as is - one way sync */ | |
49 | define('ENROL_EXT_REMOVED_KEEP', 1); | |
50 | ||
f2a9be5f EL |
51 | /** enrol plugin feature describing requested restore type */ |
52 | define('ENROL_RESTORE_TYPE', 'enrolrestore'); | |
53 | /** User custom backup/restore class stored in backup/moodle2/ subdirectory */ | |
54 | define('ENROL_RESTORE_CLASS', 'class'); | |
55 | /** Restore all custom fields from enrol table without any changes and all user_enrolments records */ | |
56 | define('ENROL_RESTORE_EXACT', 'exact'); | |
57 | /** Restore enrol record like ENROL_RESTORE_EXACT, but no user enrolments */ | |
58 | define('ENROL_RESTORE_NOUSERS', 'nousers'); | |
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 | */ | |
65 | define('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 | * */ | |
71 | define('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 | */ | |
78 | function 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 | */ | |
117 | function 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 | */ | |
147 | function 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 | */ | |
178 | function 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 | */ | |
194 | function 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 | */ | |
235 | function 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 | */ | |
276 | function 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 | */ | |
330 | function 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 | */ | |
355 | function 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 | */ | |
384 | function 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 | */ | |
400 | function 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 | */ | |
534 | function 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 | */ | |
639 | function 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 | */ | |
669 | function 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 | */ | |
700 | function 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 | */ | |
804 | function 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 | */ | |
821 | function 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 | */ | |
850 | function 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 | */ | |
877 | function 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 | */ | |
903 | abstract 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 | } |