Merge branch 'wip-MDL-31243-master' of https://github.com/Beedell/moodle

This commit is contained in:
Andrew Nicols
2016-10-04 09:19:42 +08:00
6 changed files with 655 additions and 345 deletions
+165 -345
View File
@@ -2035,98 +2035,6 @@ function is_viewing(context $context, $user = null, $withcapability = '') {
return true;
}
/**
* Returns true if user is enrolled (is participating) in course
* this is intended for students and teachers.
*
* Since 2.2 the result for active enrolments and current user are cached.
*
* @package core_enrol
* @category access
*
* @param context $context
* @param int|stdClass $user if null $USER is used, otherwise user object or id expected
* @param string $withcapability extra capability name
* @param bool $onlyactive consider only active enrolments in enabled plugins and time restrictions
* @return bool
*/
function is_enrolled(context $context, $user = null, $withcapability = '', $onlyactive = false) {
global $USER, $DB;
// first find the course context
$coursecontext = $context->get_course_context();
// make sure there is a real user specified
if ($user === null) {
$userid = isset($USER->id) ? $USER->id : 0;
} else {
$userid = is_object($user) ? $user->id : $user;
}
if (empty($userid)) {
// not-logged-in!
return false;
} else if (isguestuser($userid)) {
// guest account can not be enrolled anywhere
return false;
}
if ($coursecontext->instanceid == SITEID) {
// everybody participates on frontpage
} else {
// try cached info first - the enrolled flag is set only when active enrolment present
if ($USER->id == $userid) {
$coursecontext->reload_if_dirty();
if (isset($USER->enrol['enrolled'][$coursecontext->instanceid])) {
if ($USER->enrol['enrolled'][$coursecontext->instanceid] > time()) {
if ($withcapability and !has_capability($withcapability, $context, $userid)) {
return false;
}
return true;
}
}
}
if ($onlyactive) {
// look for active enrolments only
$until = enrol_get_enrolment_end($coursecontext->instanceid, $userid);
if ($until === false) {
return false;
}
if ($USER->id == $userid) {
if ($until == 0) {
$until = ENROL_MAX_TIMESTAMP;
}
$USER->enrol['enrolled'][$coursecontext->instanceid] = $until;
if (isset($USER->enrol['tempguest'][$coursecontext->instanceid])) {
unset($USER->enrol['tempguest'][$coursecontext->instanceid]);
remove_temp_course_roles($coursecontext);
}
}
} else {
// any enrolment is good for us here, even outdated, disabled or inactive
$sql = "SELECT 'x'
FROM {user_enrolments} ue
JOIN {enrol} e ON (e.id = ue.enrolid AND e.courseid = :courseid)
JOIN {user} u ON u.id = ue.userid
WHERE ue.userid = :userid AND u.deleted = 0";
$params = array('userid'=>$userid, 'courseid'=>$coursecontext->instanceid);
if (!$DB->record_exists_sql($sql, $params)) {
return false;
}
}
}
if ($withcapability and !has_capability($withcapability, $context, $userid)) {
return false;
}
return true;
}
/**
* Returns true if the user is able to access the course.
*
@@ -2244,259 +2152,6 @@ function can_access_course(stdClass $course, $user = null, $withcapability = '',
return false;
}
/**
* Returns array with sql code and parameters returning all ids
* of users enrolled into course.
*
* This function is using 'eu[0-9]+_' prefix for table names and parameters.
*
* @package core_enrol
* @category access
*
* @param context $context
* @param string $withcapability
* @param int $groupid 0 means ignore groups, any other value limits the result by group id
* @param bool $onlyactive consider only active enrolments in enabled plugins and time restrictions
* @param bool $onlysuspended inverse of onlyactive, consider only suspended enrolments
* @return array list($sql, $params)
*/
function get_enrolled_sql(context $context, $withcapability = '', $groupid = 0, $onlyactive = false, $onlysuspended = false) {
global $DB, $CFG;
// use unique prefix just in case somebody makes some SQL magic with the result
static $i = 0;
$i++;
$prefix = 'eu'.$i.'_';
// first find the course context
$coursecontext = $context->get_course_context();
$isfrontpage = ($coursecontext->instanceid == SITEID);
if ($onlyactive && $onlysuspended) {
throw new coding_exception("Both onlyactive and onlysuspended are set, this is probably not what you want!");
}
if ($isfrontpage && $onlysuspended) {
throw new coding_exception("onlysuspended is not supported on frontpage; please add your own early-exit!");
}
$joins = array();
$wheres = array();
$params = array();
list($contextids, $contextpaths) = get_context_info_list($context);
// get all relevant capability info for all roles
if ($withcapability) {
list($incontexts, $cparams) = $DB->get_in_or_equal($contextids, SQL_PARAMS_NAMED, 'ctx');
$cparams['cap'] = $withcapability;
$defs = array();
$sql = "SELECT rc.id, rc.roleid, rc.permission, ctx.path
FROM {role_capabilities} rc
JOIN {context} ctx on rc.contextid = ctx.id
WHERE rc.contextid $incontexts AND rc.capability = :cap";
$rcs = $DB->get_records_sql($sql, $cparams);
foreach ($rcs as $rc) {
$defs[$rc->path][$rc->roleid] = $rc->permission;
}
$access = array();
if (!empty($defs)) {
foreach ($contextpaths as $path) {
if (empty($defs[$path])) {
continue;
}
foreach($defs[$path] as $roleid => $perm) {
if ($perm == CAP_PROHIBIT) {
$access[$roleid] = CAP_PROHIBIT;
continue;
}
if (!isset($access[$roleid])) {
$access[$roleid] = (int)$perm;
}
}
}
}
unset($defs);
// make lists of roles that are needed and prohibited
$needed = array(); // one of these is enough
$prohibited = array(); // must not have any of these
foreach ($access as $roleid => $perm) {
if ($perm == CAP_PROHIBIT) {
unset($needed[$roleid]);
$prohibited[$roleid] = true;
} else if ($perm == CAP_ALLOW and empty($prohibited[$roleid])) {
$needed[$roleid] = true;
}
}
$defaultuserroleid = isset($CFG->defaultuserroleid) ? $CFG->defaultuserroleid : 0;
$defaultfrontpageroleid = isset($CFG->defaultfrontpageroleid) ? $CFG->defaultfrontpageroleid : 0;
$nobody = false;
if ($isfrontpage) {
if (!empty($prohibited[$defaultuserroleid]) or !empty($prohibited[$defaultfrontpageroleid])) {
$nobody = true;
} else if (!empty($needed[$defaultuserroleid]) or !empty($needed[$defaultfrontpageroleid])) {
// everybody not having prohibit has the capability
$needed = array();
} else if (empty($needed)) {
$nobody = true;
}
} else {
if (!empty($prohibited[$defaultuserroleid])) {
$nobody = true;
} else if (!empty($needed[$defaultuserroleid])) {
// everybody not having prohibit has the capability
$needed = array();
} else if (empty($needed)) {
$nobody = true;
}
}
if ($nobody) {
// nobody can match so return some SQL that does not return any results
$wheres[] = "1 = 2";
} else {
if ($needed) {
$ctxids = implode(',', $contextids);
$roleids = implode(',', array_keys($needed));
$joins[] = "JOIN {role_assignments} {$prefix}ra3 ON ({$prefix}ra3.userid = {$prefix}u.id AND {$prefix}ra3.roleid IN ($roleids) AND {$prefix}ra3.contextid IN ($ctxids))";
}
if ($prohibited) {
$ctxids = implode(',', $contextids);
$roleids = implode(',', array_keys($prohibited));
$joins[] = "LEFT JOIN {role_assignments} {$prefix}ra4 ON ({$prefix}ra4.userid = {$prefix}u.id AND {$prefix}ra4.roleid IN ($roleids) AND {$prefix}ra4.contextid IN ($ctxids))";
$wheres[] = "{$prefix}ra4.id IS NULL";
}
if ($groupid) {
$joins[] = "JOIN {groups_members} {$prefix}gm ON ({$prefix}gm.userid = {$prefix}u.id AND {$prefix}gm.groupid = :{$prefix}gmid)";
$params["{$prefix}gmid"] = $groupid;
}
}
} else {
if ($groupid) {
$joins[] = "JOIN {groups_members} {$prefix}gm ON ({$prefix}gm.userid = {$prefix}u.id AND {$prefix}gm.groupid = :{$prefix}gmid)";
$params["{$prefix}gmid"] = $groupid;
}
}
$wheres[] = "{$prefix}u.deleted = 0 AND {$prefix}u.id <> :{$prefix}guestid";
$params["{$prefix}guestid"] = $CFG->siteguest;
if ($isfrontpage) {
// all users are "enrolled" on the frontpage
} else {
$where1 = "{$prefix}ue.status = :{$prefix}active AND {$prefix}e.status = :{$prefix}enabled";
$where2 = "{$prefix}ue.timestart < :{$prefix}now1 AND ({$prefix}ue.timeend = 0 OR {$prefix}ue.timeend > :{$prefix}now2)";
$ejoin = "JOIN {enrol} {$prefix}e ON ({$prefix}e.id = {$prefix}ue.enrolid AND {$prefix}e.courseid = :{$prefix}courseid)";
$params[$prefix.'courseid'] = $coursecontext->instanceid;
if (!$onlysuspended) {
$joins[] = "JOIN {user_enrolments} {$prefix}ue ON {$prefix}ue.userid = {$prefix}u.id";
$joins[] = $ejoin;
if ($onlyactive) {
$wheres[] = "$where1 AND $where2";
}
} else {
// Suspended only where there is enrolment but ALL are suspended.
// Consider multiple enrols where one is not suspended or plain role_assign.
$enrolselect = "SELECT DISTINCT {$prefix}ue.userid FROM {user_enrolments} {$prefix}ue $ejoin WHERE $where1 AND $where2";
$joins[] = "JOIN {user_enrolments} {$prefix}ue1 ON {$prefix}ue1.userid = {$prefix}u.id";
$joins[] = "JOIN {enrol} {$prefix}e1 ON ({$prefix}e1.id = {$prefix}ue1.enrolid AND {$prefix}e1.courseid = :{$prefix}_e1_courseid)";
$params["{$prefix}_e1_courseid"] = $coursecontext->instanceid;
$wheres[] = "{$prefix}u.id NOT IN ($enrolselect)";
}
if ($onlyactive || $onlysuspended) {
$now = round(time(), -2); // rounding helps caching in DB
$params = array_merge($params, array($prefix.'enabled'=>ENROL_INSTANCE_ENABLED,
$prefix.'active'=>ENROL_USER_ACTIVE,
$prefix.'now1'=>$now, $prefix.'now2'=>$now));
}
}
$joins = implode("\n", $joins);
$wheres = "WHERE ".implode(" AND ", $wheres);
$sql = "SELECT DISTINCT {$prefix}u.id
FROM {user} {$prefix}u
$joins
$wheres";
return array($sql, $params);
}
/**
* Returns list of users enrolled into course.
*
* @package core_enrol
* @category access
*
* @param context $context
* @param string $withcapability
* @param int $groupid 0 means ignore groups, any other value limits the result by group id
* @param string $userfields requested user record fields
* @param string $orderby
* @param int $limitfrom return a subset of records, starting at this point (optional, required if $limitnum is set).
* @param int $limitnum return a subset comprising this many records (optional, required if $limitfrom is set).
* @param bool $onlyactive consider only active enrolments in enabled plugins and time restrictions
* @return array of user records
*/
function get_enrolled_users(context $context, $withcapability = '', $groupid = 0, $userfields = 'u.*', $orderby = null,
$limitfrom = 0, $limitnum = 0, $onlyactive = false) {
global $DB;
list($esql, $params) = get_enrolled_sql($context, $withcapability, $groupid, $onlyactive);
$sql = "SELECT $userfields
FROM {user} u
JOIN ($esql) je ON je.id = u.id
WHERE u.deleted = 0";
if ($orderby) {
$sql = "$sql ORDER BY $orderby";
} else {
list($sort, $sortparams) = users_order_by_sql('u');
$sql = "$sql ORDER BY $sort";
$params = array_merge($params, $sortparams);
}
return $DB->get_records_sql($sql, $params, $limitfrom, $limitnum);
}
/**
* Counts list of users enrolled into course (as per above function)
*
* @package core_enrol
* @category access
*
* @param context $context
* @param string $withcapability
* @param int $groupid 0 means ignore groups, any other value limits the result by group id
* @param bool $onlyactive consider only active enrolments in enabled plugins and time restrictions
* @return array of user records
*/
function count_enrolled_users(context $context, $withcapability = '', $groupid = 0, $onlyactive = false) {
global $DB;
list($esql, $params) = get_enrolled_sql($context, $withcapability, $groupid, $onlyactive);
$sql = "SELECT count(u.id)
FROM {user} u
JOIN ($esql) je ON je.id = u.id
WHERE u.deleted = 0";
return $DB->count_records_sql($sql, $params);
}
/**
* Loads the capability definitions for the component (from file).
*
@@ -7622,3 +7277,168 @@ function get_suspended_userids(context $context, $usecache = false) {
return $susers;
}
/**
* Gets sql for finding users with a capability in the given context
*
* @param context $context
* @param string $capability
* @return array($sql, $params)
*/
function get_with_capability_sql(context $context, $capability) {
static $i = 0;
$i++;
$prefix = 'cu' . $i . '_';
$capjoin = get_with_capability_join($context, $capability, $prefix . 'u.id');
$sql = "SELECT DISTINCT {$prefix}u.id
FROM {user} {$prefix}u
$capjoin->joins
WHERE {$prefix}u.deleted = 0 AND $capjoin->wheres";
return array($sql, $capjoin->params);
}
/**
* Gets sql joins for finding users with a capability in the given context
*
* @param context $context
* @param string $capability
* @param string $useridcolumn e.g. u.id
* @return \core\dml\sql_join Contains joins, wheres, params
*/
function get_with_capability_join(context $context, $capability, $useridcolumn) {
global $DB, $CFG;
// Use unique prefix just in case somebody makes some SQL magic with the result.
static $i = 0;
$i++;
$prefix = 'eu' . $i . '_';
// First find the course context.
$coursecontext = $context->get_course_context();
$isfrontpage = ($coursecontext->instanceid == SITEID);
$joins = array();
$wheres = array();
$params = array();
list($contextids, $contextpaths) = get_context_info_list($context);
list($incontexts, $cparams) = $DB->get_in_or_equal($contextids, SQL_PARAMS_NAMED, 'ctx');
$cparams['cap'] = $capability;
$defs = array();
$sql = "SELECT rc.id, rc.roleid, rc.permission, ctx.path
FROM {role_capabilities} rc
JOIN {context} ctx on rc.contextid = ctx.id
WHERE rc.contextid $incontexts AND rc.capability = :cap";
$rcs = $DB->get_records_sql($sql, $cparams);
foreach ($rcs as $rc) {
$defs[$rc->path][$rc->roleid] = $rc->permission;
}
$access = array();
if (!empty($defs)) {
foreach ($contextpaths as $path) {
if (empty($defs[$path])) {
continue;
}
foreach ($defs[$path] as $roleid => $perm) {
if ($perm == CAP_PROHIBIT) {
$access[$roleid] = CAP_PROHIBIT;
continue;
}
if (!isset($access[$roleid])) {
$access[$roleid] = (int) $perm;
}
}
}
}
unset($defs);
// Make lists of roles that are needed and prohibited.
$needed = array(); // One of these is enough.
$prohibited = array(); // Must not have any of these.
foreach ($access as $roleid => $perm) {
if ($perm == CAP_PROHIBIT) {
unset($needed[$roleid]);
$prohibited[$roleid] = true;
} else {
if ($perm == CAP_ALLOW and empty($prohibited[$roleid])) {
$needed[$roleid] = true;
}
}
}
$defaultuserroleid = isset($CFG->defaultuserroleid) ? $CFG->defaultuserroleid : 0;
$defaultfrontpageroleid = isset($CFG->defaultfrontpageroleid) ? $CFG->defaultfrontpageroleid : 0;
$nobody = false;
if ($isfrontpage) {
if (!empty($prohibited[$defaultuserroleid]) or !empty($prohibited[$defaultfrontpageroleid])) {
$nobody = true;
} else {
if (!empty($needed[$defaultuserroleid]) or !empty($needed[$defaultfrontpageroleid])) {
// Everybody not having prohibit has the capability.
$needed = array();
} else {
if (empty($needed)) {
$nobody = true;
}
}
}
} else {
if (!empty($prohibited[$defaultuserroleid])) {
$nobody = true;
} else {
if (!empty($needed[$defaultuserroleid])) {
// Everybody not having prohibit has the capability.
$needed = array();
} else {
if (empty($needed)) {
$nobody = true;
}
}
}
}
if ($nobody) {
// Nobody can match so return some SQL that does not return any results.
$wheres[] = "1 = 2";
} else {
if ($needed) {
$ctxids = implode(',', $contextids);
$roleids = implode(',', array_keys($needed));
$joins[] = "JOIN {role_assignments} {$prefix}ra3
ON ({$prefix}ra3.userid = $useridcolumn
AND {$prefix}ra3.roleid IN ($roleids)
AND {$prefix}ra3.contextid IN ($ctxids))";
}
if ($prohibited) {
$ctxids = implode(',', $contextids);
$roleids = implode(',', array_keys($prohibited));
$joins[] = "LEFT JOIN {role_assignments} {$prefix}ra4
ON ({$prefix}ra4.userid = $useridcolumn
AND {$prefix}ra4.roleid IN ($roleids)
AND {$prefix}ra4.contextid IN ($ctxids))";
$wheres[] = "{$prefix}ra4.id IS NULL";
}
}
$wheres[] = "$useridcolumn <> :{$prefix}guestid";
$params["{$prefix}guestid"] = $CFG->siteguest;
$joins = implode("\n", $joins);
$wheres = "(" . implode(" AND ", $wheres) . ")";
return new \core\dml\sql_join($joins, $wheres, $params);
}
+69
View File
@@ -0,0 +1,69 @@
<?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/>.
/**
* An object that contains sql join fragments.
*
* @since Moodle 3.1
* @package core
* @category dml
* @copyright 2016 The Open University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core\dml;
defined('MOODLE_INTERNAL') || die();
/**
* An object that contains sql join fragments.
*
* @since Moodle 3.1
* @package core
* @category dml
* @copyright 2016 The Open University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class sql_join {
/**
* @var string joins.
*/
public $joins;
/**
* @var string wheres.
*/
public $wheres;
/**
* @var array params.
*/
public $params;
/**
* Create an object that contains sql join fragments.
*
* @param string $joins The join sql fragment.
* @param string $wheres The where sql fragment.
* @param array $params Any parameter values.
*/
public function __construct($joins = '', $wheres = '', $params = array()) {
$this->joins = $joins;
$this->wheres = $wheres;
$this->params = $params;
}
}
+294
View File
@@ -1065,6 +1065,300 @@ function enrol_accessing_via_instance(stdClass $instance) {
return $DB->record_exists('user_enrolments', array('userid'=>$USER->id, 'enrolid'=>$instance->id));
}
/**
* Returns true if user is enrolled (is participating) in course
* this is intended for students and teachers.
*
* Since 2.2 the result for active enrolments and current user are cached.
*
* @param context $context
* @param int|stdClass $user if null $USER is used, otherwise user object or id expected
* @param string $withcapability extra capability name
* @param bool $onlyactive consider only active enrolments in enabled plugins and time restrictions
* @return bool
*/
function is_enrolled(context $context, $user = null, $withcapability = '', $onlyactive = false) {
global $USER, $DB;
// First find the course context.
$coursecontext = $context->get_course_context();
// Make sure there is a real user specified.
if ($user === null) {
$userid = isset($USER->id) ? $USER->id : 0;
} else {
$userid = is_object($user) ? $user->id : $user;
}
if (empty($userid)) {
// Not-logged-in!
return false;
} else if (isguestuser($userid)) {
// Guest account can not be enrolled anywhere.
return false;
}
// Note everybody participates on frontpage, so for other contexts...
if ($coursecontext->instanceid != SITEID) {
// Try cached info first - the enrolled flag is set only when active enrolment present.
if ($USER->id == $userid) {
$coursecontext->reload_if_dirty();
if (isset($USER->enrol['enrolled'][$coursecontext->instanceid])) {
if ($USER->enrol['enrolled'][$coursecontext->instanceid] > time()) {
if ($withcapability and !has_capability($withcapability, $context, $userid)) {
return false;
}
return true;
}
}
}
if ($onlyactive) {
// Look for active enrolments only.
$until = enrol_get_enrolment_end($coursecontext->instanceid, $userid);
if ($until === false) {
return false;
}
if ($USER->id == $userid) {
if ($until == 0) {
$until = ENROL_MAX_TIMESTAMP;
}
$USER->enrol['enrolled'][$coursecontext->instanceid] = $until;
if (isset($USER->enrol['tempguest'][$coursecontext->instanceid])) {
unset($USER->enrol['tempguest'][$coursecontext->instanceid]);
remove_temp_course_roles($coursecontext);
}
}
} else {
// Any enrolment is good for us here, even outdated, disabled or inactive.
$sql = "SELECT 'x'
FROM {user_enrolments} ue
JOIN {enrol} e ON (e.id = ue.enrolid AND e.courseid = :courseid)
JOIN {user} u ON u.id = ue.userid
WHERE ue.userid = :userid AND u.deleted = 0";
$params = array('userid' => $userid, 'courseid' => $coursecontext->instanceid);
if (!$DB->record_exists_sql($sql, $params)) {
return false;
}
}
}
if ($withcapability and !has_capability($withcapability, $context, $userid)) {
return false;
}
return true;
}
/**
* Returns an array of joins, wheres and params that will limit the group of
* users to only those enrolled and with given capability (if specified).
*
* @param context $context
* @param string $prefix optional, a prefix to the user id column
* @param string $capability optional, may include a capability name
* @param int $group optional, 0 indicates no current group, otherwise the group id
* @param bool $onlyactive consider only active enrolments in enabled plugins and time restrictions
* @param bool $onlysuspended inverse of onlyactive, consider only suspended enrolments
* @return \core\dml\sql_join Contains joins, wheres, params
*/
function get_enrolled_with_capabilities_join(context $context, $prefix = '', $capability = '', $group = 0,
$onlyactive = false, $onlysuspended = false) {
$uid = $prefix . 'u.id';
$joins = array();
$wheres = array();
$enrolledjoin = get_enrolled_join($context, $uid, $onlyactive, $onlysuspended);
$joins[] = $enrolledjoin->joins;
$wheres[] = $enrolledjoin->wheres;
$params = $enrolledjoin->params;
if (!empty($capability)) {
$capjoin = get_with_capability_join($context, $capability, $uid);
$joins[] = $capjoin->joins;
$wheres[] = $capjoin->wheres;
$params = array_merge($params, $capjoin->params);
}
if ($group) {
$groupjoin = groups_get_members_join($group, $uid);
$joins[] = $groupjoin->joins;
$params = array_merge($params, $groupjoin->params);
}
$joins = implode("\n", $joins);
$wheres[] = "{$prefix}u.deleted = 0";
$wheres = implode(" AND ", $wheres);
return new \core\dml\sql_join($joins, $wheres, $params);
}
/**
* Returns array with sql code and parameters returning all ids
* of users enrolled into course.
*
* This function is using 'eu[0-9]+_' prefix for table names and parameters.
*
* @param context $context
* @param string $withcapability
* @param int $groupid 0 means ignore groups, any other value limits the result by group id
* @param bool $onlyactive consider only active enrolments in enabled plugins and time restrictions
* @param bool $onlysuspended inverse of onlyactive, consider only suspended enrolments
* @return array list($sql, $params)
*/
function get_enrolled_sql(context $context, $withcapability = '', $groupid = 0, $onlyactive = false, $onlysuspended = false) {
// Use unique prefix just in case somebody makes some SQL magic with the result.
static $i = 0;
$i++;
$prefix = 'eu' . $i . '_';
$capjoin = get_enrolled_with_capabilities_join(
$context, $prefix, $withcapability, $groupid, $onlyactive, $onlysuspended);
$sql = "SELECT DISTINCT {$prefix}u.id
FROM {user} {$prefix}u
$capjoin->joins
WHERE $capjoin->wheres";
return array($sql, $capjoin->params);
}
/**
* Returns array with sql joins and parameters returning all ids
* of users enrolled into course.
*
* This function is using 'ej[0-9]+_' prefix for table names and parameters.
*
* @throws coding_exception
*
* @param context $context
* @param string $useridcolumn User id column used the calling query, e.g. u.id
* @param bool $onlyactive consider only active enrolments in enabled plugins and time restrictions
* @param bool $onlysuspended inverse of onlyactive, consider only suspended enrolments
* @return \core\dml\sql_join Contains joins, wheres, params
*/
function get_enrolled_join(context $context, $useridcolumn, $onlyactive = false, $onlysuspended = false) {
// Use unique prefix just in case somebody makes some SQL magic with the result.
static $i = 0;
$i++;
$prefix = 'ej' . $i . '_';
// First find the course context.
$coursecontext = $context->get_course_context();
$isfrontpage = ($coursecontext->instanceid == SITEID);
if ($onlyactive && $onlysuspended) {
throw new coding_exception("Both onlyactive and onlysuspended are set, this is probably not what you want!");
}
if ($isfrontpage && $onlysuspended) {
throw new coding_exception("onlysuspended is not supported on frontpage; please add your own early-exit!");
}
$joins = array();
$wheres = array();
$params = array();
$wheres[] = "1 = 1"; // Prevent broken where clauses later on.
// Note all users are "enrolled" on the frontpage, but for others...
if (!$isfrontpage) {
$where1 = "{$prefix}ue.status = :{$prefix}active AND {$prefix}e.status = :{$prefix}enabled";
$where2 = "{$prefix}ue.timestart < :{$prefix}now1 AND ({$prefix}ue.timeend = 0 OR {$prefix}ue.timeend > :{$prefix}now2)";
$ejoin = "JOIN {enrol} {$prefix}e ON ({$prefix}e.id = {$prefix}ue.enrolid AND {$prefix}e.courseid = :{$prefix}courseid)";
$params[$prefix.'courseid'] = $coursecontext->instanceid;
if (!$onlysuspended) {
$joins[] = "JOIN {user_enrolments} {$prefix}ue ON {$prefix}ue.userid = $useridcolumn";
$joins[] = $ejoin;
if ($onlyactive) {
$wheres[] = "$where1 AND $where2";
}
} else {
// Suspended only where there is enrolment but ALL are suspended.
// Consider multiple enrols where one is not suspended or plain role_assign.
$enrolselect = "SELECT DISTINCT {$prefix}ue.userid FROM {user_enrolments} {$prefix}ue $ejoin WHERE $where1 AND $where2";
$joins[] = "JOIN {user_enrolments} {$prefix}ue1 ON {$prefix}ue1.userid = $useridcolumn";
$joins[] = "JOIN {enrol} {$prefix}e1 ON ({$prefix}e1.id = {$prefix}ue1.enrolid
AND {$prefix}e1.courseid = :{$prefix}_e1_courseid)";
$params["{$prefix}_e1_courseid"] = $coursecontext->instanceid;
$wheres[] = "$useridcolumn NOT IN ($enrolselect)";
}
if ($onlyactive || $onlysuspended) {
$now = round(time(), -2); // Rounding helps caching in DB.
$params = array_merge($params, array($prefix . 'enabled' => ENROL_INSTANCE_ENABLED,
$prefix . 'active' => ENROL_USER_ACTIVE,
$prefix . 'now1' => $now, $prefix . 'now2' => $now));
}
}
$joins = implode("\n", $joins);
$wheres = implode(" AND ", $wheres);
return new \core\dml\sql_join($joins, $wheres, $params);
}
/**
* Returns list of users enrolled into course.
*
* @param context $context
* @param string $withcapability
* @param int $groupid 0 means ignore groups, any other value limits the result by group id
* @param string $userfields requested user record fields
* @param string $orderby
* @param int $limitfrom return a subset of records, starting at this point (optional, required if $limitnum is set).
* @param int $limitnum return a subset comprising this many records (optional, required if $limitfrom is set).
* @param bool $onlyactive consider only active enrolments in enabled plugins and time restrictions
* @return array of user records
*/
function get_enrolled_users(context $context, $withcapability = '', $groupid = 0, $userfields = 'u.*', $orderby = null,
$limitfrom = 0, $limitnum = 0, $onlyactive = false) {
global $DB;
list($esql, $params) = get_enrolled_sql($context, $withcapability, $groupid, $onlyactive);
$sql = "SELECT $userfields
FROM {user} u
JOIN ($esql) je ON je.id = u.id
WHERE u.deleted = 0";
if ($orderby) {
$sql = "$sql ORDER BY $orderby";
} else {
list($sort, $sortparams) = users_order_by_sql('u');
$sql = "$sql ORDER BY $sort";
$params = array_merge($params, $sortparams);
}
return $DB->get_records_sql($sql, $params, $limitfrom, $limitnum);
}
/**
* Counts list of users enrolled into course (as per above function)
*
* @param context $context
* @param string $withcapability
* @param int $groupid 0 means ignore groups, any other value limits the result by group id
* @param bool $onlyactive consider only active enrolments in enabled plugins and time restrictions
* @return array of user records
*/
function count_enrolled_users(context $context, $withcapability = '', $groupid = 0, $onlyactive = false) {
global $DB;
$capjoin = get_enrolled_with_capabilities_join(
$context, '', $withcapability, $groupid, $onlyactive);
$sql = "SELECT count(u.id)
FROM {user} u
$capjoin->joins
WHERE $capjoin->wheres AND u.deleted = 0";
return $DB->count_records_sql($sql, $capjoin->params);
}
/**
* All enrol plugins should be based on this class,
+36
View File
@@ -931,6 +931,42 @@ function groups_group_visible($groupid, $course, $cm = null, $userid = null) {
return false;
}
/**
* Get sql and parameters that will return user ids for a group
*
* @param int $groupid
* @return array($sql, $params)
*/
function groups_get_members_ids_sql($groupid) {
$groupjoin = groups_get_members_join($groupid, 'u.id');
$sql = "SELECT DISTINCT u.id
FROM {user} u
$groupjoin->joins
WHERE u.deleted = 0";
return array($sql, $groupjoin->params);
}
/**
* Get sql join to return users in a group
*
* @param int $groupid
* @param string $useridcolumn The column of the user id from the calling SQL, e.g. u.id
* @return \core\dml\sql_join Contains joins, wheres, params
*/
function groups_get_members_join($groupid, $useridcolumn) {
// Use unique prefix just in case somebody makes some SQL magic with the result.
static $i = 0;
$i++;
$prefix = 'gm' . $i . '_';
$join = "JOIN {groups_members} {$prefix}gm ON ({$prefix}gm.userid = $useridcolumn AND {$prefix}gm.groupid = :{$prefix}gmid)";
$param = array("{$prefix}gmid" => $groupid);
return new \core\dml\sql_join($join, '', $param);
}
/**
* Internal method, sets up $SESSION->activegroup and verifies previous value
*
+55
View File
@@ -3066,6 +3066,61 @@ class core_accesslib_testcase extends advanced_testcase {
$this->assertEquals(2, count_role_users($roleid1, context_course::instance($course->id), false));
$this->assertEquals(3, count_role_users($roleid1, context_course::instance($course->id), true));
}
/**
* Test updating of role capabilities during upgrade
* @return void
*/
public function test_get_with_capability_sql() {
global $DB;
$this->resetAfterTest();
$course = $this->getDataGenerator()->create_course();
$coursecontext = context_course::instance($course->id);
$teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher'), '*', MUST_EXIST);
$teacher = $this->getDataGenerator()->create_user();
$studentrole = $DB->get_record('role', array('shortname' => 'student'), '*', MUST_EXIST);
$student = $this->getDataGenerator()->create_user();
$guest = $DB->get_record('user', array('username' => 'guest'));
role_assign($teacherrole->id, $teacher->id, $coursecontext);
role_assign($studentrole->id, $student->id, $coursecontext);
$admin = $DB->get_record('user', array('username' => 'admin'));
// Note: Here are used default capabilities, the full test is in permission evaluation below,
// use two capabilities that teacher has and one does not, none of them should be allowed for not-logged-in user.
$this->assertTrue($DB->record_exists('capabilities', array('name' => 'moodle/backup:backupcourse')));
$this->assertTrue($DB->record_exists('capabilities', array('name' => 'moodle/site:approvecourse')));
list($sql, $params) = get_with_capability_sql($coursecontext, 'moodle/backup:backupcourse');
$users = $DB->get_records_sql($sql, $params);
$this->assertTrue(array_key_exists($teacher->id, $users));
$this->assertFalse(array_key_exists($admin->id, $users));
$this->assertFalse(array_key_exists($student->id, $users));
$this->assertFalse(array_key_exists($guest->id, $users));
list($sql, $params) = get_with_capability_sql($coursecontext, 'moodle/site:approvecourse');
$users = $DB->get_records_sql($sql, $params);
$this->assertFalse(array_key_exists($teacher->id, $users));
$this->assertFalse(array_key_exists($admin->id, $users));
$this->assertFalse(array_key_exists($student->id, $users));
$this->assertFalse(array_key_exists($guest->id, $users));
// Test role override.
assign_capability('moodle/site:backupcourse', CAP_PROHIBIT, $teacherrole->id, $coursecontext, true);
assign_capability('moodle/site:backupcourse', CAP_ALLOW, $studentrole->id, $coursecontext, true);
list($sql, $params) = get_with_capability_sql($coursecontext, 'moodle/site:backupcourse');
$users = $DB->get_records_sql($sql, $params);
$this->assertFalse(array_key_exists($teacher->id, $users));
$this->assertFalse(array_key_exists($admin->id, $users));
$this->assertTrue(array_key_exists($student->id, $users));
$this->assertFalse(array_key_exists($guest->id, $users));
}
}
/**
+36
View File
@@ -176,6 +176,42 @@ class core_grouplib_testcase extends advanced_testcase {
$this->assertEquals($grouping, groups_get_grouping_by_idnumber($course->id, $idnumber2));
}
public function test_groups_get_members_ids_sql() {
global $DB;
$this->resetAfterTest(true);
$generator = $this->getDataGenerator();
$course = $generator->create_course();
$student = $generator->create_user();
$plugin = enrol_get_plugin('manual');
$role = $DB->get_record('role', array('shortname' => 'student'));
$group = $generator->create_group(array('courseid' => $course->id));
$instance = $DB->get_record('enrol', array(
'courseid' => $course->id,
'enrol' => 'manual',
));
$this->assertNotEquals($instance, false);
// Enrol the user in the course.
$plugin->enrol_user($instance, $student->id, $role->id);
list($sql, $params) = groups_get_members_ids_sql($group->id, true);
// Test an empty group.
$users = $DB->get_records_sql($sql, $params);
$this->assertFalse(array_key_exists($student->id, $users));
groups_add_member($group->id, $student->id);
// Test with a group member.
$users = $DB->get_records_sql($sql, $params);
$this->assertTrue(array_key_exists($student->id, $users));
}
public function test_groups_get_group_by_name() {
$this->resetAfterTest(true);