switch roles: MDL-18132 New Role allow switches page finished.

I also set up a reasonable set of default allows, including setting up some sensible allow_overrides, whatever the rant in MDL-15841 says.
This commit is contained in:
tjhunt 2009-03-25 04:20:57 +00:00
parent 91eb445ca0
commit c468795ce9
8 changed files with 199 additions and 50 deletions

View File

@ -24,12 +24,9 @@
///////////////////////////////////////////////////////////////////////////
/**
* this page defines what roles can access (grant user that role and override that roles'
* capabilities in different context. For example, we can say that Teachers can only grant
* student role or modify student role's capabilities. Note that you need both the right
* capability moodle/role:assign or moodle/role:manage and this database table roles_deny_grant
* to be able to grant roles. If a user has moodle/role:manage at site level assignment
* then he can modify the roles_allow_assign table via this interface.
* this page defines what roles can do things with other roles. For example
* which roles can assign which other roles, or which roles can switch to
* which other roles.
*
* @license http://www.gnu.org/copyleft/gpl.html GNU Public License
* @package roles

View File

@ -1251,8 +1251,8 @@ class existing_role_holders_site_admin extends existing_role_holders {
}
/**
* Base class to hold all the code shared between the role allow assign/override/switch
* pages.
* 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;
@ -1260,6 +1260,10 @@ abstract class role_allow_role_page {
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;
@ -1267,7 +1271,7 @@ abstract class role_allow_role_page {
}
/**
* Load all the roles we will need information about.
* Load information about all the roles we will need information about.
*/
protected function load_required_roles() {
/// Get all roles
@ -1278,7 +1282,9 @@ abstract class role_allow_role_page {
/**
* Update the data with the new settings submitted by the user.
*/
public function process_submission() { /// Delete all records, then add back the ones that should be allowed.
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) {
@ -1291,11 +1297,14 @@ abstract class role_allow_role_page {
/**
* Set one allow in the database.
* @param $fromroleid
* @param $targetroleid
* @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
@ -1310,34 +1319,57 @@ abstract class role_allow_role_page {
}
}
/**
* @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 stdClass;
$table->tablealign = 'center';
$table->cellpadding = 5;
$table->cellspacing = 0;
$table->width = '90%';
$table->align[] = 'left';
$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"';
} else {
$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 . ' />' .
$row[] = '<input type="checkbox" name="' . $name . '" id="' . $name .
'" title="' . $tooltip . '" value="1"' . $checked . $disabled . ' />' .
'<label for="' . $name . '" class="accesshide">' . $tooltip . '</label>';
}
$table->data[] = $row;
@ -1346,9 +1378,16 @@ abstract class role_allow_role_page {
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');
@ -1370,6 +1409,9 @@ class role_allow_assign_page extends role_allow_role_page {
}
}
/**
* 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');
@ -1391,4 +1433,39 @@ class role_allow_override_page extends role_allow_role_page {
}
}
/**
* 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');
}
}
?>

View File

@ -71,6 +71,7 @@ $string['configallowemailaddresses'] = 'If you want to restrict all new email ad
$string['configallowobjectembed'] = 'As a default security measure, normal users are not allowed to embed multimedia (like Flash) within texts using explicit EMBED and OBJECT tags in their HTML (although it can still be done safely using the mediaplugins filter). If you wish to allow these tags then enable this option.';
$string['configallowoverride'] = 'You can allow people with the roles on the left side to override some of the column roles';
$string['configallowoverride2'] = 'Select which role(s) can be overridden by each role in the left column.<br />Note that these settings only apply to users who have either the capability moodle/role:override or the capability moodle/role:safeoverride allowed.';
$string['configallowswitch'] = 'Select which roles a user may switch to, based on which roles they already have. In addition to an entry in this table, a user must also have the moodle/role:switchroles capability to be able to switch.<br />Note that it is only possible to switch to roles that have the moodle/course:view capability, and that do not have the moodle/site:doanything capability, so some columns in this table are disabled.';
$string['configallowunenroll'] = 'If this is set \'Yes\', then students are allowed to unenrol themselves from courses whenever they like. Otherwise they are not allowed, and this process will be solely controlled by the teachers and administrators.';
$string['configallowuserblockhiding'] = 'Do you want to allow users to hide/show side blocks throughout this site? This feature uses Javascript and cookies to remember the state of each collapsible block, and only affects the user\'s own view.';
$string['configallowusermailcharset'] = 'Enabling this, every user in the site will be able to specify his own charset for email.';

View File

@ -11,6 +11,7 @@ $string['allowed'] = 'Allowed';
$string['allowoverride'] = 'Allow role overrides';
$string['allowroletoassign'] = 'Allow users with role $a->fromrole to assign the role $a->targetrole';
$string['allowroletooverride'] = 'Allow users with role $a->fromrole to override the role $a->targetrole';
$string['allowroletoswitch'] = 'Allow users with role $a->fromrole to switch roles to the role $a->targetrole';
$string['allowswitch'] = 'Allow role switches';
$string['allsiteusers'] = 'All site users';
$string['assignanotherrole'] = 'Assign another role';

View File

@ -4221,7 +4221,7 @@ function get_user_roles($context, $userid=0, $checkparentcontexts=true, $order='
}
/**
* Creates a record in the allow_override table
* Creates a record in the role_allow_override table
* @param int sroleid - source roleid
* @param int troleid - target roleid
* @return int - id or false
@ -4232,11 +4232,11 @@ function allow_override($sroleid, $troleid) {
$record = new object();
$record->roleid = $sroleid;
$record->allowoverride = $troleid;
return $DB->insert_record('role_allow_override', $record);
$DB->insert_record('role_allow_override', $record);
}
/**
* Creates a record in the allow_assign table
* Creates a record in the role_allow_assign table
* @param int sroleid - source roleid
* @param int troleid - target roleid
* @return int - id or false
@ -4247,7 +4247,22 @@ function allow_assign($fromroleid, $targetroleid) {
$record = new object;
$record->roleid = $fromroleid;
$record->allowassign = $targetroleid;
return $DB->insert_record('role_allow_assign', $record);
$DB->insert_record('role_allow_assign', $record);
}
/**
* Creates a record in the role_allow_switch table
* @param int sroleid - source roleid
* @param int troleid - target roleid
* @return int - id or false
*/
function allow_switch($fromroleid, $targetroleid) {
global $DB;
$record = new object;
$record->roleid = $fromroleid;
$record->allowswitch = $targetroleid;
$DB->insert_record('role_allow_switch', $record);
}
/**
@ -4364,7 +4379,7 @@ function get_switchable_roles($context) {
$extrajoins = "JOIN {role_allow_switch} ras ON ras.allowswitch = rc.roleid
JOIN {role_assignments} ra ON ra.roleid = ras.roleid";
$extrawhere = "AND ra.userid = :userid
AND ra.contextid IN ($contexts)";
AND ra.contextid IN ($contexts)";
$params['userid'] = $USER->id;
}
@ -4375,11 +4390,12 @@ function get_switchable_roles($context) {
FROM {role_capabilities} rc
$extrajoins
WHERE rc.capability = :viewcap
AND rc.permission = " . CAP_ALLOW . "
AND rc.contextid = :syscontextid
$extrawhere
AND NOT EXISTS (
SELECT 1 FROM {role_capabilities} irc WHERE irc.roleid = rc.roleid AND
irc.capability = :anythingcap)
irc.capability = :anythingcap AND irc.permission = " . CAP_ALLOW . ")
) idlist
JOIN {role} r ON r.id = idlist.roleid
ORDER BY r.sortorder";
@ -4391,6 +4407,34 @@ function get_switchable_roles($context) {
return role_fix_names($rolenames, $context, ROLENAME_ALIAS);
}
/**
* Get an array of role ids that might possibly be the target of a switchrole.
* Our policy is that you cannot switch to a role with moodle/site:doanything
* and you can only switch to a role with moodle/course:view. This method returns
* a list of those role ids.
*
* @return an array whose keys are the allowed role ids.
*/
function get_allowed_switchable_roles() {
global $DB;
$systemcontext = get_context_instance(CONTEXT_SYSTEM);
$query = "
SELECT DISTINCT rc.roleid, 1
FROM {role_capabilities} rc
WHERE rc.capability = :viewcap
AND rc.permission = " . CAP_ALLOW . "
AND rc.contextid = :syscontextid
AND NOT EXISTS (
SELECT 1 FROM {role_capabilities} irc WHERE irc.roleid = rc.roleid AND
irc.capability = :anythingcap AND irc.permission = " . CAP_ALLOW . ")";
$params = array('syscontextid' => $systemcontext->id,
'viewcap' => 'moodle/course:view', 'anythingcap' => 'moodle/site:doanything');
return $DB->get_records_sql_menu($query, $params);
}
/**
* Gets a list of roles that this user can override in this context.
*

View File

@ -184,21 +184,24 @@ function xmldb_main_install() {
role_assign($guestrole, $guest->id, 0, $syscontext->id);
role_assign($adminrole, $admin->id, 0, $syscontext->id);
/// Insert the correct records for legacy roles
allow_assign($coursecreatorrole, $noneditteacherrole);
allow_assign($coursecreatorrole, $editteacherrole);
allow_assign($coursecreatorrole, $studentrole);
allow_assign($coursecreatorrole, $guestrole);
/// Default allow assign/override/switch.
$defaultallows = array(
$coursecreatorrole => $noneditteacherrole,
$coursecreatorrole => $editteacherrole,
$coursecreatorrole => $studentrole,
$coursecreatorrole => $guestrole,
allow_assign($editteacherrole, $noneditteacherrole);
allow_assign($editteacherrole, $studentrole);
allow_assign($editteacherrole, $guestrole);
$editteacherrole => $noneditteacherrole,
$editteacherrole => $studentrole,
$editteacherrole => $guestrole,
);
/// Set up default allow override matrix
//See MDL-15841 TODO FOR MOODLE 2.0 XXX
//allow_override($editteacherrole, $noneditteacherrole);
//allow_override($editteacherrole, $studentrole);
//allow_override($editteacherrole, $guestrole);
foreach ($defaultallows as $fromroleid => $toroleid) {
allow_assign($fromroleid, $toroleid);
allow_override($fromroleid, $toroleid); // There is a rant about this in MDL-15841.
allow_switch($fromroleid, $toroleid);
}
allow_switch($noneditteacherrole, $studentrole);
/// Set up the context levels where you can assign each role.
set_role_contextlevels($adminrole, get_default_contextlevels('admin'));

View File

@ -253,7 +253,6 @@ class accesslib_test extends UnitTestCaseUsingDatabase {
$this->create_test_tables($tablenames, 'lib');
$this->switch_to_test_db();
$saveduserid = $USER->id;
// Ensure SYSCONTEXTID is set.
get_context_instance(CONTEXT_SYSTEM);
@ -335,5 +334,28 @@ class accesslib_test extends UnitTestCaseUsingDatabase {
$this->drop_test_tables($tablenames);
accesslib_clear_all_caches_for_unit_testing();
}
function test_get_allowed_switchable_roles() {
$this->create_test_table('role_capabilities', 'lib');
$this->load_test_data('role_capabilities',
array('roleid', 'capability', 'contextid', 'permission'), array(
array( 1, 'moodle/forum:replypost', SYSCONTEXTID, CAP_ALLOW),
array( 2, 'moodle/course:view', SYSCONTEXTID, CAP_ALLOW),
array( 3, 'moodle/site:doanything', SYSCONTEXTID, CAP_ALLOW),
array( 4, 'moodle/site:doanything', SYSCONTEXTID, CAP_ALLOW),
array( 4, 'moodle/course:view', SYSCONTEXTID, CAP_ALLOW),
array( 5, 'moodle/course:view', SYSCONTEXTID, CAP_ALLOW),
array( 5, 'moodle/site:doanything', SYSCONTEXTID, CAP_PREVENT),
array( 6, 'moodle/course:view', SYSCONTEXTID, CAP_PREVENT),
));
$this->switch_to_test_db();
$this->assert(new ArraysHaveSameValuesExpectation(array(2, 5)), array_keys(get_allowed_switchable_roles()));
$this->revert_to_real_db();
$this->drop_test_table('role_capabilities');
}
}
?>

View File

@ -4761,10 +4761,11 @@ function print_png($url, $sizex, $sizey, $return, $parameters='alt=""') {
* <li>$table->cellspacing - Spacing between cells
* <li>$table->class - class attribute to put on the table
* <li>$table->id - id attribute to put on the table.
* <li>$table->rowclass[] - classes to add to particular rows.
* <li>$table->rowclass[] - classes to add to particular rows. (space-separated string)
* <li>$table->colclass[] - classes to add to every cell in a particular colummn. (space-separated string)
* <li>$table->summary - Description of the contents for screen readers.
* <li>$table->headspan can be used to make a heading span multiple columns.
* <li>$table->rotateheaders - Causes the contents of the heading cells to be rotated 90%.
* <li>$table->rotateheaders - Causes the contents of the heading cells to be rotated 90 degrees.
* </ul>
* @param bool $return whether to return an output string or echo now
* @return boolean or $string
@ -4842,7 +4843,7 @@ function print_table($table, $return=false) {
$keys = array_keys($table->head);
$lastkey = end($keys);
foreach ($table->head as $key => $heading) {
$classes = array('header', 'c' . $key);
if (!isset($size[$key])) {
$size[$key] = '';
}
@ -4855,9 +4856,10 @@ function print_table($table, $return=false) {
$colspan = '';
}
if ($key == $lastkey) {
$extraclass = ' lastcol';
} else {
$extraclass = '';
$classes[] = 'lastcol';
}
if (isset($table->colclasses[$key])) {
$classes[] = $table->colclasses[$key];
}
if ($table->rotateheaders) {
$wrapperstart = '<span>';
@ -4868,7 +4870,7 @@ function print_table($table, $return=false) {
}
$output .= '<th style="'. $align[$key].$size[$key] .
';white-space:nowrap;" class="header c'.$key.$extraclass.'" scope="col"' . $colspan . '>'.
';white-space:nowrap;" class="'.implode(' ', $classes).'" scope="col"' . $colspan . '>'.
$wrapperstart . $heading . $wrapperend . '</th>';
}
$output .= '</tr>'."\n";
@ -4890,9 +4892,10 @@ function print_table($table, $return=false) {
if ($row == 'hr' and $countcols) {
$output .= '<td colspan="'. $countcols .'"><div class="tabledivider"></div></td>';
} else { /// it's a normal row of data
$keys2=array_keys($row);
$keys2 = array_keys($row);
$lastkey = end($keys2);
foreach ($row as $key => $item) {
$classes = array('cell', 'c' . $key);
if (!isset($size[$key])) {
$size[$key] = '';
}
@ -4903,11 +4906,12 @@ function print_table($table, $return=false) {
$wrap[$key] = '';
}
if ($key == $lastkey) {
$extraclass = ' lastcol';
} else {
$extraclass = '';
$classes[] = 'lastcol';
}
$output .= '<td style="'. $align[$key].$size[$key].$wrap[$key] .'" class="cell c'.$key.$extraclass.'">'. $item .'</td>';
if (isset($table->colclasses[$key])) {
$classes[] = $table->colclasses[$key];
}
$output .= '<td style="'. $align[$key].$size[$key].$wrap[$key] .'" class="'.implode(' ', $classes).'">'. $item .'</td>';
}
}
$output .= '</tr>'."\n";