diff --git a/lib/accesslib.php b/lib/accesslib.php
index 6be2688..95904bb 100755
--- a/lib/accesslib.php
+++ b/lib/accesslib.php
@@ -4177,11 +4177,15 @@ function get_default_course_role($course) {
 
 
 /**
- * who has this capability in this context
- * does not handling user level resolving!!!
- * (!)pleaes note if $fields is empty this function attempts to get u.*
- * which can get rather large.
- * i.e 1 person has 2 roles 1 allow, 1 prevent, this will not work properly
+ * 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 - object
  * @param $capability - string capability
  * @param $fields - fields to be pulled
@@ -4206,17 +4210,63 @@ 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 ASC, ctx.depth ASC";
+    // 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) {
+
+        $roleids[] = (int)$rc->roleid;
+        if ($rc->permission < 0) {
+            $negperm = true;
         }
     }
+    $roleids = array_unique($roleids);
+
+    if (count($roleids)===0) { // noone here!
+        return false;
+    }
+
+    // is the default role interesting? does it have
+    // a relevant rolecap? (we use this a lot later)
+    if (in_array((int)$CFG->defaultuserroleid, $roleids, true)) {
+        $defaultroleinteresting = true;
+    } else {
+        $defaultroleinteresting = false;
+    }
 
-/// Sorting out groups
+    //
+    // Prepare query clauses
+    //
+    $wherecond = array();
+    /// Groups
     if ($groups) {
         if (is_array($groups)) {
             $grouptest = 'gm.groupid IN (' . implode(',', $groups) . ')';
@@ -4229,84 +4279,414 @@ 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 .')';
         }
+    }
+
+    /// User exceptions
+    if (!empty($exceptions)) {
+        $wherecond['userexceptions'] = ' u.id NOT IN ('.$exceptions.')';
+    }
+
+    /// Set up hidden role-assignments sql
+    if ($view && !has_capability('moodle/role:viewhiddenassigns', $context)) {
+        $condhiddenra = 'AND ra.hidden = 0 ';
     } else {
-        $groupsql = '';
+        $condhiddenra = '';
     }
 
-/// Sorting out exceptions
-    $exceptionsql = $exceptions ? "AND u.id NOT IN ($exceptions)" : '';
+    // Collect WHERE conditions
+    $where = implode(' AND ', array_values($wherecond));
+    if ($where != '') {
+        $where = 'WHERE ' . $where;
+    }
 
-/// Set up default fields
+    /// Set up default fields
     if (empty($fields)) {
-        $fields = 'u.*, ul.timeaccess as lastaccess, ra.hidden';
+        if ($iscoursepage) {
+            $fields = 'u.*, ul.timeaccess as lastaccess';
+        } else {
+            $fields = 'u.*';
+        }
     }
 
-/// Set up default sort
-    if (empty($sort)) {
-        $sort = 'ul.timeaccess';
+    /// 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 = '';
+    }
+
+    //
+    // 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);
+        }
+
+        // all site users have it, anyway
+        if ($defaultroleinteresting) {
+            $sql = "SELECT $fields
+                    FROM {$CFG->prefix}user u
+                    $uljoin
+                    $where
+                    ORDER BY $sort";
+            return get_records_sql($sql, $limitfrom, $limitnum);
+        }
+
+        /// Simple SQL assuming no negative rolecaps.
+        /// We use a subselect to grab the role assignments
+        /// ensuring only one row per user -- even if they
+        /// have many "relevant" role assignments.
+        $select = " SELECT $fields";
+        $from   = " FROM {$CFG->prefix}user u
+                    JOIN (SELECT DISTINCT ssra.userid
+                          FROM {$CFG->prefix}role_assignments ssra
+                          WHERE ssra.contextid IN ($ctxids)
+                                AND ssra.roleid IN (".implode(',',$roleids) .")
+                                $condhiddenra
+                          ) ra ON ra.userid = u.id
+                    $uljoin ";
+        $where  = " WHERE u.deleted = 0 ";
+        if (count(array_keys($wherecond))) {
+            $where .= ' AND ' . implode(' AND ', array_values($wherecond));
+        }
+        return get_records_sql($select.$from.$where.$sortby, $limitfrom, $limitnum);
     }
 
-/// 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
+    //
+    // If there are any negative rolecaps, we need to
+    // work through a subselect that will bring several rows
+    // per user (one per RA).
+    // Since we cannot do the job in pure SQL (not without SQL stored
+    // procedures anyway), we end up tied to processing the data in PHP
+    // all the way down to pagination.
+    //
+    // In some cases, this will mean bringing across a ton of data --
+    // when paginating, we have to walk the permisisons of all the rows
+    // in the _previous_ pages to get the pagination correct in the case
+    // of users that end up not having the permission - this removed.
+    //
+
+    // Prepare the role permissions datastructure for fast lookups
+    $roleperms = array(); // each role cap and depth
+    foreach ($capdefs AS $rcid=>$rc) {
+
+        $rid       = (int)$rc->roleid;
+        $perm      = (int)$rc->permission;
+        $rcdepth   = (int)$rc->ctxdepth;
+        if (!isset($roleperms[$rc->capability][$rid])) {
+            $roleperms[$rc->capability][$rid] = (object)array('perm'  => $perm,
+                                                              'rcdepth' => $rcdepth);
+        } else {
+            if ($roleperms[$rc->capability][$rid]->perm == CAP_PROHIBIT) {
+                continue;
             }
-            $doanythingroles = get_roles_with_capability('moodle/site:doanything', CAP_ALLOW, $sitecontext);
+            // override - as we are going
+            // from general to local perms
+            // (as per the ORDER BY...depth ASC above)
+            // and local perms win...
+            $roleperms[$rc->capability][$rid] = (object)array('perm'  => $perm,
+                                                              'rcdepth' => $rcdepth);
         }
+        
+    }
 
-        $validroleids = array();
-        foreach ($possibleroles as $possiblerole) {
-            if (!$doanything) {
-                if (isset($doanythingroles[$possiblerole->id])) {  // We don't want these included
-                    continue;
+    if ($context->contextlevel == CONTEXT_SYSTEM
+        || $isfrontpage
+        || $defaultroleinteresting) {
+
+        // Handle system / sitecourse / defaultrole-with-perhaps-neg-overrides
+        // with a SELECT FROM user LEFT OUTER JOIN against ra -
+        // This is expensive on the SQL and PHP sides -
+        // moves a ton of data across the wire.
+        $ss = "SELECT u.id as userid, ra.roleid,
+                      ctx.depth
+               FROM {$CFG->prefix}user u
+               LEFT OUTER JOIN {$CFG->prefix}role_assignments ra 
+                 ON (ra.userid = u.id
+                     AND ra.contextid IN ($ctxids)
+                     AND ra.roleid IN (".implode(',',$roleids) .")
+                     $condhiddenra)
+               LEFT OUTER JOIN {$CFG->prefix}context ctx
+                 ON ra.contextid=ctx.id
+               WHERE u.deleted=0";
+    } else {
+        // "Normal complex case" - the rolecaps we are after will
+        // be defined in a role assignment somewhere.
+        $ss = "SELECT ra.userid as userid, ra.roleid,
+                      ctx.depth
+               FROM {$CFG->prefix}role_assignments ra 
+               JOIN {$CFG->prefix}context ctx
+                 ON ra.contextid=ctx.id
+               WHERE ra.contextid IN ($ctxids)
+                     $condhiddenra
+                     AND ra.roleid IN (".implode(',',$roleids) .")";
+    }
+
+    $select = "SELECT $fields ,ra.roleid, ra.depth ";
+    $from   = "FROM ($ss) ra
+               JOIN {$CFG->prefix}user u
+                 ON ra.userid=u.id
+               $uljoin ";
+    $where  = "WHERE u.deleted = 0 ";
+    if (count(array_keys($wherecond))) {
+        $where .= ' AND ' . implode(' AND ', array_values($wherecond));
+    }
+
+    // Each user's entries MUST come clustered together
+    // and RAs ordered in depth DESC - the role/cap resolution
+    // code depends on this.
+    $sort .= ' , ra.userid ASC, ra.depth DESC';
+    $sortby .= ' , ra.userid ASC, ra.depth DESC ';
+
+    $rs = get_recordset_sql($select.$from.$where.$sortby);
+
+    //
+    // Process the user accounts+RAs, folding repeats together...
+    //
+    // The processing for this recordset is tricky - to fold
+    // the role/perms of users with multiple role-assignments
+    // correctly while still processing one-row-at-a-time
+    // we need to add a few additional 'private' fields to
+    // the results array - so we can treat the rows as a
+    // state machine to track the cap/perms and at what RA-depth
+    // and RC-depth they were defined.
+    //
+    // So what we do here is:
+    // - loop over rows, checking pagination limits
+    // - when we find a new user, if we are in the page add it to the
+    //   $results, and start building $ras array with its role-assignments
+    // - when we are dealing with the next user, or are at the end of the userlist
+    //   (last rec or last in page), trigger the check-permission idiom
+    // - the check permission idiom will
+    //   - add the default enrolment if needed
+    //   - call has_capability_from_rarc(), which based on RAs and RCs will return a bool
+    //     (should be fairly tight code ;-) )
+    // - if the user has permission, all is good, just $c++ (counter)
+    // - ...else, decrease the counter - so pagination is kept straight,
+    //      and (if we are in the page) remove from the results
+    // 
+    $results = array();
+
+    // pagination controls
+    $c = 0;
+    $limitfrom = (int)$limitfrom;
+    $limitnum = (int)$limitnum;
+
+    //
+    // Track our last user id so we know when we are dealing
+    // with a new user... 
+    //
+    $lastuserid  = 0;
+    //
+    // In this loop, we 
+    // $ras: role assignments, multidimensional array
+    // treat as a stack - going from local to general
+    // $ras = (( roleid=> x, $depth=>y) , ( roleid=> x, $depth=>y))
+    //
+    while ($user = rs_fetch_next_record($rs)) {
+
+        //error_log(" Record: " . print_r($user,1));
+
+        //
+        // Pagination controls 
+        // Note that we might end up removing a user
+        // that ends up _not_ having the rights,
+        // therefore rolling back $c
+        //
+        if ($lastuserid != $user->id) {
+
+            // Did the last user end up with a positive permission?
+            if ($lastuserid !=0) {
+                if ($defaultroleinteresting) {
+                    // add the role at the end of $ras
+                    $ras[] = array( 'roleid' => $CFG->defaultuserroleid,
+                                    'depth'  => 1 );
                 }
+                if (has_capability_from_rarc($ras, $roleperms, $capability, $doanything)) {
+                    $c++;
+                } else {
+                    // remove the user from the result set,
+                    // only if we are 'in the page'
+                    if ($limitfrom === 0 || $c >= $limitfrom) {
+                        unset($results[$lastuserid]);
+                    }
+                }
+            }
+
+            // Did we hit pagination limit?
+            if ($limitnum !==0 && $c > ($limitfrom+$limitnum)) { // we are done!
+                break;
+            }
+
+            // New user setup, and $ras reset
+            $lastuserid = $user->id;
+            $ras = array();
+            if (!empty($user->roleid)) {
+                $ras[] = array( 'roleid' => (int)$user->roleid,
+                                'depth'  => (int)$user->depth );
             }
-            if ($caps = role_context_capabilities($possiblerole->id, $context, $capability)) { // resolved list
-                if (isset($caps[$capability]) && $caps[$capability] > 0) { // resolved capability > 0
-                    $validroleids[] = $possiblerole->id;
+
+            // if we are 'in the page', also add the rec
+            // to the results...
+            if ($limitfrom === 0 || $c >= $limitfrom) {
+                $results[$user->id] = $user; // trivial
+            }
+        } else {
+            // Additional RA for $lastuserid
+            $ras[] = array( 'roleid'=>(int)$user->roleid,
+                            'depth'=>(int)$user->depth );
+        }
+
+    } // end while(fetch)
+
+    // Prune last entry if necessary
+    if ($lastuserid !=0) {
+        if ($defaultroleinteresting) {
+            // add the role at the end of $ras
+            $ras[] = array( 'roleid' => $CFG->defaultuserroleid,
+                            'depth'  => 1 );
+        }
+        if (!has_capability_from_rarc($ras, $roleperms, $capability, $doanything)) {
+            // remove the user from the result set,
+            // only if we are 'in the page'
+            if ($limitfrom === 0 || $c >= $limitfrom) {
+                if (isset($results[$lastuserid])) {
+                    unset($results[$lastuserid]);
                 }
             }
         }
-        if (empty($validroleids)) {
-            return false;
+    }
+
+    return $results;
+}
+
+/*
+ * Fast (fast!) utility function to resolve if a capability is granted,
+ * based on Role Assignments and Role Capabilities.
+ *
+ * Used (at least) by get_users_by_capability().
+ *
+ * If PHP had fast built-in memoize functions, we could
+ * add a $contextid parameter and memoize the return values.
+ *
+ * @param array $ras - role assignments
+ * @param array $roleperms - role permissions
+ * @param string $capability - name of the capability
+ * @param bool $doanything
+ * @return boolean
+ * 
+ */
+function has_capability_from_rarc($ras, $roleperms, $capability, $doanything) {
+    // Mini-state machine, using $hascap
+    // $hascap[ 'moodle/foo:bar' ]->perm = CAP_SOMETHING (numeric constant)
+    // $hascap[ 'moodle/foo:bar' ]->radepth = depth of the role assignment that set it
+    // $hascap[ 'moodle/foo:bar' ]->rcdepth = depth of the rolecap that set it
+    // -- when resolving conflicts, we need to look into radepth first, if unresolved
+    
+    $caps = array($capability);
+    if ($doanything) {
+        $caps[] = 'moodle/site:candoanything';
+    }
+
+    $hascap = array();
+
+    //
+    // Compute which permission/roleassignment/rolecap
+    // wins for each capability we are walking
+    //
+    foreach ($ras as $ra) {
+        foreach ($caps as $cap) {
+            if (!isset($roleperms[$cap][$ra['roleid']])) {
+                // nothing set for this cap - skip
+                continue;
+            }
+            // We explicitly clone here as we
+            // add more properties to it
+            // that must stay separate from the
+            // original roleperm data structure
+            $rp = clone($roleperms[$cap][$ra['roleid']]);
+            $rp->radepth = $ra['depth'];
+
+            // Trivial case, we are the first to set
+            if (!isset($hascap[$cap])) {
+                $hascap[$cap] = $rp;
+            }
+
+            //
+            // Resolve who prevails, in order of precendence
+            // - Prohibits always wins
+            // - Locality of RA
+            // - Locality of RC
+            //
+            //// Prohibits...
+            if ($rp->perm === CAP_PROHIBIT) {
+                $hascap[$cap] = $rp;
+                continue;
+            }
+            if ($hascap[$cap]->perm === CAP_PROHIBIT) {
+                continue;
+            }
+
+            // Locality of RA - the look is ordered by depth DESC
+            // so from local to general -
+            // Higher RA loses to local RA... unless perm===0
+            /// Thanks to the order of the records, $rp->radepth <= $hascap[$cap]->radepth
+            if ($rp->radepth > $hascap[$cap]->radepth) {
+                error_log('Should not happen @ ' . __FUNCTION__.':'.__LINE__);
+            }
+            if ($rp->radepth < $hascap[$cap]->radepth) {
+                if ($hascap[$cap]->perm!==0) {
+                    // Wider RA loses to local RAs...
+                    continue;
+                } else {
+                    // "Higher RA resolves conflict" case,
+                    // local RAs had cancelled eachother
+                    $hascap[$cap] = $rp;
+                    continue;
+                }
+            }
+            // Same ralevel - locality of RC wins
+            if ($rp->rcdepth  > $hascap[$cap]->rcdepth) {
+                $hascap[$cap] = $rp;
+                continue;
+            }
+            if ($rp->rcdepth  > $hascap[$cap]->rcdepth) {
+                continue;
+            }
+            // We match depth - add them                
+            $hascap[$cap]->perm += $rp->perm;
         }
-        $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";
-
-    return get_records_sql($select.$from.$where.$sortby, $limitfrom, $limitnum);
+    }
+    if ($hascap[$capability]->perm > 0
+        || ($doanything && isset($hascap['moodle/site:candoanything'])
+            && $hascap['moodle/site:candoanything']->perm > 0)) {
+        return true;
+    }
+    return false;
 }
 
 /**
