From 26843e106b3d6a7a0dedb4c661cfc1ea1e008008 Mon Sep 17 00:00:00 2001 From: martinlanghoff Date: Sun, 6 Jan 2008 23:22:26 +0000 Subject: [PATCH] accesslib: get_user_by_capability() - First, handle simple cases This patch reorganises the conditions and field handling so we can spot if this is a call we can resolve in a single SQL query that we just pass back the results. If there are any PREVENTs or PROHIBITs, we need to delve into more involved stuff... MDL-12452 --- lib/accesslib.php | 189 ++++++++++++++++++++++++++++++---------------- 1 file changed, 123 insertions(+), 66 deletions(-) diff --git a/lib/accesslib.php b/lib/accesslib.php index 2754213414..856b220b2e 100755 --- a/lib/accesslib.php +++ b/lib/accesslib.php @@ -4194,17 +4194,55 @@ function get_users_by_capability($context, $capability, $fields='', $sort='', $view=false, $useviewallgroups=false) { global $CFG; -/// check for front page course, and see if default front page role has the required capability - - $frontpagectx = get_context_instance(CONTEXT_COURSE, SITEID); - if (!empty($CFG->defaultfrontpageroleid) && ($context->id == $frontpagectx->id || strstr($context->path, '/'.$frontpagectx->id.'/'))) { - $roles = get_roles_with_capability($capability, CAP_ALLOW, $context); - if (in_array($CFG->defaultfrontpageroleid, array_keys($roles))) { - return get_records_sql("SELECT $fields FROM {$CFG->prefix}user u ORDER BY $sort", $limitfrom, $limitnum); + $ctxids = substr($context->path, 1); // kill leading slash + $ctxids = str_replace('/', ',', $ctxids); + + // Context is the frontpage + $isfrontpage = false; + $iscoursepage = false; // coursepage other than fp + if ($context->contextlevel == CONTEXT_COURSE) { + if ($context->instanceid == SITEID) { + $isfrontpage = true; + } else { + $iscoursepage = true; + } + } + + // What roles/rolecaps are interesting? + $caps = "'$capability'"; + if ($doanything===true) { + $caps.=",'moodle/site:doanything'"; + } + $sql = "SELECT rc.id, rc.roleid, rc.permission, rc.capability, + ctx.depth AS ctxdepth, ctx.contextlevel AS ctxlevel + FROM {$CFG->prefix}role_capabilities rc + JOIN {$CFG->prefix}context ctx on rc.contextid = ctx.id + WHERE rc.capability IN ($caps) AND ctx.id IN ($ctxids) + ORDER BY rc.roleid, ctx.depth"; + // fetch all records - we'll walk several + // times over them, and should be a small set + $capdefs = get_records_sql($sql); + + $negperm = false; // has any negative (<0) permission? + $roleids = array(); + foreach ($capdefs AS $rcid=>$rc) { + //$c = make_context_subobj($c); + $roleids[] = (int)$rc->roleid; + if ($rc->permission < 0) { + $negperm = true; } } + $roleids = array_unique($roleids); -/// Sorting out groups + if (count($roleids)===0) { // noone here! + return false; + } + + // + // Prepare query clauses + // + $wherecond = array(); + /// Groups if ($groups) { if (is_array($groups)) { $grouptest = 'gm.groupid IN (' . implode(',', $groups) . ')'; @@ -4217,82 +4255,101 @@ function get_users_by_capability($context, $capability, $fields='', $sort='', if ($useviewallgroups) { $viewallgroupsusers = get_users_by_capability($context, 'moodle/site:accessallgroups', 'u.id, u.id', '', '', '', '', $exceptions); - $groupsql = ' AND (' . $grouptest . ' OR ra.userid IN (' . - implode(',', array_keys($viewallgroupsusers)) . '))'; + $wherecond['groups'] = '('. $grouptest . ' OR ra.userid IN (' . + implode(',', array_keys($viewallgroupsusers)) . ')))'; } else { - $groupsql = ' AND ' . $grouptest; + $wherecond['groups'] = '(' . $grouptest .')'; } - } else { - $groupsql = ''; } -/// Sorting out exceptions - $exceptionsql = $exceptions ? "AND u.id NOT IN ($exceptions)" : ''; + /// User exceptions + if (!empty($exceptions)) { + $wherecond['userexceptions'] = ' u.id NOT IN ('.$exceptions.')'; + } -/// Set up default fields - if (empty($fields)) { - $fields = 'u.*, ul.timeaccess as lastaccess, ra.hidden'; + /// Set up hidden role-assignments sql + if ($view && !has_capability('moodle/role:viewhiddenassigns', $context)) { + $wherecond['hiddenra'] = ' ra.hidden = 0 '; + } + + // Collect WHERE conditions + $where = implode(' AND ', array_values($wherecond)); + if ($where != '') { + $where = 'WHERE ' . $where; } -/// Set up default sort - if (empty($sort)) { - $sort = 'ul.timeaccess'; + /// Set up default fields + if (empty($fields)) { + if ($iscoursepage) { + $fields = 'u.*, ul.timeaccess as lastaccess, ra.hidden'; + } else { + $fields = 'u.*, ra.hidden'; + } } + /// Set up default sort + if (empty($sort)) { // default to course lastaccess or just lastaccess + if ($iscoursepage) { + $sort = 'ul.timeaccess'; + } else { + $sort = 'u.lastaccess'; + } + } $sortby = $sort ? " ORDER BY $sort " : ''; -/// Set up hidden sql - $hiddensql = ($view && !has_capability('moodle/role:viewhiddenassigns', $context))? ' AND ra.hidden = 0 ':''; -/// If context is a course, then construct sql for ul - if ($context->contextlevel == CONTEXT_COURSE) { - $courseid = $context->instanceid; - $coursesql1 = "AND ul.courseid = $courseid"; + // User lastaccess JOIN + if ($iscoursepage) { + $uljoin = "LEFT OUTER JOIN {$CFG->prefix}user_lastaccess ul + ON (ul.userid = u.id AND ul.courseid = $courseid)"; } else { - $coursesql1 = ''; + $uljoin = ''; } -/// Sorting out roles with this capability set - if ($possibleroles = get_roles_with_capability($capability, CAP_ALLOW, $context)) { - if (!$doanything) { - if (!$sitecontext = get_context_instance(CONTEXT_SYSTEM)) { - return false; // Something is seriously wrong - } - $doanythingroles = get_roles_with_capability('moodle/site:doanything', CAP_ALLOW, $sitecontext); + // + // Simple cases - No negative permissions means we can take shortcuts + // + if (!$negperm) { + + // at the frontpage, and all site users have it - easy! + if ($isfrontpage && !empty($CFG->defaultfrontpageroleid) + && in_array((int)$CFG->defaultfrontpageroleid, $roleids, true)) { + + return get_records_sql("SELECT $fields + FROM {$CFG->prefix}user u + ORDER BY $sort", + $limitfrom, $limitnum); } - $validroleids = array(); - foreach ($possibleroles as $possiblerole) { - if (!$doanything) { - if (isset($doanythingroles[$possiblerole->id])) { // We don't want these included - continue; - } - } - if ($caps = role_context_capabilities($possiblerole->id, $context, $capability)) { // resolved list - if (isset($caps[$capability]) && $caps[$capability] > 0) { // resolved capability > 0 - $validroleids[] = $possiblerole->id; - } - } + // all site users have it, anyway + if (in_array((int)$CFG->defaultuserroleid, $roleids, true)) { + $sql = "SELECT $fields + FROM {$CFG->prefix}user u + $uljoin + $where + ORDER BY $sort"; + return get_records_sql($sql, $limitfrom, $limitnum); } - if (empty($validroleids)) { - return false; + + /// Simple SQL assuming no negative rolecaps + $select = " SELECT $fields"; + $from = " FROM {$CFG->prefix}user u + INNER JOIN {$CFG->prefix}role_assignments ra ON ra.userid = u.id + INNER JOIN {$CFG->prefix}role r ON r.id = ra.roleid "; + if ($context->contextlevel==CONTEXT_COURSE && !$isfrontpage) { + $from .= " LEFT OUTER JOIN {$CFG->prefix}user_lastaccess ul + ON (ul.userid = u.id AND ul.courseid = $courseid)"; } - $roleids = '('.implode(',', $validroleids).')'; - } else { - return false; // No need to continue, since no roles have this capability set - } - -/// Construct the main SQL - $select = " SELECT $fields"; - $from = " FROM {$CFG->prefix}user u - INNER JOIN {$CFG->prefix}role_assignments ra ON ra.userid = u.id - INNER JOIN {$CFG->prefix}role r ON r.id = ra.roleid - LEFT OUTER JOIN {$CFG->prefix}user_lastaccess ul ON (ul.userid = u.id $coursesql1)"; - $where = " WHERE ra.contextid ".get_related_contexts_string($context)." - AND u.deleted = 0 - AND ra.roleid in $roleids - $exceptionsql - $groupsql - $hiddensql"; + $where = " WHERE ra.contextid IN ($ctxids) + AND u.deleted = 0 + AND ra.roleid IN (".implode(',',$roleids) .")"; + if (count(array_keys($wherecond))) { + $where .= ' AND ' . implode(' AND ', array_values($wherecond)); + } + return get_records_sql($select.$from.$where.$sortby, $limitfrom, $limitnum); + } + + // This will be more complex - + return get_records_sql($select.$from.$where.$sortby, $limitfrom, $limitnum); } -- 2.39.5