enrol/meta MDL-25808 - error when deleting metacourse
[moodle.git] / enrol / meta / locallib.php
CommitLineData
df997f84
PS
1<?php
2
3// This file is part of Moodle - http://moodle.org/
4//
5// Moodle is free software: you can redistribute it and/or modify
6// it under the terms of the GNU General Public License as published by
7// the Free Software Foundation, either version 3 of the License, or
8// (at your option) any later version.
9//
10// Moodle is distributed in the hope that it will be useful,
11// but WITHOUT ANY WARRANTY; without even the implied warranty of
12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13// GNU General Public License for more details.
14//
15// You should have received a copy of the GNU General Public License
16// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
17
18/**
19 * Local stuff for meta course enrolment plugin.
20 *
e2382027
PS
21 * @package enrol
22 * @subpackage meta
23 * @copyright 2010 Petr Skoda {@link http://skodak.org}
24 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
df997f84
PS
25 */
26
e2382027 27defined('MOODLE_INTERNAL') || die();
df997f84
PS
28
29/**
30 * Event handler for meta enrolment plugin.
31 *
32 * We try to keep everything in sync via listening to events,
33 * it may fail sometimes, so we always do a full sync in cron too.
34 */
35class enrol_meta_handler {
36 public function role_assigned($ra) {
37 global $DB;
38
39 if (!enrol_is_enabled('meta')) {
40 return true;
41 }
42
43 // prevent circular dependencies - we can not sync meta roles recursively
44 if ($ra->component === 'enrol_meta') {
45 return true;
46 }
47
48 //only course level roles are interesting
49 $parentcontext = get_context_instance_by_id($ra->contextid);
50 if ($parentcontext->contextlevel != CONTEXT_COURSE) {
51 return true;
52 }
53
54 // does anything want to sync with this parent?
55 if (!$enrols = $DB->get_records('enrol', array('customint1'=>$parentcontext->instanceid, 'enrol'=>'meta'), 'id ASC')) {
56 return true;
57 }
58
59 // make sure the role sync is not prevented
60 $plugin = enrol_get_plugin('meta');
61 if ($disabled = $plugin->get_config('nosyncroleids')) {
62 if (in_array($ra->roleid, explode(',', $disabled))) {
76ce0f34 63 return true;
df997f84
PS
64 }
65 }
66
67 foreach ($enrols as $enrol) {
68 // Is the user enrolled? We want to sync only really enrolled users
69 if (!$DB->record_exists('user_enrolments', array('userid'=>$ra->userid, 'enrolid'=>$enrol->id))) {
70 continue;
71 }
72 $context = get_context_instance(CONTEXT_COURSE, $enrol->courseid);
73
74 // just try to assign role, no problem if role assignment already exists
75 role_assign($ra->roleid, $ra->userid, $context->id, 'enrol_meta', $enrol->id);
76 }
77
78 return true;
79 }
80
81 public function role_unassigned($ra) {
82 global $DB;
83
84 //note: do not test if plugin enabled, we want to keep removing previous roles
85
86 // prevent circular dependencies - we can not sync meta roles recursively
87 if ($ra->component === 'enrol_meta') {
88 return true;
89 }
90
91 // only course level roles are interesting
92 $parentcontext = get_context_instance_by_id($ra->contextid);
93 if ($parentcontext->contextlevel != CONTEXT_COURSE) {
94 return true;
95 }
96
97 // does anything want to sync with this parent?
98 if (!$enrols = $DB->get_records('enrol', array('customint1'=>$parentcontext->instanceid, 'enrol'=>'meta'), 'id ASC')) {
99 return true;
100 }
101
102 //note: do not check 'nosyncroleids', somebody might have just enabled it, we want to get rid of nosync roles gradually
103
104 foreach ($enrols as $enrol) {
105 // Is the user enrolled? We want to sync only really enrolled users
106 if (!$DB->record_exists('user_enrolments', array('userid'=>$ra->userid, 'enrolid'=>$enrol->id))) {
107 continue;
108 }
109 $context = get_context_instance(CONTEXT_COURSE, $enrol->courseid);
110
111 // now make sure the user does not have the role through some other enrol plugin
112 $params = array('contextid'=>$ra->contextid, 'roleid'=>$ra->roleid, 'userid'=>$ra->userid);
113 if ($DB->record_exists_select('role_assignments', "contextid = :contextid AND roleid = :roleid AND userid = :userid AND component <> 'enrol_meta'", $params)) {
114 continue;
115 }
116
117 // unassing role, there is no other role assignment in parent course
118 role_unassign($ra->roleid, $ra->userid, $ra->contextid, 'enrol_meta', $enrol->id);
119 }
120
121 return true;
122 }
123
124 public function user_enrolled($ue) {
125 global $DB;
126
127 if (!enrol_is_enabled('meta')) {
128 return true;
129 }
130
131 // does anything want to sync with this parent?
132 if (!$enrols = $DB->get_records('enrol', array('customint1'=>$ue->courseid, 'enrol'=>'meta'), 'id ASC')) {
133 return true;
134 }
135
136 $plugin = enrol_get_plugin('meta');
137 foreach ($enrols as $enrol) {
138 // no problem if already enrolled
139 $plugin->enrol_user($enrol, $ue->userid);
140 }
141
142 return true;
143 }
144
145 public function user_unenrolled($ue) {
146 global $DB;
147
148 //note: do not test if plugin enabled, we want to keep removing previously linked courses
149
150 // look for unenrolment candidates - it may be possible that user has multiple enrolments...
424d60d9 151 $sql = "SELECT e.*
df997f84
PS
152 FROM {enrol} e
153 JOIN {user_enrolments} ue ON (ue.enrolid = e.id AND ue.userid = :userid)
154 JOIN {enrol} pe ON (pe.courseid = e.customint1 AND pe.enrol <> 'meta' AND pe.courseid = :courseid)
155 LEFT JOIN {user_enrolments} pue ON (pue.enrolid = pe.id AND pue.userid = ue.userid)
156 WHERE pue.id IS NULL";
157 $params = array('courseid'=>$ue->courseid, 'userid'=>$ue->userid);
158
424d60d9 159 $rs = $DB->get_recordset_sql($sql, $params);
df997f84
PS
160
161 $plugin = enrol_get_plugin('meta');
424d60d9 162 foreach ($rs as $enrol) {
df997f84
PS
163 $plugin->unenrol_user($enrol, $ue->userid);
164 }
424d60d9 165 $rs->close();
df997f84
PS
166
167 return true;
168 }
169
170 public function course_deleted($course) {
171 global $DB;
172
173 //note: do not test if plugin enabled, we want to keep removing previously linked courses
174
175 // does anything want to sync with this parent?
869483b6 176 if (!$enrols = $DB->get_records('enrol', array('customint1'=>$course->id, 'enrol'=>'meta'), 'id ASC')) {
df997f84
PS
177 return true;
178 }
179
180 $plugin = enrol_get_plugin('meta');
181 foreach ($enrols as $enrol) {
182 //unenrol all users
85fd37d3 183 $ues = $DB->get_recordset('user_enrolments', array('enrolid'=>$enrol->id));
df997f84
PS
184 foreach ($ues as $ue) {
185 $plugin->unenrol_user($enrol, $ue->userid);
186 }
187 $ues->close();
188 }
189
190 return true;
191 }
192}
193
194/**
195 * Sync all meta course links.
196 * @param int $courseid one course, empty mean all
197 * @return void
198 */
199function enrol_meta_sync($courseid = NULL) {
200 global $CFG, $DB;
201
202 // unfortunately this may take a loooong time
203 @set_time_limit(0); //if this fails during upgrade we can continue from cron, no big deal
204
205 $meta = enrol_get_plugin('meta');
206
207 $onecourse = $courseid ? "AND e.courseid = :courseid" : "";
208
209 // iterate through all not enrolled yet users
210 if (enrol_is_enabled('meta')) {
211 list($enabled, $params) = $DB->get_in_or_equal(explode(',', $CFG->enrol_plugins_enabled), SQL_PARAMS_NAMED, 'e00');
212 $onecourse = "";
213 if ($courseid) {
214 $params['courseid'] = $courseid;
215 $onecourse = "AND e.courseid = :courseid";
216 }
217 $sql = "SELECT pue.userid, e.id AS enrolid
218 FROM {user_enrolments} pue
219 JOIN {enrol} pe ON (pe.id = pue.enrolid AND pe.enrol <> 'meta' AND pe.enrol $enabled )
220 JOIN {enrol} e ON (e.customint1 = pe.courseid AND e.enrol = 'meta' AND e.status = :statusenabled $onecourse)
221 LEFT JOIN {user_enrolments} ue ON (ue.enrolid = e.id AND ue.userid = pue.userid)
222 WHERE ue.id IS NULL";
223 $params['statusenabled'] = ENROL_INSTANCE_ENABLED;
224 $params['courseid'] = $courseid;
225
226 $rs = $DB->get_recordset_sql($sql, $params);
227 $instances = array(); //cache
228 foreach($rs as $ue) {
229 if (!isset($instances[$ue->enrolid])) {
230 $instances[$ue->enrolid] = $DB->get_record('enrol', array('id'=>$ue->enrolid));
231 }
232 $meta->enrol_user($instances[$ue->enrolid], $ue->userid);
233 }
234 $rs->close();
235 unset($instances);
236 }
237
238 // unenrol as necessary - ignore enabled flag, we want to get rid of all
239 $sql = "SELECT ue.userid, e.id AS enrolid
240 FROM {user_enrolments} ue
241 JOIN {enrol} e ON (e.id = ue.enrolid AND e.enrol = 'meta' $onecourse)
242 LEFT JOIN (SELECT xue.userid, xe.courseid
243 FROM {enrol} xe
244 JOIN {user_enrolments} xue ON (xue.enrolid = xe.id)
245 ) pue ON (pue.courseid = e.customint1 AND pue.userid = ue.userid)
246 WHERE pue.courseid IS NULL";
247 //TODO: this may use a bit of SQL optimisation
248 $rs = $DB->get_recordset_sql($sql, array('courseid'=>$courseid));
249 $instances = array(); //cache
250 foreach($rs as $ue) {
251 if (!isset($instances[$ue->enrolid])) {
252 $instances[$ue->enrolid] = $DB->get_record('enrol', array('id'=>$ue->enrolid));
253 }
254 $meta->unenrol_user($instances[$ue->enrolid], $ue->userid);
255 }
256 $rs->close();
257 unset($instances);
258
259 // now assign all necessary roles
260 if (enrol_is_enabled('meta')) {
261 $enabled = explode(',', $CFG->enrol_plugins_enabled);
262 foreach($enabled as $k=>$v) {
263 if ($v === 'meta') {
264 continue; // no meta sync of meta roles
265 }
266 $enabled[$k] = 'enrol_'.$v;
267 }
268 $enabled[] = $DB->sql_empty(); // manual assignments are replicated too
269
270 list($enabled, $params) = $DB->get_in_or_equal($enabled, SQL_PARAMS_NAMED, 'e00');
271 $sql = "SELECT DISTINCT pra.roleid, pra.userid, c.id AS contextid, e.id AS enrolid
272 FROM {role_assignments} pra
ba572e34 273 JOIN {user} u ON (u.id = pra.userid AND u.deleted = 0)
df997f84
PS
274 JOIN {context} pc ON (pc.id = pra.contextid AND pc.contextlevel = :coursecontext AND pra.component $enabled)
275 JOIN {enrol} e ON (e.customint1 = pc.instanceid AND e.enrol = 'meta' AND e.status = :statusenabled $onecourse)
276 JOIN {context} c ON (c.contextlevel = pc.contextlevel AND c.instanceid = e.courseid)
277 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')
278 WHERE ra.id IS NULL";
279 $params['statusenabled'] = ENROL_INSTANCE_ENABLED;
280 $params['coursecontext'] = CONTEXT_COURSE;
281 $params['courseid'] = $courseid;
282
283 if ($ignored = $meta->get_config('nosyncroleids')) {
284 list($notignored, $xparams) = $DB->get_in_or_equal(explode(',', $ignored), SQL_PARAMS_NAMED, 'i00', false);
285 $params = array_merge($params, $xparams);
286 $sql = "$sql AND pra.roleid $notignored";
287 }
288
289 $rs = $DB->get_recordset_sql($sql, $params);
290 foreach($rs as $ra) {
291 role_assign($ra->roleid, $ra->userid, $ra->contextid, 'enrol_meta', $ra->enrolid);
292 }
293 $rs->close();
294 }
295
296 // remove unwanted roles - include ignored roles and disabled plugins too
297 $params = array('coursecontext' => CONTEXT_COURSE, 'courseid' => $courseid);
298 if ($ignored = $meta->get_config('nosyncroleids')) {
299 list($notignored, $xparams) = $DB->get_in_or_equal(explode(',', $ignored), SQL_PARAMS_NAMED, 'i00', false);
300 $params = array_merge($params, $xparams);
301 $notignored = "AND pra.roleid $notignored";
302 } else {
303 $notignored = "";
304 }
305 $sql = "SELECT ra.roleid, ra.userid, ra.contextid, ra.itemid
306 FROM {role_assignments} ra
307 JOIN {enrol} e ON (e.id = ra.itemid AND ra.component = 'enrol_meta' AND e.enrol = 'meta' $onecourse)
308 JOIN {context} pc ON (pc.instanceid = e.customint1 AND pc.contextlevel = :coursecontext)
309 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)
310 WHERE pra.id IS NULL";
311
312 $rs = $DB->get_recordset_sql($sql, $params);
313 foreach($rs as $ra) {
314 role_unassign($ra->roleid, $ra->userid, $ra->contextid, 'enrol_meta', $ra->itemid);
315 }
316 $rs->close();
317
318}