You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

262 lines
10 KiB

<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Local stuff for category enrolment plugin.
*
* @package enrol_category
* @copyright 2010 Petr Skoda {@link http://skodak.org}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* Sync all category enrolments in one course
* @param stdClass $course
* @return void
*/
function enrol_category_sync_course($course) {
global $DB;
if (!enrol_is_enabled('category')) {
return;
}
$plugin = enrol_get_plugin('category');
$syscontext = context_system::instance();
$roles = get_roles_with_capability('enrol/category:synchronised', CAP_ALLOW, $syscontext);
if (!$roles) {
// Nothing to sync, so remove the instance completely if exists.
if ($instances = $DB->get_records('enrol', array('courseid'=>$course->id, 'enrol'=>'category'))) {
foreach ($instances as $instance) {
$plugin->delete_instance($instance);
}
}
return;
}
// First find out if any parent category context contains interesting role assignments.
$coursecontext = context_course::instance($course->id);
$contextids = $coursecontext->get_parent_context_ids();
array_pop($contextids); // Remove system context, we are interested in categories only.
list($roleids, $params) = $DB->get_in_or_equal(array_keys($roles), SQL_PARAMS_NAMED, 'r');
list($contextids, $contextparams) = $DB->get_in_or_equal($contextids, SQL_PARAMS_NAMED, 'c');
$params = array_merge($params, $contextparams);
$params['courseid'] = $course->id;
$sql = "SELECT 'x'
FROM {role_assignments}
WHERE roleid $roleids AND contextid $contextids";
if (!$DB->record_exists_sql($sql, $params)) {
if ($instances = $DB->get_records('enrol', array('courseid'=>$course->id, 'enrol'=>'category'))) {
// Should be max one instance, but anyway.
foreach ($instances as $instance) {
$plugin->delete_instance($instance);
}
}
return;
}
// Make sure the enrol instance exists - there should be always only one instance.
$delinstances = array();
if ($instances = $DB->get_records('enrol', array('courseid'=>$course->id, 'enrol'=>'category'))) {
$instance = array_shift($instances);
$delinstances = $instances;
} else {
$i = $plugin->add_instance($course);
$instance = $DB->get_record('enrol', array('id'=>$i));
}
// Add new enrolments.
$sql = "SELECT ra.userid, ra.estart
FROM (SELECT xra.userid, MIN(xra.timemodified) AS estart
FROM {role_assignments} xra
JOIN {user} xu ON (xu.id = xra.userid AND xu.deleted = 0)
WHERE xra.roleid $roleids AND xra.contextid $contextids
GROUP BY xra.userid
) ra
LEFT JOIN {user_enrolments} ue ON (ue.enrolid = :instanceid AND ue.userid = ra.userid)
WHERE ue.id IS NULL";
$params['instanceid'] = $instance->id;
$rs = $DB->get_recordset_sql($sql, $params);
foreach ($rs as $ra) {
$plugin->enrol_user($instance, $ra->userid, null, $ra->estart);
}
$rs->close();
// Remove unwanted enrolments.
$sql = "SELECT DISTINCT ue.userid
FROM {user_enrolments} ue
LEFT JOIN {role_assignments} ra ON (ra.roleid $roleids AND ra.contextid $contextids AND ra.userid = ue.userid)
WHERE ue.enrolid = :instanceid AND ra.id IS NULL";
$rs = $DB->get_recordset_sql($sql, $params);
foreach ($rs as $ra) {
$plugin->unenrol_user($instance, $ra->userid);
}
$rs->close();
if ($delinstances) {
// We have to do this as the last step in order to prevent temporary unenrolment.
foreach ($delinstances as $delinstance) {
$plugin->delete_instance($delinstance);
}
}
}
/**
* Synchronise courses in all categories.
*
* It gets out-of-sync if:
* - you move course to different category
* - reorder categories
* - disable enrol_category and enable it again
*
* @param progress_trace $trace
* @return int exit code - 0 is ok, 1 means error, 2 if plugin disabled
*/
function enrol_category_sync_full(progress_trace $trace) {
global $DB;
if (!enrol_is_enabled('category')) {
$trace->finished();
return 2;
}
// We may need a lot of time here.
core_php_time_limit::raise();
$plugin = enrol_get_plugin('category');
$syscontext = context_system::instance();
// Any interesting roles worth synchronising?
if (!$roles = get_roles_with_capability('enrol/category:synchronised', CAP_ALLOW, $syscontext)) {
// yay, nothing to do, so let's remove all leftovers
$trace->output("No roles with 'enrol/category:synchronised' capability found.");
if ($instances = $DB->get_records('enrol', array('enrol'=>'category'))) {
$trace->output("Deleting all category enrol instances...");
foreach ($instances as $instance) {
$trace->output("deleting category enrol instance from course {$instance->courseid}", 1);
$plugin->delete_instance($instance);
}
$trace->output("...all instances deleted.");
}
$trace->finished();
return 0;
}
$rolenames = role_fix_names($roles, null, ROLENAME_SHORT, true);
$trace->output('Synchronising category enrolments for roles: '.implode(', ', $rolenames).'...');
list($roleids, $params) = $DB->get_in_or_equal(array_keys($roles), SQL_PARAMS_NAMED, 'r');
$params['courselevel'] = CONTEXT_COURSE;
$params['catlevel'] = CONTEXT_COURSECAT;
// First of all add necessary enrol instances to all courses.
$parentcat = $DB->sql_concat("cat.path", "'/%'");
$parentcctx = $DB->sql_concat("cctx.path", "'/%'");
// Need whole course records to be used by add_instance(), use inner view (ci) to
// get distinct records only.
// TODO: Moodle 2.1. Improve enrol API to accept courseid / courserec
$sql = "SELECT c.*
FROM {course} c
JOIN (
SELECT DISTINCT c.id
FROM {course} c
JOIN {context} ctx ON (ctx.instanceid = c.id AND ctx.contextlevel = :courselevel)
JOIN (SELECT DISTINCT cctx.path
FROM {course_categories} cc
JOIN {context} cctx ON (cctx.instanceid = cc.id AND cctx.contextlevel = :catlevel)
JOIN {role_assignments} ra ON (ra.contextid = cctx.id AND ra.roleid $roleids)
) cat ON (ctx.path LIKE $parentcat)
LEFT JOIN {enrol} e ON (e.courseid = c.id AND e.enrol = 'category')
WHERE e.id IS NULL) ci ON (c.id = ci.id)";
$rs = $DB->get_recordset_sql($sql, $params);
foreach($rs as $course) {
$plugin->add_instance($course);
}
$rs->close();
// Now look for courses that do not have any interesting roles in parent contexts,
// but still have the instance and delete them.
$sql = "SELECT e.*
FROM {enrol} e
JOIN {context} ctx ON (ctx.instanceid = e.courseid AND ctx.contextlevel = :courselevel)
LEFT JOIN ({course_categories} cc
JOIN {context} cctx ON (cctx.instanceid = cc.id AND cctx.contextlevel = :catlevel)
JOIN {role_assignments} ra ON (ra.contextid = cctx.id AND ra.roleid $roleids)
) ON (ctx.path LIKE $parentcctx)
WHERE e.enrol = 'category' AND cc.id IS NULL";
$rs = $DB->get_recordset_sql($sql, $params);
foreach($rs as $instance) {
$plugin->delete_instance($instance);
}
$rs->close();
// Add missing enrolments.
$sql = "SELECT e.*, cat.userid, cat.estart
FROM {enrol} e
JOIN {context} ctx ON (ctx.instanceid = e.courseid AND ctx.contextlevel = :courselevel)
JOIN (SELECT cctx.path, ra.userid, MIN(ra.timemodified) AS estart
FROM {course_categories} cc
JOIN {context} cctx ON (cctx.instanceid = cc.id AND cctx.contextlevel = :catlevel)
JOIN {role_assignments} ra ON (ra.contextid = cctx.id AND ra.roleid $roleids)
GROUP BY cctx.path, ra.userid
) cat ON (ctx.path LIKE $parentcat)
LEFT JOIN {user_enrolments} ue ON (ue.enrolid = e.id AND ue.userid = cat.userid)
WHERE e.enrol = 'category' AND ue.id IS NULL";
$rs = $DB->get_recordset_sql($sql, $params);
foreach($rs as $instance) {
$userid = $instance->userid;
$estart = $instance->estart;
unset($instance->userid);
unset($instance->estart);
$plugin->enrol_user($instance, $userid, null, $estart);
$trace->output("enrolling: user $userid ==> course $instance->courseid", 1);
}
$rs->close();
// Remove stale enrolments.
$sql = "SELECT e.*, ue.userid
FROM {enrol} e
JOIN {context} ctx ON (ctx.instanceid = e.courseid AND ctx.contextlevel = :courselevel)
JOIN {user_enrolments} ue ON (ue.enrolid = e.id)
LEFT JOIN ({course_categories} cc
JOIN {context} cctx ON (cctx.instanceid = cc.id AND cctx.contextlevel = :catlevel)
JOIN {role_assignments} ra ON (ra.contextid = cctx.id AND ra.roleid $roleids)
) ON (ctx.path LIKE $parentcctx AND ra.userid = ue.userid)
WHERE e.enrol = 'category' AND cc.id IS NULL";
$rs = $DB->get_recordset_sql($sql, $params);
foreach($rs as $instance) {
$userid = $instance->userid;
unset($instance->userid);
$plugin->unenrol_user($instance, $userid);
$trace->output("unenrolling: user $userid ==> course $instance->courseid", 1);
}
$rs->close();
$trace->output('...user enrolment synchronisation finished.');
$trace->finished();
return 0;
}