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/>. 16 17 /** 18 * Local stuff for category enrolment plugin. 19 * 20 * @package enrol_category 21 * @copyright 2010 Petr Skoda {@link http://skodak.org} 22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 */ 24 25 defined('MOODLE_INTERNAL') || die(); 26 27 /** 28 * Sync all category enrolments in one course 29 * @param stdClass $course 30 * @return void 31 */ 32 function enrol_category_sync_course($course) { 33 global $DB; 34 35 if (!enrol_is_enabled('category')) { 36 return; 37 } 38 39 $plugin = enrol_get_plugin('category'); 40 41 $syscontext = context_system::instance(); 42 $roles = get_roles_with_capability('enrol/category:synchronised', CAP_ALLOW, $syscontext); 43 44 if (!$roles) { 45 // Nothing to sync, so remove the instance completely if exists. 46 if ($instances = $DB->get_records('enrol', array('courseid'=>$course->id, 'enrol'=>'category'))) { 47 foreach ($instances as $instance) { 48 $plugin->delete_instance($instance); 49 } 50 } 51 return; 52 } 53 54 // First find out if any parent category context contains interesting role assignments. 55 $coursecontext = context_course::instance($course->id); 56 $contextids = $coursecontext->get_parent_context_ids(); 57 array_pop($contextids); // Remove system context, we are interested in categories only. 58 59 list($roleids, $params) = $DB->get_in_or_equal(array_keys($roles), SQL_PARAMS_NAMED, 'r'); 60 list($contextids, $contextparams) = $DB->get_in_or_equal($contextids, SQL_PARAMS_NAMED, 'c'); 61 $params = array_merge($params, $contextparams); 62 $params['courseid'] = $course->id; 63 64 $sql = "SELECT 'x' 65 FROM {role_assignments} 66 WHERE roleid $roleids AND contextid $contextids"; 67 if (!$DB->record_exists_sql($sql, $params)) { 68 if ($instances = $DB->get_records('enrol', array('courseid'=>$course->id, 'enrol'=>'category'))) { 69 // Should be max one instance, but anyway. 70 foreach ($instances as $instance) { 71 $plugin->delete_instance($instance); 72 } 73 } 74 return; 75 } 76 77 // Make sure the enrol instance exists - there should be always only one instance. 78 $delinstances = array(); 79 if ($instances = $DB->get_records('enrol', array('courseid'=>$course->id, 'enrol'=>'category'))) { 80 $instance = array_shift($instances); 81 $delinstances = $instances; 82 } else { 83 $i = $plugin->add_instance($course); 84 $instance = $DB->get_record('enrol', array('id'=>$i)); 85 } 86 87 // Add new enrolments. 88 $sql = "SELECT ra.userid, ra.estart 89 FROM (SELECT xra.userid, MIN(xra.timemodified) AS estart 90 FROM {role_assignments} xra 91 JOIN {user} xu ON (xu.id = xra.userid AND xu.deleted = 0) 92 WHERE xra.roleid $roleids AND xra.contextid $contextids 93 GROUP BY xra.userid 94 ) ra 95 LEFT JOIN {user_enrolments} ue ON (ue.enrolid = :instanceid AND ue.userid = ra.userid) 96 WHERE ue.id IS NULL"; 97 $params['instanceid'] = $instance->id; 98 $rs = $DB->get_recordset_sql($sql, $params); 99 foreach ($rs as $ra) { 100 $plugin->enrol_user($instance, $ra->userid, null, $ra->estart); 101 } 102 $rs->close(); 103 104 // Remove unwanted enrolments. 105 $sql = "SELECT DISTINCT ue.userid 106 FROM {user_enrolments} ue 107 LEFT JOIN {role_assignments} ra ON (ra.roleid $roleids AND ra.contextid $contextids AND ra.userid = ue.userid) 108 WHERE ue.enrolid = :instanceid AND ra.id IS NULL"; 109 $rs = $DB->get_recordset_sql($sql, $params); 110 foreach ($rs as $ra) { 111 $plugin->unenrol_user($instance, $ra->userid); 112 } 113 $rs->close(); 114 115 if ($delinstances) { 116 // We have to do this as the last step in order to prevent temporary unenrolment. 117 foreach ($delinstances as $delinstance) { 118 $plugin->delete_instance($delinstance); 119 } 120 } 121 } 122 123 /** 124 * Synchronise courses in all categories. 125 * 126 * It gets out-of-sync if: 127 * - you move course to different category 128 * - reorder categories 129 * - disable enrol_category and enable it again 130 * 131 * @param progress_trace $trace 132 * @return int exit code - 0 is ok, 1 means error, 2 if plugin disabled 133 */ 134 function enrol_category_sync_full(progress_trace $trace) { 135 global $DB; 136 137 138 if (!enrol_is_enabled('category')) { 139 $trace->finished(); 140 return 2; 141 } 142 143 // We may need a lot of time here. 144 core_php_time_limit::raise(); 145 146 $plugin = enrol_get_plugin('category'); 147 148 $syscontext = context_system::instance(); 149 150 // Any interesting roles worth synchronising? 151 if (!$roles = get_roles_with_capability('enrol/category:synchronised', CAP_ALLOW, $syscontext)) { 152 // yay, nothing to do, so let's remove all leftovers 153 $trace->output("No roles with 'enrol/category:synchronised' capability found."); 154 if ($instances = $DB->get_records('enrol', array('enrol'=>'category'))) { 155 $trace->output("Deleting all category enrol instances..."); 156 foreach ($instances as $instance) { 157 $trace->output("deleting category enrol instance from course {$instance->courseid}", 1); 158 $plugin->delete_instance($instance); 159 } 160 $trace->output("...all instances deleted."); 161 } 162 $trace->finished(); 163 return 0; 164 } 165 $rolenames = role_fix_names($roles, null, ROLENAME_SHORT, true); 166 $trace->output('Synchronising category enrolments for roles: '.implode(', ', $rolenames).'...'); 167 168 list($roleids, $params) = $DB->get_in_or_equal(array_keys($roles), SQL_PARAMS_NAMED, 'r'); 169 $params['courselevel'] = CONTEXT_COURSE; 170 $params['catlevel'] = CONTEXT_COURSECAT; 171 172 // First of all add necessary enrol instances to all courses. 173 $parentcat = $DB->sql_concat("cat.path", "'/%'"); 174 $parentcctx = $DB->sql_concat("cctx.path", "'/%'"); 175 // Need whole course records to be used by add_instance(), use inner view (ci) to 176 // get distinct records only. 177 // TODO: Moodle 2.1. Improve enrol API to accept courseid / courserec 178 $sql = "SELECT c.* 179 FROM {course} c 180 JOIN ( 181 SELECT DISTINCT c.id 182 FROM {course} c 183 JOIN {context} ctx ON (ctx.instanceid = c.id AND ctx.contextlevel = :courselevel) 184 JOIN (SELECT DISTINCT cctx.path 185 FROM {course_categories} cc 186 JOIN {context} cctx ON (cctx.instanceid = cc.id AND cctx.contextlevel = :catlevel) 187 JOIN {role_assignments} ra ON (ra.contextid = cctx.id AND ra.roleid $roleids) 188 ) cat ON (ctx.path LIKE $parentcat) 189 LEFT JOIN {enrol} e ON (e.courseid = c.id AND e.enrol = 'category') 190 WHERE e.id IS NULL) ci ON (c.id = ci.id)"; 191 192 $rs = $DB->get_recordset_sql($sql, $params); 193 foreach($rs as $course) { 194 $plugin->add_instance($course); 195 } 196 $rs->close(); 197 198 // Now look for courses that do not have any interesting roles in parent contexts, 199 // but still have the instance and delete them. 200 $sql = "SELECT e.* 201 FROM {enrol} e 202 JOIN {context} ctx ON (ctx.instanceid = e.courseid AND ctx.contextlevel = :courselevel) 203 LEFT JOIN ({course_categories} cc 204 JOIN {context} cctx ON (cctx.instanceid = cc.id AND cctx.contextlevel = :catlevel) 205 JOIN {role_assignments} ra ON (ra.contextid = cctx.id AND ra.roleid $roleids) 206 ) ON (ctx.path LIKE $parentcctx) 207 WHERE e.enrol = 'category' AND cc.id IS NULL"; 208 209 $rs = $DB->get_recordset_sql($sql, $params); 210 foreach($rs as $instance) { 211 $plugin->delete_instance($instance); 212 } 213 $rs->close(); 214 215 // Add missing enrolments. 216 $sql = "SELECT e.*, cat.userid, cat.estart 217 FROM {enrol} e 218 JOIN {context} ctx ON (ctx.instanceid = e.courseid AND ctx.contextlevel = :courselevel) 219 JOIN (SELECT cctx.path, ra.userid, MIN(ra.timemodified) AS estart 220 FROM {course_categories} cc 221 JOIN {context} cctx ON (cctx.instanceid = cc.id AND cctx.contextlevel = :catlevel) 222 JOIN {role_assignments} ra ON (ra.contextid = cctx.id AND ra.roleid $roleids) 223 GROUP BY cctx.path, ra.userid 224 ) cat ON (ctx.path LIKE $parentcat) 225 LEFT JOIN {user_enrolments} ue ON (ue.enrolid = e.id AND ue.userid = cat.userid) 226 WHERE e.enrol = 'category' AND ue.id IS NULL"; 227 $rs = $DB->get_recordset_sql($sql, $params); 228 foreach($rs as $instance) { 229 $userid = $instance->userid; 230 $estart = $instance->estart; 231 unset($instance->userid); 232 unset($instance->estart); 233 $plugin->enrol_user($instance, $userid, null, $estart); 234 $trace->output("enrolling: user $userid ==> course $instance->courseid", 1); 235 } 236 $rs->close(); 237 238 // Remove stale enrolments. 239 $sql = "SELECT e.*, ue.userid 240 FROM {enrol} e 241 JOIN {context} ctx ON (ctx.instanceid = e.courseid AND ctx.contextlevel = :courselevel) 242 JOIN {user_enrolments} ue ON (ue.enrolid = e.id) 243 LEFT JOIN ({course_categories} cc 244 JOIN {context} cctx ON (cctx.instanceid = cc.id AND cctx.contextlevel = :catlevel) 245 JOIN {role_assignments} ra ON (ra.contextid = cctx.id AND ra.roleid $roleids) 246 ) ON (ctx.path LIKE $parentcctx AND ra.userid = ue.userid) 247 WHERE e.enrol = 'category' AND cc.id IS NULL"; 248 $rs = $DB->get_recordset_sql($sql, $params); 249 foreach($rs as $instance) { 250 $userid = $instance->userid; 251 unset($instance->userid); 252 $plugin->unenrol_user($instance, $userid); 253 $trace->output("unenrolling: user $userid ==> course $instance->courseid", 1); 254 } 255 $rs->close(); 256 257 $trace->output('...user enrolment synchronisation finished.'); 258 $trace->finished(); 259 260 return 0; 261 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body