diff --git a/lib/accesslib.php b/lib/accesslib.php
index 6be2688..3b15c6a 100755
--- a/lib/accesslib.php
+++ b/lib/accesslib.php
@@ -4206,17 +4206,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 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;
+    }
 
-/// Sorting out groups
+    //
+    // Prepare query clauses
+    //
+    $wherecond = array();
+    /// Groups
     if ($groups) {
         if (is_array($groups)) {
             $grouptest = 'gm.groupid IN (' . implode(',', $groups) . ')';
@@ -4229,84 +4267,265 @@ 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 ';
     }
 
-/// Set up default sort
-    if (empty($sort)) {
-        $sort = 'ul.timeaccess';
+    // Collect WHERE conditions
+    $where = implode(' AND ', array_values($wherecond));
+    if ($where != '') {
+        $where = 'WHERE ' . $where;
     }
 
+    /// 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
+    //
+    // 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 (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);
+        }
+
+        /// Simple SQL assuming no negative rolecaps
+        $select = " SELECT $fields";
+        $from   = " FROM {$CFG->prefix}user u
+                    JOIN {$CFG->prefix}role_assignments ra ON ra.userid = u.id
+                    $uljoin ";
+        $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);
+    }
+
+    //
+    // If there are any negative rolecaps, we need to
+    // work through a subselect - which is a lot more complex,
+    // and we cannot do the job in pure SQL (not without SQL stored
+    // procedures anyway). We will have to select with multiple-rows
+    // and apply $limitfrom/$limitnum on the PHP side.
+    //
+    // Did I say yuck?
+    //
+
+    // 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;
+        $depth = (int)$rc->ctxdepth;
+        if (!isset($roleperms[$rid])) {
+            $roleperms[$rid] = (object)array('perm'  => $perm,
+                                             'depth' => $depth);
+        } else {
+            if ($roleperms[$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[$rid] = (object)array('perm'  => $perm,
+                                             'depth' => $depth);
+        }
+        
+    }
+
+    if ($context->contextlevel == CONTEXT_SYSTEM
+        || $isfrontpage
+        || in_array((int)$CFG->defaultuserroleid, $roleids, true)) {
+
+        // Handle system / sitecourse / defaultcap-with-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.
+
+        // TODO -- test!
+        $ss = "SELECT u.id as userid, ra.roleid,
+                      ctx.depth
+               FROM {$CFG->prefix}user u
+               LEFT OUTER {$CFG->prefix}role_assignments ra 
+                 ON (ra.userid = u.id
+                     AND ra.contextid IN ($ctxids)
+                     AND ra.roleid IN (".implode(',',$roleids) .")
+               LEFT OUTER JOIN {$CFG->prefix}context ctx
+                 ON ra.contextid=ctx.id
+               WHERE u.deleted=0";
+    } else {
+        // "Normal" 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)
+                     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 should 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, folding repeats together...
+    $users = array();
+    $lastuserid = 0;
+    $c = 0;
+    $limitfrom = (int)$limitfrom;
+    $limitnum = (int)$limitnum;
+
+    while ($user = rs_fetch_next_record($rs)) {
+
+        // Pagination controls
+        if ($lastuserid != $user->id) {
+            $lastuserid = $user->id;
+            $c++;
+            $newuser = true;
+            if ($limitnum !==0 && $c > ($limitfrom+$limitnum)) { // we are done!
+                break;
+            }
+        } else {
+            $newuser = false;
+        }
+        if ($limitfrom !==0 && $c <= $limitfrom) {
+            continue;
+        }
+
+        // provide a default enrolment where needed
+        if (empty($user->roleid)) {
+            $user->roleid = (int)$CFG->defaultuserroleid;
+            $user->depth  = 1;
         }
 
-        $validroleids = array();
-        foreach ($possibleroles as $possiblerole) {
-            if (!$doanything) {
-                if (isset($doanythingroles[$possiblerole->id])) {  // We don't want these included
+        // Add the users - adding the perms that win
+        if ($newuser) {
+            $users[$user->id] = $user; // trivial
+        } else {
+
+            $inplace = $users[$user->id];
+
+            //
+            // Resolve who wins, in order of precendence
+            // - Prohibits always wins
+            // - Locality of RA
+            // - Locality of RC
+            //
+            //// Prohibits...
+            if ($roleperms[$user->roleid]->perm == CAP_PROHIBIT) {
+                $users[$user->id] = $user;
+                continue;
+            }
+            if ($roleperms[$inplace->roleid]->perm == CAP_PROHIBIT) {
+                continue;
+            }
+
+            // Locality of RA -- we order by depth DESC
+            // so what's in $users already should win, unless its perm is 0
+
+            // Higher RA loses to local RA...
+            if ($user->depth < $inplace->depth) {
+                if (!empty($users[$user->id]->mergedperm)) {
+                    // Wider RA loses to local RA...
+                    continue;
+                } else {
+                    // "Resolve conflict" case, more local RAs had cancelled eachother
+                    $users[$user->id] = $user;
                     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;
-                }
+            // Same-level RA - Locality of RC wins
+            if ($roleperms[$user->roleid]->depth > $roleperms[$inplace->roleid]->depth) {
+                $users[$user->id] = $user;
+                continue;
+            }
+            if ($roleperms[$user->roleid]->depth < $roleperms[$inplace->roleid]->depth) {
+                continue;
+            }
+            // We match depth - add them
+            if (isset($inplace->mergedperm)) {
+                $users[ $user->id ]->mergedperm = ($inplace->mergedperm 
+                                                   + $roleperms[$user->roleid]->perm);
+            } else {
+                $users[ $user->id ]->mergedperm = ($roleperms[$inplace->roleid]->perms
+                                                   + $roleperms[$user->roleid]->perm);
             }
         }
-        if (empty($validroleids)) {
-            return false;
-        }
-        $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);
+    }
+
+    return $users;
 }
 
 /**
