]> git.mjollnir.org Git - moodle.git/commitdiff
accesslib: Introducing get_user_sitewide_access($userid)
authormartinlanghoff <martinlanghoff>
Wed, 19 Sep 2007 07:02:04 +0000 (07:02 +0000)
committermartinlanghoff <martinlanghoff>
Wed, 19 Sep 2007 07:02:04 +0000 (07:02 +0000)
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

index 725c1eccbc2c8090eba5a01495d7203cb87f23b3..83d9645ca46e174a27c9a078884f8396a54caced 100755 (executable)
@@ -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