mirror of
https://github.com/moodle/moodle.git
synced 2025-04-14 04:52:36 +02:00
Merge branch 'MDL-68402' of https://github.com/timhunt/moodle
This commit is contained in:
commit
2685accca8
@ -776,7 +776,7 @@ function has_capability_in_accessdata($capability, context $context, array &$acc
|
||||
// Build $paths as a list of current + all parent "paths" with order bottom-to-top
|
||||
$path = $context->path;
|
||||
$paths = array($path);
|
||||
while($path = rtrim($path, '0123456789')) {
|
||||
while ($path = rtrim($path, '0123456789')) {
|
||||
$path = rtrim($path, '/');
|
||||
if ($path === '') {
|
||||
break;
|
||||
@ -1106,7 +1106,7 @@ function remove_temp_course_roles(context_course $coursecontext) {
|
||||
$ras = $DB->get_records_sql($sql, array('contextid'=>$coursecontext->id, 'userid'=>$USER->id));
|
||||
|
||||
$USER->access['ra'][$coursecontext->path] = array();
|
||||
foreach($ras as $r) {
|
||||
foreach ($ras as $r) {
|
||||
$USER->access['ra'][$coursecontext->path][(int)$r->id] = (int)$r->id;
|
||||
}
|
||||
}
|
||||
@ -1663,7 +1663,7 @@ function role_unassign_all(array $params, $subcontexts = false, $includemanual =
|
||||
}
|
||||
|
||||
$ras = $DB->get_records('role_assignments', $params);
|
||||
foreach($ras as $ra) {
|
||||
foreach ($ras as $ra) {
|
||||
$DB->delete_records('role_assignments', array('id'=>$ra->id));
|
||||
if ($context = context::instance_by_id($ra->contextid, IGNORE_MISSING)) {
|
||||
// Role assignments have changed, so mark user as dirty.
|
||||
@ -1697,10 +1697,10 @@ function role_unassign_all(array $params, $subcontexts = false, $includemanual =
|
||||
if ($context) {
|
||||
$contexts = $context->get_child_contexts();
|
||||
$mparams = $params;
|
||||
foreach($contexts as $context) {
|
||||
foreach ($contexts as $context) {
|
||||
$mparams['contextid'] = $context->id;
|
||||
$ras = $DB->get_records('role_assignments', $mparams);
|
||||
foreach($ras as $ra) {
|
||||
foreach ($ras as $ra) {
|
||||
$DB->delete_records('role_assignments', array('id'=>$ra->id));
|
||||
// Role assignments have changed, so mark user as dirty.
|
||||
mark_user_dirty($ra->userid);
|
||||
@ -1978,7 +1978,7 @@ function can_access_course(stdClass $course, $user = null, $withcapability = '',
|
||||
// if not enrolled try to gain temporary guest access
|
||||
$instances = $DB->get_records('enrol', array('courseid'=>$course->id, 'status'=>ENROL_INSTANCE_ENABLED), 'sortorder, id ASC');
|
||||
$enrols = enrol_get_plugins(true);
|
||||
foreach($instances as $instance) {
|
||||
foreach ($instances as $instance) {
|
||||
if (!isset($enrols[$instance->enrol])) {
|
||||
continue;
|
||||
}
|
||||
@ -2067,7 +2067,7 @@ function get_default_capabilities($archetype) {
|
||||
$alldefs = array_merge($alldefs, load_capability_def($cap['component']));
|
||||
}
|
||||
}
|
||||
foreach($alldefs as $name=>$def) {
|
||||
foreach ($alldefs as $name=>$def) {
|
||||
// Use array 'archetypes if available. Only if not specified, use 'legacy'.
|
||||
if (isset($def['archetypes'])) {
|
||||
if (isset($def['archetypes'][$archetype])) {
|
||||
@ -2188,7 +2188,7 @@ function reset_role_capabilities($roleid) {
|
||||
$DB->delete_records('role_capabilities',
|
||||
array('roleid' => $roleid, 'contextid' => $systemcontext->id));
|
||||
|
||||
foreach($defaultcaps as $cap=>$permission) {
|
||||
foreach ($defaultcaps as $cap=>$permission) {
|
||||
assign_capability($cap, $permission, $roleid, $systemcontext->id);
|
||||
}
|
||||
|
||||
@ -2215,7 +2215,7 @@ function update_capabilities($component = 'moodle') {
|
||||
$storedcaps = array();
|
||||
|
||||
$filecaps = load_capability_def($component);
|
||||
foreach($filecaps as $capname=>$unused) {
|
||||
foreach ($filecaps as $capname=>$unused) {
|
||||
if (!preg_match('|^[a-z]+/[a-z_0-9]+:[a-z_0-9]+$|', $capname)) {
|
||||
debugging("Coding problem: Invalid capability name '$capname', use 'clonepermissionsfrom' field for migration.");
|
||||
}
|
||||
@ -2345,7 +2345,7 @@ function capabilities_cleanup($component, $newcapdef = null) {
|
||||
|
||||
// Delete from roles.
|
||||
if ($roles = get_roles_with_capability($cachedcap->name)) {
|
||||
foreach($roles as $role) {
|
||||
foreach ($roles as $role) {
|
||||
if (!unassign_capability($cachedcap->name, $role->id)) {
|
||||
print_error('cannotunassigncap', 'error', '', (object)array('cap'=>$cachedcap->name, 'role'=>$role->name));
|
||||
}
|
||||
@ -3427,36 +3427,25 @@ function set_role_contextlevels($roleid, array $contextlevels) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Who has this capability in this context?
|
||||
* Gets sql joins for finding users with capability in the given context.
|
||||
*
|
||||
* This can be a very expensive call - use sparingly and keep
|
||||
* the results if you are going to need them again soon.
|
||||
*
|
||||
* Note if $fields is empty this function attempts to get u.*
|
||||
* which can get rather large - and has a serious perf impact
|
||||
* on some DBs.
|
||||
*
|
||||
* @param context $context
|
||||
* @param string|array $capability - capability name(s)
|
||||
* @param string $fields - fields to be pulled. The user table is aliased to 'u'. u.id MUST be included.
|
||||
* @param string $sort - the sort order. Default is lastaccess time.
|
||||
* @param mixed $limitfrom - number of records to skip (offset)
|
||||
* @param mixed $limitnum - number of records to fetch
|
||||
* @param string|array $groups - single group or array of groups - only return
|
||||
* users who are in one of these group(s).
|
||||
* @param string|array $exceptions - list of users to exclude, comma separated or array
|
||||
* @param bool $doanything_ignored not used any more, admin accounts are never returned
|
||||
* @param bool $view_ignored - use get_enrolled_sql() instead
|
||||
* @param bool $useviewallgroups if $groups is set the return users who
|
||||
* have capability both $capability and moodle/site:accessallgroups
|
||||
* in this context, as well as users who have $capability and who are
|
||||
* in $groups.
|
||||
* @return array of user records
|
||||
* @param context $context Context for the join.
|
||||
* @param string|array $capability Capability name or array of names.
|
||||
* If an array is provided then this is the equivalent of a logical 'OR',
|
||||
* i.e. the user needs to have one of these capabilities.
|
||||
* @param string $useridcolumn e.g. 'u.id'.
|
||||
* @return \core\dml\sql_join Contains joins, wheres, params.
|
||||
* This function will set ->cannotmatchanyrows if applicable.
|
||||
* This may let you skip doing a DB query.
|
||||
*/
|
||||
function get_users_by_capability(context $context, $capability, $fields = '', $sort = '', $limitfrom = '', $limitnum = '',
|
||||
$groups = '', $exceptions = '', $doanything_ignored = null, $view_ignored = null, $useviewallgroups = false) {
|
||||
function get_with_capability_join(context $context, $capability, $useridcolumn) {
|
||||
global $CFG, $DB;
|
||||
|
||||
// Add a unique prefix to param names to ensure they are unique.
|
||||
static $i = 0;
|
||||
$i++;
|
||||
$paramprefix = 'eu' . $i . '_';
|
||||
|
||||
$defaultuserroleid = isset($CFG->defaultuserroleid) ? $CFG->defaultuserroleid : 0;
|
||||
$defaultfrontpageroleid = isset($CFG->defaultfrontpageroleid) ? $CFG->defaultfrontpageroleid : 0;
|
||||
|
||||
@ -3464,26 +3453,18 @@ function get_users_by_capability(context $context, $capability, $fields = '', $s
|
||||
$ctxids = str_replace('/', ',', $ctxids);
|
||||
|
||||
// Context is the frontpage
|
||||
$iscoursepage = false; // coursepage other than fp
|
||||
$isfrontpage = false;
|
||||
if ($context->contextlevel == CONTEXT_COURSE) {
|
||||
if ($context->instanceid == SITEID) {
|
||||
$isfrontpage = true;
|
||||
} else {
|
||||
$iscoursepage = true;
|
||||
}
|
||||
}
|
||||
$isfrontpage = ($isfrontpage || is_inside_frontpage($context));
|
||||
$isfrontpage = $context->contextlevel == CONTEXT_COURSE && $context->instanceid == SITEID;
|
||||
$isfrontpage = $isfrontpage || is_inside_frontpage($context);
|
||||
|
||||
$caps = (array)$capability;
|
||||
$caps = (array) $capability;
|
||||
|
||||
// construct list of context paths bottom-->top
|
||||
// Construct list of context paths bottom --> top.
|
||||
list($contextids, $paths) = get_context_info_list($context);
|
||||
|
||||
// we need to find out all roles that have these capabilities either in definition or in overrides
|
||||
$defs = array();
|
||||
list($incontexts, $params) = $DB->get_in_or_equal($contextids, SQL_PARAMS_NAMED, 'con');
|
||||
list($incaps, $params2) = $DB->get_in_or_equal($caps, SQL_PARAMS_NAMED, 'cap');
|
||||
// We need to find out all roles that have these capabilities either in definition or in overrides.
|
||||
$defs = [];
|
||||
list($incontexts, $params) = $DB->get_in_or_equal($contextids, SQL_PARAMS_NAMED, $paramprefix . 'con');
|
||||
list($incaps, $params2) = $DB->get_in_or_equal($caps, SQL_PARAMS_NAMED, $paramprefix . 'cap');
|
||||
|
||||
// Check whether context locking is enabled.
|
||||
// Filter out any write capability if this is the case.
|
||||
@ -3507,15 +3488,15 @@ function get_users_by_capability(context $context, $capability, $fields = '', $s
|
||||
$defs[$rc->capability][$rc->path][$rc->roleid] = $rc->permission;
|
||||
}
|
||||
|
||||
// go through the permissions bottom-->top direction to evaluate the current permission,
|
||||
// first one wins (prohibit is an exception that always wins)
|
||||
$access = array();
|
||||
// Go through the permissions bottom-->top direction to evaluate the current permission,
|
||||
// first one wins (prohibit is an exception that always wins).
|
||||
$access = [];
|
||||
foreach ($caps as $cap) {
|
||||
foreach ($paths as $path) {
|
||||
if (empty($defs[$cap][$path])) {
|
||||
continue;
|
||||
}
|
||||
foreach($defs[$cap][$path] as $roleid => $perm) {
|
||||
foreach ($defs[$cap][$path] as $roleid => $perm) {
|
||||
if ($perm == CAP_PROHIBIT) {
|
||||
$access[$cap][$roleid] = CAP_PROHIBIT;
|
||||
continue;
|
||||
@ -3527,9 +3508,9 @@ function get_users_by_capability(context $context, $capability, $fields = '', $s
|
||||
}
|
||||
}
|
||||
|
||||
// make lists of roles that are needed and prohibited in this context
|
||||
$needed = array(); // one of these is enough
|
||||
$prohibited = array(); // must not have any of these
|
||||
// Make lists of roles that are needed and prohibited in this context.
|
||||
$needed = []; // One of these is enough.
|
||||
$prohibited = []; // Must not have any of these.
|
||||
foreach ($caps as $cap) {
|
||||
if (empty($access[$cap])) {
|
||||
continue;
|
||||
@ -3543,11 +3524,11 @@ function get_users_by_capability(context $context, $capability, $fields = '', $s
|
||||
}
|
||||
}
|
||||
if (empty($needed[$cap]) or !empty($prohibited[$cap][$defaultuserroleid])) {
|
||||
// easy, nobody has the permission
|
||||
// Easy, nobody has the permission.
|
||||
unset($needed[$cap]);
|
||||
unset($prohibited[$cap]);
|
||||
} else if ($isfrontpage and !empty($prohibited[$cap][$defaultfrontpageroleid])) {
|
||||
// everybody is disqualified on the frontpage
|
||||
// Everybody is disqualified on the frontpage.
|
||||
unset($needed[$cap]);
|
||||
unset($prohibited[$cap]);
|
||||
}
|
||||
@ -3557,23 +3538,145 @@ function get_users_by_capability(context $context, $capability, $fields = '', $s
|
||||
}
|
||||
|
||||
if (empty($needed)) {
|
||||
// there can not be anybody if no roles match this request
|
||||
return array();
|
||||
// There can not be anybody if no roles match this request.
|
||||
return new \core\dml\sql_join('', '1 = 2', [], true);
|
||||
}
|
||||
|
||||
if (empty($prohibited)) {
|
||||
// we can compact the needed roles
|
||||
$n = array();
|
||||
// We can compact the needed roles.
|
||||
$n = [];
|
||||
foreach ($needed as $cap) {
|
||||
foreach ($cap as $roleid=>$unused) {
|
||||
foreach ($cap as $roleid => $unused) {
|
||||
$n[$roleid] = true;
|
||||
}
|
||||
}
|
||||
$needed = array('any'=>$n);
|
||||
$needed = ['any' => $n];
|
||||
unset($n);
|
||||
}
|
||||
|
||||
// ***** Set up default fields ******
|
||||
// Prepare query clauses.
|
||||
$wherecond = [];
|
||||
$params = [];
|
||||
$joins = [];
|
||||
$cannotmatchanyrows = false;
|
||||
|
||||
// We never return deleted users or guest account.
|
||||
// Use a hack to get the deleted user column without an API change.
|
||||
$deletedusercolumn = substr($useridcolumn, 0, -2) . 'deleted';
|
||||
$wherecond[] = "$deletedusercolumn = 0 AND $useridcolumn <> :{$paramprefix}guestid";
|
||||
$params[$paramprefix . 'guestid'] = $CFG->siteguest;
|
||||
|
||||
// Now add the needed and prohibited roles conditions as joins.
|
||||
if (!empty($needed['any'])) {
|
||||
// Simple case - there are no prohibits involved.
|
||||
if (!empty($needed['any'][$defaultuserroleid]) ||
|
||||
($isfrontpage && !empty($needed['any'][$defaultfrontpageroleid]))) {
|
||||
// Everybody.
|
||||
} else {
|
||||
$joins[] = "JOIN (SELECT DISTINCT userid
|
||||
FROM {role_assignments}
|
||||
WHERE contextid IN ($ctxids)
|
||||
AND roleid IN (" . implode(',', array_keys($needed['any'])) . ")
|
||||
) ra ON ra.userid = $useridcolumn";
|
||||
}
|
||||
} else {
|
||||
$unions = [];
|
||||
$everybody = false;
|
||||
foreach ($needed as $cap => $unused) {
|
||||
if (empty($prohibited[$cap])) {
|
||||
if (!empty($needed[$cap][$defaultuserroleid]) ||
|
||||
($isfrontpage && !empty($needed[$cap][$defaultfrontpageroleid]))) {
|
||||
$everybody = true;
|
||||
break;
|
||||
} else {
|
||||
$unions[] = "SELECT userid
|
||||
FROM {role_assignments}
|
||||
WHERE contextid IN ($ctxids)
|
||||
AND roleid IN (".implode(',', array_keys($needed[$cap])) .")";
|
||||
}
|
||||
} else {
|
||||
if (!empty($prohibited[$cap][$defaultuserroleid]) ||
|
||||
($isfrontpage && !empty($prohibited[$cap][$defaultfrontpageroleid]))) {
|
||||
// Nobody can have this cap because it is prohibited in default roles.
|
||||
continue;
|
||||
|
||||
} else if (!empty($needed[$cap][$defaultuserroleid]) ||
|
||||
($isfrontpage && !empty($needed[$cap][$defaultfrontpageroleid]))) {
|
||||
// Everybody except the prohibited - hiding does not matter.
|
||||
$unions[] = "SELECT id AS userid
|
||||
FROM {user}
|
||||
WHERE id NOT IN (SELECT userid
|
||||
FROM {role_assignments}
|
||||
WHERE contextid IN ($ctxids)
|
||||
AND roleid IN (" . implode(',', array_keys($prohibited[$cap])) . "))";
|
||||
|
||||
} else {
|
||||
$unions[] = "SELECT userid
|
||||
FROM {role_assignments}
|
||||
WHERE contextid IN ($ctxids) AND roleid IN (" . implode(',', array_keys($needed[$cap])) . ")
|
||||
AND userid NOT IN (
|
||||
SELECT userid
|
||||
FROM {role_assignments}
|
||||
WHERE contextid IN ($ctxids)
|
||||
AND roleid IN (" . implode(',', array_keys($prohibited[$cap])) . "))";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!$everybody) {
|
||||
if ($unions) {
|
||||
$joins[] = "JOIN (
|
||||
SELECT DISTINCT userid
|
||||
FROM (
|
||||
" . implode("\n UNION \n", $unions) . "
|
||||
) us
|
||||
) ra ON ra.userid = $useridcolumn";
|
||||
} else {
|
||||
// Only prohibits found - nobody can be matched.
|
||||
$wherecond[] = "1 = 2";
|
||||
$cannotmatchanyrows = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new \core\dml\sql_join(implode("\n", $joins), implode(" AND ", $wherecond), $params, $cannotmatchanyrows);
|
||||
}
|
||||
|
||||
/**
|
||||
* Who has this capability in this context?
|
||||
*
|
||||
* This can be a very expensive call - use sparingly and keep
|
||||
* the results if you are going to need them again soon.
|
||||
*
|
||||
* Note if $fields is empty this function attempts to get u.*
|
||||
* which can get rather large - and has a serious perf impact
|
||||
* on some DBs.
|
||||
*
|
||||
* @param context $context
|
||||
* @param string|array $capability - capability name(s)
|
||||
* @param string $fields - fields to be pulled. The user table is aliased to 'u'. u.id MUST be included.
|
||||
* @param string $sort - the sort order. Default is lastaccess time.
|
||||
* @param mixed $limitfrom - number of records to skip (offset)
|
||||
* @param mixed $limitnum - number of records to fetch
|
||||
* @param string|array $groups - single group or array of groups - only return
|
||||
* users who are in one of these group(s).
|
||||
* @param string|array $exceptions - list of users to exclude, comma separated or array
|
||||
* @param bool $notuseddoanything not used any more, admin accounts are never returned
|
||||
* @param bool $notusedview - use get_enrolled_sql() instead
|
||||
* @param bool $useviewallgroups if $groups is set the return users who
|
||||
* have capability both $capability and moodle/site:accessallgroups
|
||||
* in this context, as well as users who have $capability and who are
|
||||
* in $groups.
|
||||
* @return array of user records
|
||||
*/
|
||||
function get_users_by_capability(context $context, $capability, $fields = '', $sort = '', $limitfrom = '', $limitnum = '',
|
||||
$groups = '', $exceptions = '', $notuseddoanything = null, $notusedview = null, $useviewallgroups = false) {
|
||||
global $CFG, $DB;
|
||||
|
||||
// Context is a course page other than the frontpage.
|
||||
$iscoursepage = $context->contextlevel == CONTEXT_COURSE && $context->instanceid != SITEID;
|
||||
|
||||
// Set up default fields list if necessary.
|
||||
if (empty($fields)) {
|
||||
if ($iscoursepage) {
|
||||
$fields = 'u.*, ul.timeaccess AS lastaccess';
|
||||
@ -3586,7 +3689,7 @@ function get_users_by_capability(context $context, $capability, $fields = '', $s
|
||||
}
|
||||
}
|
||||
|
||||
// Set up default sort
|
||||
// Set up default sort if necessary.
|
||||
if (empty($sort)) { // default to course lastaccess or just lastaccess
|
||||
if ($iscoursepage) {
|
||||
$sort = 'ul.timeaccess';
|
||||
@ -3595,14 +3698,20 @@ function get_users_by_capability(context $context, $capability, $fields = '', $s
|
||||
}
|
||||
}
|
||||
|
||||
// Prepare query clauses
|
||||
$wherecond = array();
|
||||
$params = array();
|
||||
$joins = array();
|
||||
// Get the bits of SQL relating to capabilities.
|
||||
$sqljoin = get_with_capability_join($context, $capability, 'u.id');
|
||||
if ($sqljoin->cannotmatchanyrows) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// User lastaccess JOIN
|
||||
// Prepare query clauses.
|
||||
$wherecond = [$sqljoin->wheres];
|
||||
$params = $sqljoin->params;
|
||||
$joins = [$sqljoin->joins];
|
||||
|
||||
// Add user lastaccess JOIN, if required.
|
||||
if ((strpos($sort, 'ul.timeaccess') === false) and (strpos($fields, 'ul.timeaccess') === false)) {
|
||||
// user_lastaccess is not required MDL-13810
|
||||
// Here user_lastaccess is not required MDL-13810.
|
||||
} else {
|
||||
if ($iscoursepage) {
|
||||
$joins[] = "LEFT OUTER JOIN {user_lastaccess} ul ON (ul.userid = u.id AND ul.courseid = {$context->instanceid})";
|
||||
@ -3611,11 +3720,7 @@ function get_users_by_capability(context $context, $capability, $fields = '', $s
|
||||
}
|
||||
}
|
||||
|
||||
// We never return deleted users or guest account.
|
||||
$wherecond[] = "u.deleted = 0 AND u.id <> :guestid";
|
||||
$params['guestid'] = $CFG->siteguest;
|
||||
|
||||
// Groups
|
||||
// Groups.
|
||||
if ($groups) {
|
||||
$groups = (array)$groups;
|
||||
list($grouptest, $grpparams) = $DB->get_in_or_equal($groups, SQL_PARAMS_NAMED, 'grp');
|
||||
@ -3636,7 +3741,7 @@ function get_users_by_capability(context $context, $capability, $fields = '', $s
|
||||
$wherecond[] = "($grouptest)";
|
||||
}
|
||||
|
||||
// User exceptions
|
||||
// User exceptions.
|
||||
if (!empty($exceptions)) {
|
||||
$exceptions = (array)$exceptions;
|
||||
list($exsql, $exparams) = $DB->get_in_or_equal($exceptions, SQL_PARAMS_NAMED, 'exc', false);
|
||||
@ -3644,77 +3749,14 @@ function get_users_by_capability(context $context, $capability, $fields = '', $s
|
||||
$wherecond[] = "u.id $exsql";
|
||||
}
|
||||
|
||||
// now add the needed and prohibited roles conditions as joins
|
||||
if (!empty($needed['any'])) {
|
||||
// simple case - there are no prohibits involved
|
||||
if (!empty($needed['any'][$defaultuserroleid]) or ($isfrontpage and !empty($needed['any'][$defaultfrontpageroleid]))) {
|
||||
// everybody
|
||||
} else {
|
||||
$joins[] = "JOIN (SELECT DISTINCT userid
|
||||
FROM {role_assignments}
|
||||
WHERE contextid IN ($ctxids)
|
||||
AND roleid IN (".implode(',', array_keys($needed['any'])) .")
|
||||
) ra ON ra.userid = u.id";
|
||||
}
|
||||
} else {
|
||||
$unions = array();
|
||||
$everybody = false;
|
||||
foreach ($needed as $cap=>$unused) {
|
||||
if (empty($prohibited[$cap])) {
|
||||
if (!empty($needed[$cap][$defaultuserroleid]) or ($isfrontpage and !empty($needed[$cap][$defaultfrontpageroleid]))) {
|
||||
$everybody = true;
|
||||
break;
|
||||
} else {
|
||||
$unions[] = "SELECT userid
|
||||
FROM {role_assignments}
|
||||
WHERE contextid IN ($ctxids)
|
||||
AND roleid IN (".implode(',', array_keys($needed[$cap])) .")";
|
||||
}
|
||||
} else {
|
||||
if (!empty($prohibited[$cap][$defaultuserroleid]) or ($isfrontpage and !empty($prohibited[$cap][$defaultfrontpageroleid]))) {
|
||||
// nobody can have this cap because it is prevented in default roles
|
||||
continue;
|
||||
|
||||
} else if (!empty($needed[$cap][$defaultuserroleid]) or ($isfrontpage and !empty($needed[$cap][$defaultfrontpageroleid]))) {
|
||||
// everybody except the prohibitted - hiding does not matter
|
||||
$unions[] = "SELECT id AS userid
|
||||
FROM {user}
|
||||
WHERE id NOT IN (SELECT userid
|
||||
FROM {role_assignments}
|
||||
WHERE contextid IN ($ctxids)
|
||||
AND roleid IN (".implode(',', array_keys($prohibited[$cap])) ."))";
|
||||
|
||||
} else {
|
||||
$unions[] = "SELECT userid
|
||||
FROM {role_assignments}
|
||||
WHERE contextid IN ($ctxids) AND roleid IN (".implode(',', array_keys($needed[$cap])) .")
|
||||
AND userid NOT IN (
|
||||
SELECT userid
|
||||
FROM {role_assignments}
|
||||
WHERE contextid IN ($ctxids)
|
||||
AND roleid IN (" . implode(',', array_keys($prohibited[$cap])) . ")
|
||||
)";
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!$everybody) {
|
||||
if ($unions) {
|
||||
$joins[] = "JOIN (SELECT DISTINCT userid FROM ( ".implode(' UNION ', $unions)." ) us) ra ON ra.userid = u.id";
|
||||
} else {
|
||||
// only prohibits found - nobody can be matched
|
||||
$wherecond[] = "1 = 2";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Collect WHERE conditions and needed joins
|
||||
// Collect WHERE conditions and needed joins.
|
||||
$where = implode(' AND ', $wherecond);
|
||||
if ($where !== '') {
|
||||
$where = 'WHERE ' . $where;
|
||||
}
|
||||
$joins = implode("\n", $joins);
|
||||
|
||||
// Ok, let's get the users!
|
||||
// Finally! we have all the bits, run the query.
|
||||
$sql = "SELECT $fields
|
||||
FROM {user} u
|
||||
$joins
|
||||
@ -4063,7 +4105,7 @@ function get_user_capability_course($capability, $userid = null, $doanything = t
|
||||
$fieldlist = '';
|
||||
if ($fieldsexceptid) {
|
||||
$fields = array_map('trim', explode(',', $fieldsexceptid));
|
||||
foreach($fields as $field) {
|
||||
foreach ($fields as $field) {
|
||||
// Context fields have a different alias.
|
||||
if (strpos($field, 'ctx') === 0) {
|
||||
switch($field) {
|
||||
@ -4086,7 +4128,7 @@ function get_user_capability_course($capability, $userid = null, $doanything = t
|
||||
if ($orderby) {
|
||||
$fields = explode(',', $orderby);
|
||||
$orderby = '';
|
||||
foreach($fields as $field) {
|
||||
foreach ($fields as $field) {
|
||||
if ($orderby) {
|
||||
$orderby .= ',';
|
||||
}
|
||||
@ -4576,7 +4618,7 @@ function get_roles_with_cap_in_context($context, $capability) {
|
||||
|
||||
$forbidden = array();
|
||||
$needed = array();
|
||||
foreach($capdefs as $def) {
|
||||
foreach ($capdefs as $def) {
|
||||
if (isset($forbidden[$def->roleid])) {
|
||||
continue;
|
||||
}
|
||||
@ -4596,7 +4638,7 @@ function get_roles_with_cap_in_context($context, $capability) {
|
||||
unset($capdefs);
|
||||
|
||||
// remove all those roles not allowing
|
||||
foreach($needed as $key=>$value) {
|
||||
foreach ($needed as $key=>$value) {
|
||||
if (!$value) {
|
||||
unset($needed[$key]);
|
||||
} else {
|
||||
@ -4618,7 +4660,7 @@ function get_roles_with_cap_in_context($context, $capability) {
|
||||
function get_roles_with_caps_in_context($context, $capabilities) {
|
||||
$neededarr = array();
|
||||
$forbiddenarr = array();
|
||||
foreach($capabilities as $caprequired) {
|
||||
foreach ($capabilities as $caprequired) {
|
||||
list($neededarr[], $forbiddenarr[]) = get_roles_with_cap_in_context($context, $caprequired);
|
||||
}
|
||||
|
||||
@ -4732,7 +4774,7 @@ function role_change_permission($roleid, $context, $capname, $permission) {
|
||||
ORDER BY ctx.depth DESC";
|
||||
|
||||
if ($existing = $DB->get_records_sql($sql, $params)) {
|
||||
foreach($existing as $e) {
|
||||
foreach ($existing as $e) {
|
||||
if ($e->permission == CAP_PROHIBIT) {
|
||||
// prohibit can not be overridden, no point in changing anything
|
||||
return;
|
||||
@ -4889,7 +4931,7 @@ abstract class context extends stdClass implements IteratorAggregate {
|
||||
|
||||
if (self::$cache_count >= CONTEXT_CACHE_MAX_SIZE) {
|
||||
$i = 0;
|
||||
foreach(self::$cache_contextsbyid as $ctx) {
|
||||
foreach (self::$cache_contextsbyid as $ctx) {
|
||||
$i++;
|
||||
if ($i <= 100) {
|
||||
// we want to keep the first contexts to be loaded on this page, hopefully they will be needed again later
|
||||
@ -7586,160 +7628,3 @@ function get_with_capability_sql(context $context, $capability) {
|
||||
|
||||
return array($sql, $capjoin->params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets sql joins for finding users with capability in the given context
|
||||
*
|
||||
* @param context $context Context for the join
|
||||
* @param string|array $capability Capability name or array of names.
|
||||
* If an array is provided then this is the equivalent of a logical 'OR',
|
||||
* i.e. the user needs to have one of these capabilities.
|
||||
* @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');
|
||||
|
||||
list($incaps, $capsparams) = $DB->get_in_or_equal($capability, SQL_PARAMS_NAMED, 'cap');
|
||||
|
||||
// Check whether context locking is enabled.
|
||||
// Filter out any write capability if this is the case.
|
||||
$excludelockedcaps = '';
|
||||
$excludelockedcapsparams = [];
|
||||
if (!empty($CFG->contextlocking) && $context->locked) {
|
||||
$excludelockedcaps = 'AND (cap.captype = :capread OR cap.name = :managelockscap)';
|
||||
$excludelockedcapsparams['capread'] = 'read';
|
||||
$excludelockedcapsparams['managelockscap'] = 'moodle/site:managecontextlocks';
|
||||
}
|
||||
|
||||
$defs = array();
|
||||
$sql = "SELECT rc.id, rc.roleid, rc.permission, ctx.path
|
||||
FROM {role_capabilities} rc
|
||||
JOIN {capabilities} cap ON rc.capability = cap.name
|
||||
JOIN {context} ctx on rc.contextid = ctx.id
|
||||
WHERE rc.contextid $incontexts AND rc.capability $incaps $excludelockedcaps";
|
||||
$rcs = $DB->get_records_sql($sql, array_merge($cparams, $capsparams, $excludelockedcapsparams));
|
||||
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);
|
||||
}
|
||||
|
@ -31,6 +31,14 @@ defined('MOODLE_INTERNAL') || die();
|
||||
/**
|
||||
* An object that contains sql join fragments.
|
||||
*
|
||||
* An example of how to use this class in a simple query, where you have got
|
||||
* a join that is a join to the user table:
|
||||
*
|
||||
* $users = $DB->get_records_sql("SELECT u.*
|
||||
* FROM {user} u
|
||||
* {$sqljoin->joins}
|
||||
* WHERE {$sqljoin->wheres}", $sqljoin->params);
|
||||
*
|
||||
* @since Moodle 3.1
|
||||
* @package core
|
||||
* @category dml
|
||||
@ -54,16 +62,31 @@ class sql_join {
|
||||
*/
|
||||
public $params;
|
||||
|
||||
/**
|
||||
* @var bool if true this join is guaranteed to never match any rows.
|
||||
* In this case, the calling code may be able to completely
|
||||
* skip doing the database query.
|
||||
* @since Moodle 3.9/3.8.3/3.7.6.
|
||||
*/
|
||||
public $cannotmatchanyrows;
|
||||
|
||||
/**
|
||||
* Create an object that contains sql join fragments.
|
||||
*
|
||||
* Note, even if you set $cannotmatchanyrows to true, it is
|
||||
* important to also set the other fields because the calling
|
||||
* code is not required to check it. For example
|
||||
* new \core\dml\sql_join('', '1 = 2', [], true);
|
||||
*
|
||||
* @param string $joins The join sql fragment.
|
||||
* @param string $wheres The where sql fragment.
|
||||
* @param array $params Any parameter values.
|
||||
* @param bool $cannotmatchanyrows If true, this join is guaranteed to match no rows. See comment on the field above.
|
||||
*/
|
||||
public function __construct($joins = '', $wheres = '', $params = array()) {
|
||||
public function __construct($joins = '', $wheres = '', $params = array(), $cannotmatchanyrows = false) {
|
||||
$this->joins = $joins;
|
||||
$this->wheres = $wheres;
|
||||
$this->params = $params;
|
||||
$this->cannotmatchanyrows = $cannotmatchanyrows;
|
||||
}
|
||||
}
|
||||
|
@ -3850,10 +3850,6 @@ class core_accesslib_testcase extends advanced_testcase {
|
||||
$this->assertFalse(array_key_exists($guest->id, $users));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test updating of role capabilities during upgrade
|
||||
* @return void
|
||||
*/
|
||||
public function test_get_with_capability_sql() {
|
||||
global $DB;
|
||||
|
||||
@ -3905,6 +3901,73 @@ class core_accesslib_testcase extends advanced_testcase {
|
||||
$this->assertFalse(array_key_exists($guest->id, $users));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the test cases for {@link test_get_with_capability_join_when_overrides_present()}.
|
||||
*
|
||||
* The particular capabilties used here do not really matter. What is important is
|
||||
* that they are capabilities which the Student roles has by default, but the
|
||||
* authenticated suser role does not.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_get_with_capability_join_override_cases() {
|
||||
return [
|
||||
'no overrides' => [true, []],
|
||||
'one override' => [true, ['moodle/course:viewscales']],
|
||||
'both overrides' => [false, ['moodle/course:viewscales', 'moodle/question:flag']],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Test get_with_capability_join.
|
||||
*
|
||||
* @dataProvider get_get_with_capability_join_override_cases
|
||||
*
|
||||
* @param bool $studentshouldbereturned whether, with this combination of capabilities, the student should be in the results.
|
||||
* @param array $capabilitiestoprevent capabilities to override to prevent in the course context.
|
||||
*/
|
||||
public function test_get_with_capability_join_when_overrides_present(
|
||||
bool $studentshouldbereturned, array $capabilitiestoprevent) {
|
||||
global $DB;
|
||||
$this->resetAfterTest();
|
||||
$generator = $this->getDataGenerator();
|
||||
|
||||
// Create a course.
|
||||
$category = $generator->create_category();
|
||||
$course = $generator->create_course(['category' => $category->id]);
|
||||
|
||||
// Create a user.
|
||||
$student = $generator->create_user();
|
||||
$studentrole = $DB->get_record('role', ['shortname' => 'student'], '*', MUST_EXIST);
|
||||
$generator->enrol_user($student->id, $course->id, $studentrole->id);
|
||||
|
||||
// This test assumes that by default the student roles has the two
|
||||
// capabilities. Check this now in case the role definitions are every changed.
|
||||
$coursecontext = context_course::instance($course->id);
|
||||
$this->assertTrue(has_capability('moodle/course:viewscales', $coursecontext, $student));
|
||||
$this->assertTrue(has_capability('moodle/question:flag', $coursecontext, $student));
|
||||
|
||||
// We test cases where there are a varying number of prevent overrides.
|
||||
foreach ($capabilitiestoprevent as $capability) {
|
||||
role_change_permission($studentrole->id, $coursecontext, $capability, CAP_PREVENT);
|
||||
}
|
||||
|
||||
// So now, assemble our query using the method under test, and verify that it returns the student.
|
||||
$sqljoin = get_with_capability_join($coursecontext,
|
||||
['moodle/course:viewscales', 'moodle/question:flag'], 'u.id');
|
||||
|
||||
$users = $DB->get_records_sql("SELECT u.*
|
||||
FROM {user} u
|
||||
{$sqljoin->joins}
|
||||
WHERE {$sqljoin->wheres}", $sqljoin->params);
|
||||
if ($studentshouldbereturned) {
|
||||
$this->assertEquals([$student->id], array_keys($users));
|
||||
} else {
|
||||
$this->assertEmpty($users);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the get_profile_roles() function.
|
||||
*/
|
||||
|
Loading…
x
Reference in New Issue
Block a user