mirror of
https://github.com/moodle/moodle.git
synced 2025-01-19 14:27:22 +01:00
1510 lines
56 KiB
PHP
1510 lines
56 KiB
PHP
<?php
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
// //
|
|
// NOTICE OF COPYRIGHT //
|
|
// //
|
|
// Moodle - Modular Object-Oriented Dynamic Learning Environment //
|
|
// http://moodle.org //
|
|
// //
|
|
// Copyright (C) 1999 onwards Martin Dougiamas http://dougiamas.com //
|
|
// //
|
|
// This program is free software; you can redistribute it and/or modify //
|
|
// it under the terms of the GNU General Public License as published by //
|
|
// the Free Software Foundation; either version 2 of the License, or //
|
|
// (at your option) any later version. //
|
|
// //
|
|
// This program is distributed in the hope that it will be useful, //
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
|
|
// GNU General Public License for more details: //
|
|
// //
|
|
// http://www.gnu.org/copyleft/gpl.html //
|
|
// //
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
/**
|
|
* Library code used by the roles administration interfaces.
|
|
*
|
|
* @license http://www.gnu.org/copyleft/gpl.html GNU Public License
|
|
* @package roles
|
|
*//** */
|
|
|
|
require_once($CFG->libdir.'/adminlib.php');
|
|
require_once($CFG->dirroot.'/user/selector/lib.php');
|
|
|
|
// Classes for producing tables with one row per capability ====================
|
|
|
|
/**
|
|
* This class represents a table with one row for each of a list of capabilities
|
|
* where the first cell in the row contains the capability name, and there is
|
|
* arbitrary stuff in the rest of the row. This class is used by
|
|
* admin/roles/manage.php, override.php and check.php.
|
|
*
|
|
* An ajaxy search UI shown at the top, if JavaScript is on.
|
|
*/
|
|
abstract class capability_table_base {
|
|
/** The context this table relates to. */
|
|
protected $context;
|
|
|
|
/** The capabilities to display. Initialised as fetch_context_capabilities($context). */
|
|
protected $capabilities = array();
|
|
|
|
/** Added as an id="" attribute to the table on output. */
|
|
protected $id;
|
|
|
|
/** Added to the class="" attribute on output. */
|
|
protected $classes = array('rolecap');
|
|
|
|
/** Default number of capabilities in the table for the search UI to be shown. */
|
|
const NUM_CAPS_FOR_SEARCH = 12;
|
|
|
|
/**
|
|
* Constructor
|
|
* @param object $context the context this table relates to.
|
|
* @param string $id what to put in the id="" attribute.
|
|
*/
|
|
public function __construct($context, $id) {
|
|
$this->context = $context;
|
|
$this->capabilities = fetch_context_capabilities($context);
|
|
$this->id = $id;
|
|
}
|
|
|
|
/**
|
|
* Use this to add class="" attributes to the table. You get the rolecap by
|
|
* default.
|
|
* @param array $classnames of class names.
|
|
*/
|
|
public function add_classes($classnames) {
|
|
$this->classes = array_unique(array_merge($this->classes, $classnames));
|
|
}
|
|
|
|
/**
|
|
* Display the table.
|
|
*/
|
|
public function display() {
|
|
echo '<table class="' . implode(' ', $this->classes) . '" id="' . $this->id . '">' . "\n<thead>\n";
|
|
echo '<tr><th class="name" align="left" scope="col">' . get_string('capability','role') . '</th>';
|
|
$this->add_header_cells();
|
|
echo "</tr>\n</thead>\n<tbody>\n";
|
|
|
|
/// Loop over capabilities.
|
|
$contextlevel = 0;
|
|
$component = '';
|
|
foreach ($this->capabilities as $capability) {
|
|
if ($this->skip_row($capability)) {
|
|
continue;
|
|
}
|
|
|
|
/// Prints a breaker if component or name or context level has changed
|
|
if (component_level_changed($capability, $component, $contextlevel)) {
|
|
$this->print_heading_row($capability);
|
|
}
|
|
$contextlevel = $capability->contextlevel;
|
|
$component = $capability->component;
|
|
|
|
/// Start the row.
|
|
echo '<tr class="' . implode(' ', array_unique(array_merge(array('rolecap'),
|
|
$this->get_row_classes($capability)))) . '">';
|
|
|
|
/// Table cell for the capability name.
|
|
echo '<th scope="row" class="name"><span class="cap-desc">' . get_capability_docs_link($capability) .
|
|
'<span class="cap-name">' . $capability->name . '</span></span></th>';
|
|
|
|
/// Add the cells specific to this table.
|
|
$this->add_row_cells($capability);
|
|
|
|
/// End the row.
|
|
echo "</tr>\n";
|
|
}
|
|
|
|
/// End of the table.
|
|
echo "</tbody>\n</table>\n";
|
|
if (count($this->capabilities) > capability_table_base::NUM_CAPS_FOR_SEARCH) {
|
|
global $CFG, $PAGE;
|
|
$PAGE->requires->yui_lib('dom-event');
|
|
$PAGE->requires->js($CFG->admin . '/roles/roles.js');
|
|
$PAGE->requires->js_function_call('cap_table_filter.init',
|
|
array($this->id, get_string('filter'), get_string('clear')));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Used to output a heading rows when the context level or component changes.
|
|
* @param object $capability gives the new component and contextlevel.
|
|
*/
|
|
protected function print_heading_row($capability) {
|
|
echo '<tr class="rolecapheading header"><td colspan="' . (1 + $this->num_extra_columns()) . '" class="header"><strong>' .
|
|
get_component_string($capability->component, $capability->contextlevel) .
|
|
'</strong></td></tr>';
|
|
|
|
}
|
|
|
|
/** For subclasses to override, output header cells, after the initial capability one. */
|
|
protected abstract function add_header_cells();
|
|
|
|
/** For subclasses to override, return the number of cells that add_header_cells/add_row_cells output. */
|
|
protected abstract function num_extra_columns();
|
|
|
|
/**
|
|
* For subclasses to override. Allows certain capabilties (e.g. legacy capabilities)
|
|
* to be left out of the table.
|
|
*
|
|
* @param object $capability the capability this row relates to.
|
|
* @return boolean. If true, this row is omitted from the table.
|
|
*/
|
|
protected function skip_row($capability) {
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* For subclasses to override. A change to reaturn class names that are added
|
|
* to the class="" attribute on the <tr> for this capability.
|
|
*
|
|
* @param object $capability the capability this row relates to.
|
|
* @return array of class name strings.
|
|
*/
|
|
protected function get_row_classes($capability) {
|
|
return array();
|
|
}
|
|
|
|
/**
|
|
* For subclasses to override. Output the data cells for this capability. The
|
|
* capability name cell will already have been output.
|
|
*
|
|
* You can rely on get_row_classes always being called before add_row_cells.
|
|
*
|
|
* @param object $capability the capability this row relates to.
|
|
*/
|
|
protected abstract function add_row_cells($capability);
|
|
}
|
|
|
|
/**
|
|
* Subclass of capability_table_base for use on the Check permissions page.
|
|
*
|
|
* We have two additional columns, Allowed, which contains yes/no, and Explanation,
|
|
* which contains a pop-up link to explainhascapability.php.
|
|
*/
|
|
class explain_capability_table extends capability_table_base {
|
|
protected $user;
|
|
protected $fullname;
|
|
protected $baseurl;
|
|
protected $contextname;
|
|
protected $stryes;
|
|
protected $strno;
|
|
protected $strexplanation;
|
|
private $hascap;
|
|
|
|
/**
|
|
* Constructor
|
|
* @param object $context the context this table relates to.
|
|
* @param object $user the user we are generating the results for.
|
|
* @param string $contextname print_context_name($context) - to save recomputing.
|
|
*/
|
|
public function __construct($context, $user, $contextname) {
|
|
global $CFG;
|
|
parent::__construct($context, 'explaincaps');
|
|
$this->user = $user;
|
|
$this->fullname = fullname($user);
|
|
$this->contextname = $contextname;
|
|
$this->baseurl = $CFG->wwwroot . '/' . $CFG->admin .
|
|
'/roles/explain.php?user=' . $user->id .
|
|
'&contextid=' . $context->id . '&capability=';
|
|
$this->stryes = get_string('yes');
|
|
$this->strno = get_string('no');
|
|
$this->strexplanation = get_string('explanation');
|
|
}
|
|
|
|
protected function add_header_cells() {
|
|
echo '<th>' . get_string('allowed', 'role') . '</th>';
|
|
echo '<th>' . $this->strexplanation . '</th>';
|
|
}
|
|
|
|
protected function num_extra_columns() {
|
|
return 2;
|
|
}
|
|
|
|
protected function skip_row($capability) {
|
|
return $capability->name != 'moodle/site:doanything' && is_legacy($capability->name);
|
|
}
|
|
|
|
protected function get_row_classes($capability) {
|
|
$this->hascap = has_capability($capability->name, $this->context, $this->user->id);
|
|
if ($this->hascap) {
|
|
return array('yes');
|
|
} else {
|
|
return array('no');
|
|
}
|
|
}
|
|
|
|
protected function add_row_cells($capability) {
|
|
global $OUTPUT;
|
|
if ($this->hascap) {
|
|
$result = $this->stryes;
|
|
$tooltip = 'whydoesuserhavecap';
|
|
} else {
|
|
$result = $this->strno;
|
|
$tooltip = 'whydoesusernothavecap';
|
|
}
|
|
$a = new stdClass;
|
|
$a->fullname = $this->fullname;
|
|
$a->capability = $capability->name;
|
|
$a->context = $this->contextname;
|
|
echo '<td>' . $result . '</td>';
|
|
echo '<td>';
|
|
|
|
$link = html_link::make($this->baseurl . $capability->name, $this->strexplanation);
|
|
$link->add_action(new popup_action('click', $link->url, 'hascapabilityexplanation', array('height' => 600, 'width' => 600)));
|
|
$link->title = get_string($tooltip, 'role', $a);
|
|
echo $OUTPUT->link($link);
|
|
|
|
echo '</td>';
|
|
}
|
|
}
|
|
|
|
/**
|
|
* This subclass is the bases for both the define roles and override roles
|
|
* pages. As well as adding the risks columns, this also provides generic
|
|
* facilities for showing a certain number of permissions columns, and
|
|
* recording the current and submitted permissions for each capability.
|
|
*/
|
|
abstract class capability_table_with_risks extends capability_table_base {
|
|
protected $allrisks;
|
|
protected $allpermissions; // We don't need perms ourself, but all our subclasses do.
|
|
protected $strperms; // Language string cache.
|
|
protected $risksurl; // URL in moodledocs about risks.
|
|
protected $riskicons = array(); // Cache to avoid regenerating the HTML for each risk icon.
|
|
/** The capabilities to highlight as default/interited. */
|
|
protected $parentpermissions;
|
|
protected $displaypermissions;
|
|
protected $permissions;
|
|
protected $changed;
|
|
protected $roleid;
|
|
|
|
public function __construct($context, $id, $roleid) {
|
|
parent::__construct($context, $id);
|
|
|
|
$this->allrisks = get_all_risks();
|
|
$this->risksurl = get_docs_url(s(get_string('risks', 'role')));
|
|
|
|
$this->allpermissions = array(
|
|
CAP_INHERIT => 'inherit',
|
|
CAP_ALLOW => 'allow',
|
|
CAP_PREVENT => 'prevent' ,
|
|
CAP_PROHIBIT => 'prohibit',
|
|
);
|
|
|
|
$this->strperms = array();
|
|
foreach ($this->allpermissions as $permname) {
|
|
$this->strperms[$permname] = get_string($permname, 'role');
|
|
}
|
|
|
|
$this->roleid = $roleid;
|
|
$this->load_current_permissions();
|
|
|
|
/// Fill in any blank permissions with an explicit CAP_INHERIT, and init a locked field.
|
|
foreach ($this->capabilities as $capid => $cap) {
|
|
if (!isset($this->permissions[$cap->name])) {
|
|
$this->permissions[$cap->name] = CAP_INHERIT;
|
|
}
|
|
$this->capabilities[$capid]->locked = false;
|
|
}
|
|
}
|
|
|
|
protected function load_current_permissions() {
|
|
global $DB;
|
|
|
|
/// Load the overrides/definition in this context.
|
|
if ($this->roleid) {
|
|
$this->permissions = $DB->get_records_menu('role_capabilities', array('roleid' => $this->roleid,
|
|
'contextid' => $this->context->id), '', 'capability,permission');
|
|
} else {
|
|
$this->permissions = array();
|
|
}
|
|
}
|
|
|
|
protected abstract function load_parent_permissions();
|
|
|
|
/**
|
|
* Update $this->permissions based on submitted data, while making a list of
|
|
* changed capabilities in $this->changed.
|
|
*/
|
|
public function read_submitted_permissions() {
|
|
$this->changed = array();
|
|
|
|
foreach ($this->capabilities as $cap) {
|
|
if ($cap->locked || $this->skip_row($cap)) {
|
|
/// The user is not allowed to change the permission for this capapability
|
|
continue;
|
|
}
|
|
|
|
$permission = optional_param($cap->name, null, PARAM_PERMISSION);
|
|
if (is_null($permission)) {
|
|
/// A permission was not specified in submitted data.
|
|
continue;
|
|
}
|
|
|
|
/// If the permission has changed, update $this->permissions and
|
|
/// Record the fact there is data to save.
|
|
if ($this->permissions[$cap->name] != $permission) {
|
|
$this->permissions[$cap->name] = $permission;
|
|
$this->changed[] = $cap->name;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Save the new values of any permissions that have been changed.
|
|
*/
|
|
public function save_changes() {
|
|
/// Set the permissions.
|
|
foreach ($this->changed as $changedcap) {
|
|
assign_capability($changedcap, $this->permissions[$changedcap],
|
|
$this->roleid, $this->context->id, true);
|
|
}
|
|
|
|
/// Force accessinfo refresh for users visiting this context.
|
|
mark_context_dirty($this->context->path);
|
|
}
|
|
|
|
public function display() {
|
|
$this->load_parent_permissions();
|
|
foreach ($this->capabilities as $cap) {
|
|
if (!isset($this->parentpermissions[$cap->name])) {
|
|
$this->parentpermissions[$cap->name] = CAP_INHERIT;
|
|
}
|
|
}
|
|
parent::display();
|
|
}
|
|
|
|
protected function add_header_cells() {
|
|
global $OUTPUT;
|
|
echo '<th colspan="' . count($this->displaypermissions) . '" scope="col">' .
|
|
get_string('permission', 'role') . ' ' . $OUTPUT->help_icon(moodle_help_icon::make('permissions', get_string('permissions', 'role'))) . '</th>';
|
|
echo '<th class="risk" colspan="' . count($this->allrisks) . '" scope="col">' . get_string('risks','role') . '</th>';
|
|
}
|
|
|
|
protected function num_extra_columns() {
|
|
return count($this->displaypermissions) + count($this->allrisks);
|
|
}
|
|
|
|
protected function get_row_classes($capability) {
|
|
$rowclasses = array();
|
|
foreach ($this->allrisks as $riskname => $risk) {
|
|
if ($risk & (int)$capability->riskbitmask) {
|
|
$rowclasses[] = $riskname;
|
|
}
|
|
}
|
|
return $rowclasses;
|
|
}
|
|
|
|
protected abstract function add_permission_cells($capability);
|
|
|
|
protected function add_row_cells($capability) {
|
|
$this->add_permission_cells($capability);
|
|
/// One cell for each possible risk.
|
|
foreach ($this->allrisks as $riskname => $risk) {
|
|
echo '<td class="risk ' . str_replace('risk', '', $riskname) . '">';
|
|
if ($risk & (int)$capability->riskbitmask) {
|
|
echo $this->get_risk_icon($riskname);
|
|
}
|
|
echo '</td>';
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Print a risk icon, as a link to the Risks page on Moodle Docs.
|
|
*
|
|
* @param string $type the type of risk, will be one of the keys from the
|
|
* get_all_risks array. Must start with 'risk'.
|
|
*/
|
|
function get_risk_icon($type) {
|
|
global $OUTPUT;
|
|
if (!isset($this->riskicons[$type])) {
|
|
$iconurl = $OUTPUT->old_icon_url('i/' . str_replace('risk', 'risk_', $type));
|
|
|
|
$link = html_link::make($this->risksurl, '<img src="' . $iconurl . '" alt="' . get_string($type . 'short', 'admin') . '" />');
|
|
$link->add_action(new popup_action('click', $link->url, 'docspopup'));
|
|
$link->title = get_string($type, 'admin');
|
|
|
|
$this->riskicons[$type] = $OUTPUT->link($link);
|
|
}
|
|
return $this->riskicons[$type];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* As well as tracking the permissions information about the role we are creating
|
|
* or editing, we aslo track the other infromation about the role. (This class is
|
|
* starting to be more and more like a formslib form in some respects.)
|
|
*/
|
|
class define_role_table_advanced extends capability_table_with_risks {
|
|
/** Used to store other information (besides permissions) about the role we are creating/editing. */
|
|
protected $role;
|
|
/** Used to store errors found when validating the data. */
|
|
protected $errors;
|
|
protected $contextlevels;
|
|
protected $allcontextlevels;
|
|
protected $legacyroles;
|
|
protected $disabled = '';
|
|
|
|
public function __construct($context, $roleid) {
|
|
$this->roleid = $roleid;
|
|
parent::__construct($context, 'defineroletable', $roleid);
|
|
$this->displaypermissions = $this->allpermissions;
|
|
$this->strperms[$this->allpermissions[CAP_INHERIT]] = get_string('notset', 'role');
|
|
|
|
$this->allcontextlevels = array(
|
|
CONTEXT_SYSTEM => get_string('coresystem'),
|
|
CONTEXT_USER => get_string('user'),
|
|
CONTEXT_COURSECAT => get_string('category'),
|
|
CONTEXT_COURSE => get_string('course'),
|
|
CONTEXT_MODULE => get_string('activitymodule'),
|
|
CONTEXT_BLOCK => get_string('block')
|
|
);
|
|
|
|
$this->legacyroles = get_legacy_roles();
|
|
}
|
|
|
|
protected function load_current_permissions() {
|
|
global $DB;
|
|
if ($this->roleid) {
|
|
if (!$this->role = $DB->get_record('role', array('id' => $this->roleid))) {
|
|
throw new moodle_exception('invalidroleid');
|
|
}
|
|
$this->role->legacytype = get_legacy_type($this->roleid);
|
|
$contextlevels = get_role_contextlevels($this->roleid);
|
|
// Put the contextlevels in the array keys, as well as the values.
|
|
if (!empty($contextlevels)) {
|
|
$this->contextlevels = array_combine($contextlevels, $contextlevels);
|
|
} else {
|
|
$this->contextlevels = array();
|
|
}
|
|
} else {
|
|
$this->role = new stdClass;
|
|
$this->role->name = '';
|
|
$this->role->shortname = '';
|
|
$this->role->description = '';
|
|
$this->role->legacytype = '';
|
|
$this->contextlevels = array();
|
|
}
|
|
parent::load_current_permissions();
|
|
}
|
|
|
|
public function read_submitted_permissions() {
|
|
global $DB;
|
|
$this->errors = array();
|
|
|
|
// Role name.
|
|
$name = optional_param('name', null, PARAM_MULTILANG);
|
|
if (!is_null($name)) {
|
|
$this->role->name = $name;
|
|
if (html_is_blank($this->role->name)) {
|
|
$this->errors['name'] = get_string('errorbadrolename', 'role');
|
|
}
|
|
}
|
|
if ($DB->record_exists_select('role', 'name = ? and id <> ?', array($this->role->name, $this->roleid))) {
|
|
$this->errors['name'] = get_string('errorexistsrolename', 'role');
|
|
}
|
|
|
|
// Role short name. We clean this in a special way. We want to end up
|
|
// with only lowercase safe ASCII characters.
|
|
$shortname = optional_param('shortname', null, PARAM_RAW);
|
|
if (!is_null($shortname)) {
|
|
$this->role->shortname = $shortname;
|
|
$this->role->shortname = textlib_get_instance()->specialtoascii($this->role->shortname);
|
|
$this->role->shortname = moodle_strtolower(clean_param($this->role->shortname, PARAM_ALPHANUMEXT));
|
|
if (empty($this->role->shortname)) {
|
|
$this->errors['shortname'] = get_string('errorbadroleshortname', 'role');
|
|
}
|
|
}
|
|
if ($DB->record_exists_select('role', 'shortname = ? and id <> ?', array($this->role->shortname, $this->roleid))) {
|
|
$this->errors['shortname'] = get_string('errorexistsroleshortname', 'role');
|
|
}
|
|
|
|
// Description.
|
|
$description = optional_param('description', null, PARAM_CLEAN);
|
|
if (!is_null($description)) {
|
|
$this->role->description = $description;
|
|
}
|
|
|
|
// Legacy type.
|
|
$legacytype = optional_param('legacytype', null, PARAM_RAW);
|
|
if (!is_null($legacytype)) {
|
|
if (array_key_exists($legacytype, $this->legacyroles)) {
|
|
$this->role->legacytype = $legacytype;
|
|
} else {
|
|
$this->role->legacytype = '';
|
|
}
|
|
}
|
|
|
|
// Assignable context levels.
|
|
foreach ($this->allcontextlevels as $cl => $notused) {
|
|
$assignable = optional_param('contextlevel' . $cl, null, PARAM_BOOL);
|
|
if (!is_null($assignable)) {
|
|
if ($assignable) {
|
|
$this->contextlevels[$cl] = $cl;
|
|
} else {
|
|
unset($this->contextlevels[$cl]);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Now read the permissions for each capability.
|
|
parent::read_submitted_permissions();
|
|
}
|
|
|
|
public function is_submission_valid() {
|
|
return empty($this->errors);
|
|
}
|
|
|
|
/**
|
|
* Call this after the table has been initialised, so to indicate that
|
|
* when save is called, we want to make a duplicate role.
|
|
*/
|
|
public function make_copy() {
|
|
$this->roleid = 0;
|
|
unset($this->role->id);
|
|
$this->role->name .= ' ' . get_string('copyasnoun');
|
|
$this->role->shortname .= 'copy';
|
|
}
|
|
|
|
public function get_role_name() {
|
|
return $this->role->name;
|
|
}
|
|
|
|
public function get_role_id() {
|
|
return $this->role->id;
|
|
}
|
|
|
|
public function get_legacy_type() {
|
|
return $this->role->legacytype;
|
|
}
|
|
|
|
protected function load_parent_permissions() {
|
|
if ($this->role->legacytype) {
|
|
$this->parentpermissions = get_default_capabilities($this->role->legacytype);
|
|
} else {
|
|
$this->parentpermissions = array();
|
|
}
|
|
}
|
|
|
|
public function save_changes() {
|
|
global $DB;
|
|
|
|
if (!$this->roleid) {
|
|
// Creating role
|
|
if (isset($this->legacyroles[$this->role->legacytype])) {
|
|
$legacycap = $this->legacyroles[$this->role->legacytype];
|
|
} else {
|
|
$legacycap = '';
|
|
}
|
|
$this->role->id = create_role($this->role->name, $this->role->shortname, $this->role->description, $legacycap);
|
|
$this->roleid = $this->role->id; // Needed to make the parent::save_changes(); call work.
|
|
} else {
|
|
// Updating role
|
|
$DB->update_record('role', $this->role);
|
|
|
|
// Legacy type
|
|
foreach($this->legacyroles as $type => $cap) {
|
|
if ($type == $this->role->legacytype) {
|
|
assign_capability($cap, CAP_ALLOW, $this->role->id, $this->context->id);
|
|
} else {
|
|
unassign_capability($cap, $this->role->id);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Assignable contexts.
|
|
set_role_contextlevels($this->role->id, $this->contextlevels);
|
|
|
|
// Permissions.
|
|
parent::save_changes();
|
|
}
|
|
|
|
protected function skip_row($capability) {
|
|
return is_legacy($capability->name);
|
|
}
|
|
|
|
protected function get_name_field($id) {
|
|
return '<input type="text" id="' . $id . '" name="' . $id . '" maxlength="254" value="' . s($this->role->name) . '" />';
|
|
}
|
|
|
|
protected function get_shortname_field($id) {
|
|
return '<input type="text" id="' . $id . '" name="' . $id . '" maxlength="254" value="' . s($this->role->shortname) . '" />';
|
|
}
|
|
|
|
protected function get_description_field($id) {
|
|
return print_textarea(true, 10, 50, 50, 10, 'description', $this->role->description, 0, true);
|
|
}
|
|
|
|
protected function get_legacy_type_field($id) {
|
|
global $OUTPUT;
|
|
$options = array();
|
|
$options[''] = get_string('none');
|
|
foreach($this->legacyroles as $type => $cap) {
|
|
$options[$type] = get_string('legacy:'.$type, 'role');
|
|
}
|
|
return $OUTPUT->select(html_select::make($options, 'legacytype', $this->role->legacytype, false));
|
|
}
|
|
|
|
protected function get_assignable_levels_control() {
|
|
$output = '';
|
|
foreach ($this->allcontextlevels as $cl => $clname) {
|
|
$extraarguments = $this->disabled;
|
|
if (in_array($cl, $this->contextlevels)) {
|
|
$extraarguments .= 'checked="checked" ';
|
|
}
|
|
if (!$this->disabled) {
|
|
$output .= '<input type="hidden" name="contextlevel' . $cl . '" value="0" />';
|
|
}
|
|
$output .= '<input type="checkbox" id="cl' . $cl . '" name="contextlevel' . $cl .
|
|
'" value="1" ' . $extraarguments . '/> ';
|
|
$output .= '<label for="cl' . $cl . '">' . $clname . "</label><br />\n";
|
|
}
|
|
return $output;
|
|
}
|
|
|
|
protected function print_field($name, $caption, $field) {
|
|
global $OUTPUT;
|
|
// Attempt to generate HTML like formslib.
|
|
echo '<div class="fitem">';
|
|
echo '<div class="fitemtitle">';
|
|
if ($name) {
|
|
echo '<label for="' . $name . '">';
|
|
}
|
|
echo $caption;
|
|
if ($name) {
|
|
echo "</label>\n";
|
|
}
|
|
echo '</div>';
|
|
if (isset($this->errors[$name])) {
|
|
$extraclass = ' error';
|
|
} else {
|
|
$extraclass = '';
|
|
}
|
|
echo '<div class="felement' . $extraclass . '">';
|
|
if (isset($this->errors[$name])) {
|
|
echo $OUTPUT->error_text($this->errors[$name]);
|
|
}
|
|
echo $field;
|
|
echo '</div>';
|
|
echo '</div>';
|
|
}
|
|
|
|
protected function print_show_hide_advanced_button() {
|
|
echo '<p class="definenotice">' . get_string('highlightedcellsshowdefault', 'role') . ' </p>';
|
|
echo '<div class="advancedbutton">';
|
|
echo '<input type="submit" name="toggleadvanced" value="' . get_string('hideadvanced', 'form') . '" />';
|
|
echo '</div>';
|
|
}
|
|
|
|
public function display() {
|
|
// Extra fields at the top of the page.
|
|
echo '<div class="topfields clearfix">';
|
|
$this->print_field('name', get_string('name'), $this->get_name_field('name'));
|
|
$this->print_field('shortname', get_string('shortname'), $this->get_shortname_field('shortname'));
|
|
$this->print_field('edit-description', get_string('description'), $this->get_description_field('description'));
|
|
$this->print_field('menulegacytype', get_string('legacytype', 'role'), $this->get_legacy_type_field('legacytype'));
|
|
$this->print_field('', get_string('maybeassignedin', 'role'), $this->get_assignable_levels_control());
|
|
echo "</div>";
|
|
|
|
$this->print_show_hide_advanced_button();
|
|
|
|
// Now the permissions table.
|
|
parent::display();
|
|
}
|
|
|
|
protected function add_permission_cells($capability) {
|
|
/// One cell for each possible permission.
|
|
foreach ($this->displaypermissions as $perm => $permname) {
|
|
$strperm = $this->strperms[$permname];
|
|
$extraclass = '';
|
|
if ($perm == $this->parentpermissions[$capability->name]) {
|
|
$extraclass = ' capdefault';
|
|
}
|
|
$checked = '';
|
|
if ($this->permissions[$capability->name] == $perm) {
|
|
$checked = 'checked="checked" ';
|
|
}
|
|
echo '<td class="' . $permname . $extraclass . '">';
|
|
echo '<label><input type="radio" name="' . $capability->name .
|
|
'" value="' . $perm . '" ' . $checked . '/> ';
|
|
echo '<span class="note">' . $strperm . '</span>';
|
|
echo '</label></td>';
|
|
}
|
|
}
|
|
}
|
|
|
|
class define_role_table_basic extends define_role_table_advanced {
|
|
protected $stradvmessage;
|
|
protected $strallow;
|
|
|
|
public function __construct($context, $roleid) {
|
|
parent::__construct($context, $roleid);
|
|
$this->displaypermissions = array(CAP_ALLOW => $this->allpermissions[CAP_ALLOW]);
|
|
$this->stradvmessage = get_string('useshowadvancedtochange', 'role');
|
|
$this->strallow = $this->strperms[$this->allpermissions[CAP_ALLOW]];
|
|
}
|
|
|
|
protected function print_show_hide_advanced_button() {
|
|
echo '<div class="advancedbutton">';
|
|
echo '<input type="submit" name="toggleadvanced" value="' . get_string('showadvanced', 'form') . '" />';
|
|
echo '</div>';
|
|
}
|
|
|
|
protected function add_permission_cells($capability) {
|
|
$perm = $this->permissions[$capability->name];
|
|
$permname = $this->allpermissions[$perm];
|
|
$defaultperm = $this->allpermissions[$this->parentpermissions[$capability->name]];
|
|
echo '<td class="' . $permname . '">';
|
|
if ($perm == CAP_ALLOW || $perm == CAP_INHERIT) {
|
|
$checked = '';
|
|
if ($perm == CAP_ALLOW) {
|
|
$checked = 'checked="checked" ';
|
|
}
|
|
echo '<input type="hidden" name="' . $capability->name . '" value="' . CAP_INHERIT . '" />';
|
|
echo '<label><input type="checkbox" name="' . $capability->name .
|
|
'" value="' . CAP_ALLOW . '" ' . $checked . '/> ' . $this->strallow . '</label>';
|
|
} else {
|
|
echo '<input type="hidden" name="' . $capability->name . '" value="' . $perm . '" />';
|
|
echo $this->strperms[$permname] . '<span class="note">' . $this->stradvmessage . '</span>';
|
|
}
|
|
echo '</td>';
|
|
}
|
|
}
|
|
class view_role_definition_table extends define_role_table_advanced {
|
|
public function __construct($context, $roleid) {
|
|
parent::__construct($context, $roleid);
|
|
$this->displaypermissions = array(CAP_ALLOW => $this->allpermissions[CAP_ALLOW]);
|
|
$this->disabled = 'disabled="disabled" ';
|
|
}
|
|
|
|
public function save_changes() {
|
|
throw new moodle_exception('invalidaccess');
|
|
}
|
|
|
|
protected function get_name_field($id) {
|
|
return strip_tags(format_string($this->role->name));
|
|
}
|
|
|
|
protected function get_shortname_field($id) {
|
|
return $this->role->shortname;
|
|
}
|
|
|
|
protected function get_description_field($id) {
|
|
return format_text($this->role->description, FORMAT_HTML);
|
|
}
|
|
|
|
protected function get_legacy_type_field($id) {
|
|
if (empty($this->role->legacytype)) {
|
|
return get_string('none');
|
|
} else {
|
|
return get_string('legacy:'.$this->role->legacytype, 'role');
|
|
}
|
|
}
|
|
|
|
protected function print_show_hide_advanced_button() {
|
|
// Do nothing.
|
|
}
|
|
|
|
protected function add_permission_cells($capability) {
|
|
$perm = $this->permissions[$capability->name];
|
|
$permname = $this->allpermissions[$perm];
|
|
$defaultperm = $this->allpermissions[$this->parentpermissions[$capability->name]];
|
|
if ($permname != $defaultperm) {
|
|
$default = get_string('defaultx', 'role', $this->strperms[$defaultperm]);
|
|
} else {
|
|
$default = " ";
|
|
}
|
|
echo '<td class="' . $permname . '">' . $this->strperms[$permname] . '<span class="note">' .
|
|
$default . '</span></td>';
|
|
|
|
}
|
|
}
|
|
|
|
class override_permissions_table_advanced extends capability_table_with_risks {
|
|
protected $strnotset;
|
|
protected $haslockedcapabiltites = false;
|
|
|
|
/**
|
|
* Constructor
|
|
*
|
|
* This method loads loads all the information about the current state of
|
|
* the overrides, then updates that based on any submitted data. It also
|
|
* works out which capabilities should be locked for this user.
|
|
*
|
|
* @param object $context the context this table relates to.
|
|
* @param integer $roleid the role being overridden.
|
|
* @param boolean $safeoverridesonly If true, the user is only allowed to override
|
|
* capabilities with no risks.
|
|
*/
|
|
public function __construct($context, $roleid, $safeoverridesonly) {
|
|
parent::__construct($context, 'overriderolestable', $roleid);
|
|
$this->displaypermissions = $this->allpermissions;
|
|
$this->strnotset = get_string('notset', 'role');
|
|
|
|
/// Determine which capabilities should be locked.
|
|
if ($safeoverridesonly) {
|
|
foreach ($this->capabilities as $capid => $cap) {
|
|
if (!is_safe_capability($cap)) {
|
|
$this->capabilities[$capid]->locked = true;
|
|
$this->haslockedcapabiltites = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
protected function load_parent_permissions() {
|
|
global $DB;
|
|
|
|
/// Get the capabiltites from the parent context, so that can be shown in the interface.
|
|
$parentcontext = get_context_instance_by_id(get_parent_contextid($this->context));
|
|
$this->parentpermissions = role_context_capabilities($this->roleid, $parentcontext);
|
|
}
|
|
|
|
public function has_locked_capabiltites() {
|
|
return $this->haslockedcapabiltites;
|
|
}
|
|
|
|
protected function skip_row($capability) {
|
|
return is_legacy($capability->name);
|
|
}
|
|
|
|
protected function add_permission_cells($capability) {
|
|
$disabled = '';
|
|
if ($capability->locked || $this->parentpermissions[$capability->name] == CAP_PROHIBIT) {
|
|
$disabled = ' disabled="disabled"';
|
|
}
|
|
|
|
/// One cell for each possible permission.
|
|
foreach ($this->displaypermissions as $perm => $permname) {
|
|
$strperm = $this->strperms[$permname];
|
|
$extraclass = '';
|
|
if ($perm != CAP_INHERIT && $perm == $this->parentpermissions[$capability->name]) {
|
|
$extraclass = ' capcurrent';
|
|
}
|
|
$checked = '';
|
|
if ($this->permissions[$capability->name] == $perm) {
|
|
$checked = 'checked="checked" ';
|
|
}
|
|
echo '<td class="' . $permname . $extraclass . '">';
|
|
echo '<label><input type="radio" name="' . $capability->name .
|
|
'" value="' . $perm . '" ' . $checked . $disabled . '/> ';
|
|
if ($perm == CAP_INHERIT) {
|
|
$inherited = $this->parentpermissions[$capability->name];
|
|
if ($inherited == CAP_INHERIT) {
|
|
$inherited = $this->strnotset;
|
|
} else {
|
|
$inherited = $this->strperms[$this->allpermissions[$inherited]];
|
|
}
|
|
$strperm .= ' (' . $inherited . ')';
|
|
}
|
|
echo '<span class="note">' . $strperm . '</span>';
|
|
echo '</label></td>';
|
|
}
|
|
}
|
|
}
|
|
|
|
class override_permissions_table_basic extends override_permissions_table_advanced {
|
|
protected $stradvmessage;
|
|
|
|
public function __construct($context, $roleid, $safeoverridesonly) {
|
|
parent::__construct($context, $roleid, $safeoverridesonly);
|
|
unset($this->displaypermissions[CAP_PROHIBIT]);
|
|
$this->stradvmessage = get_string('useshowadvancedtochange', 'role');
|
|
}
|
|
|
|
protected function skip_row($capability) {
|
|
return is_legacy($capability->name) || $capability->locked;
|
|
}
|
|
|
|
protected function add_permission_cells($capability) {
|
|
if ($this->permissions[$capability->name] == CAP_PROHIBIT) {
|
|
$permname = $this->allpermissions[CAP_PROHIBIT];
|
|
echo '<td class="' . $permname . '" colspan="' . count($this->displaypermissions) . '">';
|
|
echo '<input type="hidden" name="' . $capability->name . '" value="' . CAP_PROHIBIT . '" />';
|
|
echo $this->strperms[$permname] . '<span class="note">' . $this->stradvmessage . '</span>';
|
|
echo '</td>';
|
|
} else {
|
|
parent::add_permission_cells($capability);
|
|
}
|
|
}
|
|
}
|
|
|
|
// User selectors for managing role assignments ================================
|
|
|
|
/**
|
|
* Base class to avoid duplicating code.
|
|
*/
|
|
abstract class role_assign_user_selector_base extends user_selector_base {
|
|
const MAX_USERS_PER_PAGE = 100;
|
|
|
|
protected $roleid;
|
|
protected $context;
|
|
|
|
/**
|
|
* @param string $name control name
|
|
* @param array $options should have two elements with keys groupid and courseid.
|
|
*/
|
|
public function __construct($name, $options) {
|
|
global $CFG;
|
|
parent::__construct($name, $options);
|
|
$this->roleid = $options['roleid'];
|
|
if (isset($options['context'])) {
|
|
$this->context = $options['context'];
|
|
} else {
|
|
$this->context = get_context_instance_by_id($options['contextid']);
|
|
}
|
|
require_once($CFG->dirroot . '/group/lib.php');
|
|
}
|
|
|
|
protected function get_options() {
|
|
global $CFG;
|
|
$options = parent::get_options();
|
|
$options['file'] = $CFG->admin . '/roles/lib.php';
|
|
$options['roleid'] = $this->roleid;
|
|
$options['contextid'] = $this->context->id;
|
|
return $options;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* User selector subclass for the list of potential users on the assign roles page,
|
|
* when we are assigning in a context below the course level. (CONTEXT_MODULE and
|
|
* some CONTEXT_BLOCK).
|
|
*
|
|
* In this case we replicate part of get_users_by_capability() get the users
|
|
* with moodle/course:view (or moodle/site:doanything). We can't use
|
|
* get_users_by_capability() becuase
|
|
* 1) get_users_by_capability() does not deal with searching by name
|
|
* 2) exceptions array can be potentially large for large courses
|
|
*/
|
|
class potential_assignees_below_course extends role_assign_user_selector_base {
|
|
public function find_users($search) {
|
|
global $DB;
|
|
|
|
// Get roles with some assignement to the 'moodle/course:view' capability.
|
|
$possibleroles = get_roles_with_capability('moodle/course:view', CAP_ALLOW, $this->context);
|
|
if (empty($possibleroles)) {
|
|
// If there aren't any, we are done.
|
|
return array();
|
|
}
|
|
|
|
// Now exclude the admin roles, and check the actual permission on
|
|
// 'moodle/course:view' to make sure it is allow.
|
|
$doanythingroles = get_roles_with_capability('moodle/site:doanything',
|
|
CAP_ALLOW, get_context_instance(CONTEXT_SYSTEM));
|
|
$validroleids = array();
|
|
|
|
foreach ($possibleroles as $possiblerole) {
|
|
if (isset($doanythingroles[$possiblerole->id])) {
|
|
continue;
|
|
}
|
|
|
|
if ($caps = role_context_capabilities($possiblerole->id, $this->context, 'moodle/course:view')) { // resolved list
|
|
if (isset($caps['moodle/course:view']) && $caps['moodle/course:view'] > 0) { // resolved capability > 0
|
|
$validroleids[] = $possiblerole->id;
|
|
}
|
|
}
|
|
}
|
|
|
|
// If there are no valid roles, we are done.
|
|
if (!$validroleids) {
|
|
return array();
|
|
}
|
|
|
|
// Now we have to go to the database.
|
|
list($wherecondition, $params) = $this->search_sql($search, 'u');
|
|
if ($wherecondition) {
|
|
$wherecondition = ' AND ' . $wherecondition;
|
|
}
|
|
$roleids = '('.implode(',', $validroleids).')';
|
|
|
|
$fields = 'SELECT DISTINCT ' . $this->required_fields_sql('u');
|
|
$countfields = 'SELECT COUNT(DISTINCT u.id)';
|
|
|
|
$sql = " FROM {user} u
|
|
JOIN {role_assignments} ra ON ra.userid = u.id
|
|
JOIN {role} r ON r.id = ra.roleid
|
|
WHERE ra.contextid " . get_related_contexts_string($this->context)."
|
|
$wherecondition
|
|
AND ra.roleid IN $roleids
|
|
AND u.id NOT IN (
|
|
SELECT u.id
|
|
FROM {role_assignments} r, {user} u
|
|
WHERE r.contextid = ?
|
|
AND u.id = r.userid
|
|
AND r.roleid = ?)";
|
|
$order = ' ORDER BY lastname ASC, firstname ASC';
|
|
|
|
$params[] = $this->context->id;
|
|
$params[] = $this->roleid;
|
|
|
|
// Check to see if there are too many to show sensibly.
|
|
if (!$this->is_validating()) {
|
|
$potentialmemberscount = $DB->count_records_sql($countfields . $sql, $params);
|
|
if ($potentialmemberscount > role_assign_user_selector_base::MAX_USERS_PER_PAGE) {
|
|
return $this->too_many_results($search, $potentialmemberscount);
|
|
}
|
|
}
|
|
|
|
// If not, show them.
|
|
$availableusers = $DB->get_records_sql($fields . $sql . $order, $params);
|
|
|
|
if (empty($availableusers)) {
|
|
return array();
|
|
}
|
|
|
|
if ($search) {
|
|
$groupname = get_string('potusersmatching', 'role', $search);
|
|
} else {
|
|
$groupname = get_string('potusers', 'role');
|
|
}
|
|
|
|
return array($groupname => $availableusers);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* User selector subclass for the list of potential users on the assign roles page,
|
|
* when we are assigning in a context at or above the course level. In this case we
|
|
* show all the users in the system who do not already have the role.
|
|
*/
|
|
class potential_assignees_course_and_above extends role_assign_user_selector_base {
|
|
public function find_users($search) {
|
|
global $DB;
|
|
|
|
list($wherecondition, $params) = $this->search_sql($search, '');
|
|
|
|
$fields = 'SELECT ' . $this->required_fields_sql('');
|
|
$countfields = 'SELECT COUNT(1)';
|
|
|
|
$sql = " FROM {user}
|
|
WHERE $wherecondition
|
|
AND id NOT IN (
|
|
SELECT u.id
|
|
FROM {role_assignments} r, {user} u
|
|
WHERE r.contextid = ?
|
|
AND u.id = r.userid
|
|
AND r.roleid = ?)";
|
|
$order = ' ORDER BY lastname ASC, firstname ASC';
|
|
|
|
$params[] = $this->context->id;
|
|
$params[] = $this->roleid;
|
|
|
|
if (!$this->is_validating()) {
|
|
$potentialmemberscount = $DB->count_records_sql($countfields . $sql, $params);
|
|
if ($potentialmemberscount > role_assign_user_selector_base::MAX_USERS_PER_PAGE) {
|
|
return $this->too_many_results($search, $potentialmemberscount);
|
|
}
|
|
}
|
|
|
|
$availableusers = $DB->get_records_sql($fields . $sql . $order, $params);
|
|
|
|
if (empty($availableusers)) {
|
|
return array();
|
|
}
|
|
|
|
if ($search) {
|
|
$groupname = get_string('potusersmatching', 'role', $search);
|
|
} else {
|
|
$groupname = get_string('potusers', 'role');
|
|
}
|
|
|
|
return array($groupname => $availableusers);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* User selector subclass for the list of users who already have the role in
|
|
* question on the assign roles page.
|
|
*/
|
|
class existing_role_holders extends role_assign_user_selector_base {
|
|
protected $strhidden;
|
|
|
|
public function __construct($name, $options) {
|
|
parent::__construct($name, $options);
|
|
$this->strhidden = get_string('hiddenassign');
|
|
}
|
|
|
|
public function find_users($search) {
|
|
global $DB;
|
|
|
|
list($wherecondition, $params) = $this->search_sql($search, 'u');
|
|
list($ctxcondition, $ctxparams) = $DB->get_in_or_equal(get_parent_contexts($this->context, true));
|
|
$params = array_merge($params, $ctxparams);
|
|
$params[] = $this->roleid;
|
|
|
|
$sql = "SELECT ra.id as raid," . $this->required_fields_sql('u') . ",ra.hidden,ra.contextid
|
|
FROM {role_assignments} ra
|
|
JOIN {user} u ON u.id = ra.userid
|
|
JOIN {context} ctx ON ra.contextid = ctx.id
|
|
WHERE
|
|
$wherecondition AND
|
|
ctx.id $ctxcondition AND
|
|
ra.roleid = ?
|
|
ORDER BY ctx.depth DESC, u.lastname, u.firstname";
|
|
$contextusers = $DB->get_records_sql($sql, $params);
|
|
|
|
// No users at all.
|
|
if (empty($contextusers)) {
|
|
return array();
|
|
}
|
|
|
|
// We have users. Out put them in groups by context depth.
|
|
// To help the loop below, tack a dummy user on the end of the results
|
|
// array, to trigger output of the last group.
|
|
$dummyuser = new stdClass;
|
|
$dummyuser->contextid = 0;
|
|
$dummyuser->id = 0;
|
|
$contextusers[] = $dummyuser;
|
|
$results = array(); // The results array we are building up.
|
|
$doneusers = array(); // Ensures we only list each user at most once.
|
|
$currentcontextid = $this->context->id;
|
|
$currentgroup = array();
|
|
foreach ($contextusers as $user) {
|
|
if (isset($doneusers[$user->id])) {
|
|
continue;
|
|
}
|
|
$doneusers[$user->id] = 1;
|
|
if ($user->contextid != $currentcontextid) {
|
|
// We have got to the end of the previous group. Add it to the results array.
|
|
if ($currentcontextid == $this->context->id) {
|
|
$groupname = $this->this_con_group_name($search, count($currentgroup));
|
|
} else {
|
|
$groupname = $this->parent_con_group_name($search, $currentcontextid);
|
|
}
|
|
$results[$groupname] = $currentgroup;
|
|
// Get ready for the next group.
|
|
$currentcontextid = $user->contextid;
|
|
$currentgroup = array();
|
|
}
|
|
// Add this user to the group we are building up.
|
|
unset($user->contextid);
|
|
if ($currentcontextid != $this->context->id) {
|
|
$user->disabled = true;
|
|
}
|
|
$currentgroup[$user->id] = $user;
|
|
}
|
|
|
|
return $results;
|
|
}
|
|
|
|
protected function this_con_group_name($search, $numusers) {
|
|
if ($this->context->contextlevel == CONTEXT_SYSTEM) {
|
|
// Special case in the System context.
|
|
if ($search) {
|
|
return get_string('extusersmatching', 'role', $search);
|
|
} else {
|
|
return get_string('extusers', 'role');
|
|
}
|
|
}
|
|
$contexttype = get_contextlevel_name($this->context->contextlevel);
|
|
if ($search) {
|
|
$a = new stdClass;
|
|
$a->search = $search;
|
|
$a->contexttype = $contexttype;
|
|
if ($numusers) {
|
|
return get_string('usersinthisxmatching', 'role', $a);
|
|
} else {
|
|
return get_string('noneinthisxmatching', 'role', $a);
|
|
}
|
|
} else {
|
|
if ($numusers) {
|
|
return get_string('usersinthisx', 'role', $contexttype);
|
|
} else {
|
|
return get_string('noneinthisx', 'role', $contexttype);
|
|
}
|
|
}
|
|
}
|
|
|
|
protected function parent_con_group_name($search, $contextid) {
|
|
$context = get_context_instance_by_id($contextid);
|
|
$contextname = print_context_name($context, true, true);
|
|
if ($search) {
|
|
$a = new stdClass;
|
|
$a->contextname = $contextname;
|
|
$a->search = $search;
|
|
return get_string('usersfrommatching', 'role', $a);
|
|
} else {
|
|
return get_string('usersfrom', 'role', $contextname);
|
|
}
|
|
}
|
|
|
|
// Override to add (hidden) to hidden role assignments.
|
|
public function output_user($user) {
|
|
$output = parent::output_user($user);
|
|
if ($user->hidden) {
|
|
$output .= ' (' . $this->strhidden . ')';
|
|
}
|
|
return $output;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A special subclass to use when unassigning admins at site level. Disables
|
|
* the option for admins to unassign themselves.
|
|
*/
|
|
class existing_role_holders_site_admin extends existing_role_holders {
|
|
public function find_users($search) {
|
|
global $USER;
|
|
$groupedusers = parent::find_users($search);
|
|
foreach ($groupedusers as $group) {
|
|
foreach ($group as &$user) {
|
|
if ($user->id == $USER->id) {
|
|
$user->disabled = true;
|
|
}
|
|
}
|
|
}
|
|
return $groupedusers;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Base class for managing the data in the grid of checkboxes on the role allow
|
|
* allow/overrides/switch editing pages (allow.php).
|
|
*/
|
|
abstract class role_allow_role_page {
|
|
protected $tablename;
|
|
protected $targetcolname;
|
|
protected $roles;
|
|
protected $allowed = null;
|
|
|
|
/**
|
|
* @param string $tablename the table where our data is stored.
|
|
* @param string $targetcolname the name of the target role id column.
|
|
*/
|
|
public function __construct($tablename, $targetcolname) {
|
|
$this->tablename = $tablename;
|
|
$this->targetcolname = $targetcolname;
|
|
$this->load_required_roles();
|
|
}
|
|
|
|
/**
|
|
* Load information about all the roles we will need information about.
|
|
*/
|
|
protected function load_required_roles() {
|
|
/// Get all roles
|
|
$this->roles = get_all_roles();
|
|
role_fix_names($this->roles, get_context_instance(CONTEXT_SYSTEM), ROLENAME_ORIGINAL);
|
|
}
|
|
|
|
/**
|
|
* Update the data with the new settings submitted by the user.
|
|
*/
|
|
public function process_submission() {
|
|
global $DB;
|
|
/// Delete all records, then add back the ones that should be allowed.
|
|
$DB->delete_records($this->tablename);
|
|
foreach ($this->roles as $fromroleid => $notused) {
|
|
foreach ($this->roles as $targetroleid => $alsonotused) {
|
|
if (optional_param('s_' . $fromroleid . '_' . $targetroleid, false, PARAM_BOOL)) {
|
|
$this->set_allow($fromroleid, $targetroleid);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Set one allow in the database.
|
|
* @param integer $fromroleid
|
|
* @param integer $targetroleid
|
|
*/
|
|
protected abstract function set_allow($fromroleid, $targetroleid);
|
|
|
|
/**
|
|
* Load the current allows from the database.
|
|
*/
|
|
public function load_current_settings() {
|
|
global $DB;
|
|
/// Load the current settings
|
|
$this->allowed = array();
|
|
foreach ($this->roles as $role) {
|
|
// Make an array $role->id => false. This is probalby too clever for its own good.
|
|
$this->allowed[$role->id] = array_combine(array_keys($this->roles), array_fill(0, count($this->roles), false));
|
|
}
|
|
$rs = $DB->get_recordset($this->tablename);
|
|
foreach ($rs as $allow) {
|
|
$this->allowed[$allow->roleid][$allow->{$this->targetcolname}] = true;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param integer $targetroleid a role id.
|
|
* @return boolean whether the user should be allowed to select this role as a
|
|
* target role.
|
|
*/
|
|
protected function is_allowed_target($targetroleid) {
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* @return object a $table structure that can be passed to print_table, containing
|
|
* one cell for each checkbox.
|
|
*/
|
|
public function get_table() {
|
|
$table = new html_table();
|
|
$table->tablealign = 'center';
|
|
$table->cellpadding = 5;
|
|
$table->cellspacing = 0;
|
|
$table->width = '90%';
|
|
$table->align = array('left');
|
|
$table->rotateheaders = true;
|
|
$table->head = array(' ');
|
|
$table->colclasses = array('');
|
|
|
|
/// Add role name headers.
|
|
foreach ($this->roles as $targetrole) {
|
|
$table->head[] = $targetrole->localname;
|
|
$table->align[] = 'left';
|
|
if ($this->is_allowed_target($targetrole->id)) {
|
|
$table->colclasses[] = '';
|
|
} else {
|
|
$table->colclasses[] = 'dimmed_text';
|
|
}
|
|
}
|
|
|
|
/// Now the rest of the table.
|
|
foreach ($this->roles as $fromrole) {
|
|
$row = array($fromrole->localname);
|
|
foreach ($this->roles as $targetrole) {
|
|
$checked = '';
|
|
$disabled = '';
|
|
if ($this->allowed[$fromrole->id][$targetrole->id]) {
|
|
$checked = 'checked="checked" ';
|
|
}
|
|
if (!$this->is_allowed_target($targetrole->id)) {
|
|
$disabled = 'disabled="disabled" ';
|
|
}
|
|
$name = 's_' . $fromrole->id . '_' . $targetrole->id;
|
|
$tooltip = $this->get_cell_tooltip($fromrole, $targetrole);
|
|
$row[] = '<input type="checkbox" name="' . $name . '" id="' . $name .
|
|
'" title="' . $tooltip . '" value="1" ' . $checked . $disabled . '/>' .
|
|
'<label for="' . $name . '" class="accesshide">' . $tooltip . '</label>';
|
|
}
|
|
$table->data[] = $row;
|
|
}
|
|
|
|
return $table;
|
|
}
|
|
|
|
/**
|
|
* Snippet of text displayed above the table, telling the admin what to do.
|
|
* @return unknown_type
|
|
*/
|
|
public abstract function get_intro_text();
|
|
}
|
|
|
|
/**
|
|
* Subclass of role_allow_role_page for the Allow assigns tab.
|
|
*/
|
|
class role_allow_assign_page extends role_allow_role_page {
|
|
public function __construct() {
|
|
parent::__construct('role_allow_assign', 'allowassign');
|
|
}
|
|
|
|
protected function set_allow($fromroleid, $targetroleid) {
|
|
allow_assign($fromroleid, $targetroleid);
|
|
}
|
|
|
|
protected function get_cell_tooltip($fromrole, $targetrole) {
|
|
$a = new stdClass;
|
|
$a->fromrole = $fromrole->localname;
|
|
$a->targetrole = $targetrole->localname;
|
|
return get_string('allowroletoassign', 'role', $a);
|
|
}
|
|
|
|
public function get_intro_text() {
|
|
return get_string('configallowassign', 'admin');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Subclass of role_allow_role_page for the Allow overrides tab.
|
|
*/
|
|
class role_allow_override_page extends role_allow_role_page {
|
|
public function __construct() {
|
|
parent::__construct('role_allow_override', 'allowoverride');
|
|
}
|
|
|
|
protected function set_allow($fromroleid, $targetroleid) {
|
|
allow_override($fromroleid, $targetroleid);
|
|
}
|
|
|
|
protected function get_cell_tooltip($fromrole, $targetrole) {
|
|
$a = new stdClass;
|
|
$a->fromrole = $fromrole->localname;
|
|
$a->targetrole = $targetrole->localname;
|
|
return get_string('allowroletooverride', 'role', $a);
|
|
}
|
|
|
|
public function get_intro_text() {
|
|
return get_string('configallowoverride2', 'admin');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Subclass of role_allow_role_page for the Allow switches tab.
|
|
*/
|
|
class role_allow_switch_page extends role_allow_role_page {
|
|
protected $allowedtargetroles;
|
|
|
|
public function __construct() {
|
|
parent::__construct('role_allow_switch', 'allowswitch');
|
|
}
|
|
|
|
protected function load_required_roles() {
|
|
parent::load_required_roles();
|
|
$this->allowedtargetroles = get_allowed_switchable_roles();
|
|
}
|
|
|
|
protected function set_allow($fromroleid, $targetroleid) {
|
|
allow_switch($fromroleid, $targetroleid);
|
|
}
|
|
|
|
protected function is_allowed_target($targetroleid) {
|
|
return isset($this->allowedtargetroles[$targetroleid]);
|
|
}
|
|
|
|
protected function get_cell_tooltip($fromrole, $targetrole) {
|
|
$a = new stdClass;
|
|
$a->fromrole = $fromrole->localname;
|
|
$a->targetrole = $targetrole->localname;
|
|
return get_string('allowroletoswitch', 'role', $a);
|
|
}
|
|
|
|
public function get_intro_text() {
|
|
return get_string('configallowswitch', 'admin');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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 potential_assignees_below_course object. Otherwise
|
|
* return a potential_assignees_course_and_above.
|
|
*
|
|
* @param $context a context.
|
|
* @param $name passed to user selector constructor.
|
|
* @param $options to user selector constructor.
|
|
* @return user_selector_base an appropriate user selector.
|
|
*/
|
|
function roles_get_potential_user_selector($context, $name, $options) {
|
|
$blockinsidecourse = false;
|
|
if ($context->contextlevel == CONTEXT_BLOCK) {
|
|
$parentcontext = get_context_instance_by_id(get_parent_contextid($context));
|
|
$blockinsidecourse = in_array($parentcontext->contextlevel, array(CONTEXT_MODULE, CONTEXT_COURSE));
|
|
}
|
|
|
|
if (($context->contextlevel == CONTEXT_MODULE || $blockinsidecourse) &&
|
|
!is_inside_frontpage($context)) {
|
|
$potentialuserselector = new potential_assignees_below_course('addselect', $options);
|
|
} else {
|
|
$potentialuserselector = new potential_assignees_course_and_above('addselect', $options);
|
|
}
|
|
return $potentialuserselector;
|
|
}
|
|
|