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.
22 * @copyright 2010 Petr Skoda {@link http://skodak.org}
23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
26 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 {
35 public function role_assigned($ra) {
38 if (!enrol_is_enabled('meta')) {
42 // prevent circular dependencies - we can not sync meta roles recursively
43 if ($ra->component === 'enrol_meta') {
47 // only course level roles are interesting
48 $parentcontext = get_context_instance_by_id($ra->contextid);
49 if ($parentcontext->contextlevel != CONTEXT_COURSE) {
53 // does anything want to sync with this parent?
54 if (!$enrols = $DB->get_records('enrol', array('customint1'=>$parentcontext->instanceid, 'enrol'=>'meta'), 'id ASC')) {
58 // make sure the role sync is not prevented
59 $plugin = enrol_get_plugin('meta');
60 if ($disabled = $plugin->get_config('nosyncroleids')) {
61 if (in_array($ra->roleid, explode(',', $disabled))) {
66 foreach ($enrols as $enrol) {
67 // Is the user enrolled? We want to sync only really enrolled users
68 if (!$DB->record_exists('user_enrolments', array('userid'=>$ra->userid, 'enrolid'=>$enrol->id))) {
71 $context = get_context_instance(CONTEXT_COURSE, $enrol->courseid);
73 // just try to assign role, no problem if role assignment already exists
74 role_assign($ra->roleid, $ra->userid, $context->id, 'enrol_meta', $enrol->id);
80 public function role_unassigned($ra) {
83 // note: do not test if plugin enabled, we want to keep removing previous roles
85 // prevent circular dependencies - we can not sync meta roles recursively
86 if ($ra->component === 'enrol_meta') {
90 // only course level roles are interesting
91 $parentcontext = get_context_instance_by_id($ra->contextid);
92 if ($parentcontext->contextlevel != CONTEXT_COURSE) {
96 // does anything want to sync with this parent?
97 if (!$enrols = $DB->get_records('enrol', array('customint1'=>$parentcontext->instanceid, 'enrol'=>'meta'), 'id ASC')) {
101 // note: do not check 'nosyncroleids', somebody might have just enabled it, we want to get rid of nosync roles gradually
103 foreach ($enrols as $enrol) {
104 // Is the user enrolled? We want to sync only really enrolled users
105 if (!$DB->record_exists('user_enrolments', array('userid'=>$ra->userid, 'enrolid'=>$enrol->id))) {
108 $context = get_context_instance(CONTEXT_COURSE, $enrol->courseid);
110 // now make sure the user does not have the role through some other enrol plugin
111 $params = array('contextid'=>$ra->contextid, 'roleid'=>$ra->roleid, 'userid'=>$ra->userid);
112 if ($DB->record_exists_select('role_assignments', "contextid = :contextid AND roleid = :roleid AND userid = :userid AND component <> 'enrol_meta'", $params)) {
116 // unassign role, there is no other role assignment in parent course
117 role_unassign($ra->roleid, $ra->userid, $context->id, 'enrol_meta', $enrol->id);
123 public function user_enrolled($ue) {
126 if (!enrol_is_enabled('meta')) {
130 if ($ue->enrol === 'meta') {
131 // prevent circular dependencies - we can not sync meta enrolments recursively
135 // does anything want to sync with this parent?
136 if (!$enrols = $DB->get_records('enrol', array('customint1'=>$ue->courseid, 'enrol'=>'meta'), 'id ASC')) {
140 $plugin = enrol_get_plugin('meta');
141 foreach ($enrols as $enrol) {
142 // no problem if already enrolled
143 $plugin->enrol_user($enrol, $ue->userid);
149 public function user_unenrolled($ue) {
152 //note: do not test if plugin enabled, we want to keep removing previously linked courses
154 // look for unenrolment candidates - it may be possible that user has multiple enrolments...
157 JOIN {user_enrolments} ue ON (ue.enrolid = e.id AND ue.userid = :userid)
158 JOIN {enrol} pe ON (pe.courseid = e.customint1 AND pe.enrol <> 'meta' AND pe.courseid = :courseid)
159 LEFT JOIN {user_enrolments} pue ON (pue.enrolid = pe.id AND pue.userid = ue.userid)
160 WHERE pue.id IS NULL AND e.enrol = 'meta'";
161 $params = array('courseid'=>$ue->courseid, 'userid'=>$ue->userid);
163 $rs = $DB->get_recordset_sql($sql, $params);
165 $plugin = enrol_get_plugin('meta');
166 foreach ($rs as $enrol) {
167 $plugin->unenrol_user($enrol, $ue->userid);
174 public function course_deleted($course) {
177 // note: do not test if plugin enabled, we want to keep removing previously linked courses
179 // does anything want to sync with this parent?
180 if (!$enrols = $DB->get_records('enrol', array('customint1'=>$course->id, 'enrol'=>'meta'), 'id ASC')) {
184 $plugin = enrol_get_plugin('meta');
185 foreach ($enrols as $enrol) {
187 $ues = $DB->get_recordset('user_enrolments', array('enrolid'=>$enrol->id));
188 foreach ($ues as $ue) {
189 $plugin->unenrol_user($enrol, $ue->userid);
200 * Sync all meta course links.
201 * @param int $courseid one course, empty mean all
204 function enrol_meta_sync($courseid = NULL) {
207 // unfortunately this may take a loooong time
208 @set_time_limit(0); //if this fails during upgrade we can continue from cron, no big deal
210 $meta = enrol_get_plugin('meta');
212 $onecourse = $courseid ? "AND e.courseid = :courseid" : "";
214 // iterate through all not enrolled yet users
215 if (enrol_is_enabled('meta')) {
216 list($enabled, $params) = $DB->get_in_or_equal(explode(',', $CFG->enrol_plugins_enabled), SQL_PARAMS_NAMED, 'e');
219 $params['courseid'] = $courseid;
220 $onecourse = "AND e.courseid = :courseid";
222 $sql = "SELECT pue.userid, e.id AS enrolid
223 FROM {user_enrolments} pue
224 JOIN {enrol} pe ON (pe.id = pue.enrolid AND pe.enrol <> 'meta' AND pe.enrol $enabled )
225 JOIN {enrol} e ON (e.customint1 = pe.courseid AND e.enrol = 'meta' AND e.status = :statusenabled $onecourse)
226 LEFT JOIN {user_enrolments} ue ON (ue.enrolid = e.id AND ue.userid = pue.userid)
227 WHERE ue.id IS NULL";
228 $params['statusenabled'] = ENROL_INSTANCE_ENABLED;
229 $params['courseid'] = $courseid;
231 $rs = $DB->get_recordset_sql($sql, $params);
232 $instances = array(); //cache
233 foreach($rs as $ue) {
234 if (!isset($instances[$ue->enrolid])) {
235 $instances[$ue->enrolid] = $DB->get_record('enrol', array('id'=>$ue->enrolid));
237 $meta->enrol_user($instances[$ue->enrolid], $ue->userid);
243 // unenrol as necessary - ignore enabled flag, we want to get rid of all
244 $sql = "SELECT ue.userid, e.id AS enrolid
245 FROM {user_enrolments} ue
246 JOIN {enrol} e ON (e.id = ue.enrolid AND e.enrol = 'meta' $onecourse)
247 LEFT JOIN (SELECT xue.userid, xe.courseid
249 JOIN {user_enrolments} xue ON (xue.enrolid = xe.id)
250 ) pue ON (pue.courseid = e.customint1 AND pue.userid = ue.userid)
251 WHERE pue.courseid IS NULL";
252 //TODO: this may use a bit of SQL optimisation
253 $rs = $DB->get_recordset_sql($sql, array('courseid'=>$courseid));
254 $instances = array(); //cache
255 foreach($rs as $ue) {
256 if (!isset($instances[$ue->enrolid])) {
257 $instances[$ue->enrolid] = $DB->get_record('enrol', array('id'=>$ue->enrolid));
259 $meta->unenrol_user($instances[$ue->enrolid], $ue->userid);
264 // now assign all necessary roles
265 if (enrol_is_enabled('meta')) {
266 $enabled = explode(',', $CFG->enrol_plugins_enabled);
267 foreach($enabled as $k=>$v) {
269 continue; // no meta sync of meta roles
271 $enabled[$k] = 'enrol_'.$v;
273 $enabled[] = $DB->sql_empty(); // manual assignments are replicated too
275 list($enabled, $params) = $DB->get_in_or_equal($enabled, SQL_PARAMS_NAMED, 'e');
276 $sql = "SELECT DISTINCT pra.roleid, pra.userid, c.id AS contextid, e.id AS enrolid
277 FROM {role_assignments} pra
278 JOIN {user} u ON (u.id = pra.userid AND u.deleted = 0)
279 JOIN {context} pc ON (pc.id = pra.contextid AND pc.contextlevel = :coursecontext AND pra.component $enabled)
280 JOIN {enrol} e ON (e.customint1 = pc.instanceid AND e.enrol = 'meta' AND e.status = :statusenabled $onecourse)
281 JOIN {context} c ON (c.contextlevel = pc.contextlevel AND c.instanceid = e.courseid)
282 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')
283 WHERE ra.id IS NULL";
284 $params['statusenabled'] = ENROL_INSTANCE_ENABLED;
285 $params['coursecontext'] = CONTEXT_COURSE;
286 $params['courseid'] = $courseid;
288 if ($ignored = $meta->get_config('nosyncroleids')) {
289 list($notignored, $xparams) = $DB->get_in_or_equal(explode(',', $ignored), SQL_PARAMS_NAMED, 'ig', false);
290 $params = array_merge($params, $xparams);
291 $sql = "$sql AND pra.roleid $notignored";
294 $rs = $DB->get_recordset_sql($sql, $params);
295 foreach($rs as $ra) {
296 role_assign($ra->roleid, $ra->userid, $ra->contextid, 'enrol_meta', $ra->enrolid);
301 // remove unwanted roles - include ignored roles and disabled plugins too
302 $params = array('coursecontext' => CONTEXT_COURSE, 'courseid' => $courseid);
303 if ($ignored = $meta->get_config('nosyncroleids')) {
304 list($notignored, $xparams) = $DB->get_in_or_equal(explode(',', $ignored), SQL_PARAMS_NAMED, 'ig', false);
305 $params = array_merge($params, $xparams);
306 $notignored = "AND pra.roleid $notignored";
310 $sql = "SELECT ra.roleid, ra.userid, ra.contextid, ra.itemid
311 FROM {role_assignments} ra
312 JOIN {enrol} e ON (e.id = ra.itemid AND ra.component = 'enrol_meta' AND e.enrol = 'meta' $onecourse)
313 JOIN {context} pc ON (pc.instanceid = e.customint1 AND pc.contextlevel = :coursecontext)
314 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)
315 WHERE pra.id IS NULL";
317 $rs = $DB->get_recordset_sql($sql, $params);
318 foreach($rs as $ra) {
319 role_unassign($ra->roleid, $ra->userid, $ra->contextid, 'enrol_meta', $ra->itemid);