2 // This file is part of Moodle - http://moodle.org/
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.
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.
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/>.
18 * Local stuff for meta course enrolment plugin.
21 * @copyright 2010 Petr Skoda {@link http://skodak.org}
22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
25 defined('MOODLE_INTERNAL') ||
die();
29 * Event handler for meta enrolment plugin.
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.
34 class enrol_meta_handler
{
37 * Synchronise meta enrolments of this user in this course
39 * @param int $courseid
43 protected static function sync_course_instances($courseid, $userid) {
46 static $preventrecursion = false;
48 // does anything want to sync with this parent?
49 if (!$enrols = $DB->get_records('enrol', array('customint1'=>$courseid, 'enrol'=>'meta'), 'id ASC')) {
53 if ($preventrecursion) {
57 $preventrecursion = true;
60 foreach ($enrols as $enrol) {
61 self
::sync_with_parent_course($enrol, $userid);
63 } catch (Exception
$e) {
64 $preventrecursion = false;
68 $preventrecursion = false;
72 * Synchronise user enrolments in given instance as fast as possible.
74 * All roles are removed if the meta plugin disabled.
77 * @param stdClass $instance
81 protected static function sync_with_parent_course(stdClass
$instance, $userid) {
83 require_once($CFG->dirroot
. '/group/lib.php');
85 $plugin = enrol_get_plugin('meta');
87 if ($instance->customint1
== $instance->courseid
) {
88 // can not sync with self!!!
92 $context = context_course
::instance($instance->courseid
);
94 // list of enrolments in parent course (we ignore meta enrols in parents completely)
95 list($enabled, $params) = $DB->get_in_or_equal(explode(',', $CFG->enrol_plugins_enabled
), SQL_PARAMS_NAMED
, 'e');
96 $params['userid'] = $userid;
97 $params['parentcourse'] = $instance->customint1
;
98 $sql = "SELECT ue.*, e.status AS enrolstatus
99 FROM {user_enrolments} ue
100 JOIN {enrol} e ON (e.id = ue.enrolid AND e.enrol <> 'meta' AND e.courseid = :parentcourse AND e.enrol $enabled)
101 WHERE ue.userid = :userid";
102 $parentues = $DB->get_records_sql($sql, $params);
103 // current enrolments for this instance
104 $ue = $DB->get_record('user_enrolments', array('enrolid'=>$instance->id
, 'userid'=>$userid));
106 // first deal with users that are not enrolled in parent
107 if (empty($parentues)) {
108 self
::user_not_supposed_to_be_here($instance, $ue, $context, $plugin);
112 if (!$parentcontext = context_course
::instance($instance->customint1
, IGNORE_MISSING
)) {
113 // Weird, we should not get here.
117 $skiproles = $plugin->get_config('nosyncroleids', '');
118 $skiproles = empty($skiproles) ?
array() : explode(',', $skiproles);
119 $syncall = $plugin->get_config('syncall', 1);
121 // roles in parent course (meta enrols must be ignored!)
122 $parentroles = array();
123 list($ignoreroles, $params) = $DB->get_in_or_equal($skiproles, SQL_PARAMS_NAMED
, 'ri', false, -1);
124 $params['contextid'] = $parentcontext->id
;
125 $params['userid'] = $userid;
126 $select = "contextid = :contextid AND userid = :userid AND component <> 'enrol_meta' AND roleid $ignoreroles";
127 foreach($DB->get_records_select('role_assignments', $select, $params) as $ra) {
128 $parentroles[$ra->roleid
] = $ra->roleid
;
131 // roles from this instance
133 $ras = $DB->get_records('role_assignments', array('contextid'=>$context->id
, 'userid'=>$userid, 'component'=>'enrol_meta', 'itemid'=>$instance->id
));
134 foreach($ras as $ra) {
135 $roles[$ra->roleid
] = $ra->roleid
;
139 // do we want users without roles?
140 if (!$syncall and empty($parentroles)) {
141 self
::user_not_supposed_to_be_here($instance, $ue, $context, $plugin);
145 // Is parent enrol active? Find minimum timestart and maximum timeend of all active enrolments.
146 $parentstatus = ENROL_USER_SUSPENDED
;
147 $parenttimeend = null;
148 $parenttimestart = null;
149 foreach ($parentues as $pue) {
150 if ($pue->status
== ENROL_USER_ACTIVE
&& $pue->enrolstatus
== ENROL_INSTANCE_ENABLED
) {
151 $parentstatus = ENROL_USER_ACTIVE
;
152 if ($parenttimeend === null ||
$pue->timeend
== 0 ||
($parenttimeend && $parenttimeend < $pue->timeend
)) {
153 $parenttimeend = $pue->timeend
;
155 if ($parenttimestart === null ||
$parenttimestart > $pue->timestart
) {
156 $parenttimestart = $pue->timestart
;
161 // Enrol user if not enrolled yet or fix status/timestart/timeend. Use the minimum timestart and maximum timeend found above.
163 if ($parentstatus != $ue->status ||
164 ($parentstatus == ENROL_USER_ACTIVE
&& ($parenttimestart != $ue->timestart ||
$parenttimeend != $ue->timeend
))) {
165 $plugin->update_user_enrol($instance, $userid, $parentstatus, $parenttimestart, $parenttimeend);
166 $ue->status
= $parentstatus;
167 $ue->timestart
= $parenttimestart;
168 $ue->timeend
= $parenttimeend;
171 $plugin->enrol_user($instance, $userid, NULL, (int)$parenttimestart, (int)$parenttimeend, $parentstatus);
172 $ue = new stdClass();
173 $ue->userid
= $userid;
174 $ue->enrolid
= $instance->id
;
175 $ue->status
= $parentstatus;
176 if ($instance->customint2
&& $group = $DB->get_record('groups', ['id' => $instance->customint2
])) {
177 groups_add_member($group, $userid, 'enrol_meta', $instance->id
);
181 $unenrolaction = $plugin->get_config('unenrolaction', ENROL_EXT_REMOVED_SUSPENDNOROLES
);
183 // Only active users in enabled instances are supposed to have roles (we can reassign the roles any time later).
184 if ($ue->status
!= ENROL_USER_ACTIVE
or $instance->status
!= ENROL_INSTANCE_ENABLED
or
185 ($parenttimeend and $parenttimeend < time()) or ($parenttimestart > time())) {
186 if ($unenrolaction == ENROL_EXT_REMOVED_SUSPEND
) {
187 // Always keep the roles.
189 // This will only unassign roles that were assigned in this enrolment method, leaving all manual role assignments intact.
190 role_unassign_all(array('userid'=>$userid, 'contextid'=>$context->id
, 'component'=>'enrol_meta', 'itemid'=>$instance->id
));
196 foreach ($parentroles as $rid) {
197 if (!isset($roles[$rid])) {
198 role_assign($rid, $userid, $context->id
, 'enrol_meta', $instance->id
);
202 if ($unenrolaction == ENROL_EXT_REMOVED_SUSPEND
) {
203 // Always keep the roles.
208 foreach ($roles as $rid) {
209 if (!isset($parentroles[$rid])) {
210 role_unassign($rid, $userid, $context->id
, 'enrol_meta', $instance->id
);
216 * Deal with users that are not supposed to be enrolled via this instance
218 * @param stdClass $instance
219 * @param stdClass $ue
220 * @param context_course $context
221 * @param enrol_meta $plugin
224 protected static function user_not_supposed_to_be_here($instance, $ue, context_course
$context, $plugin) {
226 // Not enrolled yet - simple!
230 $userid = $ue->userid
;
231 $unenrolaction = $plugin->get_config('unenrolaction', ENROL_EXT_REMOVED_SUSPENDNOROLES
);
233 if ($unenrolaction == ENROL_EXT_REMOVED_UNENROL
) {
234 // Purges grades, group membership, preferences, etc. - admins were warned!
235 $plugin->unenrol_user($instance, $userid);
237 } else if ($unenrolaction == ENROL_EXT_REMOVED_SUSPEND
) {
238 if ($ue->status
!= ENROL_USER_SUSPENDED
) {
239 $plugin->update_user_enrol($instance, $userid, ENROL_USER_SUSPENDED
);
242 } else if ($unenrolaction == ENROL_EXT_REMOVED_SUSPENDNOROLES
) {
243 if ($ue->status
!= ENROL_USER_SUSPENDED
) {
244 $plugin->update_user_enrol($instance, $userid, ENROL_USER_SUSPENDED
);
246 role_unassign_all(array('userid'=>$userid, 'contextid'=>$context->id
, 'component'=>'enrol_meta', 'itemid'=>$instance->id
));
249 debugging('Unknown unenrol action '.$unenrolaction);
255 * Sync all meta course links.
257 * @param int $courseid one course, empty mean all
258 * @param bool $verbose verbose CLI output
259 * @return int 0 means ok, 1 means error, 2 means plugin disabled
261 function enrol_meta_sync($courseid = NULL, $verbose = false) {
263 require_once("{$CFG->dirroot}/group/lib.php");
265 // purge all roles if meta sync disabled, those can be recreated later here in cron
266 if (!enrol_is_enabled('meta')) {
268 mtrace('Meta sync plugin is disabled, unassigning all plugin roles and stopping.');
270 role_unassign_all(array('component'=>'enrol_meta'));
274 // unfortunately this may take a long time, execution can be interrupted safely
275 core_php_time_limit
::raise();
276 raise_memory_limit(MEMORY_HUGE
);
279 mtrace('Starting user enrolment synchronisation...');
282 $instances = array(); // cache instances
284 $meta = enrol_get_plugin('meta');
286 $unenrolaction = $meta->get_config('unenrolaction', ENROL_EXT_REMOVED_SUSPENDNOROLES
);
287 $skiproles = $meta->get_config('nosyncroleids', '');
288 $skiproles = empty($skiproles) ?
array() : explode(',', $skiproles);
289 $syncall = $meta->get_config('syncall', 1);
291 $allroles = get_all_roles();
294 // Iterate through all not enrolled yet users. For each active enrolment of each user find the minimum
295 // enrolment startdate and maximum enrolment enddate.
296 // This SQL relies on the fact that ENROL_USER_ACTIVE < ENROL_USER_SUSPENDED
297 // and ENROL_INSTANCE_ENABLED < ENROL_INSTANCE_DISABLED. Condition "pue.status + pe.status = 0" means
298 // that enrolment is active. When MIN(pue.status + pe.status)=0 it means there exists an active
300 $onecourse = $courseid ?
"AND e.courseid = :courseid" : "";
301 list($enabled, $params) = $DB->get_in_or_equal(explode(',', $CFG->enrol_plugins_enabled
), SQL_PARAMS_NAMED
, 'e');
302 $params['courseid'] = $courseid;
303 $sql = "SELECT pue.userid, e.id AS enrolid, MIN(pue.status + pe.status) AS status,
304 MIN(CASE WHEN (pue.status + pe.status = 0) THEN pue.timestart ELSE 9999999999 END) AS timestart,
305 MAX(CASE WHEN (pue.status + pe.status = 0) THEN
306 (CASE WHEN pue.timeend = 0 THEN 9999999999 ELSE pue.timeend END)
307 ELSE 0 END) AS timeend
308 FROM {user_enrolments} pue
309 JOIN {enrol} pe ON (pe.id = pue.enrolid AND pe.enrol <> 'meta' AND pe.enrol $enabled)
310 JOIN {enrol} e ON (e.customint1 = pe.courseid AND e.enrol = 'meta' AND e.status = :enrolstatus $onecourse)
311 JOIN {user} u ON (u.id = pue.userid AND u.deleted = 0)
312 LEFT JOIN {user_enrolments} ue ON (ue.enrolid = e.id AND ue.userid = pue.userid)
314 GROUP BY pue.userid, e.id";
315 $params['enrolstatus'] = ENROL_INSTANCE_ENABLED
;
317 $rs = $DB->get_recordset_sql($sql, $params);
318 foreach($rs as $ue) {
319 if (!isset($instances[$ue->enrolid
])) {
320 $instances[$ue->enrolid
] = $DB->get_record('enrol', array('id'=>$ue->enrolid
));
322 $instance = $instances[$ue->enrolid
];
325 // this may be slow if very many users are ignored in sync
326 $parentcontext = context_course
::instance($instance->customint1
);
327 list($ignoreroles, $params) = $DB->get_in_or_equal($skiproles, SQL_PARAMS_NAMED
, 'ri', false, -1);
328 $params['contextid'] = $parentcontext->id
;
329 $params['userid'] = $ue->userid
;
330 $select = "contextid = :contextid AND userid = :userid AND component <> 'enrol_meta' AND roleid $ignoreroles";
331 if (!$DB->record_exists_select('role_assignments', $select, $params)) {
332 // bad luck, this user does not have any role we want in parent course
334 mtrace(" skipping enrolling: $ue->userid ==> $instance->courseid (user without role)");
340 // So now we have aggregated values that we will use for the meta enrolment status, timeend and timestart.
341 // Again, we use the fact that active=0 and disabled/suspended=1. Only when MIN(pue.status + pe.status)=0 the enrolment is active:
342 $ue->status
= ($ue->status
== ENROL_USER_ACTIVE + ENROL_INSTANCE_ENABLED
) ? ENROL_USER_ACTIVE
: ENROL_USER_SUSPENDED
;
343 // Timeend 9999999999 was used instead of 0 in the "MAX()" function:
344 $ue->timeend
= ($ue->timeend
== 9999999999) ?
0 : (int)$ue->timeend
;
345 // Timestart 9999999999 is only possible when there are no active enrolments:
346 $ue->timestart
= ($ue->timestart
== 9999999999) ?
0 : (int)$ue->timestart
;
348 $meta->enrol_user($instance, $ue->userid
, null, $ue->timestart
, $ue->timeend
, $ue->status
);
349 if ($instance->customint2
&& $group = $DB->get_record('groups', ['id' => $instance->customint2
])) {
350 groups_add_member($group, $ue->userid
, 'enrol_meta', $instance->id
);
353 mtrace(" enrolling: $ue->userid ==> $instance->courseid");
359 // unenrol as necessary - ignore enabled flag, we want to get rid of existing enrols in any case
360 $onecourse = $courseid ?
"AND e.courseid = :courseid" : "";
361 list($enabled, $params) = $DB->get_in_or_equal(explode(',', $CFG->enrol_plugins_enabled
), SQL_PARAMS_NAMED
, 'e');
362 $params['courseid'] = $courseid;
364 FROM {user_enrolments} ue
365 JOIN {enrol} e ON (e.id = ue.enrolid AND e.enrol = 'meta' $onecourse)
366 LEFT JOIN ({user_enrolments} xpue
367 JOIN {enrol} xpe ON (xpe.id = xpue.enrolid AND xpe.enrol <> 'meta' AND xpe.enrol $enabled)
368 ) ON (xpe.courseid = e.customint1 AND xpue.userid = ue.userid)
369 WHERE xpue.userid IS NULL";
370 $rs = $DB->get_recordset_sql($sql, $params);
371 foreach($rs as $ue) {
372 if (!isset($instances[$ue->enrolid
])) {
373 $instances[$ue->enrolid
] = $DB->get_record('enrol', array('id'=>$ue->enrolid
));
375 $instance = $instances[$ue->enrolid
];
377 if ($unenrolaction == ENROL_EXT_REMOVED_UNENROL
) {
378 $meta->unenrol_user($instance, $ue->userid
);
380 mtrace(" unenrolling: $ue->userid ==> $instance->courseid");
383 } else if ($unenrolaction == ENROL_EXT_REMOVED_SUSPEND
) {
384 if ($ue->status
!= ENROL_USER_SUSPENDED
) {
385 $meta->update_user_enrol($instance, $ue->userid
, ENROL_USER_SUSPENDED
);
387 mtrace(" suspending: $ue->userid ==> $instance->courseid");
391 } else if ($unenrolaction == ENROL_EXT_REMOVED_SUSPENDNOROLES
) {
392 if ($ue->status
!= ENROL_USER_SUSPENDED
) {
393 $meta->update_user_enrol($instance, $ue->userid
, ENROL_USER_SUSPENDED
);
394 $context = context_course
::instance($instance->courseid
);
395 role_unassign_all(array('userid'=>$ue->userid
, 'contextid'=>$context->id
, 'component'=>'enrol_meta', 'itemid'=>$instance->id
));
397 mtrace(" suspending and removing all roles: $ue->userid ==> $instance->courseid");
405 // Update status - meta enrols are ignored to avoid recursion.
406 // Note the trick here is that the active enrolment and instance constants have value 0.
407 $onecourse = $courseid ?
"AND e.courseid = :courseid" : "";
408 list($enabled, $params) = $DB->get_in_or_equal(explode(',', $CFG->enrol_plugins_enabled
), SQL_PARAMS_NAMED
, 'e');
409 $params['courseid'] = $courseid;
410 // The query builds a a list of all the non-meta enrolments that are on courses (the children) that are linked to by a meta
411 // enrolment, it then groups them by the course that linked to them (the parents).
413 // It will only return results where the there is a difference between the status of the parent and the lowest status
414 // of the children (remember that 0 is active, any other status is some form of inactive), or the time the earliest non-zero
415 // start time of a child is different to the parent, or the longest effective end date has changed.
417 // The last two case statements in the HAVING clause are designed to ignore any inactive child records when calculating
418 // the start and end time.
419 $sql = "SELECT ue.userid, ue.enrolid,
420 MIN(xpue.status + xpe.status) AS pstatus,
421 MIN(CASE WHEN (xpue.status + xpe.status = 0) THEN xpue.timestart ELSE 9999999999 END) AS ptimestart,
422 MAX(CASE WHEN (xpue.status + xpe.status = 0) THEN
423 (CASE WHEN xpue.timeend = 0 THEN 9999999999 ELSE xpue.timeend END)
424 ELSE 0 END) AS ptimeend
425 FROM {user_enrolments} ue
426 JOIN {enrol} e ON (e.id = ue.enrolid AND e.enrol = 'meta' $onecourse)
427 JOIN {user_enrolments} xpue ON (xpue.userid = ue.userid)
428 JOIN {enrol} xpe ON (xpe.id = xpue.enrolid AND xpe.enrol <> 'meta'
429 AND xpe.enrol $enabled AND xpe.courseid = e.customint1)
430 GROUP BY ue.userid, ue.enrolid
431 HAVING (MIN(xpue.status + xpe.status) = 0 AND MIN(ue.status) > 0)
432 OR (MIN(xpue.status + xpe.status) > 0 AND MIN(ue.status) = 0)
434 MIN(CASE WHEN (xpue.status + xpe.status = 0) THEN xpue.timestart ELSE 9999999999 END) = 9999999999
437 MIN(CASE WHEN (xpue.status + xpe.status = 0) THEN xpue.timestart ELSE 9999999999 END)
438 END) <> MIN(ue.timestart))
440 WHEN MAX(CASE WHEN (xpue.status + xpe.status = 0)
441 THEN (CASE WHEN xpue.timeend = 0 THEN 9999999999 ELSE xpue.timeend END)
442 ELSE 0 END) = 9999999999
443 THEN 0 ELSE MAX(CASE WHEN (xpue.status + xpe.status = 0)
444 THEN (CASE WHEN xpue.timeend = 0 THEN 9999999999 ELSE xpue.timeend END)
446 END) <> MAX(ue.timeend))";
447 $rs = $DB->get_recordset_sql($sql, $params);
448 foreach($rs as $ue) {
449 if (!isset($instances[$ue->enrolid
])) {
450 $instances[$ue->enrolid
] = $DB->get_record('enrol', array('id'=>$ue->enrolid
));
452 $instance = $instances[$ue->enrolid
];
453 $ue->pstatus
= ($ue->pstatus
== ENROL_USER_ACTIVE + ENROL_INSTANCE_ENABLED
) ? ENROL_USER_ACTIVE
: ENROL_USER_SUSPENDED
;
454 $ue->ptimeend
= ($ue->ptimeend
== 9999999999) ?
0 : (int)$ue->ptimeend
;
455 $ue->ptimestart
= ($ue->ptimestart
== 9999999999) ?
0 : (int)$ue->ptimestart
;
457 if ($ue->pstatus
== ENROL_USER_ACTIVE
and (!$ue->ptimeend ||
$ue->ptimeend
> time())
458 and !$syncall and $unenrolaction != ENROL_EXT_REMOVED_UNENROL
) {
459 // this may be slow if very many users are ignored in sync
460 $parentcontext = context_course
::instance($instance->customint1
);
461 list($ignoreroles, $params) = $DB->get_in_or_equal($skiproles, SQL_PARAMS_NAMED
, 'ri', false, -1);
462 $params['contextid'] = $parentcontext->id
;
463 $params['userid'] = $ue->userid
;
464 $select = "contextid = :contextid AND userid = :userid AND component <> 'enrol_meta' AND roleid $ignoreroles";
465 if (!$DB->record_exists_select('role_assignments', $select, $params)) {
466 // bad luck, this user does not have any role we want in parent course
468 mtrace(" skipping unsuspending: $ue->userid ==> $instance->courseid (user without role)");
474 $meta->update_user_enrol($instance, $ue->userid
, $ue->pstatus
, $ue->ptimestart
, $ue->ptimeend
);
476 if ($ue->pstatus
== ENROL_USER_ACTIVE
) {
477 mtrace(" unsuspending: $ue->userid ==> $instance->courseid");
479 mtrace(" suspending: $ue->userid ==> $instance->courseid");
486 // now assign all necessary roles
487 $enabled = explode(',', $CFG->enrol_plugins_enabled
);
488 foreach($enabled as $k=>$v) {
490 continue; // no meta sync of meta roles
492 $enabled[$k] = 'enrol_'.$v;
494 $enabled[] = ''; // manual assignments are replicated too
496 $onecourse = $courseid ?
"AND e.courseid = :courseid" : "";
497 list($enabled, $params) = $DB->get_in_or_equal($enabled, SQL_PARAMS_NAMED
, 'e');
498 $params['coursecontext'] = CONTEXT_COURSE
;
499 $params['courseid'] = $courseid;
500 $params['activeuser'] = ENROL_USER_ACTIVE
;
501 $params['enabledinstance'] = ENROL_INSTANCE_ENABLED
;
502 $sql = "SELECT DISTINCT pra.roleid, pra.userid, c.id AS contextid, e.id AS enrolid, e.courseid
503 FROM {role_assignments} pra
504 JOIN {user} u ON (u.id = pra.userid AND u.deleted = 0)
505 JOIN {context} pc ON (pc.id = pra.contextid AND pc.contextlevel = :coursecontext AND pra.component $enabled)
506 JOIN {enrol} e ON (e.customint1 = pc.instanceid AND e.enrol = 'meta' $onecourse AND e.status = :enabledinstance)
507 JOIN {user_enrolments} ue ON (ue.enrolid = e.id AND ue.userid = u.id AND ue.status = :activeuser)
508 JOIN {context} c ON (c.contextlevel = pc.contextlevel AND c.instanceid = e.courseid)
509 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')
510 WHERE ra.id IS NULL";
512 if ($ignored = $meta->get_config('nosyncroleids')) {
513 list($notignored, $xparams) = $DB->get_in_or_equal(explode(',', $ignored), SQL_PARAMS_NAMED
, 'ig', false);
514 $params = array_merge($params, $xparams);
515 $sql = "$sql AND pra.roleid $notignored";
518 $rs = $DB->get_recordset_sql($sql, $params);
519 foreach($rs as $ra) {
520 role_assign($ra->roleid
, $ra->userid
, $ra->contextid
, 'enrol_meta', $ra->enrolid
);
522 mtrace(" assigning role: $ra->userid ==> $ra->courseid as ".$allroles[$ra->roleid
]->shortname
);
528 // remove unwanted roles - include ignored roles and disabled plugins too
529 $onecourse = $courseid ?
"AND e.courseid = :courseid" : "";
531 $params['coursecontext'] = CONTEXT_COURSE
;
532 $params['courseid'] = $courseid;
533 $params['activeuser'] = ENROL_USER_ACTIVE
;
534 $params['enabledinstance'] = ENROL_INSTANCE_ENABLED
;
535 if ($ignored = $meta->get_config('nosyncroleids')) {
536 list($notignored, $xparams) = $DB->get_in_or_equal(explode(',', $ignored), SQL_PARAMS_NAMED
, 'ig', false);
537 $params = array_merge($params, $xparams);
538 $notignored = "AND pra.roleid $notignored";
543 $sql = "SELECT ra.roleid, ra.userid, ra.contextid, ra.itemid, e.courseid
544 FROM {role_assignments} ra
545 JOIN {enrol} e ON (e.id = ra.itemid AND ra.component = 'enrol_meta' AND e.enrol = 'meta' $onecourse)
546 JOIN {context} pc ON (pc.instanceid = e.customint1 AND pc.contextlevel = :coursecontext)
547 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)
548 LEFT JOIN {user_enrolments} ue ON (ue.enrolid = e.id AND ue.userid = ra.userid AND ue.status = :activeuser)
549 WHERE pra.id IS NULL OR ue.id IS NULL OR e.status <> :enabledinstance";
551 if ($unenrolaction != ENROL_EXT_REMOVED_SUSPEND
) {
552 $rs = $DB->get_recordset_sql($sql, $params);
553 foreach($rs as $ra) {
554 role_unassign($ra->roleid
, $ra->userid
, $ra->contextid
, 'enrol_meta', $ra->itemid
);
556 mtrace(" unassigning role: $ra->userid ==> $ra->courseid as ".$allroles[$ra->roleid
]->shortname
);
563 // kick out or suspend users without synced roles if syncall disabled
565 if ($unenrolaction == ENROL_EXT_REMOVED_UNENROL
) {
566 $onecourse = $courseid ?
"AND e.courseid = :courseid" : "";
568 $params['coursecontext'] = CONTEXT_COURSE
;
569 $params['courseid'] = $courseid;
570 $sql = "SELECT ue.userid, ue.enrolid
571 FROM {user_enrolments} ue
572 JOIN {enrol} e ON (e.id = ue.enrolid AND e.enrol = 'meta' $onecourse)
573 JOIN {context} c ON (e.courseid = c.instanceid AND c.contextlevel = :coursecontext)
574 LEFT JOIN {role_assignments} ra ON (ra.contextid = c.id AND ra.itemid = e.id AND ra.userid = ue.userid)
575 WHERE ra.id IS NULL";
576 $ues = $DB->get_recordset_sql($sql, $params);
577 foreach($ues as $ue) {
578 if (!isset($instances[$ue->enrolid
])) {
579 $instances[$ue->enrolid
] = $DB->get_record('enrol', array('id'=>$ue->enrolid
));
581 $instance = $instances[$ue->enrolid
];
582 $meta->unenrol_user($instance, $ue->userid
);
584 mtrace(" unenrolling: $ue->userid ==> $instance->courseid (user without role)");
590 // just suspend the users
591 $onecourse = $courseid ?
"AND e.courseid = :courseid" : "";
593 $params['coursecontext'] = CONTEXT_COURSE
;
594 $params['courseid'] = $courseid;
595 $params['active'] = ENROL_USER_ACTIVE
;
596 $sql = "SELECT ue.userid, ue.enrolid
597 FROM {user_enrolments} ue
598 JOIN {enrol} e ON (e.id = ue.enrolid AND e.enrol = 'meta' $onecourse)
599 JOIN {context} c ON (e.courseid = c.instanceid AND c.contextlevel = :coursecontext)
600 LEFT JOIN {role_assignments} ra ON (ra.contextid = c.id AND ra.itemid = e.id AND ra.userid = ue.userid)
601 WHERE ra.id IS NULL AND ue.status = :active";
602 $ues = $DB->get_recordset_sql($sql, $params);
603 foreach($ues as $ue) {
604 if (!isset($instances[$ue->enrolid
])) {
605 $instances[$ue->enrolid
] = $DB->get_record('enrol', array('id'=>$ue->enrolid
));
607 $instance = $instances[$ue->enrolid
];
608 $meta->update_user_enrol($instance, $ue->userid
, ENROL_USER_SUSPENDED
);
610 mtrace(" suspending: $ue->userid ==> $instance->courseid (user without role)");
617 // Finally sync groups.
618 $affectedusers = groups_sync_with_enrolment('meta', $courseid);
620 foreach ($affectedusers['removed'] as $gm) {
621 mtrace("removing user from group: $gm->userid ==> $gm->courseid - $gm->groupname", 1);
623 foreach ($affectedusers['added'] as $ue) {
624 mtrace("adding user to group: $ue->userid ==> $ue->courseid - $ue->groupname", 1);
629 mtrace('...user enrolment synchronisation finished.');
636 * Create a new group with the course's name.
638 * @param int $courseid
639 * @param int $linkedcourseid
640 * @return int $groupid Group ID for this cohort.
642 function enrol_meta_create_new_group($courseid, $linkedcourseid) {
645 require_once($CFG->dirroot
.'/group/lib.php');
647 $coursename = $DB->get_field('course', 'fullname', array('id' => $linkedcourseid), MUST_EXIST
);
649 $a->name
= $coursename;
652 $groupname = trim(get_string('defaultgroupnametext', 'enrol_meta', $a));
653 // Check to see if the group name already exists in this course. Add an incremented number if it does.
654 while ($DB->record_exists('groups', array('name' => $groupname, 'courseid' => $courseid))) {
655 $a->increment
= '(' . (++
$inc) . ')';
656 $groupname = trim(get_string('defaultgroupnametext', 'enrol_meta', $a));
658 // Create a new group for the course meta sync.
659 $groupdata = new stdClass();
660 $groupdata->courseid
= $courseid;
661 $groupdata->name
= $groupname;
662 $groupid = groups_create_group($groupdata);