983530e043a9e9d6c6feedc65d4e089566249dfb
[moodle.git] / enrol / meta / locallib.php
1 <?php
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/>.
17 /**
18  * Local stuff for meta course enrolment plugin.
19  *
20  * @package    enrol_meta
21  * @copyright  2010 Petr Skoda {@link http://skodak.org}
22  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23  */
25 defined('MOODLE_INTERNAL') || die();
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  */
34 class enrol_meta_handler {
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) {
44         global $DB;
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')) {
50             return;
51         }
53         if ($preventrecursion) {
54             return;
55         }
57         $preventrecursion = true;
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         }
68         $preventrecursion = false;
69     }
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         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!!!
89             return;
90         }
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.*
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);
109             return;
110         }
112         if (!$parentcontext = context_course::instance($instance->customint1, IGNORE_MISSING)) {
113             // Weird, we should not get here.
114             return;
115         }
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;
129         }
131         // roles from this instance
132         $roles = array();
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;
136         }
137         unset($ras);
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);
142             return;
143         }
145         // is parent enrol active? (we ignore enrol starts and ends, sorry it would be too complex)
146         $parentstatus = ENROL_USER_SUSPENDED;
147         foreach ($parentues as $pue) {
148             if ($pue->status == ENROL_USER_ACTIVE) {
149                 $parentstatus = ENROL_USER_ACTIVE;
150                 break;
151             }
152         }
154         // enrol user if not enrolled yet or fix status
155         if ($ue) {
156             if ($parentstatus != $ue->status) {
157                 $plugin->update_user_enrol($instance, $userid, $parentstatus);
158                 $ue->status = $parentstatus;
159             }
160         } else {
161             $plugin->enrol_user($instance, $userid, NULL, 0, 0, $parentstatus);
162             $ue = new stdClass();
163             $ue->userid = $userid;
164             $ue->enrolid = $instance->id;
165             $ue->status = $parentstatus;
166             if ($instance->customint2) {
167                 groups_add_member($instance->customint2, $userid, 'enrol_meta', $instance->id);
168             }
169         }
171         $unenrolaction = $plugin->get_config('unenrolaction', ENROL_EXT_REMOVED_SUSPENDNOROLES);
173         // only active users in enabled instances are supposed to have roles (we can reassign the roles any time later)
174         if ($ue->status != ENROL_USER_ACTIVE or $instance->status != ENROL_INSTANCE_ENABLED) {
175             if ($unenrolaction == ENROL_EXT_REMOVED_SUSPEND) {
176                 // Always keep the roles.
177             } else if ($roles) {
178                 role_unassign_all(array('userid'=>$userid, 'contextid'=>$context->id, 'component'=>'enrol_meta', 'itemid'=>$instance->id));
179             }
180             return;
181         }
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             }
188         }
190         if ($unenrolaction == ENROL_EXT_REMOVED_SUSPEND) {
191             // Always keep the roles.
192             return;
193         }
195         // remove roles
196         foreach ($roles as $rid) {
197             if (!isset($parentroles[$rid])) {
198                 role_unassign($rid, $userid, $context->id, 'enrol_meta', $instance->id);
199             }
200         }
201     }
203     /**
204      * Deal with users that are not supposed to be enrolled via this instance
205      * @static
206      * @param stdClass $instance
207      * @param stdClass $ue
208      * @param context_course $context
209      * @param enrol_meta $plugin
210      * @return void
211      */
212     protected static function user_not_supposed_to_be_here($instance, $ue, context_course $context, $plugin) {
213         if (!$ue) {
214             // Not enrolled yet - simple!
215             return;
216         }
218         $userid = $ue->userid;
219         $unenrolaction = $plugin->get_config('unenrolaction', ENROL_EXT_REMOVED_SUSPENDNOROLES);
221         if ($unenrolaction == ENROL_EXT_REMOVED_UNENROL) {
222             // Purges grades, group membership, preferences, etc. - admins were warned!
223             $plugin->unenrol_user($instance, $userid);
225         } else if ($unenrolaction == ENROL_EXT_REMOVED_SUSPEND) {
226             if ($ue->status != ENROL_USER_SUSPENDED) {
227                 $plugin->update_user_enrol($instance, $userid, ENROL_USER_SUSPENDED);
228             }
230         } else if ($unenrolaction == ENROL_EXT_REMOVED_SUSPENDNOROLES) {
231             if ($ue->status != ENROL_USER_SUSPENDED) {
232                 $plugin->update_user_enrol($instance, $userid, ENROL_USER_SUSPENDED);
233             }
234             role_unassign_all(array('userid'=>$userid, 'contextid'=>$context->id, 'component'=>'enrol_meta', 'itemid'=>$instance->id));
236         } else {
237             debugging('Unknown unenrol action '.$unenrolaction);
238         }
239     }
242 /**
243  * Sync all meta course links.
244  *
245  * @param int $courseid one course, empty mean all
246  * @param bool $verbose verbose CLI output
247  * @return int 0 means ok, 1 means error, 2 means plugin disabled
248  */
249 function enrol_meta_sync($courseid = NULL, $verbose = false) {
250     global $CFG, $DB;
251     require_once("{$CFG->dirroot}/group/lib.php");
253     // purge all roles if meta sync disabled, those can be recreated later here in cron
254     if (!enrol_is_enabled('meta')) {
255         if ($verbose) {
256             mtrace('Meta sync plugin is disabled, unassigning all plugin roles and stopping.');
257         }
258         role_unassign_all(array('component'=>'enrol_meta'));
259         return 2;
260     }
262     // unfortunately this may take a long time, execution can be interrupted safely
263     core_php_time_limit::raise();
264     raise_memory_limit(MEMORY_HUGE);
266     if ($verbose) {
267         mtrace('Starting user enrolment synchronisation...');
268     }
270     $instances = array(); // cache instances
272     $meta = enrol_get_plugin('meta');
274     $unenrolaction = $meta->get_config('unenrolaction', ENROL_EXT_REMOVED_SUSPENDNOROLES);
275     $skiproles     = $meta->get_config('nosyncroleids', '');
276     $skiproles     = empty($skiproles) ? array() : explode(',', $skiproles);
277     $syncall       = $meta->get_config('syncall', 1);
279     $allroles = get_all_roles();
282     // iterate through all not enrolled yet users
283     $onecourse = $courseid ? "AND e.courseid = :courseid" : "";
284     list($enabled, $params) = $DB->get_in_or_equal(explode(',', $CFG->enrol_plugins_enabled), SQL_PARAMS_NAMED, 'e');
285     $params['courseid'] = $courseid;
286     $sql = "SELECT pue.userid, e.id AS enrolid, pue.status
287               FROM {user_enrolments} pue
288               JOIN {enrol} pe ON (pe.id = pue.enrolid AND pe.enrol <> 'meta' AND pe.enrol $enabled)
289               JOIN {enrol} e ON (e.customint1 = pe.courseid AND e.enrol = 'meta' $onecourse)
290               JOIN {user} u ON (u.id = pue.userid AND u.deleted = 0)
291          LEFT JOIN {user_enrolments} ue ON (ue.enrolid = e.id AND ue.userid = pue.userid)
292              WHERE ue.id IS NULL";
294     $rs = $DB->get_recordset_sql($sql, $params);
295     foreach($rs as $ue) {
296         if (!isset($instances[$ue->enrolid])) {
297             $instances[$ue->enrolid] = $DB->get_record('enrol', array('id'=>$ue->enrolid));
298         }
299         $instance = $instances[$ue->enrolid];
301         if (!$syncall) {
302             // this may be slow if very many users are ignored in sync
303             $parentcontext = context_course::instance($instance->customint1);
304             list($ignoreroles, $params) = $DB->get_in_or_equal($skiproles, SQL_PARAMS_NAMED, 'ri', false, -1);
305             $params['contextid'] = $parentcontext->id;
306             $params['userid'] = $ue->userid;
307             $select = "contextid = :contextid AND userid = :userid AND component <> 'enrol_meta' AND roleid $ignoreroles";
308             if (!$DB->record_exists_select('role_assignments', $select, $params)) {
309                 // bad luck, this user does not have any role we want in parent course
310                 if ($verbose) {
311                     mtrace("  skipping enrolling: $ue->userid ==> $instance->courseid (user without role)");
312                 }
313                 continue;
314             }
315         }
317         $meta->enrol_user($instance, $ue->userid, null, 0, 0, $ue->status);
318         if ($instance->customint2) {
319             groups_add_member($instance->customint2, $ue->userid, 'enrol_meta', $instance->id);
320         }
321         if ($verbose) {
322             mtrace("  enrolling: $ue->userid ==> $instance->courseid");
323         }
324     }
325     $rs->close();
328     // unenrol as necessary - ignore enabled flag, we want to get rid of existing enrols in any case
329     $onecourse = $courseid ? "AND e.courseid = :courseid" : "";
330     list($enabled, $params) = $DB->get_in_or_equal(explode(',', $CFG->enrol_plugins_enabled), SQL_PARAMS_NAMED, 'e');
331     $params['courseid'] = $courseid;
332     $sql = "SELECT ue.*
333               FROM {user_enrolments} ue
334               JOIN {enrol} e ON (e.id = ue.enrolid AND e.enrol = 'meta' $onecourse)
335          LEFT JOIN ({user_enrolments} xpue
336                       JOIN {enrol} xpe ON (xpe.id = xpue.enrolid AND xpe.enrol <> 'meta' AND xpe.enrol $enabled)
337                    ) ON (xpe.courseid = e.customint1 AND xpue.userid = ue.userid)
338              WHERE xpue.userid IS NULL";
339     $rs = $DB->get_recordset_sql($sql, $params);
340     foreach($rs as $ue) {
341         if (!isset($instances[$ue->enrolid])) {
342             $instances[$ue->enrolid] = $DB->get_record('enrol', array('id'=>$ue->enrolid));
343         }
344         $instance = $instances[$ue->enrolid];
346         if ($unenrolaction == ENROL_EXT_REMOVED_UNENROL) {
347             $meta->unenrol_user($instance, $ue->userid);
348             if ($verbose) {
349                 mtrace("  unenrolling: $ue->userid ==> $instance->courseid");
350             }
352         } else if ($unenrolaction == ENROL_EXT_REMOVED_SUSPEND) {
353             if ($ue->status != ENROL_USER_SUSPENDED) {
354                 $meta->update_user_enrol($instance, $ue->userid, ENROL_USER_SUSPENDED);
355                 if ($verbose) {
356                     mtrace("  suspending: $ue->userid ==> $instance->courseid");
357                 }
358             }
360         } else if ($unenrolaction == ENROL_EXT_REMOVED_SUSPENDNOROLES) {
361             if ($ue->status != ENROL_USER_SUSPENDED) {
362                 $meta->update_user_enrol($instance, $ue->userid, ENROL_USER_SUSPENDED);
363                 $context = context_course::instance($instance->courseid);
364                 role_unassign_all(array('userid'=>$ue->userid, 'contextid'=>$context->id, 'component'=>'enrol_meta', 'itemid'=>$instance->id));
365                 if ($verbose) {
366                     mtrace("  suspending and removing all roles: $ue->userid ==> $instance->courseid");
367                 }
368             }
369         }
370     }
371     $rs->close();
374     // update status - meta enrols + start and end dates are ignored, sorry
375     // note the trick here is that the active enrolment and instance constants have value 0
376     $onecourse = $courseid ? "AND e.courseid = :courseid" : "";
377     list($enabled, $params) = $DB->get_in_or_equal(explode(',', $CFG->enrol_plugins_enabled), SQL_PARAMS_NAMED, 'e');
378     $params['courseid'] = $courseid;
379     $sql = "SELECT ue.userid, ue.enrolid, pue.pstatus
380               FROM {user_enrolments} ue
381               JOIN {enrol} e ON (e.id = ue.enrolid AND e.enrol = 'meta' $onecourse)
382               JOIN (SELECT xpue.userid, xpe.courseid, MIN(xpue.status + xpe.status) AS pstatus
383                       FROM {user_enrolments} xpue
384                       JOIN {enrol} xpe ON (xpe.id = xpue.enrolid AND xpe.enrol <> 'meta' AND xpe.enrol $enabled)
385                   GROUP BY xpue.userid, xpe.courseid
386                    ) pue ON (pue.courseid = e.customint1 AND pue.userid = ue.userid)
387              WHERE (pue.pstatus = 0 AND ue.status > 0) OR (pue.pstatus > 0 and ue.status = 0)";
388     $rs = $DB->get_recordset_sql($sql, $params);
389     foreach($rs as $ue) {
390         if (!isset($instances[$ue->enrolid])) {
391             $instances[$ue->enrolid] = $DB->get_record('enrol', array('id'=>$ue->enrolid));
392         }
393         $instance = $instances[$ue->enrolid];
394         $ue->pstatus = ($ue->pstatus == ENROL_USER_ACTIVE) ? ENROL_USER_ACTIVE : ENROL_USER_SUSPENDED;
396         if ($ue->pstatus == ENROL_USER_ACTIVE and !$syncall and $unenrolaction != ENROL_EXT_REMOVED_UNENROL) {
397             // this may be slow if very many users are ignored in sync
398             $parentcontext = context_course::instance($instance->customint1);
399             list($ignoreroles, $params) = $DB->get_in_or_equal($skiproles, SQL_PARAMS_NAMED, 'ri', false, -1);
400             $params['contextid'] = $parentcontext->id;
401             $params['userid'] = $ue->userid;
402             $select = "contextid = :contextid AND userid = :userid AND component <> 'enrol_meta' AND roleid $ignoreroles";
403             if (!$DB->record_exists_select('role_assignments', $select, $params)) {
404                 // bad luck, this user does not have any role we want in parent course
405                 if ($verbose) {
406                     mtrace("  skipping unsuspending: $ue->userid ==> $instance->courseid (user without role)");
407                 }
408                 continue;
409             }
410         }
412         $meta->update_user_enrol($instance, $ue->userid, $ue->pstatus);
413         if ($verbose) {
414             if ($ue->pstatus == ENROL_USER_ACTIVE) {
415                 mtrace("  unsuspending: $ue->userid ==> $instance->courseid");
416             } else {
417                 mtrace("  suspending: $ue->userid ==> $instance->courseid");
418             }
419         }
420     }
421     $rs->close();
424     // now assign all necessary roles
425     $enabled = explode(',', $CFG->enrol_plugins_enabled);
426     foreach($enabled as $k=>$v) {
427         if ($v === 'meta') {
428             continue; // no meta sync of meta roles
429         }
430         $enabled[$k] = 'enrol_'.$v;
431     }
432     $enabled[] = ''; // manual assignments are replicated too
434     $onecourse = $courseid ? "AND e.courseid = :courseid" : "";
435     list($enabled, $params) = $DB->get_in_or_equal($enabled, SQL_PARAMS_NAMED, 'e');
436     $params['coursecontext'] = CONTEXT_COURSE;
437     $params['courseid'] = $courseid;
438     $params['activeuser'] = ENROL_USER_ACTIVE;
439     $params['enabledinstance'] = ENROL_INSTANCE_ENABLED;
440     $sql = "SELECT DISTINCT pra.roleid, pra.userid, c.id AS contextid, e.id AS enrolid, e.courseid
441               FROM {role_assignments} pra
442               JOIN {user} u ON (u.id = pra.userid AND u.deleted = 0)
443               JOIN {context} pc ON (pc.id = pra.contextid AND pc.contextlevel = :coursecontext AND pra.component $enabled)
444               JOIN {enrol} e ON (e.customint1 = pc.instanceid AND e.enrol = 'meta' $onecourse AND e.status = :enabledinstance)
445               JOIN {user_enrolments} ue ON (ue.enrolid = e.id AND ue.userid = u.id AND ue.status = :activeuser)
446               JOIN {context} c ON (c.contextlevel = pc.contextlevel AND c.instanceid = e.courseid)
447          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')
448              WHERE ra.id IS NULL";
450     if ($ignored = $meta->get_config('nosyncroleids')) {
451         list($notignored, $xparams) = $DB->get_in_or_equal(explode(',', $ignored), SQL_PARAMS_NAMED, 'ig', false);
452         $params = array_merge($params, $xparams);
453         $sql = "$sql AND pra.roleid $notignored";
454     }
456     $rs = $DB->get_recordset_sql($sql, $params);
457     foreach($rs as $ra) {
458         role_assign($ra->roleid, $ra->userid, $ra->contextid, 'enrol_meta', $ra->enrolid);
459         if ($verbose) {
460             mtrace("  assigning role: $ra->userid ==> $ra->courseid as ".$allroles[$ra->roleid]->shortname);
461         }
462     }
463     $rs->close();
466     // remove unwanted roles - include ignored roles and disabled plugins too
467     $onecourse = $courseid ? "AND e.courseid = :courseid" : "";
468     $params = array();
469     $params['coursecontext'] = CONTEXT_COURSE;
470     $params['courseid'] = $courseid;
471     $params['activeuser'] = ENROL_USER_ACTIVE;
472     $params['enabledinstance'] = ENROL_INSTANCE_ENABLED;
473     if ($ignored = $meta->get_config('nosyncroleids')) {
474         list($notignored, $xparams) = $DB->get_in_or_equal(explode(',', $ignored), SQL_PARAMS_NAMED, 'ig', false);
475         $params = array_merge($params, $xparams);
476         $notignored = "AND pra.roleid $notignored";
477     } else {
478         $notignored = "";
479     }
481     $sql = "SELECT ra.roleid, ra.userid, ra.contextid, ra.itemid, e.courseid
482               FROM {role_assignments} ra
483               JOIN {enrol} e ON (e.id = ra.itemid AND ra.component = 'enrol_meta' AND e.enrol = 'meta' $onecourse)
484               JOIN {context} pc ON (pc.instanceid = e.customint1 AND pc.contextlevel = :coursecontext)
485          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)
486          LEFT JOIN {user_enrolments} ue ON (ue.enrolid = e.id AND ue.userid = ra.userid AND ue.status = :activeuser)
487              WHERE pra.id IS NULL OR ue.id IS NULL OR e.status <> :enabledinstance";
489     if ($unenrolaction != ENROL_EXT_REMOVED_SUSPEND) {
490         $rs = $DB->get_recordset_sql($sql, $params);
491         foreach($rs as $ra) {
492             role_unassign($ra->roleid, $ra->userid, $ra->contextid, 'enrol_meta', $ra->itemid);
493             if ($verbose) {
494                 mtrace("  unassigning role: $ra->userid ==> $ra->courseid as ".$allroles[$ra->roleid]->shortname);
495             }
496         }
497         $rs->close();
498     }
501     // kick out or suspend users without synced roles if syncall disabled
502     if (!$syncall) {
503         if ($unenrolaction == ENROL_EXT_REMOVED_UNENROL) {
504             $onecourse = $courseid ? "AND e.courseid = :courseid" : "";
505             $params = array();
506             $params['coursecontext'] = CONTEXT_COURSE;
507             $params['courseid'] = $courseid;
508             $sql = "SELECT ue.userid, ue.enrolid
509                       FROM {user_enrolments} ue
510                       JOIN {enrol} e ON (e.id = ue.enrolid AND e.enrol = 'meta' $onecourse)
511                       JOIN {context} c ON (e.courseid = c.instanceid AND c.contextlevel = :coursecontext)
512                  LEFT JOIN {role_assignments} ra ON (ra.contextid = c.id AND ra.itemid = e.id AND ra.userid = ue.userid)
513                      WHERE ra.id IS NULL";
514             $ues = $DB->get_recordset_sql($sql, $params);
515             foreach($ues as $ue) {
516                 if (!isset($instances[$ue->enrolid])) {
517                     $instances[$ue->enrolid] = $DB->get_record('enrol', array('id'=>$ue->enrolid));
518                 }
519                 $instance = $instances[$ue->enrolid];
520                 $meta->unenrol_user($instance, $ue->userid);
521                 if ($verbose) {
522                     mtrace("  unenrolling: $ue->userid ==> $instance->courseid (user without role)");
523                 }
524             }
525             $ues->close();
527         } else {
528             // just suspend the users
529             $onecourse = $courseid ? "AND e.courseid = :courseid" : "";
530             $params = array();
531             $params['coursecontext'] = CONTEXT_COURSE;
532             $params['courseid'] = $courseid;
533             $params['active'] = ENROL_USER_ACTIVE;
534             $sql = "SELECT ue.userid, ue.enrolid
535                       FROM {user_enrolments} ue
536                       JOIN {enrol} e ON (e.id = ue.enrolid AND e.enrol = 'meta' $onecourse)
537                       JOIN {context} c ON (e.courseid = c.instanceid AND c.contextlevel = :coursecontext)
538                  LEFT JOIN {role_assignments} ra ON (ra.contextid = c.id AND ra.itemid = e.id AND ra.userid = ue.userid)
539                      WHERE ra.id IS NULL AND ue.status = :active";
540             $ues = $DB->get_recordset_sql($sql, $params);
541             foreach($ues as $ue) {
542                 if (!isset($instances[$ue->enrolid])) {
543                     $instances[$ue->enrolid] = $DB->get_record('enrol', array('id'=>$ue->enrolid));
544                 }
545                 $instance = $instances[$ue->enrolid];
546                 $meta->update_user_enrol($instance, $ue->userid, ENROL_USER_SUSPENDED);
547                 if ($verbose) {
548                     mtrace("  suspending: $ue->userid ==> $instance->courseid (user without role)");
549                 }
550             }
551             $ues->close();
552         }
553     }
555     // Finally sync groups.
556     $affectedusers = groups_sync_with_enrolment('meta', $courseid);
557     if ($verbose) {
558         foreach ($affectedusers['removed'] as $gm) {
559             mtrace("removing user from group: $gm->userid ==> $gm->courseid - $gm->groupname", 1);
560         }
561         foreach ($affectedusers['added'] as $ue) {
562             mtrace("adding user to group: $ue->userid ==> $ue->courseid - $ue->groupname", 1);
563         }
564     }
566     if ($verbose) {
567         mtrace('...user enrolment synchronisation finished.');
568     }
570     return 0;
573 /**
574  * Create a new group with the course's name.
575  *
576  * @param int $courseid
577  * @param int $linkedcourseid
578  * @return int $groupid Group ID for this cohort.
579  */
580 function enrol_meta_create_new_group($courseid, $linkedcourseid) {
581     global $DB, $CFG;
583     require_once($CFG->dirroot.'/group/lib.php');
585     $coursename = $DB->get_field('course', 'fullname', array('id' => $linkedcourseid), MUST_EXIST);
586     $a = new stdClass();
587     $a->name = $coursename;
588     $a->increment = '';
589     $inc = 1;
590     $groupname = trim(get_string('defaultgroupnametext', 'enrol_meta', $a));
591     // Check to see if the group name already exists in this course. Add an incremented number if it does.
592     while ($DB->record_exists('groups', array('name' => $groupname, 'courseid' => $courseid))) {
593         $a->increment = '(' . (++$inc) . ')';
594         $groupname = trim(get_string('defaultgroupnametext', 'enrol_meta', $a));
595     }
596     // Create a new group for the course meta sync.
597     $groupdata = new stdClass();
598     $groupdata->courseid = $courseid;
599     $groupdata->name = $groupname;
600     $groupid = groups_create_group($groupdata);
602     return $groupid;