weekly release 2.6dev
[moodle.git] / enrol / meta / locallib.php
CommitLineData
df997f84 1<?php
df997f84
PS
2// This file is part of Moodle - http://moodle.org/
3//
4// Moodle is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// Moodle is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
16
17/**
18 * Local stuff for meta course enrolment plugin.
19 *
31ac2aef 20 * @package enrol_meta
e2382027
PS
21 * @copyright 2010 Petr Skoda {@link http://skodak.org}
22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
df997f84
PS
23 */
24
e2382027 25defined('MOODLE_INTERNAL') || die();
df997f84 26
ad68ed74 27
df997f84
PS
28/**
29 * Event handler for meta enrolment plugin.
30 *
31 * We try to keep everything in sync via listening to events,
32 * it may fail sometimes, so we always do a full sync in cron too.
33 */
34class enrol_meta_handler {
ad68ed74
PS
35
36 /**
37 * Synchronise meta enrolments of this user in this course
38 * @static
39 * @param int $courseid
40 * @param int $userid
41 * @return void
42 */
43 protected static function sync_course_instances($courseid, $userid) {
df997f84
PS
44 global $DB;
45
ad68ed74
PS
46 static $preventrecursion = false;
47
48 // does anything want to sync with this parent?
49 if (!$enrols = $DB->get_records('enrol', array('customint1'=>$courseid, 'enrol'=>'meta'), 'id ASC')) {
50 return;
51 }
52
53 if ($preventrecursion) {
54 return;
55 }
56
57 $preventrecursion = true;
58
59 try {
60 foreach ($enrols as $enrol) {
61 self::sync_with_parent_course($enrol, $userid);
62 }
63 } catch (Exception $e) {
64 $preventrecursion = false;
65 throw $e;
66 }
67
68 $preventrecursion = false;
69 }
70
71 /**
72 * Synchronise user enrolments in given instance as fast as possible.
73 *
74 * All roles are removed if the meta plugin disabled.
75 *
76 * @static
77 * @param stdClass $instance
78 * @param int $userid
79 * @return void
80 */
81 protected static function sync_with_parent_course(stdClass $instance, $userid) {
82 global $DB, $CFG;
83
84 $plugin = enrol_get_plugin('meta');
85
86 if ($instance->customint1 == $instance->courseid) {
87 // can not sync with self!!!
88 return;
89 }
90
91 $context = context_course::instance($instance->courseid);
92
93 if (!$parentcontext = context_course::instance($instance->customint1, IGNORE_MISSING)) {
94 // linking to missing course is not possible
95 role_unassign_all(array('userid'=>$userid, 'contextid'=>$context->id, 'component'=>'enrol_meta'));
96 return;
97 }
98
99 // list of enrolments in parent course (we ignore meta enrols in parents completely)
100 list($enabled, $params) = $DB->get_in_or_equal(explode(',', $CFG->enrol_plugins_enabled), SQL_PARAMS_NAMED, 'e');
101 $params['userid'] = $userid;
102 $params['parentcourse'] = $instance->customint1;
103 $sql = "SELECT ue.*
104 FROM {user_enrolments} ue
105 JOIN {enrol} e ON (e.id = ue.enrolid AND e.enrol <> 'meta' AND e.courseid = :parentcourse AND e.enrol $enabled)
106 WHERE ue.userid = :userid";
107 $parentues = $DB->get_records_sql($sql, $params);
108 // current enrolments for this instance
109 $ue = $DB->get_record('user_enrolments', array('enrolid'=>$instance->id, 'userid'=>$userid));
110
111 // first deal with users that are not enrolled in parent
112 if (empty($parentues)) {
113 self::user_not_supposed_to_be_here($instance, $ue, $context, $plugin);
114 return;
115 }
116
df997f84 117 if (!enrol_is_enabled('meta')) {
ad68ed74
PS
118 if ($ue) {
119 role_unassign_all(array('userid'=>$userid, 'contextid'=>$context->id, 'component'=>'enrol_meta'));
120 }
121 return;
df997f84
PS
122 }
123
ad68ed74
PS
124 $skiproles = $plugin->get_config('nosyncroleids', '');
125 $skiproles = empty($skiproles) ? array() : explode(',', $skiproles);
126 $syncall = $plugin->get_config('syncall', 1);
127
128 // roles in parent course (meta enrols must be ignored!)
129 $parentroles = array();
130 list($ignoreroles, $params) = $DB->get_in_or_equal($skiproles, SQL_PARAMS_NAMED, 'ri', false, -1);
131 $params['contextid'] = $parentcontext->id;
132 $params['userid'] = $userid;
133 $select = "contextid = :contextid AND userid = :userid AND component <> 'enrol_meta' AND roleid $ignoreroles";
134 foreach($DB->get_records_select('role_assignments', $select, $params) as $ra) {
135 $parentroles[$ra->roleid] = $ra->roleid;
df997f84
PS
136 }
137
ad68ed74
PS
138 // roles from this instance
139 $roles = array();
140 $ras = $DB->get_records('role_assignments', array('contextid'=>$context->id, 'userid'=>$userid, 'component'=>'enrol_meta', 'itemid'=>$instance->id));
141 foreach($ras as $ra) {
142 $roles[$ra->roleid] = $ra->roleid;
df997f84 143 }
ad68ed74 144 unset($ras);
df997f84 145
ad68ed74
PS
146 // do we want users without roles?
147 if (!$syncall and empty($parentroles)) {
148 self::user_not_supposed_to_be_here($instance, $ue, $context, $plugin);
149 return;
df997f84
PS
150 }
151
ad68ed74
PS
152 // is parent enrol active? (we ignore enrol starts and ends, sorry it would be too complex)
153 $parentstatus = ENROL_USER_SUSPENDED;
154 foreach ($parentues as $pue) {
155 if ($pue->status == ENROL_USER_ACTIVE) {
156 $parentstatus = ENROL_USER_ACTIVE;
157 break;
df997f84
PS
158 }
159 }
160
ad68ed74
PS
161 // enrol user if not enrolled yet or fix status
162 if ($ue) {
163 if ($parentstatus != $ue->status) {
164 $plugin->update_user_enrol($instance, $userid, $parentstatus);
165 $ue->status = $parentstatus;
166 }
167 } else {
168 $plugin->enrol_user($instance, $userid, NULL, 0, 0, $parentstatus);
169 $ue = new stdClass();
170 $ue->userid = $userid;
171 $ue->enrolid = $instance->id;
172 $ue->status = $parentstatus;
173 }
174
175 // only active users in enabled instances are supposed to have roles (we can reassign the roles any time later)
176 if ($ue->status != ENROL_USER_ACTIVE or $instance->status != ENROL_INSTANCE_ENABLED) {
177 if ($roles) {
178 role_unassign_all(array('userid'=>$userid, 'contextid'=>$context->id, 'component'=>'enrol_meta', 'itemid'=>$instance->id));
df997f84 179 }
ad68ed74
PS
180 return;
181 }
df997f84 182
ad68ed74
PS
183 // add new roles
184 foreach ($parentroles as $rid) {
185 if (!isset($roles[$rid])) {
186 role_assign($rid, $userid, $context->id, 'enrol_meta', $instance->id);
187 }
df997f84
PS
188 }
189
ad68ed74
PS
190 // remove roles
191 foreach ($roles as $rid) {
192 if (!isset($parentroles[$rid])) {
193 role_unassign($rid, $userid, $context->id, 'enrol_meta', $instance->id);
194 }
195 }
df997f84
PS
196 }
197
ad68ed74
PS
198 /**
199 * Deal with users that are not supposed to be enrolled via this instance
200 * @static
201 * @param stdClass $instance
202 * @param stdClass $ue
203 * @param context_course $context
204 * @param enrol_meta $plugin
205 * @return void
206 */
207 protected static function user_not_supposed_to_be_here($instance, $ue, context_course $context, $plugin) {
208 if (!$ue) {
209 // not enrolled yet - simple!
210 return;
211 }
212
213 $userid = $ue->userid;
214 $unenrolaction = $plugin->get_config('unenrolaction', ENROL_EXT_REMOVED_UNENROL);
215
216 if ($unenrolaction == ENROL_EXT_REMOVED_UNENROL) {
217 // purges grades, group membership, preferences, etc. - admins were warned!
218 $plugin->unenrol_user($instance, $userid);
219 return;
220
221 } else { // ENROL_EXT_REMOVED_SUSPENDNOROLES
222 // just suspend users and remove all roles (we can reassign the roles any time later)
223 if ($ue->status != ENROL_USER_SUSPENDED) {
224 $plugin->update_user_enrol($instance, $userid, ENROL_USER_SUSPENDED);
225 role_unassign_all(array('userid'=>$userid, 'contextid'=>$context->id, 'component'=>'enrol_meta', 'itemid'=>$instance->id));
226 }
227 return;
228 }
229 }
df997f84 230
ad68ed74
PS
231 /**
232 * Triggered via role assigned event.
233 * @static
234 * @param stdClass $ra
235 * @return bool success
236 */
237 public static function role_assigned($ra) {
238 if (!enrol_is_enabled('meta')) {
239 return true;
240 }
df997f84
PS
241
242 // prevent circular dependencies - we can not sync meta roles recursively
243 if ($ra->component === 'enrol_meta') {
244 return true;
245 }
246
247 // only course level roles are interesting
ad68ed74 248 if (!$parentcontext = context::instance_by_id($ra->contextid, IGNORE_MISSING)) {
df997f84
PS
249 return true;
250 }
ad68ed74 251 if ($parentcontext->contextlevel != CONTEXT_COURSE) {
df997f84
PS
252 return true;
253 }
254
ad68ed74 255 self::sync_course_instances($parentcontext->instanceid, $ra->userid);
df997f84 256
ad68ed74
PS
257 return true;
258 }
df997f84 259
ad68ed74
PS
260 /**
261 * Triggered via role unassigned event.
262 * @static
263 * @param stdClass $ra
264 * @return bool success
265 */
266 public static function role_unassigned($ra) {
267 if (!enrol_is_enabled('meta')) {
268 // all roles are removed via cron automatically
269 return true;
270 }
271
272 // prevent circular dependencies - we can not sync meta roles recursively
273 if ($ra->component === 'enrol_meta') {
274 return true;
275 }
df997f84 276
ad68ed74
PS
277 // only course level roles are interesting
278 if (!$parentcontext = context::instance_by_id($ra->contextid, IGNORE_MISSING)) {
279 return true;
280 }
281 if ($parentcontext->contextlevel != CONTEXT_COURSE) {
282 return true;
df997f84
PS
283 }
284
ad68ed74
PS
285 self::sync_course_instances($parentcontext->instanceid, $ra->userid);
286
df997f84
PS
287 return true;
288 }
289
ad68ed74
PS
290 /**
291 * Triggered via user enrolled event.
292 * @static
293 * @param stdClass $ue
294 * @return bool success
295 */
296 public static function user_enrolled($ue) {
df997f84 297 if (!enrol_is_enabled('meta')) {
ad68ed74 298 // no more enrolments for disabled plugins
df997f84
PS
299 return true;
300 }
301
fc7cd35c
PS
302 if ($ue->enrol === 'meta') {
303 // prevent circular dependencies - we can not sync meta enrolments recursively
304 return true;
305 }
306
ad68ed74 307 self::sync_course_instances($ue->courseid, $ue->userid);
df997f84
PS
308
309 return true;
310 }
311
ad68ed74
PS
312 /**
313 * Triggered via user unenrolled event.
314 * @static
315 * @param stdClass $ue
316 * @return bool success
317 */
318 public static function user_unenrolled($ue) {
df997f84 319
ad68ed74 320 // keep unenrolling even if plugin disabled
df997f84 321
ad68ed74
PS
322 if ($ue->enrol === 'meta') {
323 // prevent circular dependencies - we can not sync meta enrolments recursively
324 return true;
df997f84 325 }
ad68ed74
PS
326
327 self::sync_course_instances($ue->courseid, $ue->userid);
df997f84
PS
328
329 return true;
330 }
331
ad68ed74
PS
332 /**
333 * Triggered via user enrolment modification event.
334 * @static
335 * @param stdClass $ue
336 * @return bool success
337 */
338 public static function user_enrol_modified($ue) {
379434e4 339 if (!enrol_is_enabled('meta')) {
ad68ed74 340 // no modifications if plugin disabled
379434e4
PS
341 return true;
342 }
343
344 if ($ue->enrol === 'meta') {
345 // prevent circular dependencies - we can not sync meta enrolments recursively
346 return true;
347 }
348
ad68ed74 349 self::sync_course_instances($ue->courseid, $ue->userid);
379434e4
PS
350
351 return true;
352 }
353
ad68ed74
PS
354 /**
355 * Triggered via course_deleted event.
356 * @static
357 * @param stdClass $course
358 * @return bool success
359 */
360 public static function course_deleted($course) {
df997f84
PS
361 global $DB;
362
ad68ed74 363 // NOTE: do not test if plugin enabled, we want to keep disabling instances with invalid course links
df997f84
PS
364
365 // does anything want to sync with this parent?
ad68ed74 366 if (!$enrols = $DB->get_records('enrol', array('customint1'=>$course->id, 'enrol'=>'meta'), 'courseid ASC, id ASC')) {
df997f84
PS
367 return true;
368 }
369
370 $plugin = enrol_get_plugin('meta');
ad68ed74
PS
371
372 // hack the DB info for all courses first
df997f84 373 foreach ($enrols as $enrol) {
ad68ed74
PS
374 $enrol->customint1 = 0;
375 $enrol->status = ENROL_INSTANCE_DISABLED;
376 $DB->update_record('enrol', $enrol);
377 $context = context_course::instance($enrol->courseid);
378 role_unassign_all(array('contextid'=>$context->id, 'component'=>'enrol_meta', 'itemid'=>$enrol->id));
379 }
380
381 // now trigger sync for each instance and purge caches
382 foreach ($enrols as $enrol) {
383 $plugin->update_status($enrol, ENROL_INSTANCE_DISABLED);
df997f84
PS
384 }
385
386 return true;
387 }
388}
389
0a0b535e 390
df997f84
PS
391/**
392 * Sync all meta course links.
ad68ed74 393 *
df997f84 394 * @param int $courseid one course, empty mean all
ad68ed74
PS
395 * @param bool $verbose verbose CLI output
396 * @return int 0 means ok, 1 means error, 2 means plugin disabled
df997f84 397 */
ad68ed74 398function enrol_meta_sync($courseid = NULL, $verbose = false) {
df997f84
PS
399 global $CFG, $DB;
400
ad68ed74
PS
401 // purge all roles if meta sync disabled, those can be recreated later here in cron
402 if (!enrol_is_enabled('meta')) {
403 if ($verbose) {
404 mtrace('Meta sync plugin is disabled, unassigning all plugin roles and stopping.');
405 }
406 role_unassign_all(array('component'=>'enrol_meta'));
407 return 2;
408 }
409
410 // unfortunately this may take a long time, execution can be interrupted safely
411 @set_time_limit(0);
412 raise_memory_limit(MEMORY_HUGE);
df997f84 413
ad68ed74
PS
414 if ($verbose) {
415 mtrace('Starting user enrolment synchronisation...');
416 }
417
418 $instances = array(); // cache instances
379434e4 419
df997f84
PS
420 $meta = enrol_get_plugin('meta');
421
ad68ed74
PS
422 $unenrolaction = $meta->get_config('unenrolaction', ENROL_EXT_REMOVED_UNENROL);
423 $skiproles = $meta->get_config('nosyncroleids', '');
424 $skiproles = empty($skiproles) ? array() : explode(',', $skiproles);
425 $syncall = $meta->get_config('syncall', 1);
426
427 $allroles = get_all_roles();
428
df997f84
PS
429
430 // iterate through all not enrolled yet users
ad68ed74
PS
431 $onecourse = $courseid ? "AND e.courseid = :courseid" : "";
432 list($enabled, $params) = $DB->get_in_or_equal(explode(',', $CFG->enrol_plugins_enabled), SQL_PARAMS_NAMED, 'e');
433 $params['courseid'] = $courseid;
434 $sql = "SELECT pue.userid, e.id AS enrolid, pue.status
435 FROM {user_enrolments} pue
436 JOIN {enrol} pe ON (pe.id = pue.enrolid AND pe.enrol <> 'meta' AND pe.enrol $enabled)
437 JOIN {enrol} e ON (e.customint1 = pe.courseid AND e.enrol = 'meta' $onecourse)
f3bd4ea6 438 JOIN {user} u ON (u.id = pue.userid AND u.deleted = 0)
ad68ed74
PS
439 LEFT JOIN {user_enrolments} ue ON (ue.enrolid = e.id AND ue.userid = pue.userid)
440 WHERE ue.id IS NULL";
441
442 $rs = $DB->get_recordset_sql($sql, $params);
443 foreach($rs as $ue) {
444 if (!isset($instances[$ue->enrolid])) {
445 $instances[$ue->enrolid] = $DB->get_record('enrol', array('id'=>$ue->enrolid));
df997f84 446 }
ad68ed74
PS
447 $instance = $instances[$ue->enrolid];
448
449 if (!$syncall) {
450 // this may be slow if very many users are ignored in sync
451 $parentcontext = context_course::instance($instance->customint1);
452 list($ignoreroles, $params) = $DB->get_in_or_equal($skiproles, SQL_PARAMS_NAMED, 'ri', false, -1);
453 $params['contextid'] = $parentcontext->id;
454 $params['userid'] = $ue->userid;
455 $select = "contextid = :contextid AND userid = :userid AND component <> 'enrol_meta' AND roleid $ignoreroles";
456 if (!$DB->record_exists_select('role_assignments', $select, $params)) {
457 // bad luck, this user does not have any role we want in parent course
458 if ($verbose) {
459 mtrace(" skipping enrolling: $ue->userid ==> $instance->courseid (user without role)");
460 }
461 continue;
df997f84 462 }
df997f84 463 }
ad68ed74
PS
464
465 $meta->enrol_user($instance, $ue->userid, $ue->status);
466 if ($verbose) {
467 mtrace(" enrolling: $ue->userid ==> $instance->courseid");
468 }
df997f84 469 }
ad68ed74
PS
470 $rs->close();
471
df997f84 472
ad68ed74
PS
473 // unenrol as necessary - ignore enabled flag, we want to get rid of existing enrols in any case
474 $onecourse = $courseid ? "AND e.courseid = :courseid" : "";
475 list($enabled, $params) = $DB->get_in_or_equal(explode(',', $CFG->enrol_plugins_enabled), SQL_PARAMS_NAMED, 'e');
476 $params['courseid'] = $courseid;
477 $sql = "SELECT ue.*
df997f84
PS
478 FROM {user_enrolments} ue
479 JOIN {enrol} e ON (e.id = ue.enrolid AND e.enrol = 'meta' $onecourse)
26a06fa7 480 LEFT JOIN ({user_enrolments} xpue
ad68ed74 481 JOIN {enrol} xpe ON (xpe.id = xpue.enrolid AND xpe.enrol <> 'meta' AND xpe.enrol $enabled)
26a06fa7
MA
482 ) ON (xpe.courseid = e.customint1 AND xpue.userid = ue.userid)
483 WHERE xpue.userid IS NULL";
ad68ed74 484 $rs = $DB->get_recordset_sql($sql, $params);
df997f84
PS
485 foreach($rs as $ue) {
486 if (!isset($instances[$ue->enrolid])) {
487 $instances[$ue->enrolid] = $DB->get_record('enrol', array('id'=>$ue->enrolid));
488 }
ad68ed74
PS
489 $instance = $instances[$ue->enrolid];
490
491 if ($unenrolaction == ENROL_EXT_REMOVED_UNENROL) {
492 $meta->unenrol_user($instance, $ue->userid);
493 if ($verbose) {
494 mtrace(" unenrolling: $ue->userid ==> $instance->courseid");
495 }
496 continue;
497
498 } else { // ENROL_EXT_REMOVED_SUSPENDNOROLES
499 // just disable and ignore any changes
500 if ($ue->status != ENROL_USER_SUSPENDED) {
501 $meta->update_user_enrol($instance, $ue->userid, ENROL_USER_SUSPENDED);
502 $context = context_course::instance($instance->courseid);
503 role_unassign_all(array('userid'=>$ue->userid, 'contextid'=>$context->id, 'component'=>'enrol_meta'));
504 if ($verbose) {
505 mtrace(" suspending and removing all roles: $ue->userid ==> $instance->courseid");
506 }
507 }
508 continue;
509 }
df997f84
PS
510 }
511 $rs->close();
df997f84 512
ad68ed74
PS
513
514 // update status - meta enrols + start and end dates are ignored, sorry
515 // note the trick here is that the active enrolment and instance constants have value 0
516 $onecourse = $courseid ? "AND e.courseid = :courseid" : "";
517 list($enabled, $params) = $DB->get_in_or_equal(explode(',', $CFG->enrol_plugins_enabled), SQL_PARAMS_NAMED, 'e');
518 $params['courseid'] = $courseid;
519 $sql = "SELECT ue.userid, ue.enrolid, pue.pstatus
520 FROM {user_enrolments} ue
521 JOIN {enrol} e ON (e.id = ue.enrolid AND e.enrol = 'meta' $onecourse)
522 JOIN (SELECT xpue.userid, xpe.courseid, MIN(xpue.status + xpe.status) AS pstatus
523 FROM {user_enrolments} xpue
524 JOIN {enrol} xpe ON (xpe.id = xpue.enrolid AND xpe.enrol <> 'meta' AND xpe.enrol $enabled)
525 GROUP BY xpue.userid, xpe.courseid
526 ) pue ON (pue.courseid = e.customint1 AND pue.userid = ue.userid)
527 WHERE (pue.pstatus = 0 AND ue.status > 0) OR (pue.pstatus > 0 and ue.status = 0)";
528 $rs = $DB->get_recordset_sql($sql, $params);
529 foreach($rs as $ue) {
530 if (!isset($instances[$ue->enrolid])) {
531 $instances[$ue->enrolid] = $DB->get_record('enrol', array('id'=>$ue->enrolid));
532 }
533 $instance = $instances[$ue->enrolid];
534 $ue->pstatus = ($ue->pstatus == ENROL_USER_ACTIVE) ? ENROL_USER_ACTIVE : ENROL_USER_SUSPENDED;
535
536 if ($ue->pstatus == ENROL_USER_ACTIVE and !$syncall and $unenrolaction != ENROL_EXT_REMOVED_UNENROL) {
537 // this may be slow if very many users are ignored in sync
538 $parentcontext = context_course::instance($instance->customint1);
539 list($ignoreroles, $params) = $DB->get_in_or_equal($skiproles, SQL_PARAMS_NAMED, 'ri', false, -1);
540 $params['contextid'] = $parentcontext->id;
541 $params['userid'] = $ue->userid;
542 $select = "contextid = :contextid AND userid = :userid AND component <> 'enrol_meta' AND roleid $ignoreroles";
543 if (!$DB->record_exists_select('role_assignments', $select, $params)) {
544 // bad luck, this user does not have any role we want in parent course
545 if ($verbose) {
546 mtrace(" skipping unsuspending: $ue->userid ==> $instance->courseid (user without role)");
547 }
548 continue;
df997f84 549 }
ad68ed74
PS
550 }
551
552 $meta->update_user_enrol($instance, $ue->userid, $ue->pstatus);
553 if ($verbose) {
554 if ($ue->pstatus == ENROL_USER_ACTIVE) {
555 mtrace(" unsuspending: $ue->userid ==> $instance->courseid");
556 } else {
557 mtrace(" suspending: $ue->userid ==> $instance->courseid");
558 }
559 }
560 }
561 $rs->close();
562
563
564 // now assign all necessary roles
565 $enabled = explode(',', $CFG->enrol_plugins_enabled);
566 foreach($enabled as $k=>$v) {
567 if ($v === 'meta') {
568 continue; // no meta sync of meta roles
569 }
570 $enabled[$k] = 'enrol_'.$v;
571 }
77a5c093 572 $enabled[] = ''; // manual assignments are replicated too
ad68ed74
PS
573
574 $onecourse = $courseid ? "AND e.courseid = :courseid" : "";
575 list($enabled, $params) = $DB->get_in_or_equal($enabled, SQL_PARAMS_NAMED, 'e');
576 $params['coursecontext'] = CONTEXT_COURSE;
577 $params['courseid'] = $courseid;
578 $params['activeuser'] = ENROL_USER_ACTIVE;
579 $params['enabledinstance'] = ENROL_INSTANCE_ENABLED;
580 $sql = "SELECT DISTINCT pra.roleid, pra.userid, c.id AS contextid, e.id AS enrolid, e.courseid
581 FROM {role_assignments} pra
582 JOIN {user} u ON (u.id = pra.userid AND u.deleted = 0)
583 JOIN {context} pc ON (pc.id = pra.contextid AND pc.contextlevel = :coursecontext AND pra.component $enabled)
584 JOIN {enrol} e ON (e.customint1 = pc.instanceid AND e.enrol = 'meta' $onecourse AND e.status = :enabledinstance)
585 JOIN {user_enrolments} ue ON (ue.enrolid = e.id AND ue.userid = u.id AND ue.status = :activeuser)
586 JOIN {context} c ON (c.contextlevel = pc.contextlevel AND c.instanceid = e.courseid)
587 LEFT JOIN {role_assignments} ra ON (ra.contextid = c.id AND ra.userid = pra.userid AND ra.roleid = pra.roleid AND ra.itemid = e.id AND ra.component = 'enrol_meta')
588 WHERE ra.id IS NULL";
589
590 if ($ignored = $meta->get_config('nosyncroleids')) {
591 list($notignored, $xparams) = $DB->get_in_or_equal(explode(',', $ignored), SQL_PARAMS_NAMED, 'ig', false);
592 $params = array_merge($params, $xparams);
593 $sql = "$sql AND pra.roleid $notignored";
df997f84
PS
594 }
595
ad68ed74
PS
596 $rs = $DB->get_recordset_sql($sql, $params);
597 foreach($rs as $ra) {
598 role_assign($ra->roleid, $ra->userid, $ra->contextid, 'enrol_meta', $ra->enrolid);
599 if ($verbose) {
600 mtrace(" assigning role: $ra->userid ==> $ra->courseid as ".$allroles[$ra->roleid]->shortname);
601 }
602 }
603 $rs->close();
604
605
df997f84 606 // remove unwanted roles - include ignored roles and disabled plugins too
ad68ed74
PS
607 $onecourse = $courseid ? "AND e.courseid = :courseid" : "";
608 $params = array();
609 $params['coursecontext'] = CONTEXT_COURSE;
610 $params['courseid'] = $courseid;
611 $params['activeuser'] = ENROL_USER_ACTIVE;
612 $params['enabledinstance'] = ENROL_INSTANCE_ENABLED;
df997f84 613 if ($ignored = $meta->get_config('nosyncroleids')) {
cf717dc2 614 list($notignored, $xparams) = $DB->get_in_or_equal(explode(',', $ignored), SQL_PARAMS_NAMED, 'ig', false);
df997f84
PS
615 $params = array_merge($params, $xparams);
616 $notignored = "AND pra.roleid $notignored";
617 } else {
618 $notignored = "";
619 }
ad68ed74 620 $sql = "SELECT ra.roleid, ra.userid, ra.contextid, ra.itemid, e.courseid
df997f84
PS
621 FROM {role_assignments} ra
622 JOIN {enrol} e ON (e.id = ra.itemid AND ra.component = 'enrol_meta' AND e.enrol = 'meta' $onecourse)
623 JOIN {context} pc ON (pc.instanceid = e.customint1 AND pc.contextlevel = :coursecontext)
624 LEFT JOIN {role_assignments} pra ON (pra.contextid = pc.id AND pra.userid = ra.userid AND pra.roleid = ra.roleid AND pra.component <> 'enrol_meta' $notignored)
ad68ed74
PS
625 LEFT JOIN {user_enrolments} ue ON (ue.enrolid = e.id AND ue.userid = ra.userid AND ue.status = :activeuser)
626 WHERE pra.id IS NULL OR ue.id IS NULL OR e.status <> :enabledinstance";
df997f84
PS
627
628 $rs = $DB->get_recordset_sql($sql, $params);
629 foreach($rs as $ra) {
630 role_unassign($ra->roleid, $ra->userid, $ra->contextid, 'enrol_meta', $ra->itemid);
ad68ed74
PS
631 if ($verbose) {
632 mtrace(" unassigning role: $ra->userid ==> $ra->courseid as ".$allroles[$ra->roleid]->shortname);
633 }
df997f84
PS
634 }
635 $rs->close();
636
ad68ed74
PS
637
638 // kick out or suspend users without synced roles if syncall disabled
639 if (!$syncall) {
640 if ($unenrolaction == ENROL_EXT_REMOVED_UNENROL) {
641 $onecourse = $courseid ? "AND e.courseid = :courseid" : "";
642 $params = array();
643 $params['coursecontext'] = CONTEXT_COURSE;
379434e4 644 $params['courseid'] = $courseid;
ad68ed74
PS
645 $sql = "SELECT ue.userid, ue.enrolid
646 FROM {user_enrolments} ue
647 JOIN {enrol} e ON (e.id = ue.enrolid AND e.enrol = 'meta' $onecourse)
648 JOIN {context} c ON (e.courseid = c.instanceid AND c.contextlevel = :coursecontext)
649 LEFT JOIN {role_assignments} ra ON (ra.contextid = c.id AND ra.itemid = e.id AND ra.userid = ue.userid)
650 WHERE ra.id IS NULL";
651 $ues = $DB->get_recordset_sql($sql, $params);
652 foreach($ues as $ue) {
653 if (!isset($instances[$ue->enrolid])) {
654 $instances[$ue->enrolid] = $DB->get_record('enrol', array('id'=>$ue->enrolid));
655 }
656 $instance = $instances[$ue->enrolid];
657 $meta->unenrol_user($instance, $ue->userid);
658 if ($verbose) {
659 mtrace(" unenrolling: $ue->userid ==> $instance->courseid (user without role)");
660 }
379434e4 661 }
ad68ed74 662 $ues->close();
379434e4 663
ad68ed74
PS
664 } else {
665 // just suspend the users
666 $onecourse = $courseid ? "AND e.courseid = :courseid" : "";
667 $params = array();
668 $params['coursecontext'] = CONTEXT_COURSE;
379434e4 669 $params['courseid'] = $courseid;
ad68ed74
PS
670 $params['active'] = ENROL_USER_ACTIVE;
671 $sql = "SELECT ue.userid, ue.enrolid
672 FROM {user_enrolments} ue
673 JOIN {enrol} e ON (e.id = ue.enrolid AND e.enrol = 'meta' $onecourse)
674 JOIN {context} c ON (e.courseid = c.instanceid AND c.contextlevel = :coursecontext)
675 LEFT JOIN {role_assignments} ra ON (ra.contextid = c.id AND ra.itemid = e.id AND ra.userid = ue.userid)
676 WHERE ra.id IS NULL AND ue.status = :active";
677 $ues = $DB->get_recordset_sql($sql, $params);
678 foreach($ues as $ue) {
679 if (!isset($instances[$ue->enrolid])) {
680 $instances[$ue->enrolid] = $DB->get_record('enrol', array('id'=>$ue->enrolid));
681 }
682 $instance = $instances[$ue->enrolid];
683 $meta->update_user_enrol($instance, $ue->userid, ENROL_USER_SUSPENDED);
684 if ($verbose) {
685 mtrace(" suspending: $ue->userid ==> $instance->courseid (user without role)");
686 }
379434e4 687 }
ad68ed74 688 $ues->close();
379434e4 689 }
ad68ed74 690 }
379434e4 691
ad68ed74
PS
692 if ($verbose) {
693 mtrace('...user enrolment synchronisation finished.');
379434e4
PS
694 }
695
ad68ed74 696 return 0;
df997f84 697}