Roles MDL-23986 Added help to role archetype
[moodle.git] / admin / roles / lib.php
CommitLineData
11b749ca 1<?php
1e8e4687 2
01a2ce80
PS
3// This file is part of Moodle - http://moodle.org/
4//
5// Moodle is free software: you can redistribute it and/or modify
6// it under the terms of the GNU General Public License as published by
7// the Free Software Foundation, either version 3 of the License, or
8// (at your option) any later version.
9//
10// Moodle is distributed in the hope that it will be useful,
11// but WITHOUT ANY WARRANTY; without even the implied warranty of
12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13// GNU General Public License for more details.
14//
15// You should have received a copy of the GNU General Public License
16// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
1e8e4687 17
18/**
19 * Library code used by the roles administration interfaces.
20 *
01a2ce80
PS
21 * Responds to actions:
22 * add - add a new role
23 * duplicate - like add, only initialise the new role by using an existing one.
24 * edit - edit the definition of a role
25 * view - view the definition of a role
26 *
5d354ded 27 * @package core
01a2ce80
PS
28 * @subpackage role
29 * @copyright 1999 onwards Martin Dougiamas (http://dougiamas.com)
30 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
31 */
1e8e4687 32
33require_once($CFG->libdir.'/adminlib.php');
34require_once($CFG->dirroot.'/user/selector/lib.php');
35
1e8e4687 36// Classes for producing tables with one row per capability ====================
37
38/**
39 * This class represents a table with one row for each of a list of capabilities
40 * where the first cell in the row contains the capability name, and there is
41 * arbitrary stuff in the rest of the row. This class is used by
bed9cec8 42 * admin/roles/manage.php, override.php and check.php.
1e8e4687 43 *
44 * An ajaxy search UI shown at the top, if JavaScript is on.
45 */
46abstract class capability_table_base {
47 /** The context this table relates to. */
48 protected $context;
49
50 /** The capabilities to display. Initialised as fetch_context_capabilities($context). */
51 protected $capabilities = array();
52
53 /** Added as an id="" attribute to the table on output. */
54 protected $id;
55
56 /** Added to the class="" attribute on output. */
57 protected $classes = array('rolecap');
58
59 /** Default number of capabilities in the table for the search UI to be shown. */
60 const NUM_CAPS_FOR_SEARCH = 12;
61
62 /**
63 * Constructor
64 * @param object $context the context this table relates to.
65 * @param string $id what to put in the id="" attribute.
66 */
67 public function __construct($context, $id) {
68 $this->context = $context;
69 $this->capabilities = fetch_context_capabilities($context);
70 $this->id = $id;
71 }
72
73 /**
74 * Use this to add class="" attributes to the table. You get the rolecap by
75 * default.
76 * @param array $classnames of class names.
77 */
78 public function add_classes($classnames) {
79 $this->classes = array_unique(array_merge($this->classes, $classnames));
80 }
81
82 /**
83 * Display the table.
84 */
85 public function display() {
01a2ce80
PS
86 if (count($this->capabilities) > capability_table_base::NUM_CAPS_FOR_SEARCH) {
87 global $PAGE;
87b4981b
SH
88 $PAGE->requires->strings_for_js(array('filter','clear'),'moodle');
89 $PAGE->requires->js_init_call('M.core_role.init_cap_table_filter', array($this->id, $this->context->id));
01a2ce80 90 }
1e8e4687 91 echo '<table class="' . implode(' ', $this->classes) . '" id="' . $this->id . '">' . "\n<thead>\n";
92 echo '<tr><th class="name" align="left" scope="col">' . get_string('capability','role') . '</th>';
93 $this->add_header_cells();
94 echo "</tr>\n</thead>\n<tbody>\n";
0df0df23 95
1e8e4687 96 /// Loop over capabilities.
97 $contextlevel = 0;
98 $component = '';
99 foreach ($this->capabilities as $capability) {
100 if ($this->skip_row($capability)) {
101 continue;
102 }
103
104 /// Prints a breaker if component or name or context level has changed
105 if (component_level_changed($capability, $component, $contextlevel)) {
106 $this->print_heading_row($capability);
107 }
108 $contextlevel = $capability->contextlevel;
109 $component = $capability->component;
110
111 /// Start the row.
112 echo '<tr class="' . implode(' ', array_unique(array_merge(array('rolecap'),
113 $this->get_row_classes($capability)))) . '">';
114
115 /// Table cell for the capability name.
8aee9bcc 116 echo '<th scope="row" class="name"><span class="cap-desc">' . get_capability_docs_link($capability) .
a90d7cf9 117 '<span class="cap-name">' . $capability->name . '</span></span></th>';
1e8e4687 118
119 /// Add the cells specific to this table.
120 $this->add_row_cells($capability);
121
122 /// End the row.
123 echo "</tr>\n";
124 }
125
126 /// End of the table.
01a2ce80 127 echo "</tbody>\n</table>\n";
1e8e4687 128 }
129
130 /**
131 * Used to output a heading rows when the context level or component changes.
132 * @param object $capability gives the new component and contextlevel.
133 */
134 protected function print_heading_row($capability) {
135 echo '<tr class="rolecapheading header"><td colspan="' . (1 + $this->num_extra_columns()) . '" class="header"><strong>' .
136 get_component_string($capability->component, $capability->contextlevel) .
137 '</strong></td></tr>';
0df0df23 138
1e8e4687 139 }
140
141 /** For subclasses to override, output header cells, after the initial capability one. */
142 protected abstract function add_header_cells();
143
144 /** For subclasses to override, return the number of cells that add_header_cells/add_row_cells output. */
145 protected abstract function num_extra_columns();
146
147 /**
4f0c2d00 148 * For subclasses to override. Allows certain capabilties
1e8e4687 149 * to be left out of the table.
150 *
151 * @param object $capability the capability this row relates to.
152 * @return boolean. If true, this row is omitted from the table.
153 */
154 protected function skip_row($capability) {
155 return false;
156 }
157
158 /**
159 * For subclasses to override. A change to reaturn class names that are added
160 * to the class="" attribute on the &lt;tr> for this capability.
161 *
162 * @param object $capability the capability this row relates to.
163 * @return array of class name strings.
164 */
165 protected function get_row_classes($capability) {
166 return array();
167 }
168
169 /**
170 * For subclasses to override. Output the data cells for this capability. The
171 * capability name cell will already have been output.
172 *
173 * You can rely on get_row_classes always being called before add_row_cells.
174 *
175 * @param object $capability the capability this row relates to.
176 */
177 protected abstract function add_row_cells($capability);
178}
179
180/**
181 * Subclass of capability_table_base for use on the Check permissions page.
182 *
01a2ce80 183 * We have one additional column, Allowed, which contains yes/no.
1e8e4687 184 */
01a2ce80 185class check_capability_table extends capability_table_base {
1e8e4687 186 protected $user;
187 protected $fullname;
1e8e4687 188 protected $contextname;
189 protected $stryes;
190 protected $strno;
1e8e4687 191 private $hascap;
192
193 /**
194 * Constructor
195 * @param object $context the context this table relates to.
196 * @param object $user the user we are generating the results for.
197 * @param string $contextname print_context_name($context) - to save recomputing.
198 */
199 public function __construct($context, $user, $contextname) {
200 global $CFG;
201 parent::__construct($context, 'explaincaps');
202 $this->user = $user;
203 $this->fullname = fullname($user);
204 $this->contextname = $contextname;
1e8e4687 205 $this->stryes = get_string('yes');
206 $this->strno = get_string('no');
1e8e4687 207 }
208
209 protected function add_header_cells() {
210 echo '<th>' . get_string('allowed', 'role') . '</th>';
1e8e4687 211 }
212
213 protected function num_extra_columns() {
01a2ce80 214 return 1;
1e8e4687 215 }
216
1e8e4687 217 protected function get_row_classes($capability) {
218 $this->hascap = has_capability($capability->name, $this->context, $this->user->id);
219 if ($this->hascap) {
220 return array('yes');
221 } else {
222 return array('no');
223 }
224 }
225
226 protected function add_row_cells($capability) {
04eb4d1e 227 global $OUTPUT;
1e8e4687 228 if ($this->hascap) {
229 $result = $this->stryes;
1e8e4687 230 } else {
231 $result = $this->strno;
1e8e4687 232 }
233 $a = new stdClass;
234 $a->fullname = $this->fullname;
235 $a->capability = $capability->name;
236 $a->context = $this->contextname;
237 echo '<td>' . $result . '</td>';
01a2ce80
PS
238 }
239}
04eb4d1e 240
04eb4d1e 241
01a2ce80
PS
242/**
243 * Subclass of capability_table_base for use on the Permissions page.
244 */
245class permissions_table extends capability_table_base {
246 protected $contextname;
247 protected $allowoverrides;
248 protected $allowsafeoverrides;
249 protected $overridableroles;
250 protected $roles;
251 protected $icons = array();
252
253 /**
254 * Constructor
255 * @param object $context the context this table relates to.
256 * @param string $contextname print_context_name($context) - to save recomputing.
257 */
258 public function __construct($context, $contextname, $allowoverrides, $allowsafeoverrides, $overridableroles) {
259 global $DB;
260
261 parent::__construct($context, 'permissions');
262 $this->contextname = $contextname;
263 $this->allowoverrides = $allowoverrides;
264 $this->allowsafeoverrides = $allowsafeoverrides;
265 $this->overridableroles = $overridableroles;
266
267 $roles = $DB->get_records('role', null, 'sortorder DESC');
268 foreach ($roles as $roleid=>$role) {
269 $roles[$roleid] = $role->name;
270 }
271 $this->roles = role_fix_names($roles, $context);
272
273 }
274
275 protected function add_header_cells() {
276 echo '<th>' . get_string('risks', 'role') . '</th>';
277 echo '<th>' . get_string('neededroles', 'role') . '</th>';
278 echo '<th>' . get_string('prohibitedroles', 'role') . '</th>';
279 }
280
281 protected function num_extra_columns() {
282 return 3;
283 }
284
01a2ce80
PS
285 protected function add_row_cells($capability) {
286 global $OUTPUT, $PAGE;
287
288 $context = $this->context;
289 $contextid = $this->context->id;
290 $allowoverrides = $this->allowoverrides;
291 $allowsafeoverrides = $this->allowsafeoverrides;
292 $overridableroles = $this->overridableroles;
293 $roles = $this->roles;
294
295
296 list($needed, $forbidden) = get_roles_with_cap_in_context($context, $capability->name);
297 $neededroles = array();
298 $forbiddenroles = array();
299 $allowable = $overridableroles;
300 $forbitable = $overridableroles;
301 foreach ($neededroles as $id=>$unused) {
302 unset($allowable[$id]);
303 }
304 foreach ($forbidden as $id=>$unused) {
305 unset($allowable[$id]);
306 unset($forbitable[$id]);
307 }
308
309 foreach ($roles as $id=>$name) {
310 if (isset($needed[$id])) {
311 $neededroles[$id] = $roles[$id];
312 if (isset($overridableroles[$id]) and ($allowoverrides or ($allowsafeoverrides and is_safe_capability($capability)))) {
313 $preventurl = new moodle_url($PAGE->url, array('contextid'=>$contextid, 'roleid'=>$id, 'capability'=>$capability->name, 'prevent'=>1));
314 $neededroles[$id] .= $OUTPUT->action_icon($preventurl, new pix_icon('t/delete', get_string('prevent', 'role')));
315 }
316 }
317 }
318 $neededroles = implode(', ', $neededroles);
319 foreach ($roles as $id=>$name) {
320 if (isset($forbidden[$id]) and ($allowoverrides or ($allowsafeoverrides and is_safe_capability($capability)))) {
321 $forbiddenroles[$id] = $roles[$id];
322 if (isset($overridableroles[$id]) and prohibit_is_removable($id, $context, $capability->name)) {
323 $unprohibiturl = new moodle_url($PAGE->url, array('contextid'=>$contextid, 'roleid'=>$id, 'capability'=>$capability->name, 'unprohibit'=>1));
324 $forbiddenroles[$id] .= $OUTPUT->action_icon($unprohibiturl, new pix_icon('t/delete', get_string('delete')));
325 }
326 }
327 }
328 $forbiddenroles = implode(', ', $forbiddenroles);
329
330 if ($allowable and ($allowoverrides or ($allowsafeoverrides and is_safe_capability($capability)))) {
331 $allowurl = new moodle_url($PAGE->url, array('contextid'=>$contextid, 'capability'=>$capability->name, 'allow'=>1));
332 $neededroles .= '<div class="allowmore">'.$OUTPUT->action_icon($allowurl, new pix_icon('t/add', get_string('allow', 'role'))).'</div>';
333 }
334
335 if ($forbitable and ($allowoverrides or ($allowsafeoverrides and is_safe_capability($capability)))) {
336 $prohibiturl = new moodle_url($PAGE->url, array('contextid'=>$contextid, 'capability'=>$capability->name, 'prohibit'=>1));
337 $forbiddenroles .= '<div class="prohibitmore">'.$OUTPUT->action_icon($prohibiturl, new pix_icon('t/add', get_string('prohibit', 'role'))).'</div>';
338 }
339
340 $risks = $this->get_risks($capability);
341
342 echo '<td>' . $risks . '</td>';
343 echo '<td>' . $neededroles . '</td>';
344 echo '<td>' . $forbiddenroles . '</td>';
345 }
346
347 protected function get_risks($capability) {
348 global $OUTPUT;
349
350 $allrisks = get_all_risks();
351 $risksurl = new moodle_url(get_docs_url(s(get_string('risks', 'role'))));
352
353 $return = '';
354
355 foreach ($allrisks as $type=>$risk) {
356 if ($risk & (int)$capability->riskbitmask) {
357 if (!isset($this->icons[$type])) {
358 $pixicon = new pix_icon('/i/' . str_replace('risk', 'risk_', $type), get_string($type . 'short', 'admin'));
359 $this->icons[$type] = $OUTPUT->action_icon($risksurl, $pixicon, new popup_action('click', $risksurl));
360 }
361 $return .= $this->icons[$type];
362 }
363 }
364
365 return $return;
1e8e4687 366 }
367}
368
01a2ce80 369
a4df1cbb 370/**
371 * This subclass is the bases for both the define roles and override roles
372 * pages. As well as adding the risks columns, this also provides generic
373 * facilities for showing a certain number of permissions columns, and
374 * recording the current and submitted permissions for each capability.
375 */
c6505f1e 376abstract class capability_table_with_risks extends capability_table_base {
377 protected $allrisks;
eab8ed9f 378 protected $allpermissions; // We don't need perms ourselves, but all our subclasses do.
c6505f1e 379 protected $strperms; // Language string cache.
380 protected $risksurl; // URL in moodledocs about risks.
381 protected $riskicons = array(); // Cache to avoid regenerating the HTML for each risk icon.
eab8ed9f 382 /** The capabilities to highlight as default/inherited. */
a4df1cbb 383 protected $parentpermissions;
384 protected $displaypermissions;
385 protected $permissions;
386 protected $changed;
bbdb7070 387 protected $roleid;
c6505f1e 388
bbdb7070 389 public function __construct($context, $id, $roleid) {
c6505f1e 390 parent::__construct($context, $id);
391
392 $this->allrisks = get_all_risks();
393 $this->risksurl = get_docs_url(s(get_string('risks', 'role')));
394
395 $this->allpermissions = array(
396 CAP_INHERIT => 'inherit',
397 CAP_ALLOW => 'allow',
398 CAP_PREVENT => 'prevent' ,
399 CAP_PROHIBIT => 'prohibit',
400 );
401
402 $this->strperms = array();
403 foreach ($this->allpermissions as $permname) {
404 $this->strperms[$permname] = get_string($permname, 'role');
405 }
a4df1cbb 406
bbdb7070 407 $this->roleid = $roleid;
a4df1cbb 408 $this->load_current_permissions();
409
410 /// Fill in any blank permissions with an explicit CAP_INHERIT, and init a locked field.
411 foreach ($this->capabilities as $capid => $cap) {
412 if (!isset($this->permissions[$cap->name])) {
413 $this->permissions[$cap->name] = CAP_INHERIT;
414 }
415 $this->capabilities[$capid]->locked = false;
416 }
417 }
418
419 protected function load_current_permissions() {
420 global $DB;
421
422 /// Load the overrides/definition in this context.
bbdb7070 423 if ($this->roleid) {
424 $this->permissions = $DB->get_records_menu('role_capabilities', array('roleid' => $this->roleid,
425 'contextid' => $this->context->id), '', 'capability,permission');
426 } else {
427 $this->permissions = array();
428 }
a4df1cbb 429 }
430
431 protected abstract function load_parent_permissions();
432
bbdb7070 433 /**
434 * Update $this->permissions based on submitted data, while making a list of
435 * changed capabilities in $this->changed.
436 */
a4df1cbb 437 public function read_submitted_permissions() {
bbdb7070 438 $this->changed = array();
439
a4df1cbb 440 foreach ($this->capabilities as $cap) {
441 if ($cap->locked || $this->skip_row($cap)) {
eab8ed9f 442 /// The user is not allowed to change the permission for this capability
a4df1cbb 443 continue;
444 }
445
446 $permission = optional_param($cap->name, null, PARAM_PERMISSION);
447 if (is_null($permission)) {
448 /// A permission was not specified in submitted data.
449 continue;
450 }
451
452 /// If the permission has changed, update $this->permissions and
453 /// Record the fact there is data to save.
454 if ($this->permissions[$cap->name] != $permission) {
455 $this->permissions[$cap->name] = $permission;
456 $this->changed[] = $cap->name;
457 }
458 }
459 }
460
bbdb7070 461 /**
462 * Save the new values of any permissions that have been changed.
463 */
464 public function save_changes() {
465 /// Set the permissions.
466 foreach ($this->changed as $changedcap) {
467 assign_capability($changedcap, $this->permissions[$changedcap],
468 $this->roleid, $this->context->id, true);
469 }
470
471 /// Force accessinfo refresh for users visiting this context.
472 mark_context_dirty($this->context->path);
473 }
474
a4df1cbb 475 public function display() {
476 $this->load_parent_permissions();
477 foreach ($this->capabilities as $cap) {
478 if (!isset($this->parentpermissions[$cap->name])) {
479 $this->parentpermissions[$cap->name] = CAP_INHERIT;
480 }
481 }
482 parent::display();
c6505f1e 483 }
484
485 protected function add_header_cells() {
8fbce1c8 486 global $OUTPUT;
bed9cec8 487 echo '<th colspan="' . count($this->displaypermissions) . '" scope="col">' .
52236f05 488 get_string('permission', 'role') . ' ' . $OUTPUT->help_icon('permission', 'role') . '</th>';
8aee9bcc 489 echo '<th class="risk" colspan="' . count($this->allrisks) . '" scope="col">' . get_string('risks','role') . '</th>';
c6505f1e 490 }
491
492 protected function num_extra_columns() {
a4df1cbb 493 return count($this->displaypermissions) + count($this->allrisks);
c6505f1e 494 }
495
496 protected function get_row_classes($capability) {
497 $rowclasses = array();
498 foreach ($this->allrisks as $riskname => $risk) {
499 if ($risk & (int)$capability->riskbitmask) {
500 $rowclasses[] = $riskname;
501 }
502 }
503 return $rowclasses;
504 }
505
a4df1cbb 506 protected abstract function add_permission_cells($capability);
507
c6505f1e 508 protected function add_row_cells($capability) {
a4df1cbb 509 $this->add_permission_cells($capability);
c6505f1e 510 /// One cell for each possible risk.
511 foreach ($this->allrisks as $riskname => $risk) {
512 echo '<td class="risk ' . str_replace('risk', '', $riskname) . '">';
513 if ($risk & (int)$capability->riskbitmask) {
514 echo $this->get_risk_icon($riskname);
515 }
516 echo '</td>';
517 }
518 }
519
520 /**
521 * Print a risk icon, as a link to the Risks page on Moodle Docs.
522 *
0df0df23 523 * @param string $type the type of risk, will be one of the keys from the
c6505f1e 524 * get_all_risks array. Must start with 'risk'.
525 */
526 function get_risk_icon($type) {
5d3b9994 527 global $OUTPUT;
c6505f1e 528 if (!isset($this->riskicons[$type])) {
b5d0cafc 529 $iconurl = $OUTPUT->pix_url('i/' . str_replace('risk', 'risk_', $type));
75015e5f
PS
530 $text = '<img src="' . $iconurl . '" alt="' . get_string($type . 'short', 'admin') . '" />';
531 $action = new popup_action('click', $this->risksurl, 'docspopup');
532 $this->riskicons[$type] = $OUTPUT->action_link($this->risksurl, $text, $action, array('title'=>get_string($type, 'admin')));
c6505f1e 533 }
534 return $this->riskicons[$type];
535 }
536}
537
bbdb7070 538/**
539 * As well as tracking the permissions information about the role we are creating
eab8ed9f 540 * or editing, we also track the other information about the role. (This class is
bbdb7070 541 * starting to be more and more like a formslib form in some respects.)
542 */
f4acee5d 543class define_role_table_advanced extends capability_table_with_risks {
bbdb7070 544 /** Used to store other information (besides permissions) about the role we are creating/editing. */
545 protected $role;
546 /** Used to store errors found when validating the data. */
547 protected $errors;
548 protected $contextlevels;
549 protected $allcontextlevels;
bbdb7070 550 protected $disabled = '';
a4df1cbb 551
552 public function __construct($context, $roleid) {
553 $this->roleid = $roleid;
bbdb7070 554 parent::__construct($context, 'defineroletable', $roleid);
a4df1cbb 555 $this->displaypermissions = $this->allpermissions;
f4acee5d 556 $this->strperms[$this->allpermissions[CAP_INHERIT]] = get_string('notset', 'role');
bbdb7070 557
558 $this->allcontextlevels = array(
559 CONTEXT_SYSTEM => get_string('coresystem'),
560 CONTEXT_USER => get_string('user'),
561 CONTEXT_COURSECAT => get_string('category'),
562 CONTEXT_COURSE => get_string('course'),
563 CONTEXT_MODULE => get_string('activitymodule'),
564 CONTEXT_BLOCK => get_string('block')
565 );
a4df1cbb 566 }
567
568 protected function load_current_permissions() {
bbdb7070 569 global $DB;
570 if ($this->roleid) {
571 if (!$this->role = $DB->get_record('role', array('id' => $this->roleid))) {
572 throw new moodle_exception('invalidroleid');
573 }
bbdb7070 574 $contextlevels = get_role_contextlevels($this->roleid);
575 // Put the contextlevels in the array keys, as well as the values.
a90d7cf9 576 if (!empty($contextlevels)) {
577 $this->contextlevels = array_combine($contextlevels, $contextlevels);
578 } else {
579 $this->contextlevels = array();
580 }
a4df1cbb 581 } else {
bbdb7070 582 $this->role = new stdClass;
583 $this->role->name = '';
584 $this->role->shortname = '';
585 $this->role->description = '';
4f0c2d00 586 $this->role->archetype = '';
bbdb7070 587 $this->contextlevels = array();
a4df1cbb 588 }
bbdb7070 589 parent::load_current_permissions();
a4df1cbb 590 }
591
bbdb7070 592 public function read_submitted_permissions() {
593 global $DB;
594 $this->errors = array();
595
596 // Role name.
597 $name = optional_param('name', null, PARAM_MULTILANG);
598 if (!is_null($name)) {
599 $this->role->name = $name;
600 if (html_is_blank($this->role->name)) {
601 $this->errors['name'] = get_string('errorbadrolename', 'role');
602 }
f4acee5d 603 }
bbdb7070 604 if ($DB->record_exists_select('role', 'name = ? and id <> ?', array($this->role->name, $this->roleid))) {
605 $this->errors['name'] = get_string('errorexistsrolename', 'role');
606 }
607
608 // Role short name. We clean this in a special way. We want to end up
609 // with only lowercase safe ASCII characters.
610 $shortname = optional_param('shortname', null, PARAM_RAW);
611 if (!is_null($shortname)) {
612 $this->role->shortname = $shortname;
613 $this->role->shortname = textlib_get_instance()->specialtoascii($this->role->shortname);
614 $this->role->shortname = moodle_strtolower(clean_param($this->role->shortname, PARAM_ALPHANUMEXT));
615 if (empty($this->role->shortname)) {
616 $this->errors['shortname'] = get_string('errorbadroleshortname', 'role');
617 }
618 }
619 if ($DB->record_exists_select('role', 'shortname = ? and id <> ?', array($this->role->shortname, $this->roleid))) {
620 $this->errors['shortname'] = get_string('errorexistsroleshortname', 'role');
621 }
622
623 // Description.
cc1eebbb 624 $description = optional_param('description', null, PARAM_RAW);
bbdb7070 625 if (!is_null($description)) {
626 $this->role->description = $description;
627 }
628
629 // Legacy type.
4f0c2d00
PS
630 $archetype = optional_param('archetype', null, PARAM_RAW);
631 if ($archetype) {
632 $archetypes = get_role_archetypes();
633 if (isset($archetypes[$archetype])){
634 $this->role->archetype = $archetype;
bbdb7070 635 } else {
4f0c2d00 636 $this->role->archetype = '';
bbdb7070 637 }
638 }
639
640 // Assignable context levels.
641 foreach ($this->allcontextlevels as $cl => $notused) {
642 $assignable = optional_param('contextlevel' . $cl, null, PARAM_BOOL);
643 if (!is_null($assignable)) {
644 if ($assignable) {
645 $this->contextlevels[$cl] = $cl;
646 } else {
647 unset($this->contextlevels[$cl]);
648 }
649 }
a4df1cbb 650 }
bbdb7070 651
652 // Now read the permissions for each capability.
653 parent::read_submitted_permissions();
654 }
655
656 public function is_submission_valid() {
657 return empty($this->errors);
a4df1cbb 658 }
659
660 /**
bbdb7070 661 * Call this after the table has been initialised, so to indicate that
662 * when save is called, we want to make a duplicate role.
a4df1cbb 663 */
bbdb7070 664 public function make_copy() {
665 $this->roleid = 0;
666 unset($this->role->id);
667 $this->role->name .= ' ' . get_string('copyasnoun');
668 $this->role->shortname .= 'copy';
669 }
670
671 public function get_role_name() {
672 return $this->role->name;
673 }
674
675 public function get_role_id() {
676 return $this->role->id;
677 }
678
4f0c2d00
PS
679 public function get_archetype() {
680 return $this->role->archetype;
bbdb7070 681 }
682
683 protected function load_parent_permissions() {
4f0c2d00 684 $this->parentpermissions = get_default_capabilities($this->role->archetype);
bbdb7070 685 }
686
a4df1cbb 687 public function save_changes() {
bbdb7070 688 global $DB;
689
690 if (!$this->roleid) {
691 // Creating role
4f0c2d00 692 $this->role->id = create_role($this->role->name, $this->role->shortname, $this->role->description, $this->role->archetype);
bbdb7070 693 $this->roleid = $this->role->id; // Needed to make the parent::save_changes(); call work.
694 } else {
695 // Updating role
19a4a32e 696 $DB->update_record('role', $this->role);
a4df1cbb 697 }
698
bbdb7070 699 // Assignable contexts.
700 set_role_contextlevels($this->role->id, $this->contextlevels);
701
702 // Permissions.
703 parent::save_changes();
a4df1cbb 704 }
705
bbdb7070 706 protected function get_name_field($id) {
707 return '<input type="text" id="' . $id . '" name="' . $id . '" maxlength="254" value="' . s($this->role->name) . '" />';
708 }
709
710 protected function get_shortname_field($id) {
711 return '<input type="text" id="' . $id . '" name="' . $id . '" maxlength="254" value="' . s($this->role->shortname) . '" />';
712 }
0df0df23 713
bbdb7070 714 protected function get_description_field($id) {
715 return print_textarea(true, 10, 50, 50, 10, 'description', $this->role->description, 0, true);
716 }
717
4f0c2d00 718 protected function get_archetype_field($id) {
b324b47e 719 global $OUTPUT;
bbdb7070 720 $options = array();
721 $options[''] = get_string('none');
4f0c2d00
PS
722 foreach(get_role_archetypes() as $type) {
723 $options[$type] = get_string('archetype'.$type, 'role');
bbdb7070 724 }
4f0c2d00 725 return html_writer::select($options, 'archetype', $this->role->archetype, false);
bbdb7070 726 }
727
728 protected function get_assignable_levels_control() {
729 $output = '';
730 foreach ($this->allcontextlevels as $cl => $clname) {
731 $extraarguments = $this->disabled;
732 if (in_array($cl, $this->contextlevels)) {
733 $extraarguments .= 'checked="checked" ';
734 }
735 if (!$this->disabled) {
a90d7cf9 736 $output .= '<input type="hidden" name="contextlevel' . $cl . '" value="0" />';
bbdb7070 737 }
738 $output .= '<input type="checkbox" id="cl' . $cl . '" name="contextlevel' . $cl .
739 '" value="1" ' . $extraarguments . '/> ';
740 $output .= '<label for="cl' . $cl . '">' . $clname . "</label><br />\n";
741 }
742 return $output;
743 }
744
745 protected function print_field($name, $caption, $field) {
04eb4d1e 746 global $OUTPUT;
bbdb7070 747 // Attempt to generate HTML like formslib.
748 echo '<div class="fitem">';
749 echo '<div class="fitemtitle">';
750 if ($name) {
751 echo '<label for="' . $name . '">';
752 }
753 echo $caption;
754 if ($name) {
755 echo "</label>\n";
756 }
757 echo '</div>';
758 if (isset($this->errors[$name])) {
759 $extraclass = ' error';
760 } else {
761 $extraclass = '';
762 }
763 echo '<div class="felement' . $extraclass . '">';
764 if (isset($this->errors[$name])) {
04eb4d1e 765 echo $OUTPUT->error_text($this->errors[$name]);
bbdb7070 766 }
767 echo $field;
768 echo '</div>';
769 echo '</div>';
770 }
771
bed9cec8 772 protected function print_show_hide_advanced_button() {
773 echo '<p class="definenotice">' . get_string('highlightedcellsshowdefault', 'role') . ' </p>';
774 echo '<div class="advancedbutton">';
775 echo '<input type="submit" name="toggleadvanced" value="' . get_string('hideadvanced', 'form') . '" />';
776 echo '</div>';
777 }
778
bbdb7070 779 public function display() {
6397deae 780 global $OUTPUT;
bbdb7070 781 // Extra fields at the top of the page.
782 echo '<div class="topfields clearfix">';
783 $this->print_field('name', get_string('name'), $this->get_name_field('name'));
784 $this->print_field('shortname', get_string('shortname'), $this->get_shortname_field('shortname'));
785 $this->print_field('edit-description', get_string('description'), $this->get_description_field('description'));
6397deae 786 $this->print_field('menuarchetype', get_string('archetype', 'role').'&nbsp;'.$OUTPUT->help_icon('archetype', 'role'), $this->get_archetype_field('archetype'));
bbdb7070 787 $this->print_field('', get_string('maybeassignedin', 'role'), $this->get_assignable_levels_control());
788 echo "</div>";
789
bed9cec8 790 $this->print_show_hide_advanced_button();
791
bbdb7070 792 // Now the permissions table.
793 parent::display();
794 }
795
a4df1cbb 796 protected function add_permission_cells($capability) {
f4acee5d 797 /// One cell for each possible permission.
798 foreach ($this->displaypermissions as $perm => $permname) {
799 $strperm = $this->strperms[$permname];
800 $extraclass = '';
801 if ($perm == $this->parentpermissions[$capability->name]) {
802 $extraclass = ' capdefault';
803 }
804 $checked = '';
805 if ($this->permissions[$capability->name] == $perm) {
9c3ea652 806 $checked = 'checked="checked" ';
f4acee5d 807 }
808 echo '<td class="' . $permname . $extraclass . '">';
809 echo '<label><input type="radio" name="' . $capability->name .
9c3ea652 810 '" value="' . $perm . '" ' . $checked . '/> ';
f4acee5d 811 echo '<span class="note">' . $strperm . '</span>';
812 echo '</label></td>';
813 }
814 }
815}
816
817class define_role_table_basic extends define_role_table_advanced {
818 protected $stradvmessage;
819 protected $strallow;
820
821 public function __construct($context, $roleid) {
822 parent::__construct($context, $roleid);
823 $this->displaypermissions = array(CAP_ALLOW => $this->allpermissions[CAP_ALLOW]);
824 $this->stradvmessage = get_string('useshowadvancedtochange', 'role');
825 $this->strallow = $this->strperms[$this->allpermissions[CAP_ALLOW]];
826 }
827
bed9cec8 828 protected function print_show_hide_advanced_button() {
829 echo '<div class="advancedbutton">';
830 echo '<input type="submit" name="toggleadvanced" value="' . get_string('showadvanced', 'form') . '" />';
831 echo '</div>';
832 }
833
f4acee5d 834 protected function add_permission_cells($capability) {
835 $perm = $this->permissions[$capability->name];
836 $permname = $this->allpermissions[$perm];
bbdb7070 837 $defaultperm = $this->allpermissions[$this->parentpermissions[$capability->name]];
f4acee5d 838 echo '<td class="' . $permname . '">';
839 if ($perm == CAP_ALLOW || $perm == CAP_INHERIT) {
840 $checked = '';
841 if ($perm == CAP_ALLOW) {
842 $checked = 'checked="checked" ';
843 }
844 echo '<input type="hidden" name="' . $capability->name . '" value="' . CAP_INHERIT . '" />';
845 echo '<label><input type="checkbox" name="' . $capability->name .
9c3ea652 846 '" value="' . CAP_ALLOW . '" ' . $checked . '/> ' . $this->strallow . '</label>';
f4acee5d 847 } else {
848 echo '<input type="hidden" name="' . $capability->name . '" value="' . $perm . '" />';
849 echo $this->strperms[$permname] . '<span class="note">' . $this->stradvmessage . '</span>';
850 }
851 echo '</td>';
852 }
853}
854class view_role_definition_table extends define_role_table_advanced {
855 public function __construct($context, $roleid) {
856 parent::__construct($context, $roleid);
857 $this->displaypermissions = array(CAP_ALLOW => $this->allpermissions[CAP_ALLOW]);
bbdb7070 858 $this->disabled = 'disabled="disabled" ';
859 }
860
861 public function save_changes() {
862 throw new moodle_exception('invalidaccess');
863 }
864
865 protected function get_name_field($id) {
866 return strip_tags(format_string($this->role->name));
867 }
868
869 protected function get_shortname_field($id) {
870 return $this->role->shortname;
871 }
872
873 protected function get_description_field($id) {
874 return format_text($this->role->description, FORMAT_HTML);
875 }
876
4f0c2d00
PS
877 protected function get_archetype_field($id) {
878 if (empty($this->role->archetype)) {
bbdb7070 879 return get_string('none');
880 } else {
4f0c2d00 881 return get_string('archetype'.$this->role->archetype, 'role');
bbdb7070 882 }
f4acee5d 883 }
884
bed9cec8 885 protected function print_show_hide_advanced_button() {
886 // Do nothing.
887 }
888
f4acee5d 889 protected function add_permission_cells($capability) {
890 $perm = $this->permissions[$capability->name];
891 $permname = $this->allpermissions[$perm];
bbdb7070 892 $defaultperm = $this->allpermissions[$this->parentpermissions[$capability->name]];
893 if ($permname != $defaultperm) {
894 $default = get_string('defaultx', 'role', $this->strperms[$defaultperm]);
895 } else {
896 $default = "&#xa0;";
897 }
898 echo '<td class="' . $permname . '">' . $this->strperms[$permname] . '<span class="note">' .
899 $default . '</span></td>';
0df0df23 900
a4df1cbb 901 }
902}
903
e9abdd1b 904class override_permissions_table_advanced extends capability_table_with_risks {
8aee9bcc 905 protected $strnotset;
eab8ed9f 906 protected $haslockedcapabilities = false;
c6505f1e 907
908 /**
909 * Constructor
910 *
911 * This method loads loads all the information about the current state of
912 * the overrides, then updates that based on any submitted data. It also
913 * works out which capabilities should be locked for this user.
914 *
915 * @param object $context the context this table relates to.
916 * @param integer $roleid the role being overridden.
917 * @param boolean $safeoverridesonly If true, the user is only allowed to override
918 * capabilities with no risks.
919 */
920 public function __construct($context, $roleid, $safeoverridesonly) {
bbdb7070 921 parent::__construct($context, 'overriderolestable', $roleid);
8aee9bcc 922 $this->displaypermissions = $this->allpermissions;
923 $this->strnotset = get_string('notset', 'role');
c6505f1e 924
a4df1cbb 925 /// Determine which capabilities should be locked.
926 if ($safeoverridesonly) {
927 foreach ($this->capabilities as $capid => $cap) {
4659454a 928 if (!is_safe_capability($cap)) {
a4df1cbb 929 $this->capabilities[$capid]->locked = true;
eab8ed9f 930 $this->haslockedcapabilities = true;
a4df1cbb 931 }
c6505f1e 932 }
933 }
a4df1cbb 934 }
c6505f1e 935
a4df1cbb 936 protected function load_parent_permissions() {
937 global $DB;
c6505f1e 938
eab8ed9f 939 /// Get the capabilities from the parent context, so that can be shown in the interface.
a4df1cbb 940 $parentcontext = get_context_instance_by_id(get_parent_contextid($this->context));
941 $this->parentpermissions = role_context_capabilities($this->roleid, $parentcontext);
c6505f1e 942 }
943
eab8ed9f
PS
944 public function has_locked_capabilities() {
945 return $this->haslockedcapabilities;
c6505f1e 946 }
947
8aee9bcc 948 protected function add_permission_cells($capability) {
c6505f1e 949 $disabled = '';
a4df1cbb 950 if ($capability->locked || $this->parentpermissions[$capability->name] == CAP_PROHIBIT) {
c6505f1e 951 $disabled = ' disabled="disabled"';
952 }
953
954 /// One cell for each possible permission.
8aee9bcc 955 foreach ($this->displaypermissions as $perm => $permname) {
956 $strperm = $this->strperms[$permname];
c6505f1e 957 $extraclass = '';
a4df1cbb 958 if ($perm != CAP_INHERIT && $perm == $this->parentpermissions[$capability->name]) {
c6505f1e 959 $extraclass = ' capcurrent';
960 }
961 $checked = '';
a4df1cbb 962 if ($this->permissions[$capability->name] == $perm) {
9c3ea652 963 $checked = 'checked="checked" ';
c6505f1e 964 }
965 echo '<td class="' . $permname . $extraclass . '">';
8aee9bcc 966 echo '<label><input type="radio" name="' . $capability->name .
9c3ea652 967 '" value="' . $perm . '" ' . $checked . $disabled . '/> ';
8aee9bcc 968 if ($perm == CAP_INHERIT) {
a4df1cbb 969 $inherited = $this->parentpermissions[$capability->name];
8aee9bcc 970 if ($inherited == CAP_INHERIT) {
971 $inherited = $this->strnotset;
972 } else {
973 $inherited = $this->strperms[$this->allpermissions[$inherited]];
974 }
975 $strperm .= ' (' . $inherited . ')';
976 }
977 echo '<span class="note">' . $strperm . '</span>';
978 echo '</label></td>';
979 }
980 }
981}
982
1e8e4687 983// User selectors for managing role assignments ================================
984
985/**
986 * Base class to avoid duplicating code.
987 */
988abstract class role_assign_user_selector_base extends user_selector_base {
989 const MAX_USERS_PER_PAGE = 100;
990
991 protected $roleid;
992 protected $context;
993
994 /**
995 * @param string $name control name
996 * @param array $options should have two elements with keys groupid and courseid.
997 */
998 public function __construct($name, $options) {
999 global $CFG;
1000 parent::__construct($name, $options);
1001 $this->roleid = $options['roleid'];
1002 if (isset($options['context'])) {
1003 $this->context = $options['context'];
1004 } else {
1005 $this->context = get_context_instance_by_id($options['contextid']);
1006 }
1007 require_once($CFG->dirroot . '/group/lib.php');
1008 }
1009
1010 protected function get_options() {
8aee9bcc 1011 global $CFG;
1e8e4687 1012 $options = parent::get_options();
8aee9bcc 1013 $options['file'] = $CFG->admin . '/roles/lib.php';
1e8e4687 1014 $options['roleid'] = $this->roleid;
1015 $options['contextid'] = $this->context->id;
1016 return $options;
1017 }
1018}
1019
1020/**
1021 * User selector subclass for the list of potential users on the assign roles page,
1022 * when we are assigning in a context below the course level. (CONTEXT_MODULE and
e92c286c 1023 * some CONTEXT_BLOCK).
1e8e4687 1024 *
df997f84 1025 * This returns only enrolled users in this context.
1e8e4687 1026 */
1027class potential_assignees_below_course extends role_assign_user_selector_base {
1028 public function find_users($search) {
1029 global $DB;
1030
df997f84 1031 list($enrolsql, $eparams) = get_enrolled_sql($this->context);
1e8e4687 1032
1033 // Now we have to go to the database.
1034 list($wherecondition, $params) = $this->search_sql($search, 'u');
df997f84
PS
1035 $params = array_merge($params, $eparams);
1036
1e8e4687 1037 if ($wherecondition) {
1038 $wherecondition = ' AND ' . $wherecondition;
1039 }
1e8e4687 1040
df997f84
PS
1041 $fields = 'SELECT ' . $this->required_fields_sql('u');
1042 $countfields = 'SELECT COUNT(u.id)';
1e8e4687 1043
1044 $sql = " FROM {user} u
df997f84 1045 WHERE u.id IN ($enrolsql) $wherecondition
1e8e4687 1046 AND u.id NOT IN (
1047 SELECT u.id
1048 FROM {role_assignments} r, {user} u
4f0c2d00 1049 WHERE r.contextid = :contextid
1e8e4687 1050 AND u.id = r.userid
4f0c2d00 1051 AND r.roleid = :roleid)";
1e8e4687 1052 $order = ' ORDER BY lastname ASC, firstname ASC';
1053
4f0c2d00
PS
1054 $params['contextid'] = $this->context->id;
1055 $params['roleid'] = $this->roleid;
1e8e4687 1056
1057 // Check to see if there are too many to show sensibly.
1058 if (!$this->is_validating()) {
1059 $potentialmemberscount = $DB->count_records_sql($countfields . $sql, $params);
1060 if ($potentialmemberscount > role_assign_user_selector_base::MAX_USERS_PER_PAGE) {
1061 return $this->too_many_results($search, $potentialmemberscount);
1062 }
1063 }
1064
1065 // If not, show them.
1066 $availableusers = $DB->get_records_sql($fields . $sql . $order, $params);
1067
1068 if (empty($availableusers)) {
1069 return array();
1070 }
1071
1072 if ($search) {
1073 $groupname = get_string('potusersmatching', 'role', $search);
1074 } else {
1075 $groupname = get_string('potusers', 'role');
1076 }
1077
1078 return array($groupname => $availableusers);
1079 }
1080}
1081
1082/**
1083 * User selector subclass for the list of potential users on the assign roles page,
1084 * when we are assigning in a context at or above the course level. In this case we
1085 * show all the users in the system who do not already have the role.
1086 */
1087class potential_assignees_course_and_above extends role_assign_user_selector_base {
1088 public function find_users($search) {
1089 global $DB;
1090
1091 list($wherecondition, $params) = $this->search_sql($search, '');
1092
1093 $fields = 'SELECT ' . $this->required_fields_sql('');
1094 $countfields = 'SELECT COUNT(1)';
1095
0df0df23 1096 $sql = " FROM {user}
1e8e4687 1097 WHERE $wherecondition
1098 AND id NOT IN (
1099 SELECT u.id
1100 FROM {role_assignments} r, {user} u
4f0c2d00 1101 WHERE r.contextid = :contextid
1e8e4687 1102 AND u.id = r.userid
4f0c2d00 1103 AND r.roleid = :roleid)";
1e8e4687 1104 $order = ' ORDER BY lastname ASC, firstname ASC';
1105
4f0c2d00
PS
1106 $params['contextid'] = $this->context->id;
1107 $params['roleid'] = $this->roleid;
1e8e4687 1108
1109 if (!$this->is_validating()) {
1110 $potentialmemberscount = $DB->count_records_sql($countfields . $sql, $params);
1111 if ($potentialmemberscount > role_assign_user_selector_base::MAX_USERS_PER_PAGE) {
1112 return $this->too_many_results($search, $potentialmemberscount);
1113 }
1114 }
1115
1116 $availableusers = $DB->get_records_sql($fields . $sql . $order, $params);
1117
1118 if (empty($availableusers)) {
1119 return array();
1120 }
1121
1122 if ($search) {
1123 $groupname = get_string('potusersmatching', 'role', $search);
1124 } else {
1125 $groupname = get_string('potusers', 'role');
1126 }
1127
1128 return array($groupname => $availableusers);
1129 }
1130}
1131
1132/**
1133 * User selector subclass for the list of users who already have the role in
1134 * question on the assign roles page.
1135 */
1136class existing_role_holders extends role_assign_user_selector_base {
1e8e4687 1137
1138 public function __construct($name, $options) {
1139 parent::__construct($name, $options);
1e8e4687 1140 }
1141
1142 public function find_users($search) {
698ae7eb 1143 global $DB;
1144
1e8e4687 1145 list($wherecondition, $params) = $this->search_sql($search, 'u');
4f0c2d00 1146 list($ctxcondition, $ctxparams) = $DB->get_in_or_equal(get_parent_contexts($this->context, true), SQL_PARAMS_NAMED, 'ctx00');
698ae7eb 1147 $params = array_merge($params, $ctxparams);
4f0c2d00 1148 $params['roleid'] = $this->roleid;
1e8e4687 1149
df997f84 1150 $sql = "SELECT ra.id as raid," . $this->required_fields_sql('u') . ",ra.contextid,ra.component
698ae7eb 1151 FROM {role_assignments} ra
1152 JOIN {user} u ON u.id = ra.userid
1153 JOIN {context} ctx ON ra.contextid = ctx.id
1154 WHERE
1155 $wherecondition AND
1156 ctx.id $ctxcondition AND
4f0c2d00 1157 ra.roleid = :roleid
df997f84 1158 ORDER BY ctx.depth DESC, ra.component, u.lastname, u.firstname";
698ae7eb 1159 $contextusers = $DB->get_records_sql($sql, $params);
1160
1161 // No users at all.
1e8e4687 1162 if (empty($contextusers)) {
1163 return array();
1164 }
1165
698ae7eb 1166 // We have users. Out put them in groups by context depth.
1167 // To help the loop below, tack a dummy user on the end of the results
1168 // array, to trigger output of the last group.
1169 $dummyuser = new stdClass;
1170 $dummyuser->contextid = 0;
1171 $dummyuser->id = 0;
df997f84 1172 $dummyuser->component = '';
698ae7eb 1173 $contextusers[] = $dummyuser;
1174 $results = array(); // The results array we are building up.
1175 $doneusers = array(); // Ensures we only list each user at most once.
1176 $currentcontextid = $this->context->id;
1177 $currentgroup = array();
1178 foreach ($contextusers as $user) {
1179 if (isset($doneusers[$user->id])) {
1180 continue;
1181 }
1182 $doneusers[$user->id] = 1;
1183 if ($user->contextid != $currentcontextid) {
1184 // We have got to the end of the previous group. Add it to the results array.
1185 if ($currentcontextid == $this->context->id) {
1186 $groupname = $this->this_con_group_name($search, count($currentgroup));
1187 } else {
1188 $groupname = $this->parent_con_group_name($search, $currentcontextid);
1189 }
1190 $results[$groupname] = $currentgroup;
1191 // Get ready for the next group.
1192 $currentcontextid = $user->contextid;
1193 $currentgroup = array();
1194 }
1195 // Add this user to the group we are building up.
1196 unset($user->contextid);
1197 if ($currentcontextid != $this->context->id) {
1198 $user->disabled = true;
1199 }
df997f84
PS
1200 if ($user->component !== '') {
1201 // bad luck, you can tweak only manual role assignments
1202 $user->disabled = true;
1203 }
1204 unset($user->component);
698ae7eb 1205 $currentgroup[$user->id] = $user;
1206 }
1207
1208 return $results;
1209 }
1210
1211 protected function this_con_group_name($search, $numusers) {
e6a3587c 1212 if ($this->context->contextlevel == CONTEXT_SYSTEM) {
1213 // Special case in the System context.
1214 if ($search) {
1215 return get_string('extusersmatching', 'role', $search);
1216 } else {
1217 return get_string('extusers', 'role');
1218 }
1219 }
490740d6 1220 $contexttype = get_contextlevel_name($this->context->contextlevel);
1e8e4687 1221 if ($search) {
490740d6 1222 $a = new stdClass;
1223 $a->search = $search;
1224 $a->contexttype = $contexttype;
698ae7eb 1225 if ($numusers) {
490740d6 1226 return get_string('usersinthisxmatching', 'role', $a);
698ae7eb 1227 } else {
490740d6 1228 return get_string('noneinthisxmatching', 'role', $a);
698ae7eb 1229 }
1e8e4687 1230 } else {
698ae7eb 1231 if ($numusers) {
490740d6 1232 return get_string('usersinthisx', 'role', $contexttype);
698ae7eb 1233 } else {
490740d6 1234 return get_string('noneinthisx', 'role', $contexttype);
698ae7eb 1235 }
1e8e4687 1236 }
698ae7eb 1237 }
1e8e4687 1238
698ae7eb 1239 protected function parent_con_group_name($search, $contextid) {
1240 $context = get_context_instance_by_id($contextid);
1241 $contextname = print_context_name($context, true, true);
1242 if ($search) {
1243 $a = new stdClass;
1244 $a->contextname = $contextname;
1245 $a->search = $search;
1246 return get_string('usersfrommatching', 'role', $a);
1247 } else {
1248 return get_string('usersfrom', 'role', $contextname);
1249 }
1e8e4687 1250 }
1e8e4687 1251}
1252
9654643e 1253/**
c468795c 1254 * Base class for managing the data in the grid of checkboxes on the role allow
1255 * allow/overrides/switch editing pages (allow.php).
9654643e 1256 */
1257abstract class role_allow_role_page {
1258 protected $tablename;
1259 protected $targetcolname;
9654643e 1260 protected $roles;
1261 protected $allowed = null;
1262
c468795c 1263 /**
1264 * @param string $tablename the table where our data is stored.
1265 * @param string $targetcolname the name of the target role id column.
1266 */
9654643e 1267 public function __construct($tablename, $targetcolname) {
1268 $this->tablename = $tablename;
1269 $this->targetcolname = $targetcolname;
9654643e 1270 $this->load_required_roles();
1271 }
1272
9654643e 1273 /**
c468795c 1274 * Load information about all the roles we will need information about.
9654643e 1275 */
1276 protected function load_required_roles() {
1277 /// Get all roles
1278 $this->roles = get_all_roles();
91eb445c 1279 role_fix_names($this->roles, get_context_instance(CONTEXT_SYSTEM), ROLENAME_ORIGINAL);
9654643e 1280 }
1281
1282 /**
1283 * Update the data with the new settings submitted by the user.
1284 */
c468795c 1285 public function process_submission() {
1286 global $DB;
1287 /// Delete all records, then add back the ones that should be allowed.
9654643e 1288 $DB->delete_records($this->tablename);
1289 foreach ($this->roles as $fromroleid => $notused) {
1290 foreach ($this->roles as $targetroleid => $alsonotused) {
1291 if (optional_param('s_' . $fromroleid . '_' . $targetroleid, false, PARAM_BOOL)) {
1292 $this->set_allow($fromroleid, $targetroleid);
1293 }
1294 }
1295 }
1296 }
1297
1298 /**
1299 * Set one allow in the database.
c468795c 1300 * @param integer $fromroleid
1301 * @param integer $targetroleid
9654643e 1302 */
1303 protected abstract function set_allow($fromroleid, $targetroleid);
1304
c468795c 1305 /**
1306 * Load the current allows from the database.
1307 */
9654643e 1308 public function load_current_settings() {
1309 global $DB;
1310 /// Load the current settings
1311 $this->allowed = array();
1312 foreach ($this->roles as $role) {
eab8ed9f 1313 // Make an array $role->id => false. This is probably too clever for its own good.
9654643e 1314 $this->allowed[$role->id] = array_combine(array_keys($this->roles), array_fill(0, count($this->roles), false));
1315 }
1316 $rs = $DB->get_recordset($this->tablename);
1317 foreach ($rs as $allow) {
1318 $this->allowed[$allow->roleid][$allow->{$this->targetcolname}] = true;
1319 }
1320 }
1321
c468795c 1322 /**
1323 * @param integer $targetroleid a role id.
1324 * @return boolean whether the user should be allowed to select this role as a
1325 * target role.
1326 */
1327 protected function is_allowed_target($targetroleid) {
1328 return true;
1329 }
1330
1331 /**
1332 * @return object a $table structure that can be passed to print_table, containing
0df0df23 1333 * one cell for each checkbox.
c468795c 1334 */
9654643e 1335 public function get_table() {
414a4a91 1336 $table = new html_table();
9654643e 1337 $table->tablealign = 'center';
1338 $table->cellpadding = 5;
1339 $table->cellspacing = 0;
1340 $table->width = '90%';
c468795c 1341 $table->align = array('left');
9654643e 1342 $table->rotateheaders = true;
1343 $table->head = array('&#xa0;');
c468795c 1344 $table->colclasses = array('');
1345
9654643e 1346 /// Add role name headers.
1347 foreach ($this->roles as $targetrole) {
1348 $table->head[] = $targetrole->localname;
1349 $table->align[] = 'left';
c468795c 1350 if ($this->is_allowed_target($targetrole->id)) {
1351 $table->colclasses[] = '';
1352 } else {
1353 $table->colclasses[] = 'dimmed_text';
1354 }
9654643e 1355 }
c468795c 1356
9654643e 1357 /// Now the rest of the table.
1358 foreach ($this->roles as $fromrole) {
1359 $row = array($fromrole->localname);
1360 foreach ($this->roles as $targetrole) {
c468795c 1361 $checked = '';
1362 $disabled = '';
9654643e 1363 if ($this->allowed[$fromrole->id][$targetrole->id]) {
9c3ea652 1364 $checked = 'checked="checked" ';
c468795c 1365 }
1366 if (!$this->is_allowed_target($targetrole->id)) {
9c3ea652 1367 $disabled = 'disabled="disabled" ';
9654643e 1368 }
1369 $name = 's_' . $fromrole->id . '_' . $targetrole->id;
1370 $tooltip = $this->get_cell_tooltip($fromrole, $targetrole);
c468795c 1371 $row[] = '<input type="checkbox" name="' . $name . '" id="' . $name .
9c3ea652 1372 '" title="' . $tooltip . '" value="1" ' . $checked . $disabled . '/>' .
9654643e 1373 '<label for="' . $name . '" class="accesshide">' . $tooltip . '</label>';
1374 }
1375 $table->data[] = $row;
1376 }
1377
1378 return $table;
1379 }
91eb445c 1380
c468795c 1381 /**
1382 * Snippet of text displayed above the table, telling the admin what to do.
1383 * @return unknown_type
1384 */
91eb445c 1385 public abstract function get_intro_text();
9654643e 1386}
1387
c468795c 1388/**
1389 * Subclass of role_allow_role_page for the Allow assigns tab.
1390 */
9654643e 1391class role_allow_assign_page extends role_allow_role_page {
1392 public function __construct() {
1393 parent::__construct('role_allow_assign', 'allowassign');
1394 }
1395
1396 protected function set_allow($fromroleid, $targetroleid) {
1397 allow_assign($fromroleid, $targetroleid);
1398 }
1399
1400 protected function get_cell_tooltip($fromrole, $targetrole) {
1401 $a = new stdClass;
1402 $a->fromrole = $fromrole->localname;
1403 $a->targetrole = $targetrole->localname;
1404 return get_string('allowroletoassign', 'role', $a);
1405 }
91eb445c 1406
1407 public function get_intro_text() {
1408 return get_string('configallowassign', 'admin');
1409 }
1410}
1411
c468795c 1412/**
1413 * Subclass of role_allow_role_page for the Allow overrides tab.
1414 */
91eb445c 1415class role_allow_override_page extends role_allow_role_page {
1416 public function __construct() {
1417 parent::__construct('role_allow_override', 'allowoverride');
1418 }
1419
1420 protected function set_allow($fromroleid, $targetroleid) {
1421 allow_override($fromroleid, $targetroleid);
1422 }
1423
1424 protected function get_cell_tooltip($fromrole, $targetrole) {
1425 $a = new stdClass;
1426 $a->fromrole = $fromrole->localname;
1427 $a->targetrole = $targetrole->localname;
1428 return get_string('allowroletooverride', 'role', $a);
1429 }
1430
1431 public function get_intro_text() {
1432 return get_string('configallowoverride2', 'admin');
1433 }
9654643e 1434}
1435
c468795c 1436/**
1437 * Subclass of role_allow_role_page for the Allow switches tab.
1438 */
1439class role_allow_switch_page extends role_allow_role_page {
1440 protected $allowedtargetroles;
1441
1442 public function __construct() {
1443 parent::__construct('role_allow_switch', 'allowswitch');
1444 }
1445
1446 protected function load_required_roles() {
df997f84 1447 global $DB;
c468795c 1448 parent::load_required_roles();
da904f85 1449 $this->allowedtargetroles = $DB->get_records_menu('role', NULL, 'id');
c468795c 1450 }
1451
1452 protected function set_allow($fromroleid, $targetroleid) {
1453 allow_switch($fromroleid, $targetroleid);
1454 }
1455
1456 protected function is_allowed_target($targetroleid) {
1457 return isset($this->allowedtargetroles[$targetroleid]);
1458 }
1459
1460 protected function get_cell_tooltip($fromrole, $targetrole) {
1461 $a = new stdClass;
1462 $a->fromrole = $fromrole->localname;
1463 $a->targetrole = $targetrole->localname;
1464 return get_string('allowroletoswitch', 'role', $a);
1465 }
1466
1467 public function get_intro_text() {
1468 return get_string('configallowswitch', 'admin');
1469 }
1470}
1471
e92c286c 1472/**
1473 * Get the potential assignees selector for a given context.
1474 *
1475 * If this context is a course context, or inside a course context (module or
1476 * some blocks) then return a potential_assignees_below_course object. Otherwise
1477 * return a potential_assignees_course_and_above.
1478 *
eab8ed9f
PS
1479 * @param stdClass $context a context.
1480 * @param string $name passed to user selector constructor.
1481 * @param array $options to user selector constructor.
e92c286c 1482 * @return user_selector_base an appropriate user selector.
1483 */
1484function roles_get_potential_user_selector($context, $name, $options) {
1485 $blockinsidecourse = false;
1486 if ($context->contextlevel == CONTEXT_BLOCK) {
1487 $parentcontext = get_context_instance_by_id(get_parent_contextid($context));
1488 $blockinsidecourse = in_array($parentcontext->contextlevel, array(CONTEXT_MODULE, CONTEXT_COURSE));
1489 }
1490
1491 if (($context->contextlevel == CONTEXT_MODULE || $blockinsidecourse) &&
1492 !is_inside_frontpage($context)) {
1493 $potentialuserselector = new potential_assignees_below_course('addselect', $options);
1494 } else {
1495 $potentialuserselector = new potential_assignees_course_and_above('addselect', $options);
1496 }
1497 return $potentialuserselector;
1498}
1499
4f0c2d00
PS
1500class admins_potential_selector extends user_selector_base {
1501 /**
1502 * @param string $name control name
1503 * @param array $options should have two elements with keys groupid and courseid.
1504 */
1505 public function __construct() {
1506 global $CFG, $USER;
1507 $admins = explode(',', $CFG->siteadmins);
1508 parent::__construct('addselect', array('multiselect'=>false, 'exclude'=>$admins));
1509 }
1510
1511 public function find_users($search) {
d44df69b 1512 global $CFG, $DB;
4f0c2d00
PS
1513 list($wherecondition, $params) = $this->search_sql($search, '');
1514
1515 $fields = 'SELECT ' . $this->required_fields_sql('');
1516 $countfields = 'SELECT COUNT(1)';
1517
1518 $sql = " FROM {user}
d44df69b 1519 WHERE $wherecondition AND mnethostid = :localmnet";
4f0c2d00 1520 $order = ' ORDER BY lastname ASC, firstname ASC';
d44df69b 1521 $params['localmnet'] = $CFG->mnet_localhost_id; // it could be dangerous to make remote users admins and also this could lead to other problems
4f0c2d00 1522
01beeae9
PS
1523 // Check to see if there are too many to show sensibly.
1524 if (!$this->is_validating()) {
1525 $potentialcount = $DB->count_records_sql($countfields . $sql, $params);
1526 if ($potentialcount > 100) {
1527 return $this->too_many_results($search, $potentialcount);
1528 }
1529 }
1530
4f0c2d00
PS
1531 $availableusers = $DB->get_records_sql($fields . $sql . $order, $params);
1532
1533 if (empty($availableusers)) {
1534 return array();
1535 }
1536
1537 if ($search) {
1538 $groupname = get_string('potusersmatching', 'role', $search);
1539 } else {
1540 $groupname = get_string('potusers', 'role');
1541 }
1542
1543 return array($groupname => $availableusers);
1544 }
be42eca7
PS
1545
1546 protected function get_options() {
1547 global $CFG;
1548 $options = parent::get_options();
1549 $options['file'] = $CFG->admin . '/roles/lib.php';
1550 return $options;
1551 }
4f0c2d00
PS
1552}
1553
1554class admins_existing_selector extends user_selector_base {
1555 /**
1556 * @param string $name control name
1557 * @param array $options should have two elements with keys groupid and courseid.
1558 */
1559 public function __construct() {
1560 global $CFG, $USER;
1561 parent::__construct('removeselect', array('multiselect'=>false));
1562 }
1563
1564 public function find_users($search) {
1565 global $DB, $CFG;
1566 list($wherecondition, $params) = $this->search_sql($search, '');
1567
1568 $fields = 'SELECT ' . $this->required_fields_sql('');
1569 $countfields = 'SELECT COUNT(1)';
1570
1571 if ($wherecondition) {
1572 $wherecondition = "$wherecondition AND id IN ($CFG->siteadmins)";
1573 } else {
1574 $wherecondition = "id IN ($CFG->siteadmins)";
1575 }
1576 $sql = " FROM {user}
1577 WHERE $wherecondition";
1578 $order = ' ORDER BY lastname ASC, firstname ASC';
1579
1580 $availableusers = $DB->get_records_sql($fields . $sql . $order, $params);
1581
1582 if (empty($availableusers)) {
1583 return array();
1584 }
1585
1586 if ($search) {
1587 $groupname = get_string('extusersmatching', 'role', $search);
1588 } else {
1589 $groupname = get_string('extusers', 'role');
1590 }
1591
1592 return array($groupname => $availableusers);
1593 }
be42eca7
PS
1594
1595 protected function get_options() {
1596 global $CFG;
1597 $options = parent::get_options();
1598 $options['file'] = $CFG->admin . '/roles/lib.php';
1599 return $options;
1600 }
4f0c2d00 1601}