From: tjhunt Date: Wed, 5 Nov 2008 08:17:30 +0000 (+0000) Subject: role overrides: MDL-17070 Improve override roles page to match the recent usability... X-Git-Url: http://git.mjollnir.org/gw?a=commitdiff_plain;h=faf75fe715a23d9228f59c62a3a306a39af899b4;p=moodle.git role overrides: MDL-17070 Improve override roles page to match the recent usability improvements on the assign page. Including: MDL-11529 When assigning/overriding roles, the dropdown for switching to another role should have a number in brackets MDL-16549 Should not be able to edit the permission associated with moodle/site:doanything on any role. --- diff --git a/admin/roles/assign.php b/admin/roles/assign.php index ba5282aa08..711d1c04d5 100755 --- a/admin/roles/assign.php +++ b/admin/roles/assign.php @@ -17,13 +17,11 @@ $extendperiod = optional_param('extendperiod', 0, PARAM_INT); $extendbase = optional_param('extendbase', 0, PARAM_INT); - $errors = array(); - $baseurl = $CFG->wwwroot . '/' . $CFG->admin . '/roles/assign.php?contextid=' . $contextid; if (!empty($userid)) { $baseurl .= '&userid='.$userid; } - if (!empty($courseid)) { + if ($courseid && $courseid != SITEID) { $baseurl .= '&courseid='.$courseid; } @@ -57,8 +55,8 @@ require_capability('moodle/role:assign', $context); /// These are needed early because of tabs.php - $overridableroles = get_overridable_roles($context, ROLENAME_BOTH); list($assignableroles, $assigncounts, $nameswithcounts) = get_assignable_roles($context, ROLENAME_BOTH, true); + $overridableroles = get_overridable_roles($context, ROLENAME_BOTH); /// Make sure this user can assign this role if ($roleid && !isset($assignableroles[$roleid])) { @@ -69,13 +67,21 @@ } /// Get some language strings - $strpotentialusers = get_string('potentialusers', 'role'); - $strexistingusers = get_string('existingusers', 'role'); - $straction = get_string('assignroles', 'role'); - $strsearch = get_string('search'); - $strshowall = get_string('showall'); - $strparticipants = get_string('participants'); - $strsearchresults = get_string('searchresults'); + $straction = get_string('assignroles', 'role'); // Used by tabs.php + +/// Work out an appropriate page title. + if ($roleid) { + $a = new stdClass; + $a->role = $assignableroles[$roleid]; + $a->context = $contextname; + $title = get_string('assignrolenameincontext', 'role', $a); + } else { + if ($isfrontpage) { + $title = get_string('frontpageroles', 'admin'); + } else { + $title = get_string('assignrolesin', 'role', $contextname); + } + } /// Build the list of options for the enrolment period dropdown. $unlimitedperiod = get_string('unlimited'); @@ -134,6 +140,7 @@ } /// Process incoming role assignments + $errors = array(); if (optional_param('add', false, PARAM_BOOL) && confirm_sesskey()) { $userstoassign = $potentialuserselector->get_selected_users(); if (!empty($userstoassign)) { @@ -176,7 +183,7 @@ } if (! role_assign($roleid, $adduser->id, 0, $context->id, $timestart, $timeend, $hidden)) { $a = new stdClass; - $a->role = $rolenames[$roleid]; + $a->role = $assignableroles[$roleid]; $a->user = fullname($adduser); $errors[] = get_string('assignerror', 'role', $a); } @@ -186,7 +193,7 @@ $potentialuserselector->invalidate_selected_users(); $currentuserselector->invalidate_selected_users(); - $rolename = $DB->get_field('role', 'name', array('id'=>$roleid)); + $rolename = $assignableroles[$roleid]; add_to_log($course->id, 'role', 'assign', 'admin/roles/assign.php?contextid='.$context->id.'&roleid='.$roleid, $rolename, '', $USER->id); // Counts have changed, so reload. list($assignableroles, $assigncounts, $nameswithcounts) = get_assignable_roles($context, ROLENAME_BOTH, true); @@ -201,7 +208,7 @@ foreach ($userstounassign as $removeuser) { if (! role_unassign($roleid, $removeuser->id, 0, $context->id)) { $a = new stdClass; - $a->role = $rolenames[$roleid]; + $a->role = $assignableroles[$roleid]; $a->user = fullname($removeuser); $errors[] = get_string('unassignerror', 'role', $a); } else if ($inmeta) { @@ -216,7 +223,7 @@ $potentialuserselector->invalidate_selected_users(); $currentuserselector->invalidate_selected_users(); - $rolename = $DB->get_field('role', 'name', array('id'=>$roleid)); + $rolename = $assignableroles[$roleid]; add_to_log($course->id, 'role', 'unassign', 'admin/roles/assign.php?contextid='.$context->id.'&roleid='.$roleid, $rolename, '', $USER->id); // Counts have changed, so reload. list($assignableroles, $assigncounts, $nameswithcounts) = get_assignable_roles($context, ROLENAME_BOTH, true); @@ -233,20 +240,20 @@ $navlinks = array(); if ($courseid != SITEID) { if (has_capability('moodle/course:viewparticipants', get_context_instance(CONTEXT_COURSE, $course->id))) { - $navlinks[] = array('name' => $strparticipants, 'link' => "$CFG->wwwroot/user/index.php?id=$course->id", 'type' => 'misc'); + $navlinks[] = array('name' => get_string('participants'), 'link' => "$CFG->wwwroot/user/index.php?id=$course->id", 'type' => 'misc'); } $navlinks[] = array('name' => $fullname, 'link' => "$CFG->wwwroot/user/view.php?id=$userid&course=$courseid", 'type' => 'misc'); $navlinks[] = array('name' => $straction, 'link' => null, 'type' => 'misc'); $navigation = build_navigation($navlinks); - print_header("$fullname", "$fullname", $navigation, "", "", true, " ", navmenu($course)); + print_header($title, "$fullname", $navigation, "", "", true, " ", navmenu($course)); /// site header } else { $navlinks[] = array('name' => $fullname, 'link' => "$CFG->wwwroot/user/view.php?id=$userid&course=$courseid", 'type' => 'misc'); $navlinks[] = array('name' => $straction, 'link' => null, 'type' => 'misc'); $navigation = build_navigation($navlinks); - print_header("$course->fullname: $fullname", $course->fullname, $navigation, "", "", true, " ", navmenu($course)); + print_header($title, $course->fullname, $navigation, "", "", true, " ", navmenu($course)); } $showroles = 1; @@ -268,15 +275,12 @@ include_once('tabs.php'); } + /// Print heading. + print_heading_with_help($title, 'assignroles'); + if ($roleid) { /// Show UI for assigning a particular role to users. - /// Print heading. - $a = new stdClass; - $a->role = $assignableroles[$roleid]; - $a->context = $contextname; - print_heading_with_help(get_string('assignrolenameincontext', 'role', $a), 'assignroles'); - /// Print a warning if we are assigning system roles. if ($context->contextlevel == CONTEXT_SYSTEM) { print_box(get_string('globalroleswarning', 'role')); @@ -285,8 +289,8 @@ /// Print the form. check_theme_arrows(); ?> -
- +
+ @@ -325,7 +329,7 @@
- +
'; } else { - /// Print UI for choosing a role to assign. - - if ($isfrontpage) { - print_heading_with_help(get_string('frontpageroles', 'admin'), 'assignroles'); - } else { - print_heading_with_help(get_string('assignrolesin', 'role', $contextname), 'assignroles'); - } + /// Show UI for choosing a role to assign. // Print a warning if we are assigning system roles. if ($context->contextlevel == CONTEXT_SYSTEM) { @@ -375,7 +373,7 @@ $rolehodlernames = array(); $strmorethanmax = get_string('morethan', 'role', MAX_USERS_TO_LIST_PER_ROLE); $showroleholders = false; - foreach ($assignableroles as $roleid => $rolename) { + foreach ($assignableroles as $roleid => $notused) { $roleusers = ''; if (0 < $assigncounts[$roleid] && $assigncounts[$roleid] <= MAX_USERS_TO_LIST_PER_ROLE) { $roleusers = get_role_users($roleid, $context, false, 'u.id, u.lastname, u.firstname'); @@ -396,8 +394,6 @@ // Print overview table $table->tablealign = 'center'; - $table->cellpadding = 5; - $table->cellspacing = 0; $table->width = '60%'; $table->head = array(get_string('role'), get_string('description'), get_string('userswiththisrole', 'role')); $table->wrap = array('nowrap', '', 'nowrap'); @@ -410,7 +406,8 @@ foreach ($assignableroles as $roleid => $rolename) { $description = format_string($DB->get_field('role', 'description', array('id'=>$roleid))); - $row = array(''.$rolename.'',$description, $assigncounts[$roleid]); + $row = array(''.$rolename.'', + $description, $assigncounts[$roleid]); if ($showroleholders) { $row[] = $rolehodlernames[$roleid]; } diff --git a/admin/roles/manage.html b/admin/roles/manage.html index 22c08364a6..4618a8275e 100755 --- a/admin/roles/manage.html +++ b/admin/roles/manage.html @@ -126,7 +126,7 @@ if (!empty($role->legacytype)) { foreach ($capabilities as $capability) { //legacy caps have their own selector - if (islegacy($capability->name)) { + if (is_legacy($capability->name)) { continue; } diff --git a/admin/roles/manage.php b/admin/roles/manage.php index dba2f525be..1c823e7e51 100755 --- a/admin/roles/manage.php +++ b/admin/roles/manage.php @@ -102,7 +102,7 @@ } // legacy caps have their own selector - if (islegacy($data->{$cap->name})) { + if (is_legacy($data->{$cap->name})) { continue; } @@ -176,7 +176,7 @@ } // legacy caps have their own selector - if (islegacy($data->{$cap->name}) === 0 ) { + if (is_legacy($data->{$cap->name}) === 0 ) { continue; } diff --git a/admin/roles/override.html b/admin/roles/override.html deleted file mode 100755 index a366723034..0000000000 --- a/admin/roles/override.html +++ /dev/null @@ -1,160 +0,0 @@ - - -
-
- - - - - -
- - - - - - - - - - - name)) { - continue; - } - - // prints a breaker if component or name or context level - //if ($capability->component != $component or $capability->contextlevel != $contextlevel) { - if (component_level_changed($capability, $component, $contextlevel)) { - echo (''); - } - - // these 2 are used to see to group same mod/core capabilities together - $contextlevel = $capability->contextlevel; - $component = $capability->component; - - // check the capability override for this cap, this role in this context - if (isset($localoverrides[$capability->name])) { - $localpermission = $localoverrides[$capability->name]->permission; - } else { - $localpermission = 0; // Just inherit - } - - if (isset($r_caps[$capability->name])) { - $isallow = $r_caps[$capability->name] > 0; - $isprevent = $r_caps[$capability->name] < 0 && $r_caps[$capability->name] > -500; - $isprohibit = $r_caps[$capability->name] <= -500; - } else { - $isallow = 0; - $isprevent = 0; - $isprohibit = 0; - } - - $isdisabled = $isprohibit || $capability->locked; - - $riskinfo = ''; - - - ?> - - - - - - - - - - - - - - - -
'.get_component_string($capability->component, $capability->contextlevel).'
'; - $rowclasses = ''; - if (RISK_MANAGETRUST & (int)$capability->riskbitmask) { - $riskinfo .= ''; - $riskinfo .= ''.get_string('riskmanagetrustshort', 'admin').''; - $rowclasses .= ' riskmanagetrust'; - } - $riskinfo .= ''; - if (RISK_CONFIG & (int)$capability->riskbitmask) { - $riskinfo .= ''; - $riskinfo .= ''.get_string('riskconfigshort', 'admin').''; - $rowclasses .= ' riskconfig'; - } - $riskinfo .= ''; - if (RISK_XSS & (int)$capability->riskbitmask) { - $riskinfo .= ''; - $riskinfo .= ''.get_string('riskxssshort', 'admin').''; - $rowclasses .= ' riskxss'; - } - $riskinfo .= ''; - if (RISK_PERSONAL & (int)$capability->riskbitmask) { - $riskinfo .= ''; - $riskinfo .= ''.get_string('riskpersonalshort', 'admin').''; - $rowclasses .= ' riskpersonal'; - } - $riskinfo .= ''; - if (RISK_SPAM & (int)$capability->riskbitmask) { - $riskinfo .= ''; - $riskinfo .= ''.get_string('riskspamshort', 'admin').''; - $rowclasses .= ' riskspam'; - } - $riskinfo .= '
name); ?>name ?> - /> - - /> - - /> - - /> -
-
- - -
- - '.$strsafewarning.''; - } - - if (count($capabilities) > 12) { - print_js_call('cap_table_filter.init', - array('overriderolestable', get_string('search'), get_string('clear'))); - } - ?> - -
diff --git a/admin/roles/override.php b/admin/roles/override.php index b5c5631be7..5cef6e4b4c 100755 --- a/admin/roles/override.php +++ b/admin/roles/override.php @@ -6,27 +6,31 @@ $roleid = optional_param('roleid', 0, PARAM_INT); // requested role id $userid = optional_param('userid', 0, PARAM_INT); // needed for user tabs $courseid = optional_param('courseid', 0, PARAM_INT); // needed for user tabs - $cancel = optional_param('cancel', 0, PARAM_BOOL); - if (!$context = $DB->get_record('context', array('id'=>$contextid))) { - print_error('wrongcontextid', 'error'); + $baseurl = $CFG->wwwroot . '/' . $CFG->admin . '/roles/override.php?contextid=' . $contextid; + if (!empty($userid)) { + $baseurl .= '&userid=' . $userid; } - - if (!$sitecontext = get_context_instance(CONTEXT_SYSTEM)) { - print_error('nositeid', 'error'); + if ($courseid && $courseid != SITEID) { + $baseurl .= '&courseid=' . $courseid; } - if ($context->id == $sitecontext->id) { - print_error('cannotoverridebaserole', 'error'); + if (!$context = $DB->get_record('context', array('id'=>$contextid))) { + print_error('wrongcontextid', 'error'); } + $isfrontpage = $context->contextlevel == CONTEXT_COURSE && $context->instanceid == SITEID; + $contextname = print_context_name($context); - $canoverride = has_capability('moodle/role:override', $context); - - if (!$canoverride and !has_capability('moodle/role:safeoverride', $context)) { - print_error('nopermissions', 'error', '', 'change overrides in this context!'); + if ($context->contextlevel == CONTEXT_SYSTEM) { + print_error('cannotoverridebaserole', 'error'); } - if ($courseid) { + if ($context->contextlevel == CONTEXT_COURSE) { + $courseid = $context->instanceid; + if (!$course = $DB->get_record('course', array('id'=>$courseid))) { + print_error('invalidcourse'); + } + } if ($courseid) { // we need this for user tabs in user context if (!$course = $DB->get_record('course', array('id'=>$courseid))) { print_error('invalidcourse'); } @@ -37,96 +41,79 @@ require_login($course); - $baseurl = 'override.php?contextid='.$context->id; - if (!empty($userid)) { - $baseurl .= '&userid='.$userid; - } - if ($courseid != SITEID) { - $baseurl .= '&courseid='.$courseid; + $safeoverridesonly = !has_capability('moodle/role:override', $context); + if ($safeoverridesonly) { + require_capability('moodle/role:safeoverride', $context); } - if ($cancel) { + if (optional_param('cancel', false, PARAM_BOOL)) { redirect($baseurl); } -/// needed for tabs.php - $overridableroles = get_overridable_roles($context, ROLENAME_BOTH); +/// These are needed early because of tabs.php $assignableroles = get_assignable_roles($context, ROLENAME_BOTH); - -/// Get some language strings - - $strroletooverride = get_string('roletooverride', 'role'); - $straction = get_string('overrideroles', 'role'); - $strcurrentrole = get_string('currentrole', 'role'); - $strparticipants = get_string('participants'); + list($overridableroles, $overridecounts, $nameswithcounts) = get_overridable_roles($context, ROLENAME_BOTH, true); /// Make sure this user can override that role + if ($roleid && !isset($overridableroles[$roleid])) { + $a = stdClass; + $a->role = $roleid; + $a->context = $contextname; + print_error('cannotoverriderolehere', '', get_context_url($context), $a); + } + +/// Get some language strings + $straction = get_string('overrideroles', 'role'); // Used by tabs.php +/// Work out an appropriate page title. if ($roleid) { - if (!isset($overridableroles[$roleid])) { - error ('you can not override this role in this context'); + $a = new stdClass; + $a->role = $overridableroles[$roleid]; + $a->context = $contextname; + $title = get_string('overridepermissionsforrole', 'role', $a); + } else { + if ($isfrontpage) { + $title = get_string('frontpageoverrides', 'admin'); + } else { + $title = get_string('overridepermissionsin', 'role', $contextname); } } - if ($userid) { - $user = $DB->get_record('user', array('id'=>$userid)); - $fullname = fullname($user, has_capability('moodle/site:viewfullnames', $context)); - } - /// get all cababilities $safeoverridenotice = false; if ($roleid) { - if ($capabilities = fetch_context_capabilities($context)) { - // find out if we need to lock some capabilities - foreach ($capabilities as $capname=>$capability) { - $capabilities[$capname]->locked = false; - if ($canoverride) { - //ok no locking at all - continue; - } - //only limited safe overrides - spam only allowed - if ((RISK_DATALOSS & (int)$capability->riskbitmask) - or (RISK_MANAGETRUST & (int)$capability->riskbitmask) - or (RISK_CONFIG & (int)$capability->riskbitmask) - or (RISK_XSS & (int)$capability->riskbitmask) - or (RISK_PERSONAL & (int)$capability->riskbitmask)) { - $capabilities[$capname]->locked = true; - $safeoverridenotice = true; - } + $capabilities = fetch_context_capabilities($context); + if (!$capabilities) { + $capabilities = array(); + } + // Determine which capabilities should be locked. + foreach ($capabilities as $capname=>$capability) { + $capabilities[$capname]->locked = false; + if ($safeoverridesonly && !is_safe_capability($capability)) { + $capabilities[$capname]->locked = true; + $safeoverridenotice = true; } } - } else { - $capabilities = null; } -/// Process incoming role override - if ($data = data_submitted() and $roleid and confirm_sesskey()) { - $allowed_values = array(CAP_INHERIT, CAP_ALLOW, CAP_PREVENT, CAP_PROHIBIT); - - $localoverrides = $DB->get_records_select('role_capabilities', "roleid = ? AND contextid = ?", array($roleid, $context->id), - '', 'capability, permission, id'); + if ($roleid && optional_param('savechanges', false, PARAM_BOOL) && confirm_sesskey()) { + /// Process incoming role override + $localoverrides = $DB->get_records('role_capabilities', array('roleid' => $roleid, + 'contextid' => $context->id), '', 'capability,permission,id'); foreach ($capabilities as $cap) { - if ($cap->locked) { + if ($cap->locked || is_legacy($cap->name)) { //user not allowed to change this cap continue; } - if (!isset($data->{$cap->name})) { + $capname = $cap->name; + $value = optional_param($capname, null, PARAM_PERMISSION); + if (is_null($value)) { //cap not specified in form continue; } - if (islegacy($data->{$cap->name})) { - continue; - } - - $capname = $cap->name; - $value = clean_param($data->{$cap->name}, PARAM_INT); - if (!in_array($value, $allowed_values)) { - continue; - } - if (isset($localoverrides[$capname])) { // Something exists, so update it assign_capability($capname, $value, $roleid, $context->id, true); @@ -139,33 +126,35 @@ // force accessinfo refresh for users visiting this context... mark_context_dirty($context->path); - $rolename = $DB->get_field('role', 'name', array('id'=>$roleid)); + $rolename = $overridableroles[$roleid]; add_to_log($course->id, 'role', 'override', 'admin/roles/override.php?contextid='.$context->id.'&roleid='.$roleid, $rolename, '', $USER->id); redirect($baseurl); } - /// Print the header and tabs require_js(array('yui_yahoo', 'yui_dom', 'yui_event')); require_js($CFG->admin . '/roles/roles.js'); if ($context->contextlevel == CONTEXT_USER) { - $navlinks = array(); + $user = $DB->get_record('user', array('id'=>$userid)); + $fullname = fullname($user, has_capability('moodle/site:viewfullnames', $context)); + /// course header + $navlinks = array(); if ($course->id != SITEID) { if (has_capability('moodle/course:viewparticipants', get_context_instance(CONTEXT_COURSE, $course->id))) { - $navlinks[] = array('name' => $strparticipants, 'link' => "$CFG->wwwroot/user/index.php?id=$course->id", 'type' => 'misc'); + $navlinks[] = array('name' => get_string('participants'), 'link' => "$CFG->wwwroot/user/index.php?id=$course->id", 'type' => 'misc'); } $navlinks[] = array('name' => $fullname, 'link' => "$CFG->wwwroot/user/view.php?id=$userid&course=$courseid", 'type' => 'misc'); $navlinks[] = array('name' => $straction, 'link' => null, 'type' => 'misc'); $navigation = build_navigation($navlinks); - print_header("$fullname", "$fullname", $navigation, "", "", true, " ", navmenu($course)); + print_header($title, "$fullname", $navigation, "", "", true, " ", navmenu($course)); /// site header } else { $navlinks[] = array('name' => $fullname, 'link' => "$CFG->wwwroot/user/view.php?id=$userid&course=$courseid", 'type' => 'misc'); $navlinks[] = array('name' => $straction, 'link' => null, 'type' => 'misc'); $navigation = build_navigation($navlinks); - print_header("$course->fullname: $fullname", $course->fullname, $navigation, "", "", true, " ", navmenu($course)); + print_header($title, $course->fullname, $navigation, "", "", true, " ", navmenu($course)); } $showroles = 1; $currenttab = 'override'; @@ -181,62 +170,179 @@ include_once('tabs.php'); } - print_heading_with_help(get_string('overridepermissionsin', 'role', print_context_name($context)), 'overrides'); + print_heading_with_help($title, 'overrides'); if ($roleid) { - /// prints a form to swap roles - echo '
'; - $overridableroles = array('0'=>get_string('listallroles', 'role').'...') + $overridableroles; - popup_form("$CFG->wwwroot/$CFG->admin/roles/override.php?userid=$userid&courseid=$courseid&contextid=$contextid&roleid=", - $overridableroles, 'switchrole', $roleid, '', '', '', false, 'self', $strroletooverride); - echo '
'; - - $parentcontexts = get_parent_contexts($context); - if (!empty($parentcontexts)) { - $parentcontext = array_shift($parentcontexts); - $parentcontext = get_context_instance_by_id($parentcontext); - } else { - $parentcontext = $context; // site level in override?? - } + /// Show UI for overriding roles. + /// Get the capabiltites from the parent context, so that can be shown in the interface. + $parentcontext = get_context_instance_by_id(get_parent_contextid($context)); $r_caps = role_context_capabilities($roleid, $parentcontext); - $localoverrides = $DB->get_records_select('role_capabilities', "roleid = ? AND contextid = ?", array($roleid, $context->id), - '', 'capability, permission, id'); - - $lang = str_replace('_utf8', '', current_language()); + /// And get the current overrides in this context. + $localoverrides = $DB->get_records('role_capabilities', array('roleid' => $roleid, + 'contextid' => $context->id), '', 'capability,permission,id'); if (!empty($capabilities)) { // Print the capabilities overrideable in this context - print_simple_box_start('center'); - include('override.html'); - print_simple_box_end(); + print_box_start('generalbox boxwidthwide boxaligncenter'); + + $allrisks = get_all_risks(); + $allpermissions = array( + CAP_INHERIT => 'inherit', + CAP_ALLOW => 'allow', + CAP_PREVENT => 'prevent' , + CAP_PROHIBIT => 'prohibit', + ); + $strperms = array(); + foreach ($allpermissions as $permname) { + $strperms[$permname] = get_string($permname, 'role'); + } +?> +
" method="post">
+ + + + + + $strpermname) { + echo ''; + } + echo ''; + echo "\n"; + + /// Loop over capabilities. + $contextlevel = 0; + $component = ''; + foreach ($capabilities as $capability) { + + /// Legacy caps and doanything should not be overriden - we must use proper capabilities if needed + if (is_legacy($capability->name)) { + continue; + } + + /// Prints a breaker if component or name or context level has changed + if (component_level_changed($capability, $component, $contextlevel)) { + echo ''; + } + $contextlevel = $capability->contextlevel; + $component = $capability->component; + + /// Check the capability override for this cap, this role in this context + if (isset($localoverrides[$capability->name])) { + $localpermission = $localoverrides[$capability->name]->permission; + } else { + $localpermission = 0; // Just inherit + } + + if (!isset($r_caps[$capability->name])) { + $r_caps[$capability->name] = CAP_INHERIT; + } + + $disabled = ''; + if ($capability->locked || $r_caps[$capability->name] == CAP_PROHIBIT) { + $disabled = ' disabled="disabled"'; + } + + /// Start the table row. + $rowclasses = array('rolecap'); + foreach ($allrisks as $riskname => $risk) { + if ($risk & (int)$capability->riskbitmask) { + $rowclasses[] = $riskname; + } + } + echo ''; + + /// Table cell for the capability name. + echo ''; + + /// One cell for each possible permission. + foreach ($allpermissions as $perm => $permname) { + $extraclass = ''; + if ($perm != CAP_INHERIT && $perm == $r_caps[$capability->name]) { + $extraclass = ' capcurrent'; + } + $checked = ''; + if ($localpermission == $perm) { + $checked = ' checked="checked"'; + } + echo ''; + } + + /// One cell for each possible risk. + foreach ($allrisks as $riskname => $risk) { + echo ''; + } + + /// End of the row. + echo "\n"; + } +?> +
' . $strpermname . '' . get_string('risks','role') . '
' . + get_component_string($capability->component, $capability->contextlevel) . + '
' . get_capability_docs_link($capability) . + '' . $capability->name . ''; + echo ''; + echo ''; + if ($risk & (int)$capability->riskbitmask) { + print_risk_icon($riskname); + } + echo '
+
+ + +
+ + ' . get_string('safeoverridenotice', 'role') . "
\n"; + } + + if (count($capabilities) > 12) { + print_js_call('cap_table_filter.init', + array('overriderolestable', get_string('search'), get_string('clear'))); + } + + echo "
\n"; + print_box_end(); } else { - notice(get_string('nocapabilitiesincontext', 'role'), - $CFG->wwwroot.'/'.$CFG->admin.'/roles/'.$baseurl); + print_box(get_string('nocapabilitiesincontext', 'role'), 'generalbox boxaligncenter'); } - } else { // Print overview table + /// Print a form to swap roles, and a link back to the all roles list. + echo ''; + + } else { + /// Show UI for choosing a role to assign. $table->tablealign = 'center'; - $table->cellpadding = 5; - $table->cellspacing = 0; $table->width = '60%'; - $table->head = array(get_string('roles', 'role'), get_string('description'), get_string('overrides', 'role')); + $table->head = array(get_string('role'), get_string('description'), get_string('overrides', 'role')); $table->wrap = array('nowrap', '', 'nowrap'); $table->align = array('right', 'left', 'center'); foreach ($overridableroles as $roleid => $rolename) { $countusers = 0; - $overridecount = $DB->count_records_select('role_capabilities', "roleid = ? AND contextid = ?", array($roleid, $context->id)); $description = format_string($DB->get_field('role', 'description', array('id'=>$roleid))); - $table->data[] = array(''.$rolename.'', $description, $overridecount); + $table->data[] = array(''.$rolename.'', + $description, $overridecounts[$roleid]); } print_table($table); + + if (!$isfrontpage && ($url = get_context_url($context))) { + echo ''; + } } print_footer($course); -?> +?> \ No newline at end of file diff --git a/admin/roles/tabs.php b/admin/roles/tabs.php index cc2e68579c..893763f583 100755 --- a/admin/roles/tabs.php +++ b/admin/roles/tabs.php @@ -16,7 +16,10 @@ if ($currenttab != 'update') { $navlinks[] = array('name' => $stradministration, 'link' => '../index.php', 'type' => 'misc'); $navlinks[] = array('name' => $straction, 'link' => null, 'type' => 'misc'); $navigation = build_navigation($navlinks); - print_header($SITE->fullname, "$SITE->fullname", $navigation); + if (empty($title)) { + $title = $SITE->fullname; + } + print_header($title, "$SITE->fullname", $navigation); break; case CONTEXT_USER: @@ -37,13 +40,14 @@ if ($currenttab != 'update') { 'type' => 'misc'); $navigation = build_navigation($navlinks); - print_header("$SITE->shortname: $category->name", "$SITE->fullname: $strcourses", $navigation, "", "", true); + if (empty($title)) { + $title = "$SITE->shortname: $category->name"; + } + print_header($title, "$SITE->fullname: $strcourses", $navigation, "", "", true); break; case CONTEXT_COURSE: if ($context->instanceid != SITEID) { - $streditcoursesettings = get_string("editcoursesettings"); - $course = $DB->get_record('course', array('id'=>$context->instanceid)); require_login($course); @@ -51,7 +55,10 @@ if ($currenttab != 'update') { 'link' => "$CFG->wwwroot/admin/roles/assign.php?contextid=$context->id", 'type' => 'misc'); $navigation = build_navigation($navlinks); - print_header($streditcoursesettings, $course->fullname, $navigation); + if (empty($title)) { + $title = get_string("editcoursesettings"); + } + print_header($title, $course->fullname, $navigation); } break; @@ -76,7 +83,6 @@ if ($currenttab != 'update') { require_login($course); $fullmodulename = get_string("modulename", $module->name); - $streditinga = get_string("editinga", "moodle", $fullmodulename); $strmodulenameplural = get_string("modulenameplural", $module->name); if ($module->name == "label") { @@ -99,7 +105,10 @@ if ($currenttab != 'update') { $navigation = build_navigation($navlinks); - print_header_simple($streditinga, '', $navigation, $focuscursor, "", false); + if (empty($title)) { + $title = get_string("editinga", "moodle", $fullmodulename); + } + print_header_simple($title, '', $navigation, $focuscursor, "", false); break; diff --git a/lang/en_utf8/admin.php b/lang/en_utf8/admin.php index a6e3bc67dc..be20575097 100644 --- a/lang/en_utf8/admin.php +++ b/lang/en_utf8/admin.php @@ -397,6 +397,7 @@ $string['frontpagebackup'] = 'Front Page backup'; $string['frontpagedefaultrole'] = 'Default frontpage role'; $string['frontpageloggedin'] = 'Front page items when logged in'; $string['frontpagequestions'] = 'Front Page questions'; +$string['frontpageoverrides'] = 'Front Page permission overrides'; $string['frontpagerestore'] = 'Front Page restore'; $string['frontpageroles'] = 'Front Page roles'; $string['frontpagesettings'] = 'Front Page settings'; @@ -659,6 +660,8 @@ $string['restrictmodulesfor'] = 'Restrict modules for'; $string['reverseproxy'] = 'Reverse proxy'; $string['riskconfig'] = 'Users could change site configuration and behaviour'; $string['riskconfigshort'] = 'Configuration risk'; +$string['riskdataloss'] = 'Users could destroy large amounts of content or information'; +$string['riskdatalossshort'] = 'Data loss risk'; $string['riskmanagetrust'] = 'Users could change trust settings of other users'; $string['riskmanagetrustshort'] = 'Manage trusts'; $string['riskpersonal'] = 'Users could gain access to private information of other users'; diff --git a/lang/en_utf8/error.php b/lang/en_utf8/error.php index 5ec665fcf3..7d41d3585e 100644 --- a/lang/en_utf8/error.php +++ b/lang/en_utf8/error.php @@ -96,6 +96,7 @@ $string['cannotopenforwrit'] = 'Cannot open for writing: $a'; $string['cannotopenfile'] = 'Cannot open file ($a)'; $string['cannotopentemplate'] = 'Cannot open template file ($a)'; $string['cannotoverridebaserole'] = 'Cannot override base role capabilities'; +$string['cannotoverriderolehere'] = 'You are not allowed to override this role (id = $a->roleid) in this context ($a->context)'; $string['cannotreadtmpfile'] = 'Error reading temporary file'; $string['cannotreaduploadfile'] = 'Could not read uploaded file'; $string['cannotreadfile'] = 'Cannot read file ($a)'; diff --git a/lang/en_utf8/role.php b/lang/en_utf8/role.php index cbc3bb388c..97209746e2 100644 --- a/lang/en_utf8/role.php +++ b/lang/en_utf8/role.php @@ -59,25 +59,12 @@ $string['course:viewparticipants'] = 'View participants'; $string['course:viewscales'] = 'View scales'; $string['course:visibility'] = 'Hide/show courses'; $string['createhiddenassign'] = 'Create hidden role assignments'; -$string['deletecourseoverrides'] = 'Delete all overrides in course'; -$string['deletelocalroles'] = 'Delete all local role assignments'; -$string['grade:edit'] = 'Edit grades'; -$string['grade:export'] = 'Export grades'; -$string['grade:hide'] = 'Hide/unhide grades or items'; -$string['grade:import'] = 'Import grades'; -$string['grade:lock'] = 'Lock grades or items'; -$string['grade:manage'] = 'Manage grade items'; -$string['grade:manageletters'] = 'Manage letter grades'; -$string['grade:manageoutcomes'] = 'Manage grade outcomes'; -$string['grade:override'] = 'Override grades'; -$string['grade:unlock'] = 'Unlock grades or items'; -$string['grade:view'] = 'View own grades'; -$string['grade:viewall'] = 'View grades of other users'; -$string['grade:viewhidden'] = 'View hidden grades for owner'; $string['currentcontext'] = 'Current context'; $string['currentrole'] = 'Current role'; $string['defaultrole'] = 'Default role'; $string['defineroles'] = 'Define roles'; +$string['deletecourseoverrides'] = 'Delete all overrides in course'; +$string['deletelocalroles'] = 'Delete all local role assignments'; $string['deleterolesure'] = 'Are you sure that you want to delete role \"$a->name ($a->shortname)\"?

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

'; $string['duplicaterole'] = 'Duplicate role'; @@ -95,7 +82,22 @@ $string['extusers'] = 'Existing users'; $string['extusersmatching'] = 'Existing users matching \'$a\''; $string['globalrole'] = 'System role'; $string['globalroleswarning'] = 'WARNING! Any roles you assign from this page will apply to the assigned users throughout the entire system, including the front page and all the courses.'; +$string['grade:edit'] = 'Edit grades'; +$string['grade:export'] = 'Export grades'; +$string['grade:hide'] = 'Hide/unhide grades or items'; +$string['grade:import'] = 'Import grades'; +$string['grade:lock'] = 'Lock grades or items'; +$string['grade:manage'] = 'Manage grade items'; +$string['grade:manageletters'] = 'Manage letter grades'; +$string['grade:manageoutcomes'] = 'Manage grade outcomes'; +$string['grade:override'] = 'Override grades'; +$string['grade:unlock'] = 'Unlock grades or items'; +$string['grade:view'] = 'View own grades'; +$string['grade:viewall'] = 'View grades of other users'; +$string['grade:viewhidden'] = 'View hidden grades for owner'; $string['hidden'] = 'Hidden'; +$string['inactiveformorethan'] = 'inactive for more than $a->timeperiod'; +$string['ingroup'] = 'in the group \"$a->group\"'; $string['inherit'] = 'Inherit'; $string['legacy:admin'] = 'LEGACY ROLE: Administrator'; $string['legacy:coursecreator'] = 'LEGACY ROLE: Course Creator'; @@ -110,14 +112,16 @@ $string['localroles'] = 'Locally assigned roles'; $string['manageroles'] = 'Manage roles'; $string['metaassignerror'] = 'Can not assign this role to user \"$a\" because Manage metacourse capability is needed.'; $string['metaunassignerror'] = 'Role of user \"$a\" was automatically reassigned, please unassign the role in child courses instead.'; -$string['multipleroles'] = 'Multiple roles'; -$string['overridepermissions'] = 'Override permissions'; -$string['overridepermissionsin'] = 'Override permissions in $a'; $string['morethan'] = 'More than $a'; +$string['multipleroles'] = 'Multiple roles'; $string['my:manageblocks'] = 'Manage myMoodle page blocks'; $string['nocapabilitiesincontext'] = 'No capabilities available in this context'; $string['notset'] = 'Not set'; +$string['overrideanotherrole'] = 'Override another role'; $string['overridecontext'] = 'Override context'; +$string['overridepermissions'] = 'Override permissions'; +$string['overridepermissionsin'] = 'Override permissions in $a'; +$string['overridepermissionsforrole'] = 'Override permissions for role \'$a->role\' in $a->context'; $string['overrideroles'] = 'Override roles'; $string['overriderolesin'] = 'Override roles in $a'; $string['overrides'] = 'Overrides'; @@ -199,8 +203,6 @@ $string['userswiththisrole'] = 'Users with role'; $string['userswithrole'] = 'All users with a role'; $string['viewrole'] = 'View role details'; $string['xuserswiththerole'] = 'Users with the role \"$a->role\"'; -$string['ingroup'] = 'in the group \"$a->group\"'; -$string['inactiveformorethan'] = 'inactive for more than $a->timeperiod'; // MNET $string['site:mnetlogintoremote'] = 'Roam to a remote Moodle'; diff --git a/lib/accesslib.php b/lib/accesslib.php index a091621e6c..42c7246da1 100755 --- a/lib/accesslib.php +++ b/lib/accesslib.php @@ -1900,19 +1900,27 @@ function assign_legacy_capabilities($capability, $legacyperms) { /** - * Checks to see if a capability is a legacy capability. - * @param $capabilityname - * @return boolean + * Checks to see if a capability is one of the special capabilities + * (either a legacy capability, or moodle/site:doanything). + * @param string $capabilityname the capability name, e.g. mod/forum:view. + * @return boolean whether this is one of the special capabilities. */ -function islegacy($capabilityname) { - if (strpos($capabilityname, 'moodle/legacy') === 0) { +function is_legacy($capabilityname) { + if ($capabilityname == 'moodle/site:doanything' || strpos($capabilityname, 'moodle/legacy') === 0) { return true; } else { return false; } } - +/** + * @param object $capability a capbility - a row from the capabilitites table. + * @return boolean whether this capability is safe - that is, wether people with the + * safeoverrides capability should be allowed to change it. + */ +function is_safe_capability($capability) { + return (RISK_DATALOSS | RISK_MANAGETRUST | RISK_CONFIG | RISK_XSS | RISK_PERSONAL) & $capability->riskbitmask; +} /********************************** * Context Manipulation functions * @@ -3420,6 +3428,50 @@ function get_context_url($context) { return $url; } +/** + * Print a risk icon, as a link to the Risks page on Moodle Docs. + * + * @param string $type the type of risk, will be one of the keys from the + * get_all_risks array. Must start with 'risk'. + */ +function print_risk_icon($type) { + global $CFG; + static $risksurl = null; + if (is_null($risksurl)) { + $risksurl = get_docs_url(s(get_string('risks', 'role'))); + } + $iconurl = $CFG->pixpath . '/i/' . str_replace('risk', 'risk_', $type) . '.gif'; + echo ' ' .
+            get_string($type . 'short', 'admin') . ''; +} + +/** + * @return array all the known types of risk. The array keys can be used, for example + * as CSS class names, or in calls to print_risk_icon. The values are the + * corresponding RISK_ constants. + */ +function get_all_risks() { + return array( + 'riskmanagetrust' => RISK_MANAGETRUST, + 'riskconfig' => RISK_CONFIG, + 'riskxss' => RISK_XSS, + 'riskpersonal' => RISK_PERSONAL, + 'riskspam' => RISK_SPAM, + 'riskdataloss' => RISK_DATALOSS, + ); +} + +/** + * @param object $capability a capability - a row from the mdl_capabilities table. + * @return string the human-readable capability name as a link to Moodle Docs. + */ +function get_capability_docs_link($capability) { + global $CFG; + $url = get_docs_url('Capabilities/' . $capability->name); + return '' . get_capability_string($capability->name) . ''; +} + /** * Extracts the relevant capabilities given a contextid. * All case based, example an instance of forum context. @@ -4085,7 +4137,6 @@ function allow_assign($sroleid, $troleid) { /** * Gets a list of roles that this user can assign in this context * @param object $context the context. - * @param string $field the field to return for each role. * @param int $rolenamedisplay the type of role name to display. One of the * ROLENAME_X constants. Default ROLENAME_ALIAS. * @param $withusercounts if true, count the number of users with each role. @@ -4111,9 +4162,6 @@ function get_assignable_roles($context, $rolenamedisplay = ROLENAME_ALIAS, $with } if ($withusercounts) { - $countjoinandgroupby = - "JOIN - GROUP BY ro.id, ro.name$extrafields"; $extrafields = ', (SELECT count(u.id) FROM {role_assignments} cra JOIN {user} u ON cra.userid = u.id WHERE cra.roleid = ro.id AND cra.contextid = :conid AND u.deleted = 0 @@ -4122,17 +4170,17 @@ function get_assignable_roles($context, $rolenamedisplay = ROLENAME_ALIAS, $with } $params['userid'] = $USER->id; - if (!$roles = $DB->get_records_sql("SELECT ro.id, ro.name$extrafields - FROM {role} ro - JOIN ( - SELECT DISTINCT r.id - FROM {role} r, - {role_assignments} ra, - {role_allow_assign} raa - WHERE ra.userid = :userid AND ra.contextid IN ($contexts) - AND raa.roleid = ra.roleid AND r.id = raa.allowassign - ) inline_view ON ro.id = inline_view.id - ORDER BY ro.sortorder ASC", $params)) { + if (!$roles = $DB->get_records_sql(" + SELECT ro.id, ro.name$extrafields + FROM {role} ro + JOIN (SELECT DISTINCT r.id + FROM {role} r, + {role_assignments} ra, + {role_allow_assign} raa + WHERE ra.userid = :userid AND ra.contextid IN ($contexts) + AND raa.roleid = ra.roleid AND r.id = raa.allowassign + ) inline_view ON ro.id = inline_view.id + ORDER BY ro.sortorder ASC", $params)) { return array(); } @@ -4163,8 +4211,8 @@ function get_assignable_roles($context, $rolenamedisplay = ROLENAME_ALIAS, $with /** * Gets a list of roles that this user can assign in this context, for the switchrole menu * - * @param object $context - * @return array + * @param object $context the context. + * @return an array $roleid => $rolename. */ function get_assignable_roles_for_switchrole($context) { global $USER, $DB; @@ -4177,20 +4225,21 @@ function get_assignable_roles_for_switchrole($context) { $parents[] = $context->id; $contexts = implode(',' , $parents); - if (!$roles = $DB->get_records_sql("SELECT ro.* - FROM {role} ro, - ( - SELECT DISTINCT r.id - FROM {role} r, - {role_assignments} ra, - {role_allow_assign} raa, - {role_capabilities} rc - WHERE ra.userid = :userid AND ra.contextid IN ($contexts) - AND raa.roleid = ra.roleid AND r.id = raa.allowassign - AND r.id = rc.roleid AND rc.capability = :viewcap AND rc.capability <> :anythingcap - ) inline_view - WHERE ro.id = inline_view.id - ORDER BY ro.sortorder ASC", array('userid'=>$USER->id, 'viewcap'=>'moodle/course:view', 'anythingcap'=>'moodle/site:doanything'))) { + if (!$roles = $DB->get_records_sql(" + SELECT ro.* + FROM {role} ro + JOIN (SELECT DISTINCT r.id + FROM {role} r, + {role_assignments} ra, + {role_allow_assign} raa, + {role_capabilities} rc + WHERE ra.userid = :userid AND ra.contextid IN ($contexts) + AND raa.roleid = ra.roleid AND r.id = raa.allowassign + AND r.id = rc.roleid AND rc.capability = :viewcap AND rc.capability <> :anythingcap + ) inline_view ON ro.id = inline_view.id + ORDER BY ro.sortorder ASC", + array('userid'=>$USER->id, 'viewcap'=>'moodle/course:view', + 'anythingcap'=>'moodle/site:doanything'))) { return array(); } @@ -4202,16 +4251,20 @@ function get_assignable_roles_for_switchrole($context) { } /** - * Gets a list of roles that this user can override in this context - * @param object $context - * @param string $field - * @param int $rolenamedisplay - * @return array + * Gets a list of roles that this user can override in this context. + * + * @param object $context the context. + * @param int $rolenamedisplay the type of role name to display. One of the + * ROLENAME_X constants. Default ROLENAME_ALIAS. + * @param $withcounts if true, count the number of overrides that are set for each role. + * @return array if $withcounts is false, then an array $roleid => $rolename. + * if $withusercounts is true, returns a list of three arrays, + * $rolenames, $rolecounts, and $nameswithcounts. */ -function get_overridable_roles($context, $rolenamedisplay=ROLENAME_ALIAS) { +function get_overridable_roles($context, $rolenamedisplay = ROLENAME_ALIAS, $withcounts = false) { global $USER, $DB; - if (!has_capability('moodle/role:override', $context) and !has_capability('moodle/role:safeoverride', $context)) { + if (!has_any_capability(array('moodle/role:safeoverride', 'moodle/role:override'), $context)) { return array(); } @@ -4219,26 +4272,55 @@ function get_overridable_roles($context, $rolenamedisplay=ROLENAME_ALIAS) { $parents[] = $context->id; $contexts = implode(',' , $parents); - if (!$roles = $DB->get_records_sql("SELECT ro.* - FROM {role} ro, - ( - SELECT DISTINCT r.id - FROM {role} r, - {role_assignments} ra, - {role_allow_override} rao - WHERE ra.userid = :userid AND ra.contextid IN ($contexts) - AND rao.roleid = ra.roleid AND r.id = rao.allowoverride - ) inline_view - WHERE ro.id = inline_view.id - ORDER BY ro.sortorder ASC", array('userid'=>$USER->id))) { + $params = array(); + $extrafields = ''; + if ($rolenamedisplay == ROLENAME_ORIGINALANDSHORT) { + $extrafields .= ', ro.shortname'; + } + + $params['userid'] = $USER->id; + if ($withcounts) { + $extrafields = ', (SELECT count(rc.id) FROM {role_capabilities} rc + WHERE rc.roleid = ro.id AND rc.contextid = :conid) AS overridecount'; + $params['conid'] = $context->id; + } + + if (!$roles = $DB->get_records_sql(" + SELECT ro.id, ro.name$extrafields + FROM {role} ro + JOIN ( + SELECT DISTINCT r.id + FROM {role} r + JOIN {role_allow_override} rao ON r.id = rao.allowoverride + JOIN {role_assignments} ra ON rao.roleid = ra.roleid + WHERE ra.userid = :userid AND ra.contextid IN ($contexts) + ) inline_view ON ro.id = inline_view.id + ORDER BY ro.sortorder ASC", $params)) { return array(); } + $rolenames = array(); foreach ($roles as $role) { - $roles[$role->id] = $role->name; + $rolenames[$role->id] = $role->name; + if ($rolenamedisplay == ROLENAME_ORIGINALANDSHORT) { + $rolenames[$role->id] .= ' (' . $role->shortname . ')'; + } + } + if ($rolenamedisplay != ROLENAME_ORIGINALANDSHORT) { + $rolenames = role_fix_names($rolenames, $context, $rolenamedisplay); } - return role_fix_names($roles, $context, $rolenamedisplay); + if (!$withcounts) { + return $rolenames; + } + + $rolecounts = array(); + $nameswithcounts = array(); + foreach ($roles as $role) { + $nameswithcounts[$role->id] = $rolenames[$role->id] . ' (' . $roles[$role->id]->overridecount . ')'; + $rolecounts[$role->id] = $roles[$role->id]->overridecount; + } + return array($rolenames, $rolecounts, $nameswithcounts); } /** diff --git a/lib/moodlelib.php b/lib/moodlelib.php index 539ec3c763..9abd124fa0 100644 --- a/lib/moodlelib.php +++ b/lib/moodlelib.php @@ -246,6 +246,11 @@ define('PARAM_BASE64', 0x20000); */ define('PARAM_CAPABILITY', 0x40000); +/** + * PARAM_PERMISSION - A permission, one of CAP_INHERIT, CAP_ALLOW, CAP_PREVENT or CAP_PROHIBIT. + */ +define('PARAM_PERMISSION', 0x80000); + /// Page types /// /** * PAGE_COURSE_VIEW is a definition of a page type. For more information on the page class see moodle/lib/pagelib.php. @@ -612,6 +617,14 @@ function clean_param($param, $type) { return ''; } + case PARAM_PERMISSION: + $param = (int)$param; + if (in_array($param, array(CAP_INHERIT, CAP_ALLOW, CAP_PREVENT, CAP_PROHIBIT))) { + return $param; + } else { + return CAP_INHERIT; + } + default: // throw error, switched parameters in optional_param or another serious problem print_error("unknowparamtype", '', '', $type); } diff --git a/lib/weblib.php b/lib/weblib.php index 34607c7435..a29bdde3ba 100644 --- a/lib/weblib.php +++ b/lib/weblib.php @@ -6933,6 +6933,16 @@ function page_doc_link($text='', $iconpath='') { return doc_link($path, $text, $iconpath); } +/** + * @param string $path the end of the URL. + * @return The start of a MoodleDocs URL in the user's language. + * E.g. http://docs.moodle.org/en/ + */ +function get_docs_url($path) { + global $CFG; + return $CFG->docroot . '/' . str_replace('_utf8', '', current_language()) . '/' . $path; +} + /** * Returns a string containing a link to the user documentation. * Also contains an icon by default. Shown to teachers and admin only. @@ -6949,11 +6959,7 @@ function doc_link($path='', $text='', $iconpath='') { return ''; } - $lang = str_replace('_utf8', '', current_language()); - $url = $CFG->docroot. '/' .$lang. '/' .$path; - - $lang = str_replace('_utf8', '', current_language()); - $url = $CFG->docroot. '/' .$lang. '/' .$path; + $url = get_docs_url($path); $target = ''; if (!empty($CFG->doctonewwindow)) { diff --git a/pix/i/risk_dataloss.gif b/pix/i/risk_dataloss.gif new file mode 100644 index 0000000000..8ea9cc593e Binary files /dev/null and b/pix/i/risk_dataloss.gif differ