Пример #1
0
/**
 * Get the potential assignees selector for a given context.
 *
 * If this context is a course context, or inside a course context (module or
 * some blocks) then return a core_role_potential_assignees_below_course object. Otherwise
 * return a core_role_potential_assignees_course_and_above.
 *
 * @param context $context a context.
 * @param string $name passed to user selector constructor.
 * @param array $options to user selector constructor.
 * @return user_selector_base an appropriate user selector.
 */
function core_role_get_potential_user_selector(context $context, $name, $options)
{
    $blockinsidecourse = false;
    if ($context->contextlevel == CONTEXT_BLOCK) {
        $parentcontext = $context->get_parent_context();
        $blockinsidecourse = in_array($parentcontext->contextlevel, array(CONTEXT_MODULE, CONTEXT_COURSE));
    }
    if (($context->contextlevel == CONTEXT_MODULE || $blockinsidecourse) && !is_inside_frontpage($context)) {
        $potentialuserselector = new core_role_potential_assignees_below_course('addselect', $options);
    } else {
        $potentialuserselector = new core_role_potential_assignees_course_and_above('addselect', $options);
    }
    return $potentialuserselector;
}
Пример #2
0
    protected function definition() {
        $mform = $this->_form;

        $mform->addElement('header', 'preferencespage',
                get_string('preferencespage', 'quiz_overview'));

        if (!$this->_customdata['currentgroup']) {
            $studentsstring = get_string('participants');
        } else {
            $a = new stdClass();
            $a->coursestudent = get_string('participants');
            $a->groupname = groups_get_group_name($this->_customdata['currentgroup']);
            if (20 < strlen($a->groupname)) {
                $studentsstring = get_string('studentingrouplong', 'quiz_overview', $a);
            } else {
                $studentsstring = get_string('studentingroup', 'quiz_overview', $a);
            }
        }
        $options = array();
        if (!$this->_customdata['currentgroup']) {
            $options[quiz_attempts_report::ALL_ATTEMPTS] = get_string('optallattempts', 'quiz_overview');
        }
        if ($this->_customdata['currentgroup'] ||
                !is_inside_frontpage($this->_customdata['context'])) {
            $options[quiz_attempts_report::ALL_STUDENTS] =
                    get_string('optallstudents', 'quiz_overview', $studentsstring);
            $options[quiz_attempts_report::STUDENTS_WITH] =
                     get_string('optattemptsonly', 'quiz_overview', $studentsstring);
            $options[quiz_attempts_report::STUDENTS_WITH_NO] =
                    get_string('optnoattemptsonly', 'quiz_overview', $studentsstring);
        }
        $mform->addElement('select', 'attemptsmode',
                get_string('show', 'quiz_overview'), $options);

        $this->definition_inner($mform);

        $mform->addElement('header', 'preferencesuser',
                get_string('preferencesuser', 'quiz_overview'));

        $mform->addElement('text', 'pagesize', get_string('pagesize', 'quiz_overview'));
        $mform->setType('pagesize', PARAM_INT);

        $mform->addElement('submit', 'submitbutton',
                get_string('preferencessave', 'quiz_overview'));
    }
 protected function definition()
 {
     $mform = $this->_form;
     $mform->addElement('header', 'preferencespage', get_string('preferencespage', 'quiz_overview'));
     if (!$this->_customdata['currentgroup']) {
         $studentsstring = get_string('participants');
     } else {
         $a = new stdClass();
         $a->coursestudent = get_string('participants');
         $a->groupname = groups_get_group_name($this->_customdata['currentgroup']);
         if (20 < strlen($a->groupname)) {
             $studentsstring = get_string('studentingrouplong', 'quiz_overview', $a);
         } else {
             $studentsstring = get_string('studentingroup', 'quiz_overview', $a);
         }
     }
     $options = array();
     if (!$this->_customdata['currentgroup']) {
         $options[QUIZ_REPORT_ATTEMPTS_ALL] = get_string('optallattempts', 'quiz_overview');
     }
     if ($this->_customdata['currentgroup'] || !is_inside_frontpage($this->_customdata['context'])) {
         $options[QUIZ_REPORT_ATTEMPTS_ALL_STUDENTS] = get_string('optallstudents', 'quiz_overview', $studentsstring);
         $options[QUIZ_REPORT_ATTEMPTS_STUDENTS_WITH] = get_string('optattemptsonly', 'quiz_overview', $studentsstring);
         $options[QUIZ_REPORT_ATTEMPTS_STUDENTS_WITH_NO] = get_string('optnoattemptsonly', 'quiz_overview', $studentsstring);
     }
     $mform->addElement('select', 'attemptsmode', get_string('show', 'quiz_overview'), $options);
     $showattemptsgrp = array();
     if ($this->_customdata['qmsubselect']) {
         $gm = '<span class="highlight">' . quiz_get_grading_option_name($this->_customdata['quiz']->grademethod) . '</span>';
         $showattemptsgrp[] = $mform->createElement('advcheckbox', 'qmfilter', get_string('showattempts', 'quiz_overview'), get_string('optonlygradedattempts', 'quiz_overview', $gm), null, array(0, 1));
     }
     if (has_capability('mod/quiz:regrade', $this->_customdata['context'])) {
         $showattemptsgrp[] = $mform->createElement('advcheckbox', 'regradefilter', get_string('showattempts', 'quiz_overview'), get_string('optonlyregradedattempts', 'quiz_overview'), null, array(0, 1));
     }
     if ($showattemptsgrp) {
         $mform->addGroup($showattemptsgrp, null, get_string('showattempts', 'quiz_overview'), '<br />', false);
     }
     $mform->addElement('header', 'preferencesuser', get_string('preferencesuser', 'quiz_overview'));
     $mform->addElement('text', 'pagesize', get_string('pagesize', 'quiz_overview'));
     $mform->setType('pagesize', PARAM_INT);
     $mform->addElement('selectyesno', 'detailedmarks', get_string('showdetailedmarks', 'quiz_overview'));
     $mform->addElement('submit', 'submitbutton', get_string('preferencessave', 'quiz_overview'));
 }
Пример #4
0
 if (!($contextusers = get_role_users($roleid, $context, false, 'u.id, u.firstname, u.lastname, u.email, ra.hidden'))) {
     $contextusers = array();
 }
 $select = "username <> 'guest' AND deleted = 0 AND confirmed = 1";
 $usercount = count_records_select('user', $select) - count($contextusers);
 $searchtext = trim($searchtext);
 if ($searchtext !== '') {
     // Search for a subset of remaining users
     $LIKE = sql_ilike();
     $FULLNAME = sql_fullname();
     $selectsql = " AND ({$FULLNAME} {$LIKE} '%{$searchtext}%' OR email {$LIKE} '%{$searchtext}%') ";
     $select .= $selectsql;
 } else {
     $selectsql = "";
 }
 if ($context->contextlevel > CONTEXT_COURSE && !is_inside_frontpage($context)) {
     // mod or block (or group?)
     /************************************************************************
      *                                                                      *
      * context level is above or equal course context level                 *
      * in this case we pull out all users matching search criteria (if any) *
      *                                                                      *
      * MDL-11324                                                            *
      * a mini get_users_by_capability() call here, this is done instead of  *
      * get_users_by_capability() because                                    *
      * 1) get_users_by_capability() does not deal with searching by name    *
      * 2) exceptions array can be potentially large for large courses       *
      * 3) get_recordset_sql() is more efficient                             *
      *                                                                      *
      ************************************************************************/
     if ($possibleroles = get_roles_with_capability('moodle/course:view', CAP_ALLOW, $context)) {
Пример #5
0
/**
 * 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 $context
 * @param string|array $capability - capability name(s)
 * @param string $fields - fields to be pulled. The user table is aliased to 'u'. u.id MUST be included.
 * @param string $sort - the sort order. Default is lastaccess time.
 * @param mixed $limitfrom - number of records to skip (offset)
 * @param mixed $limitnum - number of records to fetch
 * @param string|array $groups - single group or array of groups - only return
 *               users who are in one of these group(s).
 * @param string|array $exceptions - list of users to exclude, comma separated or array
 * @param bool $doanything_ignored not used any more, admin accounts are never returned
 * @param bool $view_ignored - use get_enrolled_sql() instead
 * @param bool $useviewallgroups if $groups is set the return users who
 *               have capability both $capability and moodle/site:accessallgroups
 *               in this context, as well as users who have $capability and who are
 *               in $groups.
 * @return mixed
 */
function get_users_by_capability(context $context, $capability, $fields = '', $sort = '', $limitfrom = '', $limitnum = '', $groups = '', $exceptions = '', $doanything_ignored = null, $view_ignored = null, $useviewallgroups = false)
{
    global $CFG, $DB;
    $defaultuserroleid = isset($CFG->defaultuserroleid) ? $CFG->defaultuserroleid : 0;
    $defaultfrontpageroleid = isset($CFG->defaultfrontpageroleid) ? $CFG->defaultfrontpageroleid : 0;
    $ctxids = trim($context->path, '/');
    $ctxids = str_replace('/', ',', $ctxids);
    // Context is the frontpage
    $iscoursepage = false;
    // coursepage other than fp
    $isfrontpage = false;
    if ($context->contextlevel == CONTEXT_COURSE) {
        if ($context->instanceid == SITEID) {
            $isfrontpage = true;
        } else {
            $iscoursepage = true;
        }
    }
    $isfrontpage = $isfrontpage || is_inside_frontpage($context);
    $caps = (array) $capability;
    // construct list of context paths bottom-->top
    list($contextids, $paths) = get_context_info_list($context);
    // we need to find out all roles that have these capabilities either in definition or in overrides
    $defs = array();
    list($incontexts, $params) = $DB->get_in_or_equal($contextids, SQL_PARAMS_NAMED, 'con');
    list($incaps, $params2) = $DB->get_in_or_equal($caps, SQL_PARAMS_NAMED, 'cap');
    $params = array_merge($params, $params2);
    $sql = "SELECT rc.id, rc.roleid, rc.permission, rc.capability, ctx.path\n              FROM {role_capabilities} rc\n              JOIN {context} ctx on rc.contextid = ctx.id\n             WHERE rc.contextid {$incontexts} AND rc.capability {$incaps}";
    $rcs = $DB->get_records_sql($sql, $params);
    foreach ($rcs as $rc) {
        $defs[$rc->capability][$rc->path][$rc->roleid] = $rc->permission;
    }
    // go through the permissions bottom-->top direction to evaluate the current permission,
    // first one wins (prohibit is an exception that always wins)
    $access = array();
    foreach ($caps as $cap) {
        foreach ($paths as $path) {
            if (empty($defs[$cap][$path])) {
                continue;
            }
            foreach ($defs[$cap][$path] as $roleid => $perm) {
                if ($perm == CAP_PROHIBIT) {
                    $access[$cap][$roleid] = CAP_PROHIBIT;
                    continue;
                }
                if (!isset($access[$cap][$roleid])) {
                    $access[$cap][$roleid] = (int) $perm;
                }
            }
        }
    }
    // make lists of roles that are needed and prohibited in this context
    $needed = array();
    // one of these is enough
    $prohibited = array();
    // must not have any of these
    foreach ($caps as $cap) {
        if (empty($access[$cap])) {
            continue;
        }
        foreach ($access[$cap] as $roleid => $perm) {
            if ($perm == CAP_PROHIBIT) {
                unset($needed[$cap][$roleid]);
                $prohibited[$cap][$roleid] = true;
            } else {
                if ($perm == CAP_ALLOW and empty($prohibited[$cap][$roleid])) {
                    $needed[$cap][$roleid] = true;
                }
            }
        }
        if (empty($needed[$cap]) or !empty($prohibited[$cap][$defaultuserroleid])) {
            // easy, nobody has the permission
            unset($needed[$cap]);
            unset($prohibited[$cap]);
        } else {
            if ($isfrontpage and !empty($prohibited[$cap][$defaultfrontpageroleid])) {
                // everybody is disqualified on the frontapge
                unset($needed[$cap]);
                unset($prohibited[$cap]);
            }
        }
        if (empty($prohibited[$cap])) {
            unset($prohibited[$cap]);
        }
    }
    if (empty($needed)) {
        // there can not be anybody if no roles match this request
        return array();
    }
    if (empty($prohibited)) {
        // we can compact the needed roles
        $n = array();
        foreach ($needed as $cap) {
            foreach ($cap as $roleid => $unused) {
                $n[$roleid] = true;
            }
        }
        $needed = array('any' => $n);
        unset($n);
    }
    /// ***** Set up default fields ******
    if (empty($fields)) {
        if ($iscoursepage) {
            $fields = 'u.*, ul.timeaccess AS lastaccess';
        } else {
            $fields = 'u.*';
        }
    } else {
        if (debugging('', DEBUG_DEVELOPER) && strpos($fields, 'u.*') === false && strpos($fields, 'u.id') === false) {
            debugging('u.id must be included in the list of fields passed to get_users_by_capability().', DEBUG_DEVELOPER);
        }
    }
    /// Set up default sort
    if (empty($sort)) {
        // default to course lastaccess or just lastaccess
        if ($iscoursepage) {
            $sort = 'ul.timeaccess';
        } else {
            $sort = 'u.lastaccess';
        }
    }
    // Prepare query clauses
    $wherecond = array();
    $params = array();
    $joins = array();
    // User lastaccess JOIN
    if (strpos($sort, 'ul.timeaccess') === false and strpos($fields, 'ul.timeaccess') === false) {
        // user_lastaccess is not required MDL-13810
    } else {
        if ($iscoursepage) {
            $joins[] = "LEFT OUTER JOIN {user_lastaccess} ul ON (ul.userid = u.id AND ul.courseid = {$context->instanceid})";
        } else {
            throw new coding_exception('Invalid sort in get_users_by_capability(), ul.timeaccess allowed only for course contexts.');
        }
    }
    /// We never return deleted users or guest account.
    $wherecond[] = "u.deleted = 0 AND u.id <> :guestid";
    $params['guestid'] = $CFG->siteguest;
    /// Groups
    if ($groups) {
        $groups = (array) $groups;
        list($grouptest, $grpparams) = $DB->get_in_or_equal($groups, SQL_PARAMS_NAMED, 'grp');
        $grouptest = "u.id IN (SELECT userid FROM {groups_members} gm WHERE gm.groupid {$grouptest})";
        $params = array_merge($params, $grpparams);
        if ($useviewallgroups) {
            $viewallgroupsusers = get_users_by_capability($context, 'moodle/site:accessallgroups', 'u.id, u.id', '', '', '', '', $exceptions);
            if (!empty($viewallgroupsusers)) {
                $wherecond[] = "({$grouptest} OR u.id IN (" . implode(',', array_keys($viewallgroupsusers)) . '))';
            } else {
                $wherecond[] = "({$grouptest})";
            }
        } else {
            $wherecond[] = "({$grouptest})";
        }
    }
    /// User exceptions
    if (!empty($exceptions)) {
        $exceptions = (array) $exceptions;
        list($exsql, $exparams) = $DB->get_in_or_equal($exceptions, SQL_PARAMS_NAMED, 'exc', false);
        $params = array_merge($params, $exparams);
        $wherecond[] = "u.id {$exsql}";
    }
    // now add the needed and prohibited roles conditions as joins
    if (!empty($needed['any'])) {
        // simple case - there are no prohibits involved
        if (!empty($needed['any'][$defaultuserroleid]) or $isfrontpage and !empty($needed['any'][$defaultfrontpageroleid])) {
            // everybody
        } else {
            $joins[] = "JOIN (SELECT DISTINCT userid\n                                FROM {role_assignments}\n                               WHERE contextid IN ({$ctxids})\n                                     AND roleid IN (" . implode(',', array_keys($needed['any'])) . ")\n                             ) ra ON ra.userid = u.id";
        }
    } else {
        $unions = array();
        $everybody = false;
        foreach ($needed as $cap => $unused) {
            if (empty($prohibited[$cap])) {
                if (!empty($needed[$cap][$defaultuserroleid]) or $isfrontpage and !empty($needed[$cap][$defaultfrontpageroleid])) {
                    $everybody = true;
                    break;
                } else {
                    $unions[] = "SELECT userid\n                                   FROM {role_assignments}\n                                  WHERE contextid IN ({$ctxids})\n                                        AND roleid IN (" . implode(',', array_keys($needed[$cap])) . ")";
                }
            } else {
                if (!empty($prohibited[$cap][$defaultuserroleid]) or $isfrontpage and !empty($prohibited[$cap][$defaultfrontpageroleid])) {
                    // nobody can have this cap because it is prevented in default roles
                    continue;
                } else {
                    if (!empty($needed[$cap][$defaultuserroleid]) or $isfrontpage and !empty($needed[$cap][$defaultfrontpageroleid])) {
                        // everybody except the prohibitted - hiding does not matter
                        $unions[] = "SELECT id AS userid\n                                   FROM {user}\n                                  WHERE id NOT IN (SELECT userid\n                                                     FROM {role_assignments}\n                                                    WHERE contextid IN ({$ctxids})\n                                                          AND roleid IN (" . implode(',', array_keys($prohibited[$cap])) . "))";
                    } else {
                        $unions[] = "SELECT userid\n                                   FROM {role_assignments}\n                                  WHERE contextid IN ({$ctxids})\n                                        AND roleid IN (" . implode(',', array_keys($needed[$cap])) . ")\n                                        AND roleid NOT IN (" . implode(',', array_keys($prohibited[$cap])) . ")";
                    }
                }
            }
        }
        if (!$everybody) {
            if ($unions) {
                $joins[] = "JOIN (SELECT DISTINCT userid FROM ( " . implode(' UNION ', $unions) . " ) us) ra ON ra.userid = u.id";
            } else {
                // only prohibits found - nobody can be matched
                $wherecond[] = "1 = 2";
            }
        }
    }
    // Collect WHERE conditions and needed joins
    $where = implode(' AND ', $wherecond);
    if ($where !== '') {
        $where = 'WHERE ' . $where;
    }
    $joins = implode("\n", $joins);
    /// Ok, let's get the users!
    $sql = "SELECT {$fields}\n              FROM {user} u\n            {$joins}\n            {$where}\n          ORDER BY {$sort}";
    return $DB->get_records_sql($sql, $params, $limitfrom, $limitnum);
}
Пример #6
0
 /**
  * Handle deleting a block.
  * @return boolean true if anything was done. False if not.
  */
 public function process_url_delete()
 {
     global $CFG, $PAGE, $OUTPUT;
     $blockid = optional_param('bui_deleteid', null, PARAM_INT);
     $confirmdelete = optional_param('bui_confirm', null, PARAM_INT);
     if (!$blockid) {
         return false;
     }
     require_sesskey();
     $block = $this->page->blocks->find_instance($blockid);
     if (!$this->user_can_delete_block($block)) {
         throw new moodle_exception('nopermissions', '', $this->page->url->out(), get_string('deleteablock'));
     }
     if (!$confirmdelete) {
         $deletepage = new moodle_page();
         $deletepage->set_pagelayout('admin');
         $deletepage->set_course($this->page->course);
         $deletepage->set_context($this->page->context);
         if ($this->page->cm) {
             $deletepage->set_cm($this->page->cm);
         }
         $deleteurlbase = str_replace($CFG->wwwroot . '/', '/', $this->page->url->out_omit_querystring());
         $deleteurlparams = $this->page->url->params();
         $deletepage->set_url($deleteurlbase, $deleteurlparams);
         $deletepage->set_block_actions_done();
         // At this point we are either going to redirect, or display the form, so
         // overwrite global $PAGE ready for this. (Formslib refers to it.)
         $PAGE = $deletepage;
         //some functions like MoodleQuickForm::addHelpButton use $OUTPUT so we need to replace that too
         $output = $deletepage->get_renderer('core');
         $OUTPUT = $output;
         $site = get_site();
         $blocktitle = $block->get_title();
         $strdeletecheck = get_string('deletecheck', 'block', $blocktitle);
         $message = get_string('deleteblockcheck', 'block', $blocktitle);
         // If the block is being shown in sub contexts display a warning.
         if ($block->instance->showinsubcontexts == 1) {
             $parentcontext = context::instance_by_id($block->instance->parentcontextid);
             $systemcontext = context_system::instance();
             $messagestring = new stdClass();
             $messagestring->location = $parentcontext->get_context_name();
             // Checking for blocks that may have visibility on the front page and pages added on that.
             if ($parentcontext->id != $systemcontext->id && is_inside_frontpage($parentcontext)) {
                 $messagestring->pagetype = get_string('showonfrontpageandsubs', 'block');
             } else {
                 $pagetypes = generate_page_type_patterns($this->page->pagetype, $parentcontext);
                 $messagestring->pagetype = $block->instance->pagetypepattern;
                 if (isset($pagetypes[$block->instance->pagetypepattern])) {
                     $messagestring->pagetype = $pagetypes[$block->instance->pagetypepattern];
                 }
             }
             $message = get_string('deleteblockwarning', 'block', $messagestring);
         }
         $PAGE->navbar->add($strdeletecheck);
         $PAGE->set_title($blocktitle . ': ' . $strdeletecheck);
         $PAGE->set_heading($site->fullname);
         echo $OUTPUT->header();
         $confirmurl = new moodle_url($deletepage->url, array('sesskey' => sesskey(), 'bui_deleteid' => $block->instance->id, 'bui_confirm' => 1));
         $cancelurl = new moodle_url($deletepage->url);
         $yesbutton = new single_button($confirmurl, get_string('yes'));
         $nobutton = new single_button($cancelurl, get_string('no'));
         echo $OUTPUT->confirm($message, $yesbutton, $nobutton);
         echo $OUTPUT->footer();
         // Make sure that nothing else happens after we have displayed this form.
         exit;
     } else {
         blocks_delete_instance($block->instance);
         // bui_deleteid and bui_confirm should not be in the PAGE url.
         $this->page->ensure_param_not_in_url('bui_deleteid');
         $this->page->ensure_param_not_in_url('bui_confirm');
         return true;
     }
 }
Пример #7
0
    }
}
/// Check login and permissions.
require_login($course);
$canview = has_any_capability(array('moodle/role:assign', 'moodle/role:safeoverride', 'moodle/role:override', 'moodle/role:manage'), $context);
if (!$canview) {
    print_error('nopermissions', 'error', '', get_string('checkpermissions', 'role'));
}
/// These are needed early because of tabs.php
$assignableroles = get_assignable_roles($context, ROLENAME_BOTH);
$overridableroles = get_overridable_roles($context, ROLENAME_BOTH);
/// Get the user_selector we will need.
/// Teachers within a course just get to see the same list of people they can
/// assign roles to. Admins (people with moodle/role:manage) can run this report for any user.
$options = array('context' => $context, 'roleid' => 0);
if ($context->contextlevel > CONTEXT_COURSE && !is_inside_frontpage($context) && !has_capability('moodle/role:manage', $context)) {
    $userselector = new potential_assignees_below_course('reportuser', $options);
} else {
    $userselector = new potential_assignees_course_and_above('reportuser', $options);
}
$userselector->set_multiselect(false);
$userselector->set_rows(10);
/// Work out an appropriate page title.
$title = get_string('checkpermissionsin', 'role', $contextname);
$straction = get_string('checkpermissions', 'role');
// Used by tabs.php
/// Print the header and tabs
if ($context->contextlevel == CONTEXT_USER) {
    $user = get_record('user', 'id', $userid);
    $fullname = fullname($user, has_capability('moodle/site:viewfullnames', $context));
    /// course header
Пример #8
0
/**
 * 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, or an array of capabilities, in which
 *               case users having any of those capabilities will be returned.
 *               For performance reasons, you are advised to put the capability
 *               that the user is most likely to have first.
 * @param $fields - fields to be pulled. The user table is aliased to 'u'. u.id MUST be included.
 * @param $sort - the sort order. Default is lastaccess time.
 * @param $limitfrom - number of records to skip (offset)
 * @param $limitnum - number of records to fetch
 * @param $groups - single group or array of groups - only return
 *               users who are in one of these group(s).
 * @param $exceptions - list of users to exclude
 * @param view - set to true when roles are pulled for display only
 *               this is so that we can filter roles with no visible
 *               assignment, for example, you might want to "hide" all
 *               course creators when browsing the course participants
 *               list.
 * @param boolean $useviewallgroups if $groups is set the return users who
 *               have capability both $capability and moodle/site:accessallgroups
 *               in this context, as well as users who have $capability and who are
 *               in $groups.
 */
function get_users_by_capability($context, $capability, $fields = '', $sort = '', $limitfrom = '', $limitnum = '', $groups = '', $exceptions = '', $doanything = true, $view = false, $useviewallgroups = false)
{
    global $CFG;
    $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?
    if (is_array($capability)) {
        $caps = "'" . implode("','", $capability) . "'";
        $capabilities = $capability;
    } else {
        $caps = "'" . $capability . "'";
        $capabilities = array($capability);
    }
    if ($doanything === true) {
        $caps .= ",'moodle/site:doanything'";
        $capabilities[] = 'moodle/site:doanything';
        $doanything_join = '';
        $doanything_cond = '';
    } else {
        // This is an outer join against
        // admin-ish roleids. Any row that succeeds
        // in JOINing here ends up removed from
        // the resultset. This means we remove
        // rolecaps from roles that also have
        // 'doanything' capabilities.
        $doanything_join = "LEFT OUTER JOIN (\n                              SELECT DISTINCT rc.roleid\n                              FROM {$CFG->prefix}role_capabilities rc\n                              WHERE rc.capability='moodle/site:doanything'\n                                    AND rc.permission=" . CAP_ALLOW . "\n                                    AND rc.contextid IN ({$ctxids})\n                          ) dar\n                             ON rc.roleid=dar.roleid";
        $doanything_cond = "AND dar.roleid IS NULL";
    }
    // fetch all capability records - we'll walk several
    // times over them, and should be a small set
    $negperm = false;
    // has any negative (<0) permission?
    $roleids = array();
    $sql = "SELECT rc.id, rc.roleid, rc.permission, rc.capability,\n                   ctx.depth AS ctxdepth, ctx.contextlevel AS ctxlevel\n            FROM {$CFG->prefix}role_capabilities rc\n            JOIN {$CFG->prefix}context ctx on rc.contextid = ctx.id\n            {$doanything_join}\n            WHERE rc.capability IN ({$caps}) AND ctx.id IN ({$ctxids})\n                  {$doanything_cond}\n            ORDER BY rc.roleid ASC, ctx.depth ASC";
    if ($capdefs = get_records_sql($sql)) {
        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;
    }
    // is the default role interesting? does it have
    // a relevant rolecap? (we use this a lot later)
    if (($isfrontpage or is_inside_frontpage($context)) and !empty($CFG->defaultfrontpageroleid) and in_array((int) $CFG->defaultfrontpageroleid, $roleids, true)) {
        if (!empty($CFG->fullusersbycapabilityonfrontpage)) {
            // new in 1.9.6 - full support for defaultfrontpagerole MDL-19039
            $frontpageroleinteresting = true;
        } else {
            // old style 1.9.0-1.9.5 - much faster + fewer negative override problems on frontpage
            $frontpageroleinteresting = $context->contextlevel == CONTEXT_COURSE;
        }
    } else {
        $frontpageroleinteresting = false;
    }
    //
    // Prepare query clauses
    //
    $wherecond = array();
    // Non-deleted users. We never return deleted users.
    $wherecond['nondeleted'] = 'u.deleted = 0';
    /// Groups
    if ($groups) {
        if (is_array($groups)) {
            $grouptest = 'gm.groupid IN (' . implode(',', $groups) . ')';
        } else {
            $grouptest = 'gm.groupid = ' . $groups;
        }
        $grouptest = 'u.id IN (SELECT userid FROM ' . $CFG->prefix . 'groups_members gm WHERE ' . $grouptest . ')';
        if ($useviewallgroups) {
            $viewallgroupsusers = get_users_by_capability($context, 'moodle/site:accessallgroups', 'u.id, u.id', '', '', '', '', $exceptions);
            $wherecond['groups'] = '(' . $grouptest . ' OR u.id IN (' . implode(',', array_keys($viewallgroupsusers)) . '))';
        } else {
            $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 ';
        $sscondhiddenra = 'AND ssra.hidden = 0 ';
    } else {
        $condhiddenra = '';
        $sscondhiddenra = '';
    }
    // 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';
        } else {
            $fields = 'u.*';
        }
    } else {
        if (debugging('', DEBUG_DEVELOPER) && strpos($fields, 'u.*') === false && strpos($fields, 'u.id') === false) {
            debugging('u.id must be included in the list of fields passed to get_users_by_capability.', DEBUG_DEVELOPER);
        }
    }
    /// 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} " : '';
    // User lastaccess JOIN
    if (strpos($sort, 'ul.timeaccess') === FALSE and strpos($fields, 'ul.timeaccess') === FALSE) {
        // user_lastaccess is not required MDL-13810
        $uljoin = '';
    } else {
        $uljoin = "LEFT OUTER JOIN {$CFG->prefix}user_lastaccess ul \n                         ON (ul.userid = u.id AND ul.courseid = {$context->instanceid})";
    }
    //
    // Simple cases - No negative permissions means we can take shortcuts
    //
    if (!$negperm) {
        // at the frontpage, and all site users have it - easy!
        if ($frontpageroleinteresting) {
            return get_records_sql("SELECT {$fields}\n                                    FROM {$CFG->prefix}user u\n                                    WHERE u.deleted = 0\n                                    ORDER BY {$sort}", $limitfrom, $limitnum);
        }
        // all site users have it, anyway
        // TODO: NOT ALWAYS!  Check this case because this gets run for cases like this:
        // 1) Default role has the permission for a module thing like mod/choice:choose
        // 2) We are checking for an activity module context in a course
        // 3) Thus all users are returned even though course:view is also required
        if ($defaultroleinteresting) {
            $sql = "SELECT {$fields}\n                    FROM {$CFG->prefix}user u\n                    {$uljoin}\n                    {$where}\n                    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\n                    JOIN (SELECT DISTINCT ssra.userid\n                          FROM {$CFG->prefix}role_assignments ssra\n                          WHERE ssra.contextid IN ({$ctxids})\n                                AND ssra.roleid IN (" . implode(',', $roleids) . ")\n                                {$sscondhiddenra}\n                          ) ra ON ra.userid = u.id\n                    {$uljoin} ";
        return get_records_sql($select . $from . $where . $sortby, $limitfrom, $limitnum);
    }
    //
    // 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;
            }
            // 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);
        }
    }
    if ($context->contextlevel == CONTEXT_SYSTEM || $frontpageroleinteresting || $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,\n                      ctx.depth\n               FROM {$CFG->prefix}user u\n               LEFT OUTER JOIN {$CFG->prefix}role_assignments ra \n                 ON (ra.userid = u.id\n                     AND ra.contextid IN ({$ctxids})\n                     AND ra.roleid IN (" . implode(',', $roleids) . ")\n                     {$condhiddenra})\n               LEFT OUTER JOIN {$CFG->prefix}context ctx\n                 ON ra.contextid=ctx.id\n               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,\n                      ctx.depth\n               FROM {$CFG->prefix}role_assignments ra \n               JOIN {$CFG->prefix}context ctx\n                 ON ra.contextid=ctx.id\n               WHERE ra.contextid IN ({$ctxids})\n                     {$condhiddenra}\n                     AND ra.roleid IN (" . implode(',', $roleids) . ")";
    }
    $select = "SELECT {$fields} ,ra.roleid, ra.depth ";
    $from = "FROM ({$ss}) ra\n               JOIN {$CFG->prefix}user u\n                 ON ra.userid=u.id\n               {$uljoin} ";
    // 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_any_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 ($frontpageroleinteresting) {
                    // add frontpage role if interesting
                    $ras[] = array('roleid' => $CFG->defaultfrontpageroleid, 'depth' => $context->depth);
                }
                if ($defaultroleinteresting) {
                    // add the role at the end of $ras
                    $ras[] = array('roleid' => $CFG->defaultuserroleid, 'depth' => 1);
                }
                if (has_any_capability_from_rarc($ras, $roleperms, $capabilities)) {
                    $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 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 ($frontpageroleinteresting) {
            // add frontpage role if interesting
            $ras[] = array('roleid' => $CFG->defaultfrontpageroleid, 'depth' => $context->depth);
        }
        if ($defaultroleinteresting) {
            // add the role at the end of $ras
            $ras[] = array('roleid' => $CFG->defaultuserroleid, 'depth' => 1);
        }
        if (!has_any_capability_from_rarc($ras, $roleperms, $capabilities)) {
            // 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]);
                }
            }
        }
    }
    return $results;
}