Merge branch 'wip-MDL-31243-master' of https://github.com/Beedell/moodle
This commit is contained in:
+165
-345
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
*
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user