defined('MOODLE_INTERNAL') || die();
+
/**
* Event handler for meta enrolment plugin.
*
* it may fail sometimes, so we always do a full sync in cron too.
*/
class enrol_meta_handler {
- public function role_assigned($ra) {
+
+ /**
+ * Synchronise meta enrolments of this user in this course
+ * @static
+ * @param int $courseid
+ * @param int $userid
+ * @return void
+ */
+ protected static function sync_course_instances($courseid, $userid) {
global $DB;
+ static $preventrecursion = false;
+
+ // does anything want to sync with this parent?
+ if (!$enrols = $DB->get_records('enrol', array('customint1'=>$courseid, 'enrol'=>'meta'), 'id ASC')) {
+ return;
+ }
+
+ if ($preventrecursion) {
+ return;
+ }
+
+ $preventrecursion = true;
+
+ try {
+ foreach ($enrols as $enrol) {
+ self::sync_with_parent_course($enrol, $userid);
+ }
+ } catch (Exception $e) {
+ $preventrecursion = false;
+ throw $e;
+ }
+
+ $preventrecursion = false;
+ }
+
+ /**
+ * Synchronise user enrolments in given instance as fast as possible.
+ *
+ * All roles are removed if the meta plugin disabled.
+ *
+ * @static
+ * @param stdClass $instance
+ * @param int $userid
+ * @return void
+ */
+ protected static function sync_with_parent_course(stdClass $instance, $userid) {
+ global $DB, $CFG;
+
+ $plugin = enrol_get_plugin('meta');
+
+ if ($instance->customint1 == $instance->courseid) {
+ // can not sync with self!!!
+ return;
+ }
+
+ $context = context_course::instance($instance->courseid);
+
+ if (!$parentcontext = context_course::instance($instance->customint1, IGNORE_MISSING)) {
+ // linking to missing course is not possible
+ role_unassign_all(array('userid'=>$userid, 'contextid'=>$context->id, 'component'=>'enrol_meta'));
+ return;
+ }
+
+ // list of enrolments in parent course (we ignore meta enrols in parents completely)
+ list($enabled, $params) = $DB->get_in_or_equal(explode(',', $CFG->enrol_plugins_enabled), SQL_PARAMS_NAMED, 'e');
+ $params['userid'] = $userid;
+ $params['parentcourse'] = $instance->customint1;
+ $sql = "SELECT ue.*
+ FROM {user_enrolments} ue
+ JOIN {enrol} e ON (e.id = ue.enrolid AND e.enrol <> 'meta' AND e.courseid = :parentcourse AND e.enrol $enabled)
+ WHERE ue.userid = :userid";
+ $parentues = $DB->get_records_sql($sql, $params);
+ // current enrolments for this instance
+ $ue = $DB->get_record('user_enrolments', array('enrolid'=>$instance->id, 'userid'=>$userid));
+
+ // first deal with users that are not enrolled in parent
+ if (empty($parentues)) {
+ self::user_not_supposed_to_be_here($instance, $ue, $context, $plugin);
+ return;
+ }
+
if (!enrol_is_enabled('meta')) {
- return true;
+ if ($ue) {
+ role_unassign_all(array('userid'=>$userid, 'contextid'=>$context->id, 'component'=>'enrol_meta'));
+ }
+ return;
}
- // prevent circular dependencies - we can not sync meta roles recursively
- if ($ra->component === 'enrol_meta') {
- return true;
+ $skiproles = $plugin->get_config('nosyncroleids', '');
+ $skiproles = empty($skiproles) ? array() : explode(',', $skiproles);
+ $syncall = $plugin->get_config('syncall', 1);
+
+ // roles in parent course (meta enrols must be ignored!)
+ $parentroles = array();
+ list($ignoreroles, $params) = $DB->get_in_or_equal($skiproles, SQL_PARAMS_NAMED, 'ri', false, -1);
+ $params['contextid'] = $parentcontext->id;
+ $params['userid'] = $userid;
+ $select = "contextid = :contextid AND userid = :userid AND component <> 'enrol_meta' AND roleid $ignoreroles";
+ foreach($DB->get_records_select('role_assignments', $select, $params) as $ra) {
+ $parentroles[$ra->roleid] = $ra->roleid;
}
- // only course level roles are interesting
- $parentcontext = get_context_instance_by_id($ra->contextid);
- if ($parentcontext->contextlevel != CONTEXT_COURSE) {
- return true;
+ // roles from this instance
+ $roles = array();
+ $ras = $DB->get_records('role_assignments', array('contextid'=>$context->id, 'userid'=>$userid, 'component'=>'enrol_meta', 'itemid'=>$instance->id));
+ foreach($ras as $ra) {
+ $roles[$ra->roleid] = $ra->roleid;
}
+ unset($ras);
- // does anything want to sync with this parent?
- if (!$enrols = $DB->get_records('enrol', array('customint1'=>$parentcontext->instanceid, 'enrol'=>'meta'), 'id ASC')) {
- return true;
+ // do we want users without roles?
+ if (!$syncall and empty($parentroles)) {
+ self::user_not_supposed_to_be_here($instance, $ue, $context, $plugin);
+ return;
}
- // make sure the role sync is not prevented
- $plugin = enrol_get_plugin('meta');
- if ($disabled = $plugin->get_config('nosyncroleids')) {
- if (in_array($ra->roleid, explode(',', $disabled))) {
- return true;
+ // is parent enrol active? (we ignore enrol starts and ends, sorry it would be too complex)
+ $parentstatus = ENROL_USER_SUSPENDED;
+ foreach ($parentues as $pue) {
+ if ($pue->status == ENROL_USER_ACTIVE) {
+ $parentstatus = ENROL_USER_ACTIVE;
+ break;
}
}
- foreach ($enrols as $enrol) {
- // Is the user enrolled? We want to sync only really enrolled users
- if (!$DB->record_exists('user_enrolments', array('userid'=>$ra->userid, 'enrolid'=>$enrol->id))) {
- continue;
+ // enrol user if not enrolled yet or fix status
+ if ($ue) {
+ if ($parentstatus != $ue->status) {
+ $plugin->update_user_enrol($instance, $userid, $parentstatus);
+ $ue->status = $parentstatus;
+ }
+ } else {
+ $plugin->enrol_user($instance, $userid, NULL, 0, 0, $parentstatus);
+ $ue = new stdClass();
+ $ue->userid = $userid;
+ $ue->enrolid = $instance->id;
+ $ue->status = $parentstatus;
+ }
+
+ // only active users in enabled instances are supposed to have roles (we can reassign the roles any time later)
+ if ($ue->status != ENROL_USER_ACTIVE or $instance->status != ENROL_INSTANCE_ENABLED) {
+ if ($roles) {
+ role_unassign_all(array('userid'=>$userid, 'contextid'=>$context->id, 'component'=>'enrol_meta', 'itemid'=>$instance->id));
}
- $context = get_context_instance(CONTEXT_COURSE, $enrol->courseid);
+ return;
+ }
- // just try to assign role, no problem if role assignment already exists
- role_assign($ra->roleid, $ra->userid, $context->id, 'enrol_meta', $enrol->id);
+ // add new roles
+ foreach ($parentroles as $rid) {
+ if (!isset($roles[$rid])) {
+ role_assign($rid, $userid, $context->id, 'enrol_meta', $instance->id);
+ }
}
- return true;
+ // remove roles
+ foreach ($roles as $rid) {
+ if (!isset($parentroles[$rid])) {
+ role_unassign($rid, $userid, $context->id, 'enrol_meta', $instance->id);
+ }
+ }
}
- public function role_unassigned($ra) {
- global $DB;
+ /**
+ * Deal with users that are not supposed to be enrolled via this instance
+ * @static
+ * @param stdClass $instance
+ * @param stdClass $ue
+ * @param context_course $context
+ * @param enrol_meta $plugin
+ * @return void
+ */
+ protected static function user_not_supposed_to_be_here($instance, $ue, context_course $context, $plugin) {
+ if (!$ue) {
+ // not enrolled yet - simple!
+ return;
+ }
+
+ $userid = $ue->userid;
+ $unenrolaction = $plugin->get_config('unenrolaction', ENROL_EXT_REMOVED_UNENROL);
+
+ if ($unenrolaction == ENROL_EXT_REMOVED_UNENROL) {
+ // purges grades, group membership, preferences, etc. - admins were warned!
+ $plugin->unenrol_user($instance, $userid);
+ return;
+
+ } else { // ENROL_EXT_REMOVED_SUSPENDNOROLES
+ // just suspend users and remove all roles (we can reassign the roles any time later)
+ if ($ue->status != ENROL_USER_SUSPENDED) {
+ $plugin->update_user_enrol($instance, $userid, ENROL_USER_SUSPENDED);
+ role_unassign_all(array('userid'=>$userid, 'contextid'=>$context->id, 'component'=>'enrol_meta', 'itemid'=>$instance->id));
+ }
+ return;
+ }
+ }
- // note: do not test if plugin enabled, we want to keep removing previous roles
+ /**
+ * Triggered via role assigned event.
+ * @static
+ * @param stdClass $ra
+ * @return bool success
+ */
+ public static function role_assigned($ra) {
+ if (!enrol_is_enabled('meta')) {
+ return true;
+ }
// prevent circular dependencies - we can not sync meta roles recursively
if ($ra->component === 'enrol_meta') {
}
// only course level roles are interesting
- $parentcontext = get_context_instance_by_id($ra->contextid);
- if ($parentcontext->contextlevel != CONTEXT_COURSE) {
+ if (!$parentcontext = context::instance_by_id($ra->contextid, IGNORE_MISSING)) {
return true;
}
-
- // does anything want to sync with this parent?
- if (!$enrols = $DB->get_records('enrol', array('customint1'=>$parentcontext->instanceid, 'enrol'=>'meta'), 'id ASC')) {
+ if ($parentcontext->contextlevel != CONTEXT_COURSE) {
return true;
}
- // note: do not check 'nosyncroleids', somebody might have just enabled it, we want to get rid of nosync roles gradually
+ self::sync_course_instances($parentcontext->instanceid, $ra->userid);
- foreach ($enrols as $enrol) {
- // Is the user enrolled? We want to sync only really enrolled users
- if (!$DB->record_exists('user_enrolments', array('userid'=>$ra->userid, 'enrolid'=>$enrol->id))) {
- continue;
- }
- $context = get_context_instance(CONTEXT_COURSE, $enrol->courseid);
+ return true;
+ }
- // now make sure the user does not have the role through some other enrol plugin
- $params = array('contextid'=>$ra->contextid, 'roleid'=>$ra->roleid, 'userid'=>$ra->userid);
- if ($DB->record_exists_select('role_assignments', "contextid = :contextid AND roleid = :roleid AND userid = :userid AND component <> 'enrol_meta'", $params)) {
- continue;
- }
+ /**
+ * Triggered via role unassigned event.
+ * @static
+ * @param stdClass $ra
+ * @return bool success
+ */
+ public static function role_unassigned($ra) {
+ if (!enrol_is_enabled('meta')) {
+ // all roles are removed via cron automatically
+ return true;
+ }
+
+ // prevent circular dependencies - we can not sync meta roles recursively
+ if ($ra->component === 'enrol_meta') {
+ return true;
+ }
- // unassign role, there is no other role assignment in parent course
- role_unassign($ra->roleid, $ra->userid, $context->id, 'enrol_meta', $enrol->id);
+ // only course level roles are interesting
+ if (!$parentcontext = context::instance_by_id($ra->contextid, IGNORE_MISSING)) {
+ return true;
+ }
+ if ($parentcontext->contextlevel != CONTEXT_COURSE) {
+ return true;
}
+ self::sync_course_instances($parentcontext->instanceid, $ra->userid);
+
return true;
}
- public function user_enrolled($ue) {
- global $DB;
-
+ /**
+ * Triggered via user enrolled event.
+ * @static
+ * @param stdClass $ue
+ * @return bool success
+ */
+ public static function user_enrolled($ue) {
if (!enrol_is_enabled('meta')) {
+ // no more enrolments for disabled plugins
return true;
}
return true;
}
- // does anything want to sync with this parent?
- if (!$enrols = $DB->get_records('enrol', array('customint1'=>$ue->courseid, 'enrol'=>'meta'), 'id ASC')) {
- return true;
- }
-
- $plugin = enrol_get_plugin('meta');
- foreach ($enrols as $enrol) {
- if ($ue->status == ENROL_USER_ACTIVE) {
- $status = ENROL_USER_ACTIVE;
- } else {
- $context = get_context_instance(CONTEXT_COURSE, $enrol->courseid);
- if (is_enrolled($context, $ue->userid)) {
- // user already has active enrolment, do not change it
- $status = ENROL_USER_ACTIVE;
- } else {
- $status = $ue->status;
- }
-
- }
- // no problem if already enrolled
- $plugin->enrol_user($enrol, $ue->userid, $status);
- }
+ self::sync_course_instances($ue->courseid, $ue->userid);
return true;
}
- public function user_unenrolled($ue) {
- global $DB;
-
- //note: do not test if plugin enabled, we want to keep removing previously linked courses
-
- // look for unenrolment candidates - it may be possible that user has multiple enrolments...
- $sql = "SELECT e.*
- FROM {enrol} e
- JOIN {user_enrolments} ue ON (ue.enrolid = e.id AND ue.userid = :userid)
- JOIN {enrol} pe ON (pe.courseid = e.customint1 AND pe.enrol <> 'meta' AND pe.courseid = :courseid)
- LEFT JOIN {user_enrolments} pue ON (pue.enrolid = pe.id AND pue.userid = ue.userid)
- WHERE pue.id IS NULL AND e.enrol = 'meta'";
- $params = array('courseid'=>$ue->courseid, 'userid'=>$ue->userid);
+ /**
+ * Triggered via user unenrolled event.
+ * @static
+ * @param stdClass $ue
+ * @return bool success
+ */
+ public static function user_unenrolled($ue) {
- $rs = $DB->get_recordset_sql($sql, $params);
+ // keep unenrolling even if plugin disabled
- $plugin = enrol_get_plugin('meta');
- foreach ($rs as $enrol) {
- $plugin->unenrol_user($enrol, $ue->userid);
+ if ($ue->enrol === 'meta') {
+ // prevent circular dependencies - we can not sync meta enrolments recursively
+ return true;
}
- $rs->close();
+
+ self::sync_course_instances($ue->courseid, $ue->userid);
return true;
}
- public function user_enrol_modified($ue) {
- global $DB;
-
- // update enrolment status if necessary
-
+ /**
+ * Triggered via user enrolment modification event.
+ * @static
+ * @param stdClass $ue
+ * @return bool success
+ */
+ public static function user_enrol_modified($ue) {
if (!enrol_is_enabled('meta')) {
+ // no modifications if plugin disabled
return true;
}
return true;
}
- // does anything want to sync with this parent?
- if (!$enrols = $DB->get_records('enrol', array('customint1'=>$ue->courseid, 'enrol'=>'meta'), 'id ASC')) {
- return true;
- }
-
- $plugin = enrol_get_plugin('meta');
- foreach ($enrols as $enrol) {
- $plugin->update_user_enrol($enrol, $ue->userid, $ue->status);
- }
+ self::sync_course_instances($ue->courseid, $ue->userid);
return true;
}
- public function course_deleted($course) {
+ /**
+ * Triggered via course_deleted event.
+ * @static
+ * @param stdClass $course
+ * @return bool success
+ */
+ public static function course_deleted($course) {
global $DB;
- // note: do not test if plugin enabled, we want to keep removing previously linked courses
+ // NOTE: do not test if plugin enabled, we want to keep disabling instances with invalid course links
// does anything want to sync with this parent?
- if (!$enrols = $DB->get_records('enrol', array('customint1'=>$course->id, 'enrol'=>'meta'), 'id ASC')) {
+ if (!$enrols = $DB->get_records('enrol', array('customint1'=>$course->id, 'enrol'=>'meta'), 'courseid ASC, id ASC')) {
return true;
}
$plugin = enrol_get_plugin('meta');
+
+ // hack the DB info for all courses first
foreach ($enrols as $enrol) {
- // unenrol all users
- $ues = $DB->get_recordset('user_enrolments', array('enrolid'=>$enrol->id));
- foreach ($ues as $ue) {
- $plugin->unenrol_user($enrol, $ue->userid);
- }
- $ues->close();
+ $enrol->customint1 = 0;
+ $enrol->status = ENROL_INSTANCE_DISABLED;
+ $DB->update_record('enrol', $enrol);
+ $context = context_course::instance($enrol->courseid);
+ role_unassign_all(array('contextid'=>$context->id, 'component'=>'enrol_meta', 'itemid'=>$enrol->id));
+ }
+
+ // now trigger sync for each instance and purge caches
+ foreach ($enrols as $enrol) {
+ $plugin->update_status($enrol, ENROL_INSTANCE_DISABLED);
}
return true;
/**
* Sync all meta course links.
+ *
* @param int $courseid one course, empty mean all
- * @return void
+ * @param bool $verbose verbose CLI output
+ * @return int 0 means ok, 1 means error, 2 means plugin disabled
*/
-function enrol_meta_sync($courseid = NULL) {
+function enrol_meta_sync($courseid = NULL, $verbose = false) {
global $CFG, $DB;
- // unfortunately this may take a loooong time
- @set_time_limit(0); //if this fails during upgrade we can continue from cron, no big deal
+ // purge all roles if meta sync disabled, those can be recreated later here in cron
+ if (!enrol_is_enabled('meta')) {
+ if ($verbose) {
+ mtrace('Meta sync plugin is disabled, unassigning all plugin roles and stopping.');
+ }
+ role_unassign_all(array('component'=>'enrol_meta'));
+ return 2;
+ }
+
+ // unfortunately this may take a long time, execution can be interrupted safely
+ @set_time_limit(0);
+ raise_memory_limit(MEMORY_HUGE);
- $instances = array(); //cache
+ if ($verbose) {
+ mtrace('Starting user enrolment synchronisation...');
+ }
+
+ $instances = array(); // cache instances
$meta = enrol_get_plugin('meta');
- $onecourse = $courseid ? "AND e.courseid = :courseid" : "";
+ $unenrolaction = $meta->get_config('unenrolaction', ENROL_EXT_REMOVED_UNENROL);
+ $skiproles = $meta->get_config('nosyncroleids', '');
+ $skiproles = empty($skiproles) ? array() : explode(',', $skiproles);
+ $syncall = $meta->get_config('syncall', 1);
+
+ $allroles = get_all_roles();
+
// iterate through all not enrolled yet users
- if (enrol_is_enabled('meta')) {
- list($enabled, $params) = $DB->get_in_or_equal(explode(',', $CFG->enrol_plugins_enabled), SQL_PARAMS_NAMED, 'e');
- if ($courseid) {
- $params['courseid'] = $courseid;
+ $onecourse = $courseid ? "AND e.courseid = :courseid" : "";
+ list($enabled, $params) = $DB->get_in_or_equal(explode(',', $CFG->enrol_plugins_enabled), SQL_PARAMS_NAMED, 'e');
+ $params['courseid'] = $courseid;
+ $sql = "SELECT pue.userid, e.id AS enrolid, pue.status
+ FROM {user_enrolments} pue
+ JOIN {enrol} pe ON (pe.id = pue.enrolid AND pe.enrol <> 'meta' AND pe.enrol $enabled)
+ JOIN {enrol} e ON (e.customint1 = pe.courseid AND e.enrol = 'meta' $onecourse)
+ LEFT JOIN {user_enrolments} ue ON (ue.enrolid = e.id AND ue.userid = pue.userid)
+ WHERE ue.id IS NULL";
+
+ $rs = $DB->get_recordset_sql($sql, $params);
+ foreach($rs as $ue) {
+ if (!isset($instances[$ue->enrolid])) {
+ $instances[$ue->enrolid] = $DB->get_record('enrol', array('id'=>$ue->enrolid));
}
- $sql = "SELECT pue.userid, e.id AS enrolid
- FROM {user_enrolments} pue
- JOIN {enrol} pe ON (pe.id = pue.enrolid AND pe.enrol <> 'meta' AND pe.enrol $enabled )
- JOIN {enrol} e ON (e.customint1 = pe.courseid AND e.enrol = 'meta' AND e.status = :statusenabled $onecourse)
- LEFT JOIN {user_enrolments} ue ON (ue.enrolid = e.id AND ue.userid = pue.userid)
- WHERE ue.id IS NULL";
- $params['statusenabled'] = ENROL_INSTANCE_ENABLED;
- $params['courseid'] = $courseid;
-
- $rs = $DB->get_recordset_sql($sql, $params);
- foreach($rs as $ue) {
- if (!isset($instances[$ue->enrolid])) {
- $instances[$ue->enrolid] = $DB->get_record('enrol', array('id'=>$ue->enrolid));
+ $instance = $instances[$ue->enrolid];
+
+ if (!$syncall) {
+ // this may be slow if very many users are ignored in sync
+ $parentcontext = context_course::instance($instance->customint1);
+ list($ignoreroles, $params) = $DB->get_in_or_equal($skiproles, SQL_PARAMS_NAMED, 'ri', false, -1);
+ $params['contextid'] = $parentcontext->id;
+ $params['userid'] = $ue->userid;
+ $select = "contextid = :contextid AND userid = :userid AND component <> 'enrol_meta' AND roleid $ignoreroles";
+ if (!$DB->record_exists_select('role_assignments', $select, $params)) {
+ // bad luck, this user does not have any role we want in parent course
+ if ($verbose) {
+ mtrace(" skipping enrolling: $ue->userid ==> $instance->courseid (user without role)");
+ }
+ continue;
}
- $meta->enrol_user($instances[$ue->enrolid], $ue->userid);
}
- $rs->close();
+
+ $meta->enrol_user($instance, $ue->userid, $ue->status);
+ if ($verbose) {
+ mtrace(" enrolling: $ue->userid ==> $instance->courseid");
+ }
}
+ $rs->close();
+
- // unenrol as necessary - ignore enabled flag, we want to get rid of all
- $sql = "SELECT ue.userid, e.id AS enrolid
+ // unenrol as necessary - ignore enabled flag, we want to get rid of existing enrols in any case
+ $onecourse = $courseid ? "AND e.courseid = :courseid" : "";
+ list($enabled, $params) = $DB->get_in_or_equal(explode(',', $CFG->enrol_plugins_enabled), SQL_PARAMS_NAMED, 'e');
+ $params['courseid'] = $courseid;
+ $sql = "SELECT ue.*
FROM {user_enrolments} ue
JOIN {enrol} e ON (e.id = ue.enrolid AND e.enrol = 'meta' $onecourse)
- LEFT JOIN (SELECT xue.userid, xe.courseid
- FROM {enrol} xe
- JOIN {user_enrolments} xue ON (xue.enrolid = xe.id)
+ LEFT JOIN (SELECT xpue.userid, xpe.courseid
+ FROM {user_enrolments} xpue
+ JOIN {enrol} xpe ON (xpe.id = xpue.enrolid AND xpe.enrol <> 'meta' AND xpe.enrol $enabled)
) pue ON (pue.courseid = e.customint1 AND pue.userid = ue.userid)
- WHERE pue.courseid IS NULL";
- //TODO: this may use a bit of SQL optimisation
- $rs = $DB->get_recordset_sql($sql, array('courseid'=>$courseid));
+ WHERE pue.userid IS NULL";
+ $rs = $DB->get_recordset_sql($sql, $params);
foreach($rs as $ue) {
if (!isset($instances[$ue->enrolid])) {
$instances[$ue->enrolid] = $DB->get_record('enrol', array('id'=>$ue->enrolid));
}
- $meta->unenrol_user($instances[$ue->enrolid], $ue->userid);
+ $instance = $instances[$ue->enrolid];
+
+ if ($unenrolaction == ENROL_EXT_REMOVED_UNENROL) {
+ $meta->unenrol_user($instance, $ue->userid);
+ if ($verbose) {
+ mtrace(" unenrolling: $ue->userid ==> $instance->courseid");
+ }
+ continue;
+
+ } else { // ENROL_EXT_REMOVED_SUSPENDNOROLES
+ // just disable and ignore any changes
+ if ($ue->status != ENROL_USER_SUSPENDED) {
+ $meta->update_user_enrol($instance, $ue->userid, ENROL_USER_SUSPENDED);
+ $context = context_course::instance($instance->courseid);
+ role_unassign_all(array('userid'=>$ue->userid, 'contextid'=>$context->id, 'component'=>'enrol_meta'));
+ if ($verbose) {
+ mtrace(" suspending and removing all roles: $ue->userid ==> $instance->courseid");
+ }
+ }
+ continue;
+ }
}
$rs->close();
- // now assign all necessary roles
- if (enrol_is_enabled('meta')) {
- $enabled = explode(',', $CFG->enrol_plugins_enabled);
- foreach($enabled as $k=>$v) {
- if ($v === 'meta') {
- continue; // no meta sync of meta roles
+
+ // update status - meta enrols + start and end dates are ignored, sorry
+ // note the trick here is that the active enrolment and instance constants have value 0
+ $onecourse = $courseid ? "AND e.courseid = :courseid" : "";
+ list($enabled, $params) = $DB->get_in_or_equal(explode(',', $CFG->enrol_plugins_enabled), SQL_PARAMS_NAMED, 'e');
+ $params['courseid'] = $courseid;
+ $sql = "SELECT ue.userid, ue.enrolid, pue.pstatus
+ FROM {user_enrolments} ue
+ JOIN {enrol} e ON (e.id = ue.enrolid AND e.enrol = 'meta' $onecourse)
+ JOIN (SELECT xpue.userid, xpe.courseid, MIN(xpue.status + xpe.status) AS pstatus
+ FROM {user_enrolments} xpue
+ JOIN {enrol} xpe ON (xpe.id = xpue.enrolid AND xpe.enrol <> 'meta' AND xpe.enrol $enabled)
+ GROUP BY xpue.userid, xpe.courseid
+ ) pue ON (pue.courseid = e.customint1 AND pue.userid = ue.userid)
+ WHERE (pue.pstatus = 0 AND ue.status > 0) OR (pue.pstatus > 0 and ue.status = 0)";
+ $rs = $DB->get_recordset_sql($sql, $params);
+ foreach($rs as $ue) {
+ if (!isset($instances[$ue->enrolid])) {
+ $instances[$ue->enrolid] = $DB->get_record('enrol', array('id'=>$ue->enrolid));
+ }
+ $instance = $instances[$ue->enrolid];
+ $ue->pstatus = ($ue->pstatus == ENROL_USER_ACTIVE) ? ENROL_USER_ACTIVE : ENROL_USER_SUSPENDED;
+
+ if ($ue->pstatus == ENROL_USER_ACTIVE and !$syncall and $unenrolaction != ENROL_EXT_REMOVED_UNENROL) {
+ // this may be slow if very many users are ignored in sync
+ $parentcontext = context_course::instance($instance->customint1);
+ list($ignoreroles, $params) = $DB->get_in_or_equal($skiproles, SQL_PARAMS_NAMED, 'ri', false, -1);
+ $params['contextid'] = $parentcontext->id;
+ $params['userid'] = $ue->userid;
+ $select = "contextid = :contextid AND userid = :userid AND component <> 'enrol_meta' AND roleid $ignoreroles";
+ if (!$DB->record_exists_select('role_assignments', $select, $params)) {
+ // bad luck, this user does not have any role we want in parent course
+ if ($verbose) {
+ mtrace(" skipping unsuspending: $ue->userid ==> $instance->courseid (user without role)");
+ }
+ continue;
}
- $enabled[$k] = 'enrol_'.$v;
- }
- $enabled[] = $DB->sql_empty(); // manual assignments are replicated too
-
- list($enabled, $params) = $DB->get_in_or_equal($enabled, SQL_PARAMS_NAMED, 'e');
- $sql = "SELECT DISTINCT pra.roleid, pra.userid, c.id AS contextid, e.id AS enrolid
- FROM {role_assignments} pra
- JOIN {user} u ON (u.id = pra.userid AND u.deleted = 0)
- JOIN {context} pc ON (pc.id = pra.contextid AND pc.contextlevel = :coursecontext AND pra.component $enabled)
- JOIN {enrol} e ON (e.customint1 = pc.instanceid AND e.enrol = 'meta' AND e.status = :statusenabled $onecourse)
- JOIN {context} c ON (c.contextlevel = pc.contextlevel AND c.instanceid = e.courseid)
- LEFT JOIN {role_assignments} ra ON (ra.contextid = c.id AND ra.userid = pra.userid AND ra.roleid = pra.itemid AND ra.itemid = e.id AND ra.component = 'enrol_meta')
- WHERE ra.id IS NULL";
- $params['statusenabled'] = ENROL_INSTANCE_ENABLED;
- $params['coursecontext'] = CONTEXT_COURSE;
- $params['courseid'] = $courseid;
-
- if ($ignored = $meta->get_config('nosyncroleids')) {
- list($notignored, $xparams) = $DB->get_in_or_equal(explode(',', $ignored), SQL_PARAMS_NAMED, 'ig', false);
- $params = array_merge($params, $xparams);
- $sql = "$sql AND pra.roleid $notignored";
- }
-
- $rs = $DB->get_recordset_sql($sql, $params);
- foreach($rs as $ra) {
- role_assign($ra->roleid, $ra->userid, $ra->contextid, 'enrol_meta', $ra->enrolid);
- }
- $rs->close();
+ }
+
+ $meta->update_user_enrol($instance, $ue->userid, $ue->pstatus);
+ if ($verbose) {
+ if ($ue->pstatus == ENROL_USER_ACTIVE) {
+ mtrace(" unsuspending: $ue->userid ==> $instance->courseid");
+ } else {
+ mtrace(" suspending: $ue->userid ==> $instance->courseid");
+ }
+ }
+ }
+ $rs->close();
+
+
+ // now assign all necessary roles
+ $enabled = explode(',', $CFG->enrol_plugins_enabled);
+ foreach($enabled as $k=>$v) {
+ if ($v === 'meta') {
+ continue; // no meta sync of meta roles
+ }
+ $enabled[$k] = 'enrol_'.$v;
+ }
+ $enabled[] = $DB->sql_empty(); // manual assignments are replicated too
+
+ $onecourse = $courseid ? "AND e.courseid = :courseid" : "";
+ list($enabled, $params) = $DB->get_in_or_equal($enabled, SQL_PARAMS_NAMED, 'e');
+ $params['coursecontext'] = CONTEXT_COURSE;
+ $params['courseid'] = $courseid;
+ $params['activeuser'] = ENROL_USER_ACTIVE;
+ $params['enabledinstance'] = ENROL_INSTANCE_ENABLED;
+ $sql = "SELECT DISTINCT pra.roleid, pra.userid, c.id AS contextid, e.id AS enrolid, e.courseid
+ FROM {role_assignments} pra
+ JOIN {user} u ON (u.id = pra.userid AND u.deleted = 0)
+ JOIN {context} pc ON (pc.id = pra.contextid AND pc.contextlevel = :coursecontext AND pra.component $enabled)
+ JOIN {enrol} e ON (e.customint1 = pc.instanceid AND e.enrol = 'meta' $onecourse AND e.status = :enabledinstance)
+ JOIN {user_enrolments} ue ON (ue.enrolid = e.id AND ue.userid = u.id AND ue.status = :activeuser)
+ JOIN {context} c ON (c.contextlevel = pc.contextlevel AND c.instanceid = e.courseid)
+ 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')
+ WHERE ra.id IS NULL";
+
+ if ($ignored = $meta->get_config('nosyncroleids')) {
+ list($notignored, $xparams) = $DB->get_in_or_equal(explode(',', $ignored), SQL_PARAMS_NAMED, 'ig', false);
+ $params = array_merge($params, $xparams);
+ $sql = "$sql AND pra.roleid $notignored";
}
+ $rs = $DB->get_recordset_sql($sql, $params);
+ foreach($rs as $ra) {
+ role_assign($ra->roleid, $ra->userid, $ra->contextid, 'enrol_meta', $ra->enrolid);
+ if ($verbose) {
+ mtrace(" assigning role: $ra->userid ==> $ra->courseid as ".$allroles[$ra->roleid]->shortname);
+ }
+ }
+ $rs->close();
+
+
// remove unwanted roles - include ignored roles and disabled plugins too
- $params = array('coursecontext' => CONTEXT_COURSE, 'courseid' => $courseid);
+ $onecourse = $courseid ? "AND e.courseid = :courseid" : "";
+ $params = array();
+ $params['coursecontext'] = CONTEXT_COURSE;
+ $params['courseid'] = $courseid;
+ $params['activeuser'] = ENROL_USER_ACTIVE;
+ $params['enabledinstance'] = ENROL_INSTANCE_ENABLED;
if ($ignored = $meta->get_config('nosyncroleids')) {
list($notignored, $xparams) = $DB->get_in_or_equal(explode(',', $ignored), SQL_PARAMS_NAMED, 'ig', false);
$params = array_merge($params, $xparams);
} else {
$notignored = "";
}
- $sql = "SELECT ra.roleid, ra.userid, ra.contextid, ra.itemid
+ $sql = "SELECT ra.roleid, ra.userid, ra.contextid, ra.itemid, e.courseid
FROM {role_assignments} ra
JOIN {enrol} e ON (e.id = ra.itemid AND ra.component = 'enrol_meta' AND e.enrol = 'meta' $onecourse)
JOIN {context} pc ON (pc.instanceid = e.customint1 AND pc.contextlevel = :coursecontext)
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)
- WHERE pra.id IS NULL";
+ LEFT JOIN {user_enrolments} ue ON (ue.enrolid = e.id AND ue.userid = ra.userid AND ue.status = :activeuser)
+ WHERE pra.id IS NULL OR ue.id IS NULL OR e.status <> :enabledinstance";
$rs = $DB->get_recordset_sql($sql, $params);
foreach($rs as $ra) {
role_unassign($ra->roleid, $ra->userid, $ra->contextid, 'enrol_meta', $ra->itemid);
+ if ($verbose) {
+ mtrace(" unassigning role: $ra->userid ==> $ra->courseid as ".$allroles[$ra->roleid]->shortname);
+ }
}
$rs->close();
- // sync enrolment status
- if (enrol_is_enabled('meta')) {
- $enabled = explode(',', $CFG->enrol_plugins_enabled);
- foreach($enabled as $k=>$v) {
- if ($v === 'meta') {
- unset($enabled[$k]);
- }
- }
- list($enabled, $params) = $DB->get_in_or_equal($enabled, SQL_PARAMS_NAMED, 'e');
- if ($courseid) {
+
+ // kick out or suspend users without synced roles if syncall disabled
+ if (!$syncall) {
+ if ($unenrolaction == ENROL_EXT_REMOVED_UNENROL) {
+ $onecourse = $courseid ? "AND e.courseid = :courseid" : "";
+ $params = array();
+ $params['coursecontext'] = CONTEXT_COURSE;
$params['courseid'] = $courseid;
- }
- //note: this will probably take a long time on mysql...
- $sql = "SELECT ue.userid, e.id AS enrolid
- FROM {user_enrolments} ue
- JOIN {enrol} e ON (e.id = ue.enrolid AND e.enrol = 'meta' AND e.status = :statusenabled $onecourse)
- LEFT JOIN (SELECT pue.id, pue.userid, pe.courseid
- FROM {user_enrolments} pue
- JOIN {enrol} pe ON (pe.enrol $enabled)
- WHERE pue.enrolid = pe.id AND pue.status = :activestatus2) xx ON (xx.userid = ue.userid AND xx.courseid = e.customint1)
- WHERE ue.status = :activestatus1 AND xx.id IS NULL";
- $params['statusenabled'] = ENROL_INSTANCE_ENABLED;
- $params['activestatus1'] = ENROL_USER_ACTIVE;
- $params['activestatus2'] = ENROL_USER_ACTIVE;
- $params['courseid'] = $courseid;
-
- $rs = $DB->get_recordset_sql($sql, $params);
- foreach($rs as $ue) {
- if (!isset($instances[$ue->enrolid])) {
- $instances[$ue->enrolid] = $DB->get_record('enrol', array('id'=>$ue->enrolid));
+ $sql = "SELECT ue.userid, ue.enrolid
+ FROM {user_enrolments} ue
+ JOIN {enrol} e ON (e.id = ue.enrolid AND e.enrol = 'meta' $onecourse)
+ JOIN {context} c ON (e.courseid = c.instanceid AND c.contextlevel = :coursecontext)
+ LEFT JOIN {role_assignments} ra ON (ra.contextid = c.id AND ra.itemid = e.id AND ra.userid = ue.userid)
+ WHERE ra.id IS NULL";
+ $ues = $DB->get_recordset_sql($sql, $params);
+ foreach($ues as $ue) {
+ if (!isset($instances[$ue->enrolid])) {
+ $instances[$ue->enrolid] = $DB->get_record('enrol', array('id'=>$ue->enrolid));
+ }
+ $instance = $instances[$ue->enrolid];
+ $meta->unenrol_user($instance, $ue->userid);
+ if ($verbose) {
+ mtrace(" unenrolling: $ue->userid ==> $instance->courseid (user without role)");
+ }
}
- $meta->update_user_enrol($instances[$ue->enrolid], $ue->userid, ENROL_USER_SUSPENDED);
- }
- $rs->close();
+ $ues->close();
- list($enabled, $params) = $DB->get_in_or_equal(explode(',', $CFG->enrol_plugins_enabled), SQL_PARAMS_NAMED, 'e');
- if ($courseid) {
+ } else {
+ // just suspend the users
+ $onecourse = $courseid ? "AND e.courseid = :courseid" : "";
+ $params = array();
+ $params['coursecontext'] = CONTEXT_COURSE;
$params['courseid'] = $courseid;
- }
- // enable if at least one enrolment active in linked course
- $sql = "SELECT DISTINCT ue.userid, e.id AS enrolid
- FROM {user_enrolments} ue
- JOIN {enrol} e ON (e.id = ue.enrolid AND e.enrol = 'meta' AND e.status = :statusenabled $onecourse)
- JOIN {enrol} pe ON (pe.courseid = e.customint1 AND pe.enrol $enabled)
- JOIN {user_enrolments} pue ON (pue.enrolid = pe.id AND pue.userid = ue.userid AND pue.status = :activestatus)
- WHERE ue.status = :suspendedstatus";
- $params['statusenabled'] = ENROL_INSTANCE_ENABLED;
- $params['suspendedstatus'] = ENROL_USER_SUSPENDED;
- $params['activestatus'] = ENROL_USER_ACTIVE;
- $params['courseid'] = $courseid;
- $rs = $DB->get_recordset_sql($sql, $params);
- foreach($rs as $ue) {
- if (!isset($instances[$ue->enrolid])) {
- $instances[$ue->enrolid] = $DB->get_record('enrol', array('id'=>$ue->enrolid));
+ $params['active'] = ENROL_USER_ACTIVE;
+ $sql = "SELECT ue.userid, ue.enrolid
+ FROM {user_enrolments} ue
+ JOIN {enrol} e ON (e.id = ue.enrolid AND e.enrol = 'meta' $onecourse)
+ JOIN {context} c ON (e.courseid = c.instanceid AND c.contextlevel = :coursecontext)
+ LEFT JOIN {role_assignments} ra ON (ra.contextid = c.id AND ra.itemid = e.id AND ra.userid = ue.userid)
+ WHERE ra.id IS NULL AND ue.status = :active";
+ $ues = $DB->get_recordset_sql($sql, $params);
+ foreach($ues as $ue) {
+ if (!isset($instances[$ue->enrolid])) {
+ $instances[$ue->enrolid] = $DB->get_record('enrol', array('id'=>$ue->enrolid));
+ }
+ $instance = $instances[$ue->enrolid];
+ $meta->update_user_enrol($instance, $ue->userid, ENROL_USER_SUSPENDED);
+ if ($verbose) {
+ mtrace(" suspending: $ue->userid ==> $instance->courseid (user without role)");
+ }
}
- $meta->update_user_enrol($instances[$ue->enrolid], $ue->userid, ENROL_USER_ACTIVE);
+ $ues->close();
}
- $rs->close();
+ }
+ if ($verbose) {
+ mtrace('...user enrolment synchronisation finished.');
}
+ return 0;
}