weekly release 2.3dev (blame stronk7 for 0202 mistake)
[moodle.git] / enrol / meta / locallib.php
CommitLineData
df997f84 1<?php
df997f84
PS
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/>.
16
17/**
18 * Local stuff for meta course enrolment plugin.
19 *
e2382027
PS
20 * @package enrol
21 * @subpackage meta
22 * @copyright 2010 Petr Skoda {@link http://skodak.org}
23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
df997f84
PS
24 */
25
e2382027 26defined('MOODLE_INTERNAL') || die();
df997f84
PS
27
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 */
34class enrol_meta_handler {
35 public function role_assigned($ra) {
36 global $DB;
37
38 if (!enrol_is_enabled('meta')) {
39 return true;
40 }
41
42 // prevent circular dependencies - we can not sync meta roles recursively
43 if ($ra->component === 'enrol_meta') {
44 return true;
45 }
46
0a0b535e 47 // only course level roles are interesting
df997f84
PS
48 $parentcontext = get_context_instance_by_id($ra->contextid);
49 if ($parentcontext->contextlevel != CONTEXT_COURSE) {
50 return true;
51 }
52
53 // does anything want to sync with this parent?
54 if (!$enrols = $DB->get_records('enrol', array('customint1'=>$parentcontext->instanceid, 'enrol'=>'meta'), 'id ASC')) {
55 return true;
56 }
57
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))) {
76ce0f34 62 return true;
df997f84
PS
63 }
64 }
65
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))) {
69 continue;
70 }
71 $context = get_context_instance(CONTEXT_COURSE, $enrol->courseid);
72
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);
75 }
76
77 return true;
78 }
79
80 public function role_unassigned($ra) {
81 global $DB;
82
0a0b535e 83 // note: do not test if plugin enabled, we want to keep removing previous roles
df997f84
PS
84
85 // prevent circular dependencies - we can not sync meta roles recursively
86 if ($ra->component === 'enrol_meta') {
87 return true;
88 }
89
90 // only course level roles are interesting
91 $parentcontext = get_context_instance_by_id($ra->contextid);
92 if ($parentcontext->contextlevel != CONTEXT_COURSE) {
93 return true;
94 }
95
96 // does anything want to sync with this parent?
97 if (!$enrols = $DB->get_records('enrol', array('customint1'=>$parentcontext->instanceid, 'enrol'=>'meta'), 'id ASC')) {
98 return true;
99 }
100
0a0b535e 101 // note: do not check 'nosyncroleids', somebody might have just enabled it, we want to get rid of nosync roles gradually
df997f84
PS
102
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))) {
106 continue;
107 }
108 $context = get_context_instance(CONTEXT_COURSE, $enrol->courseid);
109
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)) {
113 continue;
114 }
115
0a0b535e 116 // unassign role, there is no other role assignment in parent course
5b2904c8 117 role_unassign($ra->roleid, $ra->userid, $context->id, 'enrol_meta', $enrol->id);
df997f84
PS
118 }
119
120 return true;
121 }
122
123 public function user_enrolled($ue) {
124 global $DB;
125
126 if (!enrol_is_enabled('meta')) {
127 return true;
128 }
129
fc7cd35c
PS
130 if ($ue->enrol === 'meta') {
131 // prevent circular dependencies - we can not sync meta enrolments recursively
132 return true;
133 }
134
df997f84
PS
135 // does anything want to sync with this parent?
136 if (!$enrols = $DB->get_records('enrol', array('customint1'=>$ue->courseid, 'enrol'=>'meta'), 'id ASC')) {
137 return true;
138 }
139
140 $plugin = enrol_get_plugin('meta');
141 foreach ($enrols as $enrol) {
379434e4
PS
142 if ($ue->status == ENROL_USER_ACTIVE) {
143 $status = ENROL_USER_ACTIVE;
144 } else {
145 $context = get_context_instance(CONTEXT_COURSE, $enrol->courseid);
146 if (is_enrolled($context, $ue->userid)) {
147 // user already has active enrolment, do not change it
148 $status = ENROL_USER_ACTIVE;
149 } else {
150 $status = $ue->status;
151 }
152
153 }
df997f84 154 // no problem if already enrolled
379434e4 155 $plugin->enrol_user($enrol, $ue->userid, $status);
df997f84
PS
156 }
157
158 return true;
159 }
160
161 public function user_unenrolled($ue) {
162 global $DB;
163
164 //note: do not test if plugin enabled, we want to keep removing previously linked courses
165
166 // look for unenrolment candidates - it may be possible that user has multiple enrolments...
424d60d9 167 $sql = "SELECT e.*
df997f84
PS
168 FROM {enrol} e
169 JOIN {user_enrolments} ue ON (ue.enrolid = e.id AND ue.userid = :userid)
170 JOIN {enrol} pe ON (pe.courseid = e.customint1 AND pe.enrol <> 'meta' AND pe.courseid = :courseid)
171 LEFT JOIN {user_enrolments} pue ON (pue.enrolid = pe.id AND pue.userid = ue.userid)
c2f53f01 172 WHERE pue.id IS NULL AND e.enrol = 'meta'";
df997f84
PS
173 $params = array('courseid'=>$ue->courseid, 'userid'=>$ue->userid);
174
424d60d9 175 $rs = $DB->get_recordset_sql($sql, $params);
df997f84
PS
176
177 $plugin = enrol_get_plugin('meta');
424d60d9 178 foreach ($rs as $enrol) {
df997f84
PS
179 $plugin->unenrol_user($enrol, $ue->userid);
180 }
424d60d9 181 $rs->close();
df997f84
PS
182
183 return true;
184 }
185
379434e4
PS
186 public function user_enrol_modified($ue) {
187 global $DB;
188
189 // update enrolment status if necessary
190
191 if (!enrol_is_enabled('meta')) {
192 return true;
193 }
194
195 if ($ue->enrol === 'meta') {
196 // prevent circular dependencies - we can not sync meta enrolments recursively
197 return true;
198 }
199
200 // does anything want to sync with this parent?
201 if (!$enrols = $DB->get_records('enrol', array('customint1'=>$ue->courseid, 'enrol'=>'meta'), 'id ASC')) {
202 return true;
203 }
204
205 $plugin = enrol_get_plugin('meta');
206 foreach ($enrols as $enrol) {
207 $plugin->update_user_enrol($enrol, $ue->userid, $ue->status);
208 }
209
210 return true;
211 }
212
df997f84
PS
213 public function course_deleted($course) {
214 global $DB;
215
0a0b535e 216 // note: do not test if plugin enabled, we want to keep removing previously linked courses
df997f84
PS
217
218 // does anything want to sync with this parent?
869483b6 219 if (!$enrols = $DB->get_records('enrol', array('customint1'=>$course->id, 'enrol'=>'meta'), 'id ASC')) {
df997f84
PS
220 return true;
221 }
222
223 $plugin = enrol_get_plugin('meta');
224 foreach ($enrols as $enrol) {
0a0b535e 225 // unenrol all users
85fd37d3 226 $ues = $DB->get_recordset('user_enrolments', array('enrolid'=>$enrol->id));
df997f84
PS
227 foreach ($ues as $ue) {
228 $plugin->unenrol_user($enrol, $ue->userid);
229 }
230 $ues->close();
231 }
232
233 return true;
234 }
235}
236
0a0b535e 237
df997f84
PS
238/**
239 * Sync all meta course links.
240 * @param int $courseid one course, empty mean all
241 * @return void
242 */
243function enrol_meta_sync($courseid = NULL) {
244 global $CFG, $DB;
245
246 // unfortunately this may take a loooong time
247 @set_time_limit(0); //if this fails during upgrade we can continue from cron, no big deal
248
379434e4
PS
249 $instances = array(); //cache
250
df997f84
PS
251 $meta = enrol_get_plugin('meta');
252
253 $onecourse = $courseid ? "AND e.courseid = :courseid" : "";
254
255 // iterate through all not enrolled yet users
256 if (enrol_is_enabled('meta')) {
cf717dc2 257 list($enabled, $params) = $DB->get_in_or_equal(explode(',', $CFG->enrol_plugins_enabled), SQL_PARAMS_NAMED, 'e');
df997f84
PS
258 if ($courseid) {
259 $params['courseid'] = $courseid;
df997f84
PS
260 }
261 $sql = "SELECT pue.userid, e.id AS enrolid
262 FROM {user_enrolments} pue
263 JOIN {enrol} pe ON (pe.id = pue.enrolid AND pe.enrol <> 'meta' AND pe.enrol $enabled )
264 JOIN {enrol} e ON (e.customint1 = pe.courseid AND e.enrol = 'meta' AND e.status = :statusenabled $onecourse)
265 LEFT JOIN {user_enrolments} ue ON (ue.enrolid = e.id AND ue.userid = pue.userid)
266 WHERE ue.id IS NULL";
267 $params['statusenabled'] = ENROL_INSTANCE_ENABLED;
268 $params['courseid'] = $courseid;
269
270 $rs = $DB->get_recordset_sql($sql, $params);
df997f84
PS
271 foreach($rs as $ue) {
272 if (!isset($instances[$ue->enrolid])) {
273 $instances[$ue->enrolid] = $DB->get_record('enrol', array('id'=>$ue->enrolid));
274 }
275 $meta->enrol_user($instances[$ue->enrolid], $ue->userid);
276 }
277 $rs->close();
df997f84
PS
278 }
279
280 // unenrol as necessary - ignore enabled flag, we want to get rid of all
281 $sql = "SELECT ue.userid, e.id AS enrolid
282 FROM {user_enrolments} ue
283 JOIN {enrol} e ON (e.id = ue.enrolid AND e.enrol = 'meta' $onecourse)
284 LEFT JOIN (SELECT xue.userid, xe.courseid
285 FROM {enrol} xe
286 JOIN {user_enrolments} xue ON (xue.enrolid = xe.id)
287 ) pue ON (pue.courseid = e.customint1 AND pue.userid = ue.userid)
288 WHERE pue.courseid IS NULL";
289 //TODO: this may use a bit of SQL optimisation
290 $rs = $DB->get_recordset_sql($sql, array('courseid'=>$courseid));
df997f84
PS
291 foreach($rs as $ue) {
292 if (!isset($instances[$ue->enrolid])) {
293 $instances[$ue->enrolid] = $DB->get_record('enrol', array('id'=>$ue->enrolid));
294 }
295 $meta->unenrol_user($instances[$ue->enrolid], $ue->userid);
296 }
297 $rs->close();
df997f84
PS
298
299 // now assign all necessary roles
300 if (enrol_is_enabled('meta')) {
301 $enabled = explode(',', $CFG->enrol_plugins_enabled);
302 foreach($enabled as $k=>$v) {
303 if ($v === 'meta') {
304 continue; // no meta sync of meta roles
305 }
306 $enabled[$k] = 'enrol_'.$v;
307 }
308 $enabled[] = $DB->sql_empty(); // manual assignments are replicated too
309
cf717dc2 310 list($enabled, $params) = $DB->get_in_or_equal($enabled, SQL_PARAMS_NAMED, 'e');
df997f84
PS
311 $sql = "SELECT DISTINCT pra.roleid, pra.userid, c.id AS contextid, e.id AS enrolid
312 FROM {role_assignments} pra
ba572e34 313 JOIN {user} u ON (u.id = pra.userid AND u.deleted = 0)
df997f84
PS
314 JOIN {context} pc ON (pc.id = pra.contextid AND pc.contextlevel = :coursecontext AND pra.component $enabled)
315 JOIN {enrol} e ON (e.customint1 = pc.instanceid AND e.enrol = 'meta' AND e.status = :statusenabled $onecourse)
316 JOIN {context} c ON (c.contextlevel = pc.contextlevel AND c.instanceid = e.courseid)
317 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')
318 WHERE ra.id IS NULL";
319 $params['statusenabled'] = ENROL_INSTANCE_ENABLED;
320 $params['coursecontext'] = CONTEXT_COURSE;
321 $params['courseid'] = $courseid;
322
323 if ($ignored = $meta->get_config('nosyncroleids')) {
cf717dc2 324 list($notignored, $xparams) = $DB->get_in_or_equal(explode(',', $ignored), SQL_PARAMS_NAMED, 'ig', false);
df997f84
PS
325 $params = array_merge($params, $xparams);
326 $sql = "$sql AND pra.roleid $notignored";
327 }
328
329 $rs = $DB->get_recordset_sql($sql, $params);
330 foreach($rs as $ra) {
331 role_assign($ra->roleid, $ra->userid, $ra->contextid, 'enrol_meta', $ra->enrolid);
332 }
333 $rs->close();
334 }
335
336 // remove unwanted roles - include ignored roles and disabled plugins too
337 $params = array('coursecontext' => CONTEXT_COURSE, 'courseid' => $courseid);
338 if ($ignored = $meta->get_config('nosyncroleids')) {
cf717dc2 339 list($notignored, $xparams) = $DB->get_in_or_equal(explode(',', $ignored), SQL_PARAMS_NAMED, 'ig', false);
df997f84
PS
340 $params = array_merge($params, $xparams);
341 $notignored = "AND pra.roleid $notignored";
342 } else {
343 $notignored = "";
344 }
345 $sql = "SELECT ra.roleid, ra.userid, ra.contextid, ra.itemid
346 FROM {role_assignments} ra
347 JOIN {enrol} e ON (e.id = ra.itemid AND ra.component = 'enrol_meta' AND e.enrol = 'meta' $onecourse)
348 JOIN {context} pc ON (pc.instanceid = e.customint1 AND pc.contextlevel = :coursecontext)
349 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)
350 WHERE pra.id IS NULL";
351
352 $rs = $DB->get_recordset_sql($sql, $params);
353 foreach($rs as $ra) {
354 role_unassign($ra->roleid, $ra->userid, $ra->contextid, 'enrol_meta', $ra->itemid);
355 }
356 $rs->close();
357
379434e4
PS
358 // sync enrolment status
359 if (enrol_is_enabled('meta')) {
dcb9a0c1
PS
360 $enabled = explode(',', $CFG->enrol_plugins_enabled);
361 foreach($enabled as $k=>$v) {
362 if ($v === 'meta') {
363 unset($enabled[$k]);
364 }
365 }
366 list($enabled, $params) = $DB->get_in_or_equal($enabled, SQL_PARAMS_NAMED, 'e');
379434e4
PS
367 if ($courseid) {
368 $params['courseid'] = $courseid;
369 }
370 //note: this will probably take a long time on mysql...
371 $sql = "SELECT ue.userid, e.id AS enrolid
372 FROM {user_enrolments} ue
373 JOIN {enrol} e ON (e.id = ue.enrolid AND e.enrol = 'meta' AND e.status = :statusenabled $onecourse)
dcb9a0c1
PS
374 LEFT JOIN (SELECT pue.id, pue.userid, pe.courseid
375 FROM {user_enrolments} pue
376 JOIN {enrol} pe ON (pe.enrol $enabled)
377 WHERE pue.enrolid = pe.id AND pue.status = :activestatus2) xx ON (xx.userid = ue.userid AND xx.courseid = e.customint1)
378 WHERE ue.status = :activestatus1 AND xx.id IS NULL";
379434e4
PS
379 $params['statusenabled'] = ENROL_INSTANCE_ENABLED;
380 $params['activestatus1'] = ENROL_USER_ACTIVE;
381 $params['activestatus2'] = ENROL_USER_ACTIVE;
382 $params['courseid'] = $courseid;
383
384 $rs = $DB->get_recordset_sql($sql, $params);
385 foreach($rs as $ue) {
386 if (!isset($instances[$ue->enrolid])) {
387 $instances[$ue->enrolid] = $DB->get_record('enrol', array('id'=>$ue->enrolid));
388 }
389 $meta->update_user_enrol($instances[$ue->enrolid], $ue->userid, ENROL_USER_SUSPENDED);
390 }
391 $rs->close();
392
393 list($enabled, $params) = $DB->get_in_or_equal(explode(',', $CFG->enrol_plugins_enabled), SQL_PARAMS_NAMED, 'e');
394 if ($courseid) {
395 $params['courseid'] = $courseid;
396 }
dcb9a0c1
PS
397 // enable if at least one enrolment active in linked course
398 $sql = "SELECT DISTINCT ue.userid, e.id AS enrolid
379434e4
PS
399 FROM {user_enrolments} ue
400 JOIN {enrol} e ON (e.id = ue.enrolid AND e.enrol = 'meta' AND e.status = :statusenabled $onecourse)
dcb9a0c1
PS
401 JOIN {enrol} pe ON (pe.courseid = e.customint1 AND pe.enrol $enabled)
402 JOIN {user_enrolments} pue ON (pue.enrolid = pe.id AND pue.userid = ue.userid AND pue.status = :activestatus)
403 WHERE ue.status = :suspendedstatus";
379434e4
PS
404 $params['statusenabled'] = ENROL_INSTANCE_ENABLED;
405 $params['suspendedstatus'] = ENROL_USER_SUSPENDED;
406 $params['activestatus'] = ENROL_USER_ACTIVE;
407 $params['courseid'] = $courseid;
408 $rs = $DB->get_recordset_sql($sql, $params);
409 foreach($rs as $ue) {
410 if (!isset($instances[$ue->enrolid])) {
411 $instances[$ue->enrolid] = $DB->get_record('enrol', array('id'=>$ue->enrolid));
412 }
413 $meta->update_user_enrol($instances[$ue->enrolid], $ue->userid, ENROL_USER_ACTIVE);
414 }
415 $rs->close();
416
417 }
418
df997f84 419}