]> git.mjollnir.org Git - moodle.git/commitdiff
accesslib - role_switch() revamp, introduce get_role_access_bycontext()
authormartinlanghoff <martinlanghoff>
Wed, 19 Sep 2007 07:05:32 +0000 (07:05 +0000)
committermartinlanghoff <martinlanghoff>
Wed, 19 Sep 2007 07:05:32 +0000 (07:05 +0000)
First stage of role_switch() rewrite - role_switch() sets up
a rsw entry in $USER->access, and makes sure we have the
appropriate role definitions in rdef.

That is where get_role_access_bycontext() comes into play -
gets all the rdef entries in one cheap sql query.

... though there may be many of them...

TODO:

 - fix callers of role_switch()
 - teach has_cap_fromsess() to read the rsw entries

lib/accesslib.php

index d31f6be9e8a247f7ca6c48d7b431eb6327ffedf3..54b5e18337c879396068bfa16a218bc5bcc397e0 100755 (executable)
@@ -1750,7 +1750,7 @@ function get_user_access_sitewide($userid) {
 
 /**
  * It add to the access ctrl array the data
- * needed for a given context
+ * needed by a user for a given context
  *
  * @param $userid  integer - the id of the user
  * @param $context context obj - needs path!
@@ -1930,7 +1930,73 @@ function get_user_access_bycontext($userid, $context, $acc=NULL) {
 }
 
 /**
- *  A convenience function to completely load all the capabilities
+ * It add to the access ctrl array the data
+ * needed by a role for a given context.
+ *
+ * The data is added in the rdef key.
+ *
+ * This role-centric function is useful for role_switching
+ * and to get an overview of what a role gets under a
+ * given context and below...
+ *
+ * @param $roleid  integer - the id of the user
+ * @param $context context obj - needs path!
+ * @param $acc     access array
+ *
+ */
+function get_role_access_bycontext($roleid, $context, $acc=NULL) {
+
+    global $CFG;
+
+    /* Get the relevant rolecaps into rdef
+     * - relevant role caps
+     *   - at ctx and above
+     *   - below this ctx
+     */
+
+    if (is_null($acc)) {
+        $acc           = array(); // named list
+        $acc['ra']     = array();
+        $acc['rdef']   = array();
+        $acc['loaded'] = array();
+    }
+    
+    $contexts = substr($context->path, 1); // kill leading slash
+    $contexts = str_replace('/', ',', $contexts);
+
+    //
+    // Walk up and down the tree to grab all the roledefs
+    // of interest to our role...
+    //
+    // NOTE: we use an IN clauses here - which
+    // might explode on huge sites with very convoluted nesting of
+    // categories... - extremely unlikely that the number of nested
+    // categories is so large that we hit the limits of IN()
+    //
+    $sql = "SELECT ctx.path, rc.capability, rc.permission
+            FROM {$CFG->prefix}role_capabilities rc
+            JOIN {$CFG->prefix}context ctx
+              ON rc.contextid=ctx.id
+            WHERE rc.roleid=$roleid AND
+                  ( ctx.id IN ($contexts) OR 
+                    ctx.path LIKE '{$context->path}/%' )
+            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}:{$roleid}";
+            $acc['rdef'][$k][$rd->capability] = $rd->permission;
+        }
+    }
+    rs_close($rs);
+
+    return $acc;
+}
+
+
+/**
+ *  A convenience function to completely load all the capabilities 
  *  for the current user.   This is what gets called from login, for example.
  */
 function load_all_capabilities() {
@@ -4817,9 +4883,16 @@ function get_roles_on_exact_context($context) {
 
 /**
  * Switches the current user to another role for the current session and only
- * in the given context.  If roleid is not valid (eg 0) or the current user
- * doesn't have permissions to be switching roles then the user's session
- * is compltely reset to have their normal roles.
+ * in the given context.
+ *
+ * The caller *must* check
+ * - that this op is allowed
+ * - that the requested role can be assigned in this ctx
+ *   (hint, use get_assignable_roles())
+ * - that the requested role is NOT $CFG->defaultuserroleid
+ *
+ * This function *will* modify $USER->access - beware
+ * 
  * @param integer $roleid
  * @param object $context
  * @return bool
@@ -4827,41 +4900,47 @@ function get_roles_on_exact_context($context) {
 function role_switch($roleid, $context) {
     global $USER, $CFG;
 
-/// If we can't use this or are already using it or no role was specified then bail completely and reset
-    if (empty($roleid) || !has_capability('moodle/role:switchroles', $context)
-        || !empty($USER->switchrole[$context->id])  || !confirm_sesskey()) {
-
-        unset($USER->switchrole[$context->id]);  // Delete old capabilities
-        unset($USER->courseeditallowed);               // drop cache for course edit button
-        load_all_capabilities();   //reload user caps
-        return true;
-    }
-
-/// We're allowed to switch but can we switch to the specified role?  Use assignable roles to check.
-    if (!$roles = get_assignable_roles($context)) {
-        return false;
-    }
-
-/// unset default user role - it would not work anyway
-    unset($roles[$CFG->defaultuserroleid]);
+    //
+    // Plan of action
+    //
+    // - Add the ghost RA to $USER->access
+    //   as $USER->access['rsw'][$path] = $roleid
+    //
+    // - Make sure $USER->access['rdef'] has the roledefs
+    //   it needs to honour the switcheroo
+    //
+    // Roledefs will get loaded "deep" here - down to the last child
+    // context. Note that
+    //
+    // - When visiting subcontexts, our selective accessinfo loading
+    //   will still work fine - though those ra/rdefs will be ignored
+    //   appropriately while the switch is in place
+    // 
+    // - If a switcheroo happens at a category with tons of courses 
+    //   (that have many overrides for switched-to role), the session
+    //   will get... quite large. Sometimes you just can't win.
+    //
+    // To un-switch just unset($USER->access['rsw'][$path])
+    // 
 
-    if (empty($roles[$roleid])) {   /// We can't switch to this particular role
-        return false;
+    // Add the switch RA
+    if (!isset($USER->access['rsw'])) {
+        $USER->access['rsw'] = array();
     }
+    $USER->access['rsw'][$context->path]=$roleid;
+    
+    // Load roledefs
+    $USER->access = get_role_access_bycontext($roleid, $context,
+                                              $USER->access);
 
-/// We have a valid roleid that this user can switch to, so let's set up the session
-
-    $USER->switchrole[$context->id] = $roleid;     // So we know later what state we are in
-    unset($USER->courseeditallowed);                     // drop cache for course edit button
-
-    load_all_capabilities();   //reload switched role caps
-
-/// Add some permissions we are really going to always need, even if the role doesn't have them!
+    /* DO WE NEED THIS AT ALL???
+    // Add some permissions we are really going 
+    // to always need, even if the role doesn't have them!
 
     $USER->capabilities[$context->id]['moodle/course:view'] = CAP_ALLOW;
+    */
 
     return true;
-
 }