f62650451f2a4af1af871b9a8b2bc6fb63e261e2
[moodle.git] / enrol / meta / locallib.php
1 <?php
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/>.
18 /**
19  * Local stuff for meta course enrolment plugin.
20  *
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
25  */
27 defined('MOODLE_INTERNAL') || die();
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  */
35 class enrol_meta_handler {
36     public function role_assigned($ra) {
37         global $DB;
39         if (!enrol_is_enabled('meta')) {
40             return true;
41         }
43         // prevent circular dependencies - we can not sync meta roles recursively
44         if ($ra->component === 'enrol_meta') {
45             return true;
46         }
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         }
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         }
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))) {
63                 return true;
64             }
65         }
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);
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         }
78         return true;
79     }
81     public function role_unassigned($ra) {
82         global $DB;
84         //note: do not test if plugin enabled, we want to keep removing previous roles
86         // prevent circular dependencies - we can not sync meta roles recursively
87         if ($ra->component === 'enrol_meta') {
88             return true;
89         }
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         }
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         }
102         //note: do not check 'nosyncroleids', somebody might have just enabled it, we want to get rid of nosync roles gradually
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);
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             }
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         }
121         return true;
122     }
124     public function user_enrolled($ue) {
125         global $DB;
127         if (!enrol_is_enabled('meta')) {
128             return true;
129         }
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         }
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         }
142         return true;
143     }
145     public function user_unenrolled($ue) {
146         global $DB;
148         //note: do not test if plugin enabled, we want to keep removing previously linked courses
150         // look for unenrolment candidates - it may be possible that user has multiple enrolments...
151         $sql = "SELECT e.*
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);
159         $rs = $DB->get_recordset_sql($sql, $params);
161         $plugin = enrol_get_plugin('meta');
162         foreach ($rs as $enrol) {
163             $plugin->unenrol_user($enrol, $ue->userid);
164         }
165         $rs->close();
167         return true;
168     }
170     public function course_deleted($course) {
171         global $DB;
173         //note: do not test if plugin enabled, we want to keep removing previously linked courses
175         // does anything want to sync with this parent?
176         if (!$enrols = $DB->get_records('enrol', array('customint1'=>$course->id, 'enrol'=>'meta'), 'id ASC')) {
177             return true;
178         }
180         $plugin = enrol_get_plugin('meta');
181         foreach ($enrols as $enrol) {
182             //unenrol all users
183             $ues = $DB->get_records_sql('user_enrolments', array('courseid'=>$enrol->courseid, 'enrolid'=>$enrol->id));
184             foreach ($ues as $ue) {
185                 $plugin->unenrol_user($enrol, $ue->userid);
186             }
187             $ues->close();
188         }
190         return true;
191     }
194 /**
195  * Sync all meta course links.
196  * @param int $courseid one course, empty mean all
197  * @return void
198  */
199 function enrol_meta_sync($courseid = NULL) {
200     global $CFG, $DB;
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
205     $meta = enrol_get_plugin('meta');
207     $onecourse = $courseid ? "AND e.courseid = :courseid" : "";
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;
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     }
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);
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
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
273                   JOIN {user} u ON (u.id = pra.userid AND u.deleted = 0)
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;
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         }
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     }
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";
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();