/** * 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; }
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')); }
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)) {
/** * 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); }
/** * 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; } }
} } /// 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
/** * 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; }