From bbdb7070cafead78e824b1fdada55918760ad510 Mon Sep 17 00:00:00 2001 From: tjhunt Date: Tue, 18 Nov 2008 07:10:00 +0000 Subject: [PATCH] manage roles: MDL-8313 Provide a basic mode for the manage roles page. * New basic define roles mode, with just an Allow checkbox for each capability. * Button to toggle this form to/from advanced mode. * Also, a separate mode for viewing a role definition, rather than just showing disabled checkboxes. * Now duplicating a role just takes to you a pre-populated add role form, so you can double-check things before saving the new role. * Deleting a role is now logged. * Role reordering code cleaned up. * You can now no longer delete the last role that has admin permissions. * This includes a general refactor of manage.php, which eliminates manage.html, and splits of define.php. --- admin/roles/define.php | 355 ++++------------ admin/roles/lib.php | 356 ++++++++++++++-- admin/roles/manage.html | 236 ----------- admin/roles/manage.php | 699 ++++++++----------------------- lang/en_utf8/error.php | 3 + lang/en_utf8/role.php | 5 + lib/accesslib.php | 130 +++--- theme/standard/styles_color.css | 3 +- theme/standard/styles_fonts.css | 3 +- theme/standard/styles_layout.css | 23 +- 10 files changed, 632 insertions(+), 1181 deletions(-) delete mode 100755 admin/roles/manage.html diff --git a/admin/roles/define.php b/admin/roles/define.php index 2e45ed6165..2714c13016 100755 --- a/admin/roles/define.php +++ b/admin/roles/define.php @@ -27,9 +27,10 @@ * Lets the user edit role definitions. * * Responds to actions: - * add - add a new role - * edit - edit the definition of a role - * view - view the definition of a role + * add - add a new role + * duplicate - like add, only initialise the new role by using an existing one. + * edit - edit the definition of a role + * view - view the definition of a role * * @license http://www.gnu.org/copyleft/gpl.html GNU Public License * @package roles @@ -39,6 +40,9 @@ require_once($CFG->dirroot . '/' . $CFG->admin . '/roles/lib.php'); $action = required_param('action', PARAM_ALPHA); + if (!in_array($action, array('add', 'duplicate', 'edit', 'view'))) { + throw new moodle_exception('invalidaccess'); + } if ($action != 'add') { $roleid = required_param('roleid', PARAM_INTEGER); } else { @@ -47,7 +51,15 @@ /// Get the base URL for this and related pages into a convenient variable. $manageurl = $CFG->wwwroot . '/' . $CFG->admin . '/roles/manage.php'; - $baseurl = $CFG->wwwroot . '/' . $CFG->admin . '/roles/define.php'; + $defineurl = $CFG->wwwroot . '/' . $CFG->admin . '/roles/define.php'; + if ($action == 'duplicate') { + $baseurl = $defineurl . '?action=add'; + } else { + $baseurl = $defineurl . '?action=' . $action; + if ($roleid) { + $baseurl .= '&roleid=' . $roleid; + } + } /// Check access permissions. $systemcontext = get_context_instance(CONTEXT_SYSTEM); @@ -69,17 +81,9 @@ /// Get some basic data we are going to need. $roles = get_all_roles(); + $rolenames = role_fix_names($roles, $systemcontext, ROLENAME_ORIGINAL); $rolescount = count($roles); - $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') - ); - /// Create the table object. if ($action == 'view') { $definitiontable = new view_role_definition_table($systemcontext, $roleid); @@ -89,232 +93,17 @@ $definitiontable = new define_role_table_basic($systemcontext, $roleid); } $definitiontable->read_submitted_permissions(); - -/// form processing, editing a role, adding a role, deleting a role etc. - $errors = array(); - $newrole = false; - - $name = optional_param('name', '', PARAM_MULTILANG); // new role name - $shortname = optional_param('shortname', '', PARAM_RAW); // new role shortname, special cleaning before storage - $description = optional_param('description', '', PARAM_CLEAN); // new role desc - - if (optional_param('savechanges', false, PARAM_BOOL) && confirm_sesskey()) { - switch ($action) { - case 'add': - - $shortname = textlib_get_instance()->specialtoascii($shortname); - - $shortname = moodle_strtolower(clean_param($shortname, PARAM_ALPHANUMEXT)); // only lowercase safe ASCII characters - $legacytype = required_param('legacytype', PARAM_RAW); - - $legacyroles = get_legacy_roles(); - if (!array_key_exists($legacytype, $legacyroles)) { - $legacytype = ''; - } - - if (empty($name)) { - $errors['name'] = get_string('errorbadrolename', 'role'); - } else if ($DB->count_records('role', array('name'=>$name))) { - $errors['name'] = get_string('errorexistsrolename', 'role'); - } - - if (empty($shortname)) { - $errors['shortname'] = get_string('errorbadroleshortname', 'role'); - } else if ($DB->count_records('role', array('shortname'=>$shortname))) { - $errors['shortname'] = get_string('errorexistsroleshortname', 'role'); - } - - if (empty($errors)) { - $newroleid = create_role($name, $shortname, $description); - - // set proper legacy type - if (!empty($legacytype)) { - assign_capability($legacyroles[$legacytype], CAP_ALLOW, $newroleid, $systemcontext->id); - } - - } else { - $newrole = new object(); - $newrole->name = $name; - $newrole->shortname = $shortname; - $newrole->description = $description; - $newrole->legacytype = $legacytype; - } - - $newcontextlevels = array(); - foreach (array_keys($allcontextlevels) as $cl) { - if (optional_param('contextlevel' . $cl, false, PARAM_BOOL)) { - $newcontextlevels[$cl] = $cl; - } - } - if (empty($errors)) { - set_role_contextlevels($newroleid, $newcontextlevels); - } - - $allowed_values = array(CAP_INHERIT, CAP_ALLOW, CAP_PREVENT, CAP_PROHIBIT); - $capabilities = fetch_context_capabilities($systemcontext); // capabilities applicable in this context - - foreach ($capabilities as $cap) { - if (!isset($data->{$cap->name})) { - continue; - } - - // legacy caps have their own selector - if (is_legacy($data->{$cap->name})) { - continue; - } - - $capname = $cap->name; - $value = clean_param($data->{$cap->name}, PARAM_INT); - if (!in_array($value, $allowed_values)) { - continue; - } - - if (empty($errors)) { - assign_capability($capname, $value, $newroleid, $systemcontext->id); - } else { - $newrole->$capname = $value; - } - } - - // added a role sitewide... - mark_context_dirty($systemcontext->path); - - if (empty($errors)) { - $rolename = $DB->get_field('role', 'name', array('id'=>$newroleid)); - add_to_log(SITEID, 'role', 'add', 'admin/roles/manage.php?action=add', $rolename, '', $USER->id); - redirect('manage.php'); - } - - break; - - case 'edit': - $shortname = moodle_strtolower(clean_param(clean_filename($shortname), PARAM_SAFEDIR)); // only lowercase safe ASCII characters - $legacytype = required_param('legacytype', PARAM_RAW); - - $legacyroles = get_legacy_roles(); - if (!array_key_exists($legacytype, $legacyroles)) { - $legacytype = ''; - } - - if (empty($name)) { - $errors['name'] = get_string('errorbadrolename', 'role'); - } else if ($rs = $DB->get_records('role', array('name'=>$name))) { - unset($rs[$roleid]); - if (!empty($rs)) { - $errors['name'] = get_string('errorexistsrolename', 'role'); - } - } - - if (empty($shortname)) { - $errors['shortname'] = get_string('errorbadroleshortname', 'role'); - } else if ($rs = $DB->get_records('role', array('shortname'=>$shortname))) { - unset($rs[$roleid]); - if (!empty($rs)) { - $errors['shortname'] = get_string('errorexistsroleshortname', 'role'); - } - } - if (!empty($errors)) { - $newrole = new object(); - $newrole->name = $name; - $newrole->shortname = $shortname; - $newrole->description = $description; - $newrole->legacytype = $legacytype; - } - - $newcontextlevels = array(); - foreach (array_keys($allcontextlevels) as $cl) { - if (optional_param('contextlevel' . $cl, false, PARAM_BOOL)) { - $newcontextlevels[$cl] = $cl; - } - } - if (empty($errors)) { - set_role_contextlevels($roleid, $newcontextlevels); - } - - $allowed_values = array(CAP_INHERIT, CAP_ALLOW, CAP_PREVENT, CAP_PROHIBIT); - $capabilities = fetch_context_capabilities($systemcontext); // capabilities applicable in this context - - foreach ($capabilities as $cap) { - if (!isset($data->{$cap->name})) { - continue; - } - - // legacy caps have their own selector - if (is_legacy($data->{$cap->name}) === 0 ) { - continue; - } - - $capname = $cap->name; - $value = clean_param($data->{$cap->name}, PARAM_INT); - if (!in_array($value, $allowed_values)) { - continue; - } - - if (!empty($errors)) { - $newrole->$capname = $value; - continue; - } - - // edit default caps - $SQL = "SELECT * - FROM {role_capabilities} - WHERE roleid = ? AND capability = ? - AND contextid = ?"; - $params = array($roleid, $capname, $systemcontext->id); - - $localoverride = $DB->get_record_sql($SQL, $params); - - if ($localoverride) { // update current overrides - if ($value == CAP_INHERIT) { // inherit = delete - unassign_capability($capname, $roleid, $systemcontext->id); - - } else { - $localoverride->permission = $value; - $localoverride->timemodified = time(); - $localoverride->modifierid = $USER->id; - $DB->update_record('role_capabilities', $localoverride); - } - } else { // insert a record - if ($value != CAP_INHERIT) { - assign_capability($capname, $value, $roleid, $systemcontext->id); - } - } - - } - - if (empty($errors)) { - // update normal role settings - $role->id = $roleid; - $role->name = $name; - $role->shortname = $shortname; - $role->description = $description; - - if (!$DB->update_record('role', $role)) { - print_error('cannotupdaterole', 'error'); - } - - // set proper legacy type - foreach($legacyroles as $ltype=>$lcap) { - if ($ltype == $legacytype) { - assign_capability($lcap, CAP_ALLOW, $roleid, $systemcontext->id); - } else { - unassign_capability($lcap, $roleid); - } - } - - // edited a role sitewide... - mark_context_dirty($systemcontext->path); - add_to_log(SITEID, 'role', 'edit', 'admin/roles/manage.php?action=edit&roleid='.$role->id, $role->name, '', $USER->id); - - redirect('manage.php'); - } - - // edited a role sitewide - with errors, but still... - mark_context_dirty($systemcontext->path); - } + if ($action == 'duplicate') { + $definitiontable->make_copy(); } - $rolenames = role_fix_names($roles, $systemcontext, ROLENAME_ORIGINAL); +/// Process submission in necessary. + if (optional_param('savechanges', false, PARAM_BOOL) && confirm_sesskey() && $definitiontable->is_submission_valid()) { + $definitiontable->save_changes(); + add_to_log(SITEID, 'role', $action, 'admin/roles/define.php?action=view&roleid=' . + $definitiontable->get_role_id(), $definitiontable->get_role_name(), '', $USER->id); + redirect($manageurl); + } /// Print the page header and tabs. admin_externalpage_print_header(); @@ -324,6 +113,8 @@ if ($action == 'add') { $title = get_string('addinganewrole', 'role'); + } else if ($action == 'duplicate') { + $title = get_string('addingrolebycopying', 'role', $rolenames[$roleid]->localname); } else if ($action == 'view') { $title = get_string('viewingdefinitionofrolex', 'role', $rolenames[$roleid]->localname); } else if ($action == 'edit') { @@ -331,60 +122,76 @@ } print_heading_with_help($title, 'roles'); -/// Display the role definition, either read-only, or for editing. +/// Work out some button labels. if ($action == 'add') { - $roleid = 0; - if (empty($errors) or empty($newrole)) { - $role = new object(); - $role->name = ''; - $role->shortname = ''; - $role->description = ''; - $role->legacytype = ''; - $rolecontextlevels = array(); - } else { - $role = $newrole; - $rolecontextlevels = $newcontextlevels; - } - } else if ($action == 'edit' and !empty($errors) and !empty($newrole)) { - $role = $newrole; - $rolecontextlevels = $newcontextlevels; + $submitlabel = get_string('addrole', 'role'); } else { - if(!$role = $DB->get_record('role', array('id'=>$roleid))) { - print_error('wrongroleid', 'error'); - } - $role->legacytype = get_legacy_type($role->id); - $rolecontextlevels = get_role_contextlevels($roleid); + $submitlabel = get_string('savechanges'); + } + if ($showadvanced) { + $showadvancedlabel = get_string('hideadvanced', 'form'); + } else { + $showadvancedlabel = get_string('showadvanced', 'form'); } - +/// On the view page, show some extra controls at the top. if ($action == 'view') { - echo '
'; - popup_form('manage.php?action=view&roleid=', $roleoptions, 'switchrole', $roleid, '', '', '', - false, 'self', get_string('selectrole', 'role')); - echo '
'; - - $legacytype = get_legacy_type($roleid); $options = array(); $options['roleid'] = $roleid; $options['action'] = 'edit'; - print_single_button('manage.php', $options, get_string('edit')); + print_single_button($defineurl, $options, get_string('edit')); $options['action'] = 'reset'; - if (empty($legacytype)) { - print_single_button('manage.php', $options, get_string('resetrolenolegacy', 'role')); + if ($definitiontable->get_legacy_type()) { + print_single_button($manageurl, $options, get_string('resetrole', 'role')); } else { - print_single_button('manage.php', $options, get_string('resetrole', 'role')); + print_single_button($manageurl, $options, get_string('resetrolenolegacy', 'role')); } $options['action'] = 'duplicate'; - print_single_button('manage.php', $options, get_string('duplicaterole', 'role')); - print_single_button('manage.php', null, get_string('listallroles', 'role')); - echo '
'; - echo '
'; + print_single_button($defineurl, $options, get_string('duplicaterole', 'role')); + print_single_button($manageurl, null, get_string('listallroles', 'role')); + echo "\n"; } - print_box_start('generalbox boxwidthwide boxaligncenter'); + // Start the form. + print_box_start('generalbox'); + if ($action == 'view') { + echo '
'; + } else { + ?> +
+ +
+ +
+
+ + +
+ display(); + +/// Close the stuff we left open above. + if ($action == 'view') { + echo '
'; + } else { + ?> +
+ + +
+
+ '; + echo '

' . get_string('backtoallroles', 'role') . '

'; + echo ''; + admin_externalpage_print_footer(); ?> diff --git a/admin/roles/lib.php b/admin/roles/lib.php index b58052cf39..90cd39f534 100644 --- a/admin/roles/lib.php +++ b/admin/roles/lib.php @@ -274,8 +274,9 @@ abstract class capability_table_with_risks extends capability_table_base { protected $displaypermissions; protected $permissions; protected $changed; + protected $roleid; - public function __construct($context, $id) { + public function __construct($context, $id, $roleid) { parent::__construct($context, $id); $this->allrisks = get_all_risks(); @@ -293,6 +294,7 @@ abstract class capability_table_with_risks extends capability_table_base { $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. @@ -308,16 +310,23 @@ abstract class capability_table_with_risks extends capability_table_base { global $DB; /// Load the overrides/definition in this context. - $this->permissions = $DB->get_records_menu('role_capabilities', array('roleid' => $this->roleid, - 'contextid' => $this->context->id), '', 'capability,permission'); + 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(); - public abstract function save_changes(); - + /** + * Update $this->permissions based on submitted data, while making a list of + * changed capabilities in $this->changed. + */ public function read_submitted_permissions() { - /// Update $this->permissions based on submitted data. + $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 @@ -339,6 +348,20 @@ abstract class capability_table_with_risks extends capability_table_base { } } + /** + * 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) { @@ -400,56 +423,278 @@ abstract class capability_table_with_risks extends capability_table_base { } } +/** + * 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 { - protected $roleid; + /** 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'); + 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() { - if (!$this->roleid) { - $this->permissions = array(); + 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. + $this->contextlevels = array_combine($contextlevels, $contextlevels); } else { - parent::load_current_permissions(); + $this->role = new stdClass; + $this->role->name = ''; + $this->role->shortname = ''; + $this->role->description = ''; + $this->role->legacytype = ''; + $this->contextlevels = array(); } + parent::load_current_permissions(); } - protected function load_parent_permissions() { - /// Get the default permissions, based on legacy role type. - // TODO - if ($this->roleid) { - $legacy = get_legacy_type($this->roleid); - } else { - $legacy = ''; + 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 (!empty($legacy)) { - $this->parentpermissions = get_default_capabilities($legacy); - } else { - $this->parentpermissions = array(); + 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); } /** - * Save any overrides that have been changed. + * 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() { - foreach ($this->changed as $changedcap) { - assign_capability($changedcap, $this->permissions[$changedcap], - $this->roleid, $this->context->id, true); + global $DB; + + if (!$this->roleid) { + // Creating role + if ($this->legacyroles[$this->role->legacytype]) { + $legacycap = $this->legacyroles[$this->role->legacytype]; + } else { + $legacycap = ''; + } + if (!$this->role->id = create_role($this->role->name, $this->role->shortname, $this->role->description, $legacycap)) { + throw new moodle_exception('errorcreatingrole'); + } + $this->roleid = $this->role->id; // Needed to make the parent::save_changes(); call work. + } else { + // Updating role + if (!$DB->update_record('role', $this->role)) { + throw new moodle_exception('cannotupdaterole'); + } + + // 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); + } + } } - // force accessinfo refresh for users visiting this context... - mark_context_dirty($this->context->path); + // 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 ''; + } + + protected function get_shortname_field($id) { + return ''; + } + + 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) { + $options = array(); + $options[''] = get_string('none'); + foreach($this->legacyroles as $type => $cap) { + $options[$type] = get_string('legacy:'.$type, 'role'); + } + return choose_from_menu($options, 'legacytype', $this->role->legacytype, '', '', 0, true); + } + + 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 .= ''; + } + $output .= ' '; + $output .= '
\n"; + } + return $output; + } + + protected function print_field($name, $caption, $field) { + // Attempt to generate HTML like formslib. + echo '
'; + echo '
'; + if ($name) { + echo '\n"; + } + echo '
'; + if (isset($this->errors[$name])) { + $extraclass = ' error'; + } else { + $extraclass = ''; + } + echo '
'; + if (isset($this->errors[$name])) { + formerr($this->errors[$name]); + echo '
'; + } + echo $field; + echo '
'; + echo '
'; + } + + public function display() { + // Extra fields at the top of the page. + echo '
'; + $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 "
"; + + // Now the permissions table. + parent::display(); + } + protected function add_permission_cells($capability) { /// One cell for each possible permission. foreach ($this->displaypermissions as $perm => $permname) { @@ -485,6 +730,7 @@ class define_role_table_basic extends define_role_table_advanced { protected function add_permission_cells($capability) { $perm = $this->permissions[$capability->name]; $permname = $this->allpermissions[$perm]; + $defaultperm = $this->allpermissions[$this->parentpermissions[$capability->name]]; echo ''; if ($perm == CAP_ALLOW || $perm == CAP_INHERIT) { $checked = ''; @@ -493,7 +739,8 @@ class define_role_table_basic extends define_role_table_advanced { } echo ''; echo ''; + '" value="' . CAP_ALLOW . '"' . $checked . ' /> ' . $this->strallow . + '' . get_string('defaultx', 'role', $this->strperms[$defaultperm]) . ''; } else { echo ''; echo $this->strperms[$permname] . '' . $this->stradvmessage . ''; @@ -505,18 +752,49 @@ 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 add_permission_cells($capability) { $perm = $this->permissions[$capability->name]; $permname = $this->allpermissions[$perm]; - echo '' . $this->strperms[$permname] . ''; + $defaultperm = $this->allpermissions[$this->parentpermissions[$capability->name]]; + if ($permname != $defaultperm) { + $default = get_string('defaultx', 'role', $this->strperms[$defaultperm]); + } else { + $default = " "; + } + echo '' . $this->strperms[$permname] . '' . + $default . ''; } } class override_permissions_table_advanced extends capability_table_with_risks { - protected $roleid; protected $strnotset; protected $haslockedcapabiltites = false; @@ -533,8 +811,7 @@ class override_permissions_table_advanced extends capability_table_with_risks { * capabilities with no risks. */ public function __construct($context, $roleid, $safeoverridesonly) { - $this->roleid = $roleid; - parent::__construct($context, 'overriderolestable'); + parent::__construct($context, 'overriderolestable', $roleid); $this->displaypermissions = $this->allpermissions; $this->strnotset = get_string('notset', 'role'); @@ -557,19 +834,6 @@ class override_permissions_table_advanced extends capability_table_with_risks { $this->parentpermissions = role_context_capabilities($this->roleid, $parentcontext); } - /** - * Save any overrides that have been changed. - */ - public function save_changes() { - 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 has_locked_capabiltites() { return $this->haslockedcapabiltites; } diff --git a/admin/roles/manage.html b/admin/roles/manage.html deleted file mode 100755 index cfec37f847..0000000000 --- a/admin/roles/manage.html +++ /dev/null @@ -1,236 +0,0 @@ - - -
-
- - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
name)); ?>
shortname); ?>
description, FORMAT_HTML)); $usehtmleditor = false; ?>
legacytype)) { - print_string('none'); - } else { - print_string('legacy:'.$role->legacytype, 'role'); - } - ?> -
name).'" />'; - if (isset($errors["name"])) formerr($errors["name"]); - ?> -
shortname).'" />'; - if (isset($errors["shortname"])) formerr($errors["shortname"]); - ?> -
description); - ?> -
$lcap) { - $options[$ltype] = get_string('legacy:'.$ltype, 'role'); - } - choose_from_menu($options, 'legacytype', $role->legacytype, ''); - ?> -
$clname) { - $extraarguments = $disabled; - if (in_array($cl, $rolecontextlevels)) { - $extraarguments .= 'checked="checked"'; - } - echo ' '; - echo '
\n"; - } - ?> -
- - - - - - - - - - - - - - -legacytype)) { - $defaultcaps = get_default_capabilities($role->legacytype); -} else { - $defaultcaps = false; -} - -foreach ($capabilities as $capability) { - - //legacy caps have their own selector - if (is_legacy($capability->name)) { - continue; - } - - // prints a breaker if component or name or context level - if (component_level_changed($capability, $component, $contextlevel)) { - //if ($capability->component != $component or $capability->contextlevel != $contextlevel) { - echo (''); - } - - // these 2 are used to see to group same mod/core capabilities together - $contextlevel = $capability->contextlevel; - $component = $capability->component; - - if (empty($errors)) { - // check the capability override for this cap, this role in this context - $localoverride = get_local_override($roleid, $sitecontext->id, $capability->name); - } else { - $localoverride = new object(); - $localoverride->permission = $role->{$capability->name}; - } - - $riskinfo = ''; - - $isinherit = (!isset($defaultcaps[$capability->name]) or $defaultcaps[$capability->name] == CAP_INHERIT) ? 'capdefault' : ''; - $isallow = (isset($defaultcaps[$capability->name]) and $defaultcaps[$capability->name] == CAP_ALLOW) ? 'capdefault' : ''; - $isprevent = (isset($defaultcaps[$capability->name]) and $defaultcaps[$capability->name] == CAP_PREVENT) ? 'capdefault' : ''; - $isprohibit = (isset($defaultcaps[$capability->name]) and $defaultcaps[$capability->name] == CAP_PROHIBIT) ? 'capdefault' : ''; - - ?> - - - - - - - - - - - - - -
'. - get_component_string($capability->component, $capability->contextlevel).'
'; - $rowclasses = ''; - if (RISK_MANAGETRUST & (int)$capability->riskbitmask) { - $riskinfo .= ''; - $riskinfo .= ''.get_string('riskmanagetrustshort', 'admin').''; - $rowclasses .= ' riskmanagetrust'; - } - $riskinfo .= ''; - if (RISK_CONFIG & (int)$capability->riskbitmask) { - $riskinfo .= ''; - $riskinfo .= ''.get_string('riskconfigshort', 'admin').''; - $rowclasses .= ' riskconfig'; - } - $riskinfo .= ''; - if (RISK_XSS & (int)$capability->riskbitmask) { - $riskinfo .= ''; - $riskinfo .= ''.get_string('riskxssshort', 'admin').''; - $rowclasses .= ' riskxss'; - } - $riskinfo .= ''; - if (RISK_PERSONAL & (int)$capability->riskbitmask) { - $riskinfo .= ''; - $riskinfo .= ''.get_string('riskpersonalshort', 'admin').''; - $rowclasses .= ' riskpersonal'; - } - $riskinfo .= ''; - if (RISK_SPAM & (int)$capability->riskbitmask) { - $riskinfo .= ''; - $riskinfo .= ''.get_string('riskspamshort', 'admin').''; - $rowclasses .= ' riskspam'; - } - $riskinfo .= '
name); ?>name ?> - permission) || $localoverride->permission==CAP_INHERIT){ echo 'checked="checked"'; }?> /> - permission) && $localoverride->permission==CAP_ALLOW){ echo 'checked="checked"'; }?> /> - permission) && $localoverride->permission==CAP_PREVENT){ echo 'checked="checked"'; }?> /> - permission) && $localoverride->permission==CAP_PROHIBIT){ echo 'checked="checked"'; }?> />
- - -
- - -
- - -
diff --git a/admin/roles/manage.php b/admin/roles/manage.php index 48bed9abaf..70918a76f3 100755 --- a/admin/roles/manage.php +++ b/admin/roles/manage.php @@ -26,6 +26,16 @@ /** * Lets the user define and edit roles. * + * Responds to actions: + * [blank] - list roles. + * delete - delete a role (with are-you-sure) + * moveup - change the sort order + * movedown - change the sort order + * reset - set a role's permissions back to the default for that legacy role type. + * + * For all but the first two of those, you also need a roleid parameter, and + * possibly some other data. + * * @license http://www.gnu.org/copyleft/gpl.html GNU Public License * @package roles *//** */ @@ -33,300 +43,45 @@ require_once(dirname(__FILE__) . '/../../config.php'); require_once($CFG->dirroot . '/' . $CFG->admin . '/roles/lib.php'); - require_once($CFG->libdir.'/adminlib.php'); - - admin_externalpage_setup('defineroles'); - - $roleid = optional_param('roleid', 0, PARAM_INT); // if set, we are editing a role - $name = optional_param('name', '', PARAM_MULTILANG); // new role name - $shortname = optional_param('shortname', '', PARAM_RAW); // new role shortname, special cleaning before storage - $description = optional_param('description', '', PARAM_CLEAN); // new role desc - $action = optional_param('action', '', PARAM_ALPHA); - $confirm = optional_param('confirm', 0, PARAM_BOOL); - $cancel = optional_param('cancel', 0, PARAM_BOOL); - - $sitecontext = get_context_instance(CONTEXT_SYSTEM); - - require_capability('moodle/role:manage', $sitecontext); - - if ($cancel) { - redirect('manage.php'); + $action = optional_param('action', '', PARAM_ALPHA); + if ($action) { + $roleid = required_param('roleid', PARAM_INT); } - $errors = array(); - $newrole = false; +/// Get the base URL for this and related pages into a convenient variable. + $baseurl = $CFG->wwwroot . '/' . $CFG->admin . '/roles/manage.php'; + $defineurl = $CFG->wwwroot . '/' . $CFG->admin . '/roles/define.php'; +/// Check access permissions. + $systemcontext = get_context_instance(CONTEXT_SYSTEM); + require_login(); + require_capability('moodle/role:manage', $systemcontext); + admin_externalpage_setup('defineroles'); + +/// Get some basic data we are going to need. $roles = get_all_roles(); + role_fix_names($roles, $systemcontext, ROLENAME_ORIGINAL); $rolescount = count($roles); - $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') - ); - -/// fix sort order if needed - $rolesort = array(); - $i = 0; - foreach ($roles as $rolex) { - $rolesort[$i] = $rolex->id; - if ($rolex->sortorder != $i) { - $r = new object(); - $r->id = $rolex->id; - $r->sortorder = $i; - $DB->update_record('role', $r); - $roles[$rolex->id]->sortorder = $i; - } - $i++; + $undeletableroles = array(); + $undeletableroles[$CFG->notloggedinroleid] = 1; + $undeletableroles[$CFG->guestroleid] = 1; + $undeletableroles[$CFG->defaultuserroleid] = 1; + $undeletableroles[$CFG->defaultcourseroleid] = 1; + // If there is only one admin role, add that to $undeletableroles too. + $adminroles = get_admin_roles(); + if (count($adminroles) == 1) { + $undeletableroles[reset($adminroles)->id] = 1; } - // do not delete these default system roles - $defaultroles = array(); - $defaultroles[] = $CFG->notloggedinroleid; - $defaultroles[] = $CFG->guestroleid; - $defaultroles[] = $CFG->defaultuserroleid; - $defaultroles[] = $CFG->defaultcourseroleid; - -/// form processing, editing a role, adding a role, deleting a role etc. +///.Process submitted data. + $confirmed = optional_param('confirm', false, PARAM_BOOL) && data_submitted() && confirm_sesskey(); switch ($action) { - case 'add': - if ($data = data_submitted() and confirm_sesskey()) { - - $shortname = textlib_get_instance()->specialtoascii($shortname); - - $shortname = moodle_strtolower(clean_param($shortname, PARAM_ALPHANUMEXT)); // only lowercase safe ASCII characters - $legacytype = required_param('legacytype', PARAM_RAW); - - $legacyroles = get_legacy_roles(); - if (!array_key_exists($legacytype, $legacyroles)) { - $legacytype = ''; - } - - if (empty($name)) { - $errors['name'] = get_string('errorbadrolename', 'role'); - } else if ($DB->count_records('role', array('name'=>$name))) { - $errors['name'] = get_string('errorexistsrolename', 'role'); - } - - if (empty($shortname)) { - $errors['shortname'] = get_string('errorbadroleshortname', 'role'); - } else if ($DB->count_records('role', array('shortname'=>$shortname))) { - $errors['shortname'] = get_string('errorexistsroleshortname', 'role'); - } - - if (empty($errors)) { - $newroleid = create_role($name, $shortname, $description); - - // set proper legacy type - if (!empty($legacytype)) { - assign_capability($legacyroles[$legacytype], CAP_ALLOW, $newroleid, $sitecontext->id); - } - - } else { - $newrole = new object(); - $newrole->name = $name; - $newrole->shortname = $shortname; - $newrole->description = $description; - $newrole->legacytype = $legacytype; - } - - $newcontextlevels = array(); - foreach (array_keys($allcontextlevels) as $cl) { - if (optional_param('contextlevel' . $cl, false, PARAM_BOOL)) { - $newcontextlevels[$cl] = $cl; - } - } - if (empty($errors)) { - set_role_contextlevels($newroleid, $newcontextlevels); - } - - $allowed_values = array(CAP_INHERIT, CAP_ALLOW, CAP_PREVENT, CAP_PROHIBIT); - $capabilities = fetch_context_capabilities($sitecontext); // capabilities applicable in this context - - foreach ($capabilities as $cap) { - if (!isset($data->{$cap->name})) { - continue; - } - - // legacy caps have their own selector - if (is_legacy($data->{$cap->name})) { - continue; - } - - $capname = $cap->name; - $value = clean_param($data->{$cap->name}, PARAM_INT); - if (!in_array($value, $allowed_values)) { - continue; - } - - if (empty($errors)) { - assign_capability($capname, $value, $newroleid, $sitecontext->id); - } else { - $newrole->$capname = $value; - } - } - - // added a role sitewide... - mark_context_dirty($sitecontext->path); - - if (empty($errors)) { - $rolename = $DB->get_field('role', 'name', array('id'=>$newroleid)); - add_to_log(SITEID, 'role', 'add', 'admin/roles/manage.php?action=add', $rolename, '', $USER->id); - redirect('manage.php'); - } - } - - break; - - case 'edit': - if ($data = data_submitted() and confirm_sesskey()) { - - $shortname = moodle_strtolower(clean_param(clean_filename($shortname), PARAM_SAFEDIR)); // only lowercase safe ASCII characters - $legacytype = required_param('legacytype', PARAM_RAW); - - $legacyroles = get_legacy_roles(); - if (!array_key_exists($legacytype, $legacyroles)) { - $legacytype = ''; - } - - if (empty($name)) { - $errors['name'] = get_string('errorbadrolename', 'role'); - } else if ($rs = $DB->get_records('role', array('name'=>$name))) { - unset($rs[$roleid]); - if (!empty($rs)) { - $errors['name'] = get_string('errorexistsrolename', 'role'); - } - } - - if (empty($shortname)) { - $errors['shortname'] = get_string('errorbadroleshortname', 'role'); - } else if ($rs = $DB->get_records('role', array('shortname'=>$shortname))) { - unset($rs[$roleid]); - if (!empty($rs)) { - $errors['shortname'] = get_string('errorexistsroleshortname', 'role'); - } - } - if (!empty($errors)) { - $newrole = new object(); - $newrole->name = $name; - $newrole->shortname = $shortname; - $newrole->description = $description; - $newrole->legacytype = $legacytype; - } - - $newcontextlevels = array(); - foreach (array_keys($allcontextlevels) as $cl) { - if (optional_param('contextlevel' . $cl, false, PARAM_BOOL)) { - $newcontextlevels[$cl] = $cl; - } - } - if (empty($errors)) { - set_role_contextlevels($roleid, $newcontextlevels); - } - - $allowed_values = array(CAP_INHERIT, CAP_ALLOW, CAP_PREVENT, CAP_PROHIBIT); - $capabilities = fetch_context_capabilities($sitecontext); // capabilities applicable in this context - - foreach ($capabilities as $cap) { - if (!isset($data->{$cap->name})) { - continue; - } - - // legacy caps have their own selector - if (is_legacy($data->{$cap->name}) === 0 ) { - continue; - } - - $capname = $cap->name; - $value = clean_param($data->{$cap->name}, PARAM_INT); - if (!in_array($value, $allowed_values)) { - continue; - } - - if (!empty($errors)) { - $newrole->$capname = $value; - continue; - } - - // edit default caps - $SQL = "SELECT * - FROM {role_capabilities} - WHERE roleid = ? AND capability = ? - AND contextid = ?"; - $params = array($roleid, $capname, $sitecontext->id); - - $localoverride = $DB->get_record_sql($SQL, $params); - - if ($localoverride) { // update current overrides - if ($value == CAP_INHERIT) { // inherit = delete - unassign_capability($capname, $roleid, $sitecontext->id); - - } else { - $localoverride->permission = $value; - $localoverride->timemodified = time(); - $localoverride->modifierid = $USER->id; - $DB->update_record('role_capabilities', $localoverride); - } - } else { // insert a record - if ($value != CAP_INHERIT) { - assign_capability($capname, $value, $roleid, $sitecontext->id); - } - } - - } - - if (empty($errors)) { - // update normal role settings - $role->id = $roleid; - $role->name = $name; - $role->shortname = $shortname; - $role->description = $description; - - if (!$DB->update_record('role', $role)) { - print_error('cannotupdaterole', 'error'); - } - - // set proper legacy type - foreach($legacyroles as $ltype=>$lcap) { - if ($ltype == $legacytype) { - assign_capability($lcap, CAP_ALLOW, $roleid, $sitecontext->id); - } else { - unassign_capability($lcap, $roleid); - } - } - - // edited a role sitewide... - mark_context_dirty($sitecontext->path); - add_to_log(SITEID, 'role', 'edit', 'admin/roles/manage.php?action=edit&roleid='.$role->id, $role->name, '', $USER->id); - - redirect('manage.php'); - } - - // edited a role sitewide - with errors, but still... - mark_context_dirty($sitecontext->path); - } - - break; - case 'delete': - if (in_array($roleid, $defaultroles)) { - print_error('cannotdeleterole', 'error', '', 'this role is used as one of the default system roles, it can not be deleted'); + if (isset($undeletableroles[$roleid])) { + print_error('cannotdeletethisrole', '', $baseurl); } - if ($confirm and data_submitted() and confirm_sesskey()) { - if (!delete_role($roleid)) { - - // partially deleted a role sitewide...? - mark_context_dirty($sitecontext->path); - - print_error('cannotdeleterolewithid', 'error', '', $roleid); - } - // deleted a role sitewide... - mark_context_dirty($sitecontext->path); - - } else if (confirm_sesskey()){ + if (!$confirmed) { // show confirmation admin_externalpage_print_header(); $optionsyes = array('action'=>'delete', 'roleid'=>$roleid, 'sesskey'=>sesskey(), 'confirm'=>1); @@ -335,126 +90,72 @@ $a->name = $roles[$roleid]->name; $a->shortname = $roles[$roleid]->shortname; $a->count = $DB->count_records('role_assignments', array('roleid'=>$roleid)); - notice_yesno(get_string('deleterolesure', 'role', $a), 'manage.php', 'manage.php', $optionsyes, NULL, 'post', 'get'); + notice_yesno(get_string('deleterolesure', 'role', $a), $baseurl, $baseurl, $optionsyes, NULL, 'post', 'get'); admin_externalpage_print_footer(); die; } - - redirect('manage.php'); + if (!delete_role($roleid)) { + // The delete failed, but mark the context dirty in case. + mark_context_dirty($systemcontext->path); + print_error('cannotdeleterolewithid', 'error', $baseurl, $roleid); + } + // Deleted a role sitewide... + mark_context_dirty($systemcontext->path); + add_to_log(SITEID, 'role', 'delete', 'admin/roles/manage.php', $roles[$roleid]->localname, '', $USER->id); + redirect($baseurl); break; case 'moveup': - if (array_key_exists($roleid, $roles) and confirm_sesskey()) { - $role = $roles[$roleid]; - if ($role->sortorder > 0) { - $above = $roles[$rolesort[$role->sortorder - 1]]; - - if (!switch_roles($role, $above)) { - print_error('cannotmoverolewithid', 'error', '', $roleid); + if (confirm_sesskey()) { + $prevrole = null; + $thisrole = null; + foreach ($roles as $role) { + if ($role->id == $roleid) { + $thisrole = $role; + break; + } else { + $prevrole = $role; } } + if (is_null($thisrole) || is_null($prevrole)) { + print_error('cannotmoverolewithid', 'error', '', $roleid); + } + if (!switch_roles($thisrole, $prevrole)) { + print_error('cannotmoverolewithid', 'error', '', $roleid); + } } - redirect('manage.php'); + redirect($baseurl); break; case 'movedown': - if (array_key_exists($roleid, $roles) and confirm_sesskey()) { - $role = $roles[$roleid]; - if ($role->sortorder + 1 < $rolescount) { - $below = $roles[$rolesort[$role->sortorder + 1]]; - - if (!switch_roles($role, $below)) { - print_error('cannotmoverolewithid', 'error', '', $roleid); + if (confirm_sesskey()) { + $thisrole = null; + $nextrole = null; + foreach ($roles as $role) { + if ($role->id == $roleid) { + $thisrole = $role; + } else if (!is_null($thisrole)) { + $nextrole = $role; + break; } } - } - - redirect('manage.php'); - break; - - case 'duplicate': - if (!array_key_exists($roleid, $roles)) { - redirect('manage.php'); - } - - if ($confirm and data_submitted() and confirm_sesskey()) { - //ok - lets duplicate! - } else { - // show confirmation - admin_externalpage_print_header(); - $optionsyes = array('action'=>'duplicate', 'roleid'=>$roleid, 'sesskey'=>sesskey(), 'confirm'=>1); - $optionsno = array('action'=>'view', 'roleid'=>$roleid); - $a = new object(); - $a->id = $roleid; - $a->name = $roles[$roleid]->name; - $a->shortname = $roles[$roleid]->shortname; - notice_yesno(get_string('duplicaterolesure', 'role', $a), 'manage.php', 'manage.php', $optionsyes, $optionsno, 'post', 'get'); - admin_externalpage_print_footer(); - die; - } - - // duplicate current role - $sourcerole = $DB->get_record('role', array('id'=>$roleid)); - - $fullname = $sourcerole->name; - $shortname = $sourcerole->shortname; - $currentfullname = ""; - $currentshortname = ""; - $counter = 0; - - // find a name for the duplicated role - do { - if ($counter) { - $suffixfull = " ".get_string("copyasnoun")." ".$counter; - $suffixshort = "_".$counter; - } else { - $suffixfull = ""; - $suffixshort = ""; + if (is_null($nextrole)) { + print_error('cannotmoverolewithid', 'error', '', $roleid); + } + if (!switch_roles($thisrole, $nextrole)) { + print_error('cannotmoverolewithid', 'error', '', $roleid); } - $currentfullname = $fullname.$suffixfull; - // Limit the size of shortname - database column accepts <= 100 chars - $currentshortname = substr($shortname, 0, 100 - strlen($suffixshort)).$suffixshort; - $coursefull = $DB->get_record("role", array("name"=>$currentfullname)); - $courseshort = $DB->get_record("role", array("shortname"=>$currentshortname)); - $counter++; - } while ($coursefull || $courseshort); - - $description = 'duplicate of '.$fullname; - if ($newrole = create_role($currentfullname, $currentshortname, $description)) { - // dupilcate all the capabilities - role_cap_duplicate($sourcerole, $newrole); - - set_role_contextlevels($newrole, get_role_contextlevels($roleid)); - - // dup'ed a role sitewide... - mark_context_dirty($sitecontext->path); - } - $rolename = $DB->get_field('role', 'name', array('id'=>$newrole)); - add_to_log(SITEID, 'role', 'duplicate', 'admin/roles/manage.php?roleid='.$newrole.'&action=duplicate', $rolename, '', $USER->id); - redirect('manage.php'); + + redirect($baseurl); break; case 'reset': - if (!array_key_exists($roleid, $roles)) { - redirect('manage.php'); + if (isset($undeletableroles[$roleid])) { + print_error('cannotresetthisrole', '', $baseurl); } - - if ($confirm and data_submitted() and confirm_sesskey()) { - set_role_contextlevels($roleid, get_default_contextlevels(get_legacy_type($roleid))); - - reset_role_capabilities($roleid); - - // reset a role sitewide... - mark_context_dirty($sitecontext->path); - - $rolename = $DB->get_field('role', 'name', array('id'=>$roleid)); - add_to_log(SITEID, 'role', 'reset', 'admin/roles/manage.php?roleid='.$roleid.'&action=reset', $rolename, '', $USER->id); - - redirect('manage.php?action=view&roleid='.$roleid); - - } else { + if (!$confirmed) { // show confirmation admin_externalpage_print_header(); $optionsyes = array('action'=>'reset', 'roleid'=>$roleid, 'sesskey'=>sesskey(), 'confirm'=>1); @@ -474,178 +175,106 @@ die; } - break; + // Do the reset. + $legacytype = get_legacy_type($roleid); + if ($legacytype) { + set_role_contextlevels($roleid, get_default_contextlevels($legacytype)); + } + reset_role_capabilities($roleid); - default: + // Mark context dirty, log and redirect. + mark_context_dirty($systemcontext->path); + add_to_log(SITEID, 'role', 'reset', 'admin/roles/manage.php?action=reset&roleid=' . $roleid, $roles[$roleid]->localname, '', $USER->id); + redirect($defineurl . '?action=view&roleid=' . $roleid); break; } -/// print UI now - require_js(array('yui_yahoo', 'yui_dom', 'yui_event')); - require_js($CFG->admin . '/roles/roles.js'); +/// Print the page header and tabs. admin_externalpage_print_header(); $currenttab = 'manage'; include_once('managetabs.php'); - if (($roleid and ($action == 'view' or $action == 'edit')) or $action == 'add') { // view or edit role details + print_heading_with_help(get_string('roles', 'role'), 'roles'); + +/// Initialise table. + $table = new object; + $table->tablealign = 'center'; + $table->align = array('left', 'left', 'left', 'left'); + $table->wrap = array('nowrap', '', 'nowrap','nowrap'); + $table->width = '90%'; + $table->head = array( + get_string('role'), + get_string('description'), + get_string('shortname'), + get_string('edit') + ); - if ($action == 'add') { - $roleid = 0; - if (empty($errors) or empty($newrole)) { - $role = new object(); - $role->name = ''; - $role->shortname = ''; - $role->description = ''; - $role->legacytype = ''; - $rolecontextlevels = array(); - } else { - $role = $newrole; - $rolecontextlevels = $newcontextlevels; - } - } else if ($action == 'edit' and !empty($errors) and !empty($newrole)) { - $role = $newrole; - $rolecontextlevels = $newcontextlevels; - +/// Get some strings outside the loop. + $stredit = get_string('edit'); + $strduplicate = get_string('duplicate'); + $strdelete = get_string('delete'); + $strmoveup = get_string('moveup'); + $strmovedown = get_string('movedown'); + +/// Print a list of roles with edit/copy/delete/reorder icons. + $table->data = array(); + $lastrole = end($roles); + foreach ($roles as $role) { + + /// Basic data. + $row = array( + '' . $role->localname . '', + format_text($role->description, FORMAT_HTML), + s($role->shortname), + '', + ); + + /// Icons: + // move up + if ($role->sortorder != 0) { + $row[3] .= get_action_icon($baseurl . '?action=moveup&roleid=' . $role->id . '&sesskey=' . sesskey(), 'up', $strmoveup, $strmoveup); } else { - if(!$role = $DB->get_record('role', array('id'=>$roleid))) { - print_error('wrongroleid', 'error'); - } - $role->legacytype = get_legacy_type($role->id); - $rolecontextlevels = get_role_contextlevels($roleid); - } - - foreach ($roles as $rolex) { - $roleoptions[$rolex->id] = strip_tags(format_string($rolex->name)); - } - - // this is the array holding capabilities of this role sorted till this context - $r_caps = role_context_capabilities($roleid, $sitecontext); - - // this is the available capabilities assignable in this context - $capabilities = fetch_context_capabilities($sitecontext); - - $usehtmleditor = can_use_html_editor(); - - switch ($action) { - case 'add': - print_heading_with_help(get_string('addrole', 'role'), 'roles'); - break; - case 'view': - print_heading_with_help(get_string('viewrole', 'role'), 'roles'); - break; - case 'edit': - print_heading_with_help(get_string('editrole', 'role'), 'roles'); - break; - } - - echo '
'; - if ($action == 'view') { - popup_form('manage.php?action=view&roleid=', $roleoptions, 'switchrole', $roleid, '', '', '', - false, 'self', get_string('selectrole', 'role')); - - echo '
'; - - $legacytype = get_legacy_type($roleid); - $options = array(); - $options['roleid'] = $roleid; - $options['action'] = 'edit'; - print_single_button('manage.php', $options, get_string('edit')); - $options['action'] = 'reset'; - if (empty($legacytype)) { - print_single_button('manage.php', $options, get_string('resetrolenolegacy', 'role')); - } else { - print_single_button('manage.php', $options, get_string('resetrole', 'role')); - } - $options['action'] = 'duplicate'; - print_single_button('manage.php', $options, get_string('duplicaterole', 'role')); - print_single_button('manage.php', null, get_string('listallroles', 'role')); - echo '
'; + $row[3] .= get_spacer(); } - echo '
'; - - $lang = str_replace('_utf8', '', current_language()); - - if ($action == 'edit' || $action == 'add') { - $disabled = ''; + // move down + if ($role->sortorder != $lastrole->sortorder) { + $row[3] .= get_action_icon($baseurl . '?action=movedown&roleid=' . $role->id . '&sesskey=' . sesskey(), 'down', $strmovedown, $strmovedown); } else { - $disabled = 'disabled="disabled" '; + $row[3] .= get_spacer(); } - - print_simple_box_start('center'); - include_once('manage.html'); - print_simple_box_end(); - - } else { - - print_heading_with_help(get_string('roles', 'role'), 'roles'); - - $table = new object; - - $table->tablealign = 'center'; - $table->align = array('right', 'left', 'left', 'left'); - $table->wrap = array('nowrap', '', 'nowrap','nowrap'); - $table->cellpadding = 5; - $table->cellspacing = 0; - $table->width = '90%'; - $table->data = array(); - - $table->head = array(get_string('name'), - get_string('description'), - get_string('shortname'), - get_string('edit')); - - /************************* - * List all current roles * - **************************/ - - foreach ($roles as $role) { - - $stredit = get_string('edit'); - $strdelete = get_string('delete'); - $strmoveup = get_string('moveup'); - $strmovedown = get_string('movedown'); - - $row = array(); - $row[0] = ''.format_string($role->name).''; - $row[1] = format_text($role->description, FORMAT_HTML); - $row[2] = s($role->shortname); - $row[3] = ''. - ''.$stredit.' '; - if (in_array($role->id, $defaultroles)) { - $row[3] .= ' '; - } else { - $row[3] .= ''. - ''.$strdelete.' '; - } - if ($role->sortorder != 0) { - $row[3] .= ''. - ''.$strmoveup.' '; - } else { - $row[3] .= ' '; - } - if ($role->sortorder+1 < $rolescount) { - $row[3] .= ''. - ''.$strmovedown.' '; - } else { - $row[3] .= ' '; - } - - $table->data[] = $row; - + // edit + $row[3] .= get_action_icon($defineurl . '?action=edit&roleid=' . $role->id, + 'edit', $stredit, get_string('editxrole', 'role', $role->localname)); + // duplicate + $row[3] .= get_action_icon($defineurl . '?action=duplicate&roleid=' . $role->id, + 'copy', $strduplicate, get_string('createrolebycopying', 'role', $role->localname)); + // delete + if (isset($undeletableroles[$role->id])) { + $row[3] .= get_spacer(); + } else { + $row[3] .= get_action_icon($baseurl . '?action=delete&roleid=' . $role->id, + 'delete', $strdelete, get_string('deletexrole', 'role', $role->localname)); } - print_table($table); - $options = new object(); - $options->action = 'add'; - echo '
'; - print_single_button('manage.php', $options, get_string('addrole', 'role'), 'get'); - echo '
'; + $table->data[] = $row; } + print_table($table); + + echo '
'; + print_single_button($defineurl, array('action' => 'add'), get_string('addrole', 'role'), 'get'); + echo '
'; admin_externalpage_print_footer(); die; - - +function get_action_icon($url, $icon, $alt, $tooltip) { + global $CFG; + return '' . + '' . $alt . ' '; +} +function get_spacer() { + global $CFG; + return ' '; +} ?> diff --git a/lang/en_utf8/error.php b/lang/en_utf8/error.php index e936595175..f1c71f72e8 100644 --- a/lang/en_utf8/error.php +++ b/lang/en_utf8/error.php @@ -47,6 +47,7 @@ $string['cannotdeletecourse'] = 'You do not have the permission to delete this c $string['cannotdeletecustomfield'] = 'Error deleting custom field data'; $string['cannotdeletedir'] = 'Cannot delete ($a)'; $string['cannotdeleterole'] = 'It cannot be deleted, because $a'; +$string['cannotdeletethisrole'] = 'You cannot delete this role because it is used by the system, or because it is the last role with administrator capabilities.'; $string['cannotdeleterolewithid'] = 'Could not delete role with ID $a'; $string['cannotdownloadcomponents'] = 'Cannot download components'; $string['cannotdownloadlanguageupdatelist'] = 'Cannot download list of language updates from download.moodle.org'; @@ -105,6 +106,7 @@ $string['cannotremovefrommeta'] = 'Could not remove the selected course from thi $string['cannotrestore'] = 'An error has occurred and the restore could not be completed!'; $string['cannotresetguestpwd'] = 'You cannot reset the guest password'; $string['cannotresetmail'] = 'Error resetting password and mailing you'; +$string['cannotresetthisrole'] ='Cannot reset this role'; $string['cannotrestoreadminorcreator'] = 'You need to be a creator or admin user to restore into new course!'; $string['cannotrestoreadminoredit'] = 'You need to be a editing teacher or admin user to restore into selected course!'; $string['cannotsaveblock'] = 'Error saving block configuration'; @@ -205,6 +207,7 @@ $string['errorcleaningdirectory'] = 'Error cleaning directory \"$a\"'; $string['errorcopyingfiles'] = 'Error copying files'; $string['errorcreatingdirectory'] = 'Error creating directory \"$a\"'; $string['errorcreatingfile'] = 'Error creating file \"$a\"'; +$string['errorcreatingrole'] = 'Error creating role'; $string['erroronline'] = 'Error on line $a'; $string['errorreadingfile'] = 'Error reading file \"$a\"'; $string['errorunzippingfiles'] = 'Error unzipping files'; diff --git a/lang/en_utf8/role.php b/lang/en_utf8/role.php index 8a87037951..ed1c1b8c31 100644 --- a/lang/en_utf8/role.php +++ b/lang/en_utf8/role.php @@ -4,6 +4,7 @@ $string['addinganewrole'] = 'Adding a new role'; $string['addrole'] = 'Add a new role'; +$string['addingrolebycopying'] = 'Adding a new role based on $a'; $string['allow'] = 'Allow'; $string['allowassign'] = 'Allow role assignments'; $string['allowed'] = 'Allowed'; @@ -64,17 +65,21 @@ $string['course:viewparticipants'] = 'View participants'; $string['course:viewscales'] = 'View scales'; $string['course:visibility'] = 'Hide/show courses'; $string['createhiddenassign'] = 'Create hidden role assignments'; +$string['createrolebycopying'] = 'Create a new role by copying $a'; $string['currentcontext'] = 'Current context'; $string['currentrole'] = 'Current role'; $string['defaultrole'] = 'Default role'; +$string['defaultx'] = 'Default: $a'; $string['defineroles'] = 'Define roles'; $string['deletecourseoverrides'] = 'Delete all overrides in course'; $string['deletelocalroles'] = 'Delete all local role assignments'; $string['deleterolesure'] = 'Are you sure that you want to delete role \"$a->name ($a->shortname)\"?

Currently this role is assigned to $a->count users.'; +$string['deletexrole'] = 'Delete $a role'; $string['duplicaterolesure'] = 'Are you sure that you want to duplicate role \"$a->name ($a->shortname)\"?

'; $string['duplicaterole'] = 'Duplicate role'; $string['editingrolex'] = 'Editing role \'$a\''; $string['editrole'] = 'Edit role'; +$string['editxrole'] = 'Edit $a role'; $string['errorbadrolename'] = 'Incorrect role name'; $string['errorbadroleshortname'] = 'Incorrect role name'; $string['errorexistsrolename'] = 'Role name already exists'; diff --git a/lib/accesslib.php b/lib/accesslib.php index e2b2792ae0..ea9d07a634 100755 --- a/lib/accesslib.php +++ b/lib/accesslib.php @@ -533,7 +533,7 @@ function is_siteadmin($userid) { * @return boolean, whether this role is an admin role. */ function is_admin_role($roleid) { - global $CFG, $DB; + global $DB; $sql = "SELECT 1 FROM {role_capabilities} rc @@ -548,6 +548,30 @@ function is_admin_role($roleid) { return $DB->record_exists_sql($sql, $params); } +/** + * @return all the roles for which is_admin_role($role->id) is true. + */ +function get_admin_roles() { + global $DB; + + $sql = "SELECT * + FROM {role} r + WHERE EXISTS ( + SELECT 1 + FROM {role_capabilities} rc + JOIN {context} ctx ON ctx.id = rc.contextid + WHERE ctx.contextlevel = 10 + AND rc.roleid = r.id + AND rc.capability IN (?, ?, ?) + GROUP BY rc.capability + HAVING SUM(rc.permission) > 0 + ) + ORDER BY r.sortorder"; + $params = array('moodle/site:config', 'moodle/legacy:admin', 'moodle/site:doanything'); + + return $DB->get_records_sql($sql, $params); +} + function get_course_from_path ($path) { // assume that nothing is more than 1 course deep if (preg_match('!^(/.+)/\d+$!', $path, $matches)) { @@ -2505,54 +2529,39 @@ function get_local_override($roleid, $contextid, $capability) { function create_role($name, $shortname, $description, $legacy='') { global $DB; - // check for duplicate role name - - if ($role = $DB->get_record('role', array('name'=>$name))) { - print_error('duplicaterolename'); - } - - if ($role = $DB->get_record('role', array('shortname'=>$shortname))) { - print_error('duplicateroleshortname'); + // Get the system context. + if (!$context = get_context_instance(CONTEXT_SYSTEM)) { + return false; } + // Insert the role record. $role = new object(); $role->name = $name; $role->shortname = $shortname; $role->description = $description; //find free sortorder number - $role->sortorder = $DB->count_records('role'); - while ($DB->get_record('role',array('sortorder'=>$role->sortorder))) { - $role->sortorder += 1; - } + $role->sortorder = $DB->get_field('role', 'MAX(sortorder) + 1', array()); + $id = $DB->insert_record('role', $role); - if (!$context = get_context_instance(CONTEXT_SYSTEM)) { + if (!$id) { return false; } - if ($id = $DB->insert_record('role', $role)) { - if ($legacy) { - assign_capability($legacy, CAP_ALLOW, $id, $context->id); - } - - /// By default, users with role:manage at site level - /// should be able to assign users to this new role, and override this new role's capabilities + if ($legacy) { + assign_capability($legacy, CAP_ALLOW, $id, $context->id); + } - // find all admin roles - if ($adminroles = get_roles_with_capability('moodle/role:manage', CAP_ALLOW, $context)) { - // foreach admin role - foreach ($adminroles as $arole) { - // write allow_assign and allow_overrid - allow_assign($arole->id, $id); - allow_override($arole->id, $id); - } + // By default, users with role:manage at site level should be able to assign + // users to this new role, and override this new role's capabilities. + if ($adminroles = get_admin_roles()) { + foreach ($adminroles as $arole) { + allow_assign($arole->id, $id); + allow_override($arole->id, $id); } - - return $id; - } else { - return false; } + return $id; } /** @@ -4374,8 +4383,11 @@ function get_default_contextlevels($roletype) { 'guest' => array(), 'user' => array() ); - - return $defaults[$roletype]; + if (isset($defaults[$roletype])) { + return $defaults[$roletype]; + } else { + return array(); + } } /** @@ -4395,7 +4407,7 @@ function set_role_contextlevels($roleid, array $contextlevels) { foreach ($contextlevels as $level) { $rcl->contextlevel = $level; if (!$DB->insert_record('role_context_levels', $rcl, false, true)) { - throw new moodle_exception('couldnotdeleterolecontextlevels', '', '', $rcl); + throw new moodle_exception('couldnotdeleterolecontextlevels', '', '', $rcl); } } } @@ -5816,47 +5828,19 @@ function fix_role_sortorder($allroles) { } /** - * switch role order (used in admin/roles/manage.php) - * - * @param int $first id of role to move down - * @param int $second id of role to move up + * Switch the sort order of two roles (used in admin/roles/manage.php). * - * @return bool success or failure + * @param object $first The first role. Actually, only ->sortorder is used. + * @param object $second The second role. Actually, only ->sortorder is used. + * @return boolean success or failure */ function switch_roles($first, $second) { global $DB; - - $status = true; - //first find temorary sortorder number - $tempsort = $DB->count_records('role') + 3; - while ($DB->get_record('role',array('sortorder'=>$tempsort))) { - $tempsort += 3; - } - - $r1 = new object(); - $r1->id = $first->id; - $r1->sortorder = $tempsort; - $r2 = new object(); - $r2->id = $second->id; - $r2->sortorder = $first->sortorder; - - if (!$DB->update_record('role', $r1)) { - debugging("Can not update role with ID $r1->id!"); - $status = false; - } - - if (!$DB->update_record('role', $r2)) { - debugging("Can not update role with ID $r2->id!"); - $status = false; - } - - $r1->sortorder = $second->sortorder; - if (!$DB->update_record('role', $r1)) { - debugging("Can not update role with ID $r1->id!"); - $status = false; - } - - return $status; + $temp = $DB->get_field('role', 'MAX(sortorder) + 1', array()); + $result = $DB->set_field('role', 'sortorder', $temp, array('sortorder' => $first->sortorder)); + $result = $result && $DB->set_field('role', 'sortorder', $first->sortorder, array('sortorder' => $second->sortorder)); + $result = $result && $DB->set_field('role', 'sortorder', $second->sortorder, array('sortorder' => $temp)); + return $result; } /** diff --git a/theme/standard/styles_color.css b/theme/standard/styles_color.css index 7a06032cf5..22f95b5a0d 100644 --- a/theme/standard/styles_color.css +++ b/theme/standard/styles_color.css @@ -1197,8 +1197,7 @@ table.explainpermissions .overridden { border-bottom-color: #cecece; } -#admin-roles-manage .rolecap .cap-desc .cap-name, -#admin-roles-override .rolecap .cap-desc .cap-name, +.rolecap .cap-name, .rolecap .note { color: #888; } diff --git a/theme/standard/styles_fonts.css b/theme/standard/styles_fonts.css index 99a762568a..ea459dd254 100644 --- a/theme/standard/styles_fonts.css +++ b/theme/standard/styles_fonts.css @@ -249,8 +249,7 @@ body#admin-index .copyright { font-size: 0.8em; } -#admin-roles-manage .rolecap .cap-desc .cap-name, -#admin-roles-override .rolecap .cap-desc .cap-name, +.rolecap .cap-name, .rolecap .note { font-size: 0.75em; } diff --git a/theme/standard/styles_layout.css b/theme/standard/styles_layout.css index 2170276ea4..b7bfbaa398 100644 --- a/theme/standard/styles_layout.css +++ b/theme/standard/styles_layout.css @@ -991,10 +991,6 @@ body#admin-modules table.generaltable td.c0 padding-bottom: 20px; } -#admin-roles-manage table.generalbox { - margin: auto; -} - #admin-stickyblocks .generalbox { text-align:center; } @@ -1050,17 +1046,20 @@ body#admin-modules table.generaltable td.c0 #admin-roles-allowassign .buttons, #admin-roles-allowoverride .buttons, #admin-roles-manage .buttons, +#admin-roles-define .buttons, #admin-roles-override .buttons { margin: 20px; text-align:center; } - #admin-roles-manage .buttons .singlebutton, +#admin-roles-define .buttons .singlebutton, #admin-roles-override .buttons .singlebutton { display: inline; padding: 5px; } - +#admin-roles-define .topfields { + margin: 1em 0 2em; +} .roleassigntable { width: 100%; } @@ -1110,13 +1109,11 @@ body#admin-modules table.generaltable td.c0 .roleassigntable #addselect_wrapper label { font-weight: normal; } - -table.roledesc { - margin-left:auto; - margin-right:auto; +#admin-roles-define .mform { + width: 100%; } - #admin-roles-manage .backlink, +#admin-roles-define .backlink, #admin-roles-explain .backlink, #admin-roles-assign .backlink, #admin-roles-override .backlink { @@ -1159,9 +1156,9 @@ table.rolecap .prohibit { table.rolecap label { display: block; width: 100%; - height: 2.5em; + min-height: 2.5em; } -table.rolecap .cap-desc .cap-name, +.rolecap .cap-name, .rolecap .note { display: block; padding: 0 0.5em; -- 2.39.5