]> git.mjollnir.org Git - moodle.git/commitdiff
manage roles: MDL-8313 Provide a basic mode for the manage roles page.
authortjhunt <tjhunt>
Tue, 18 Nov 2008 07:10:00 +0000 (07:10 +0000)
committertjhunt <tjhunt>
Tue, 18 Nov 2008 07:10:00 +0000 (07:10 +0000)
* 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
admin/roles/lib.php
admin/roles/manage.html [deleted file]
admin/roles/manage.php
lang/en_utf8/error.php
lang/en_utf8/role.php
lib/accesslib.php
theme/standard/styles_color.css
theme/standard/styles_fonts.css
theme/standard/styles_layout.css

index 2e45ed61654584365b60b23182d75a1ecab368a0..2714c13016b26a1564db950fc81416136fb4cc4d 100755 (executable)
  * 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 {
 
 /// 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 .= '&amp;roleid=' . $roleid;
+        }
+    }
 
 /// Check access permissions.
     $systemcontext = get_context_instance(CONTEXT_SYSTEM);
 
 /// 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);
         $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();
 
     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') {
     }
     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 '<div class="selector">';
-        popup_form('manage.php?action=view&amp;roleid=', $roleoptions, 'switchrole', $roleid, '', '', '',
-                   false, 'self', get_string('selectrole', 'role'));
-
         echo '<div class="buttons">';
-
-        $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 '</div>';
-        echo '</div>';
+        print_single_button($defineurl, $options, get_string('duplicaterole', 'role'));
+        print_single_button($manageurl, null, get_string('listallroles', 'role'));
+        echo "</div>\n";
     }
 
-    print_box_start('generalbox boxwidthwide boxaligncenter');
+    // Start the form.
+    print_box_start('generalbox');
+    if ($action == 'view') {
+        echo '<div class="mform">';
+    } else {
+    ?>
+<form id="rolesform" class="mform" action="<?php echo $baseurl; ?>" method="post"><div>
+<input type="hidden" name="sesskey" value="<?php p(sesskey()) ?>" />
+<div class="advancedbutton">
+    <input type="submit" name="toggleadvanced" value="<?php echo $showadvancedlabel ?>" />
+</div>
+<div class="submit buttons">
+    <input type="submit" name="savechanges" value="<?php echo $submitlabel; ?>" />
+    <input type="submit" name="cancel" value="<?php print_string('cancel'); ?>" />
+</div>
+    <?php
+    }
+
+    // Print the form controls.
     $definitiontable->display();
+
+/// Close the stuff we left open above.
+    if ($action == 'view') {
+        echo '</div>';
+    } else {
+        ?>
+<div class="submit buttons">
+    <input type="submit" name="savechanges" value="<?php echo $submitlabel; ?>" />
+    <input type="submit" name="cancel" value="<?php print_string('cancel'); ?>" />
+</div>
+</div></form>
+        <?php
+    }
     print_box_end();
 
+/// Print a link back to the all roles list.
+    echo '<div class="backlink">';
+    echo '<p><a href="' . $manageurl . '">' . get_string('backtoallroles', 'role') . '</a></p>';
+    echo '</div>';
+
     admin_externalpage_print_footer();
 ?>
index b58052cf392f6eb3cbbebb98b27355bf125edf96..90cd39f5347ed3eebc90a544c61f40bae9929c70 100644 (file)
@@ -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 '<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) {
+        $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 .= '<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) {
+        // 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])) {
+            formerr($this->errors[$name]);
+            echo '<br />';
+        }
+        echo $field;
+        echo '</div>';
+        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>";
+
+        // 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 '<td class="' . $permname . '">';
         if ($perm == CAP_ALLOW || $perm == CAP_INHERIT) {
             $checked = '';
@@ -493,7 +739,8 @@ class define_role_table_basic extends define_role_table_advanced {
             }
             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>';
+                    '" value="' . CAP_ALLOW . '"' . $checked . ' /> ' . $this->strallow .
+                    '<span class="note">' . get_string('defaultx', 'role', $this->strperms[$defaultperm]) . '</span></label>';
         } else {
             echo '<input type="hidden" name="' . $capability->name . '" value="' . $perm . '" />';
             echo $this->strperms[$permname] . '<span class="note">' . $this->stradvmessage . '</span>';
@@ -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 '<td class="' . $permname . '">' . $this->strperms[$permname] . '</td>';
+        $defaultperm = $this->allpermissions[$this->parentpermissions[$capability->name]];
+        if ($permname != $defaultperm) {
+            $default = get_string('defaultx', 'role', $this->strperms[$defaultperm]);
+        } else {
+            $default = "&#xa0;";
+        }
+        echo '<td class="' . $permname . '">' . $this->strperms[$permname] . '<span class="note">' .
+                $default . '</span></td>';
         
     }
 }
 
 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 (executable)
index cfec37f..0000000
+++ /dev/null
@@ -1,236 +0,0 @@
-<?php  //$Id$
-
-    switch ($action) {
-        case 'add':
-            $submitlabel = get_string('addrole', 'role');
-            break;
-        case 'edit':
-        default:
-            $submitlabel = get_string('savechanges');
-    }
-
-    if ($action == 'view') {
-?>
-<?php
-    }
-?>
-<form id="rolesform" action="manage.php" method="post">
-<fieldset class="invisiblefieldset">
-<input type="hidden" name="roleid" value="<?php p($roleid) ?>" />
-<input type="hidden" name="sesskey" value="<?php p(sesskey()) ?>" />
-<input type="hidden" name="action" value="<?php if ($action != 'view') { echo p($action); } ?>" />
-</fieldset>
-<table class="roledesc" cellpadding="9" cellspacing="0">
-<?php if ($action == 'view') { ?>
-    <tr valign="top">
-        <td align="right"><?php print_string('name') ?></td>
-        <td><?php p(format_string($role->name)); ?></td>
-    </tr>
-    <tr valign="top">
-        <td align="right"><?php print_string('shortname') ?></td>
-        <td><?php p($role->shortname); ?></td>
-    </tr>
-    <tr valign="top">
-        <td align="right"><?php print_string('description') ?></td>
-        <td><?php p(format_text($role->description, FORMAT_HTML)); $usehtmleditor = false; ?></td>
-    </tr>
-    <tr valign="top">
-        <td align="right"><?php print_string('legacytype', 'role') ?></td>
-        <td><?php
-                $usehtmleditor = false;
-                if (empty($role->legacytype)) {
-                    print_string('none');
-                } else {
-                    print_string('legacy:'.$role->legacytype, 'role');
-                }
-            ?>
-        </td>
-    </tr>
-<?php } else { ?>
-    <tr valign="top">
-        <td align="right"><label for="name"><?php print_string('name') ?></label></td>
-        <td><?php
-            echo '<input type="text" id="name" name="name" maxlength="254" size="50" value="'.s($role->name).'" />';
-            if (isset($errors["name"])) formerr($errors["name"]);
-            ?>
-        </td>
-    </tr>
-    <tr valign="top">
-        <td align="right"><label for="shortname"><?php print_string('shortname') ?></label></td>
-        <td><?php
-            echo '<input type="text" id="shortname" name="shortname" maxlength="100" size="15" value="'.s($role->shortname).'" />';
-            if (isset($errors["shortname"])) formerr($errors["shortname"]);
-            ?>
-        </td>
-    </tr>
-    <tr valign="top">
-        <td align="right"><label for="edit-description"><?php print_string('description') ?></label></td>
-        <td><?php
-            print_textarea($usehtmleditor, 10, 50, 50, 10, 'description', $role->description);
-            ?>
-        </td>
-    </tr>
-    <tr valign="top">
-        <td align="right"><label for="menulegacytype"><?php print_string('legacytype', 'role') ?></label></td>
-        <td><?php
-            $options = array();
-            $options[''] = get_string('none');
-            $legacyroles = get_legacy_roles();
-            foreach($legacyroles as $ltype=>$lcap) {
-                $options[$ltype] = get_string('legacy:'.$ltype, 'role');
-            }
-            choose_from_menu($options, 'legacytype', $role->legacytype, '');
-            ?>
-        </td>
-    </tr>
-<?php } ?>
-    <tr valign="top">
-        <td align="right"><?php print_string('maybeassignedin', 'role') ?></td>
-        <td><?php
-            foreach ($allcontextlevels as $cl => $clname) {
-                $extraarguments = $disabled;
-                if (in_array($cl, $rolecontextlevels)) {
-                    $extraarguments .= 'checked="checked"';
-                }
-                echo '<input type="checkbox" id="cl' . $cl . '" name="contextlevel' . $cl .
-                        '" value="1" ' . $extraarguments . '/> ';
-                echo '<label for="cl' . $cl . '">' . $clname . "</label><br />\n";
-            }
-            ?>
-        </td>
-    </tr>
-</table>
-
-<?php
-    print_heading_with_help(get_string('permissions','role'), 'permissions');
-
-    $strinherit = get_string('notset','role');
-    $strallow = get_string('allow','role');
-    $strprevent = get_string('prevent','role');
-    $strprohibit = get_string('prohibit','role');
-?>
-
-<table class="rolecap" id="managerolestable">
-
-<tr>
-<th class="name" align="left" scope="col"><?php print_string('capability','role') ?></th>
-<th class="inherit" scope="col"><?php p($strinherit); ?></th>
-<th class="allow" scope="col"><?php p($strallow); ?></th>
-<th class="prevent" scope="col"><?php p($strprevent); ?></th>
-<th class="prohibit" scope="col"><?php p($strprohibit); ?></th>
-<th class="risk" colspan="5" scope="col"><?php print_string('risks','role') ?></th>
-</tr>
-
-<?php
-
-// init these 2
-$contextlevel = 0;
-$component = '';
-
-$strrisks = s(get_string('risks', 'role'));
-// MDL-11687
-$strcapabilities = 'Capabilities';//s(get_string('capabilities', 'role'));
-
-// prepare legacy defaults
-if (!empty($role->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 ('<tr class="rolecapheading header"><td colspan="10" class="header"><strong>'.
-               get_component_string($capability->component, $capability->contextlevel).'</strong></td></tr>');
-    }
-
-    // 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 = '<td class="risk managetrust">';
-    $rowclasses = '';
-    if (RISK_MANAGETRUST & (int)$capability->riskbitmask) {
-        $riskinfo .= '<a onclick="this.target=\'docspopup\'" title="'.get_string('riskmanagetrust', 'admin').'" href="'.$CFG->docroot.'/'.$lang.'/'.$strrisks.'">';
-        $riskinfo .= '<img src="'.$CFG->pixpath.'/i/risk_managetrust.gif" alt="'.get_string('riskmanagetrustshort', 'admin').'" /></a>';
-        $rowclasses .= ' riskmanagetrust';
-    }
-    $riskinfo .= '</td><td class="risk config">';
-    if (RISK_CONFIG & (int)$capability->riskbitmask) {
-        $riskinfo .= '<a onclick="this.target=\'docspopup\'" title="'.get_string('riskconfig', 'admin').'" href="'.$CFG->docroot.'/'.$lang.'/'.$strrisks.'">';
-        $riskinfo .= '<img src="'.$CFG->pixpath.'/i/risk_config.gif" alt="'.get_string('riskconfigshort', 'admin').'" /></a>';
-        $rowclasses .= ' riskconfig';
-    }
-    $riskinfo .= '</td><td class="risk xss">';
-    if (RISK_XSS & (int)$capability->riskbitmask) {
-        $riskinfo .= '<a onclick="this.target=\'docspopup\'" title="'.get_string('riskxss', 'admin').'" href="'.$CFG->docroot.'/'.$lang.'/'.$strrisks.'">';
-        $riskinfo .= '<img src="'.$CFG->pixpath.'/i/risk_xss.gif" alt="'.get_string('riskxssshort', 'admin').'" /></a>';
-        $rowclasses .= ' riskxss';
-    }
-    $riskinfo .= '</td><td class="risk personal">';
-    if (RISK_PERSONAL & (int)$capability->riskbitmask) {
-        $riskinfo .= '<a onclick="this.target=\'docspopup\'" title="'.get_string('riskpersonal', 'admin').'" href="'.$CFG->docroot.'/'.$lang.'/'.$strrisks.'">';
-        $riskinfo .= '<img src="'.$CFG->pixpath.'/i/risk_personal.gif" alt="'.get_string('riskpersonalshort', 'admin').'" /></a>';
-        $rowclasses .= ' riskpersonal';
-    }
-    $riskinfo .= '</td><td class="risk spam">';
-    if (RISK_SPAM & (int)$capability->riskbitmask) {
-        $riskinfo .= '<a onclick="this.target=\'docspopup\'" title="'.get_string('riskspam', 'admin').'" href="'.$CFG->docroot.'/'.$lang.'/'.$strrisks.'">';
-        $riskinfo .= '<img src="'.$CFG->pixpath.'/i/risk_spam.gif" alt="'.get_string('riskspamshort', 'admin').'" /></a>';
-        $rowclasses .= ' riskspam';
-    }
-    $riskinfo .= '</td>';
-
-    $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' : '';
-
-    ?>
-
-        <tr class="rolecap <?php echo $rowclasses; ?>">
-        <td class="name"><span class="cap-desc"><a onclick="this.target='docspopup'" href="<?php echo $CFG->docroot.'/'.$lang.'/'.$strcapabilities.'/'.$capability->name ?>"><?php echo get_capability_string($capability->name); ?></a><span class="cap-name"><?php echo $capability->name ?></span></span></td>
-        <td class="inherit <?php echo $isinherit ?>">
-          <input type="radio" title="<?php p($strinherit); ?>" name="<?php echo $capability->name; ?>" value="<?php echo CAP_INHERIT ?>" <?php if (!isset($localoverride->permission) || $localoverride->permission==CAP_INHERIT){ echo 'checked="checked"'; }?> <?php echo $disabled; ?>/></td>
-        <td class="allow <?php echo $isallow ?>">
-          <input type="radio" title="<?php p($strallow); ?>" name="<?php echo $capability->name; ?>" value="<?php echo CAP_ALLOW ?>" <?php if (isset($localoverride->permission) && $localoverride->permission==CAP_ALLOW){ echo 'checked="checked"'; }?> <?php echo $disabled; ?>/></td>
-        <td class="prevent <?php echo $isprevent ?>" >
-          <input type="radio" title="<?php p($strprevent); ?>" name="<?php echo $capability->name; ?>" value="<?php echo CAP_PREVENT ?>" <?php if (isset($localoverride->permission) && $localoverride->permission==CAP_PREVENT){ echo 'checked="checked"'; }?> <?php echo $disabled; ?>/></td>
-        <td class="prohibit <?php echo $isprohibit ?>" >
-          <input type="radio" title="<?php p($strprohibit); ?>" name="<?php echo $capability->name; ?>" value="<?php echo CAP_PROHIBIT ?>" <?php if (isset($localoverride->permission) && $localoverride->permission==CAP_PROHIBIT){ echo 'checked="checked"'; }?> <?php echo $disabled; ?>/></td>
-
-        <?php echo $riskinfo; ?>
-
-        </tr>
-
-<?php } ?>
-</table>
-
-<?php if ($action != 'view') { ?>
-<div class="submit buttons">
-<input type="submit" value="<?php p($submitlabel) ?>" />
-<input type="submit" name="cancel" value="<?php print_string('cancel') ?>" />
-</div>
-<?php }
-
-print_js_call('cap_table_filter.init',
-        array('managerolestable', get_string('search'), get_string('clear')));
-?>
-
-</form>
index 48bed9abaf5a272d70ff1705bdaca29d09b9d00c..70918a76f3a7554595ff25983317c57b28a2e05f 100755 (executable)
 /**
  * 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
  *//** */
     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);
                 $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&amp;roleid='.$roleid);
-
-            } else {
+            if (!$confirmed) {
                 // show confirmation
                 admin_externalpage_print_header();
                 $optionsyes = array('action'=>'reset', 'roleid'=>$roleid, 'sesskey'=>sesskey(), 'confirm'=>1);
                 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&amp;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(
+            '<a href="' . $defineurl . '?action=view&amp;roleid=' . $role->id . '">' . $role->localname . '</a>',
+            format_text($role->description, FORMAT_HTML),
+            s($role->shortname),
+            '',
+        );
+
+    /// Icons:
+        // move up
+        if ($role->sortorder != 0) {
+            $row[3] .= get_action_icon($baseurl . '?action=moveup&amp;roleid=' . $role->id . '&amp;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 '<div class="selector">';
-        if ($action == 'view') {
-            popup_form('manage.php?action=view&amp;roleid=', $roleoptions, 'switchrole', $roleid, '', '', '',
-                       false, 'self', get_string('selectrole', 'role'));
-
-            echo '<div class="buttons">';
-
-            $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 '</div>';
+            $row[3] .= get_spacer();
         }
-        echo '</div>';
-
-        $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&amp;roleid=' . $role->id . '&amp;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] = '<a href="manage.php?roleid='.$role->id.'&amp;action=view">'.format_string($role->name).'</a>';
-            $row[1] = format_text($role->description, FORMAT_HTML);
-            $row[2] = s($role->shortname);
-            $row[3] = '<a title="'.$stredit.'" href="manage.php?action=edit&amp;roleid='.$role->id.'">'.
-                         '<img src="'.$CFG->pixpath.'/t/edit.gif" class="iconsmall" alt="'.$stredit.'" /></a> ';
-            if (in_array($role->id, $defaultroles)) {
-                $row[3] .= '<img src="'.$CFG->wwwroot.'/pix/spacer.gif" class="iconsmall" alt="" /> ';
-            } else {
-                $row[3] .= '<a title="'.$strdelete.'" href="manage.php?action=delete&amp;roleid='.$role->id.'&amp;sesskey='.sesskey().'">'.
-                             '<img src="'.$CFG->pixpath.'/t/delete.gif" class="iconsmall" alt="'.$strdelete.'" /></a> ';
-            }
-            if ($role->sortorder != 0) {
-                $row[3] .= '<a title="'.$strmoveup.'" href="manage.php?action=moveup&amp;roleid='.$role->id.'&amp;sesskey='.sesskey().'">'.
-                     '<img src="'.$CFG->pixpath.'/t/up.gif" class="iconsmall" alt="'.$strmoveup.'" /></a> ';
-            } else {
-                $row[3] .= '<img src="'.$CFG->wwwroot.'/pix/spacer.gif" class="iconsmall" alt="" /> ';
-            }
-            if ($role->sortorder+1 < $rolescount) {
-                $row[3] .= '<a title="'.$strmovedown.'" href="manage.php?action=movedown&amp;roleid='.$role->id.'&amp;sesskey='.sesskey().'">'.
-                     '<img src="'.$CFG->pixpath.'/t/down.gif" class="iconsmall" alt="'.$strmovedown.'" /></a> ';
-            } else {
-                $row[3] .= '<img src="'.$CFG->wwwroot.'/pix/spacer.gif" class="iconsmall" alt="" /> ';
-            }
-
-            $table->data[] = $row;
-
+        // edit
+        $row[3] .= get_action_icon($defineurl . '?action=edit&amp;roleid=' . $role->id,
+                'edit', $stredit, get_string('editxrole', 'role', $role->localname));
+        // duplicate
+        $row[3] .= get_action_icon($defineurl . '?action=duplicate&amp;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&amp;roleid=' . $role->id, 
+                  'delete', $strdelete, get_string('deletexrole', 'role', $role->localname));
         }
-        print_table($table);
 
-        $options = new object();
-        $options->action = 'add';
-        echo '<div class="buttons">';
-        print_single_button('manage.php', $options, get_string('addrole', 'role'), 'get');
-        echo '</div>';
+        $table->data[] = $row;
     }
+    print_table($table);
+
+    echo '<div class="buttons">';
+    print_single_button($defineurl, array('action' => 'add'), get_string('addrole', 'role'), 'get');
+    echo '</div>';
 
     admin_externalpage_print_footer();
     die;
 
-
-
+function get_action_icon($url, $icon, $alt, $tooltip) {
+    global $CFG;
+    return '<a title="' . $tooltip . '" href="'. $url . '">' .
+            '<img src="' . $CFG->pixpath . '/t/' . $icon . '.gif" class="iconsmall" alt="' . $alt . '" /></a> ';
+}
+function get_spacer() {
+    global $CFG;
+    return '<img src="' . $CFG->pixpath . '/spacer.gif" class="iconsmall" alt="" /> ';
+}
 ?>
index e9365951751e62c9255514591e0bccb2096c1275..f1c71f72e8032235ed62b0b00300f8a7e3f1e3c5 100644 (file)
@@ -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';
index 8a87037951008e7de7720d02476e16e08ea37b68..ed1c1b8c31390a927f40b7fa8fd9af45f370f4bc 100644 (file)
@@ -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)\"?</p><p>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)\"?</p>';
 $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';
index e2b2792ae002137dde8edecdfe36f4bb785c0884..ea9d07a634d10db69967b484a82551809f7dd41c 100755 (executable)
@@ -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;
 }
 
 /**
index 7a06032cf59f53a97ef407588795b99698c0cd21..22f95b5a0d5fe9ae3e9d5ce7e0b449dfd4c044d1 100644 (file)
@@ -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;
 }
index 99a762568a4d4d340939cc3f9f79f140c530a41b..ea459dd254bab9804208545952b1c104d67af18d 100644 (file)
@@ -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;
 }
index 2170276ea4e325458fc9a7e92eb312d61ed6ea5a..b7bfbaa398ddd696f9a96be04c94346a92311b5b 100644 (file)
@@ -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;