From a9bee37ea15ff8a0b98fd3e9b56d7e1a71c21b56 Mon Sep 17 00:00:00 2001 From: martinlanghoff Date: Wed, 19 Sep 2007 07:02:04 +0000 Subject: [PATCH] accesslib: Introducing get_user_sitewide_access($userid) This function carefully fetches all the data needed to handle site/category/course access for a given user as cheaply as possible. It currently takes 3 db queries. --- lib/accesslib.php | 139 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 139 insertions(+) diff --git a/lib/accesslib.php b/lib/accesslib.php index 725c1eccbc..83d9645ca4 100755 --- a/lib/accesslib.php +++ b/lib/accesslib.php @@ -1170,6 +1170,145 @@ function load_user_capability($capability='', $context=NULL, $userid=NULL, $chec } } +/** + * It will return 2 arrays showing role assignments + * all relevant role capabilities for the user at + * site/metacourse/course_category/course levels + * + * We do _not_ delve deeper than courses because the number of + * overrides at the module/block levels is HUGE. + * + * [ra] => [/path/] = roleid + * [rdef] => [/path/:roleid][capability]=permission + * + * @param $userid integer - the id of the user + * + */ +function get_user_sitewide_access($userid) { + + global $CFG; + + // this flag has not been set! + // (not clean install, or upgraded successfully to 1.7 and up) + if (empty($CFG->rolesactive)) { + return false; + } + + /* Get in 3 cheap DB queries... + * - role assignments - with role_caps + * - relevant role caps + * - above this user's RAs + * - below this user's RAs - limited to course level + */ + + $sw = array(); // named list + $sw['ra'] = array(); + $sw['rdef'] = array(); + + $sitectx = get_field('context', 'id','contextlevel', CONTEXT_SYSTEM); + $base = "/$sitectx"; + + // + // Role assignments - and any rolecaps directly linked + // because it's cheap to read rolecaps here over many + // RAs + // + $sql = "SELECT ctx.path, ra.roleid, rc.capability, rc.permission + FROM {$CFG->prefix}role_assignments ra + JOIN {$CFG->prefix}context ctx + ON ra.contextid=ctx.id + LEFT OUTER JOIN {$CFG->prefix}role_capabilities rc + ON (rc.roleid=ra.roleid AND rc.contextid=ra.contextid) + WHERE ra.userid = $userid AND ctx.contextlevel <= ".CONTEXT_COURSE." + ORDER BY ctx.depth, ctx.path"; + $rs = get_recordset_sql($sql); + // parent paths & roles we need to walk up + // this array will bulk up quite a bit with dups + // which we'll later clear up + $raparents = array(); + if ($rs->RecordCount()) { + while ($ra = rs_fetch_next_record($rs)) { + $sw['ra'][$ra->path] = $ra->roleid; + if (!empty($ra->capability)) { + $k = "{$ra->path}:{$ra->roleid}"; + $sw['rdef'][$k][$ra->capability] = $ra->permission; + } + $parentids = explode('/', $ra->path); + array_pop($parentids); array_shift($parentids); + if (isset($raparents[$ra->roleid])) { + $raparents[$ra->roleid] = array_merge($raparents[$ra->roleid], $parentids); + } else { + $raparents[$ra->roleid] = $parentids; + } + } + } + rs_close($rs); + + // Walk up the tree to grab all the roledefs + // of interest to our user... + // NOTE: we use a series of IN clauses here - which + // might explode on huge sites with very convoluted nesting of + // categories... - extremely unlikely that the number of categories + // and roletypes is so large that we hit the limits of IN() + $clauses = array(); + foreach ($raparents as $roleid=>$contexts) { + $contexts = sql_intarray_to_in(array_unique($contexts)); + if ($contexts ==! '') { + $clauses[] = "(roleid=$roleid AND contextid IN ($contexts))"; + } + } + $clauses = join(" OR ", $clauses); + $sql = "SELECT ctx.path, rc.roleid, rc.capability, rc.permission + FROM {$CFG->prefix}role_capabilities rc + JOIN {$CFG->prefix}context ctx + ON rc.contextid=ctx.id + WHERE $clauses + ORDER BY ctx.depth ASC, ctx.path DESC, rc.roleid ASC "; + + $rs = get_recordset_sql($sql); + + if ($rs->RecordCount()) { + while ($rd = rs_fetch_next_record($rs)) { + $k = "{$rd->path}:{$rd->roleid}"; + $sw['rdef'][$k][$rd->capability] = $rd->permission; + } + } + rs_close($rs); + + // + // Overrides for the role assignments IN SUBCONTEXTS + // (though we still do _not_ go below the course level. + // + // NOTE that the JOIN w sctx is with 3-way triangulation to + // catch overrides to the applicable role in any subcontext, based + // on the path field of the parent. + // + $sql = "SELECT sctx.path, ra.roleid, + ctx.path AS parentpath, + rco.capability, rco.permission + FROM {$CFG->prefix}role_assignments ra + JOIN {$CFG->prefix}context ctx + ON ra.contextid=ctx.id + JOIN {$CFG->prefix}context sctx + ON (sctx.path LIKE ctx.path||'/%') + JOIN {$CFG->prefix}role_capabilities rco + ON (rco.roleid=ra.roleid AND rco.contextid=sctx.id) + WHERE ra.userid = $userid + AND sctx.contextlevel <= ".CONTEXT_COURSE." + ORDER BY sctx.depth, sctx.path, $ra.roleid"; + if ($rs->RecordCount()) { + while ($rd = rs_fetch_next_record($rs)) { + $k = "{$rd->path}:{$rd->roleid}"; + $sw['rdef'][$k][$rd->capability] = $rd->permission; + } + } + rs_close($rs); + + // TODO: compact capsets? + + return $sw; +} + /** * A convenience function to completely load all the capabilities -- 2.39.5