Roles MDL-23986 Added help to role archetype
[moodle.git] / admin / roles / lib.php
1 <?php
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/>.
18 /**
19  * Library code used by the roles administration interfaces.
20  *
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  *
27  * @package    core
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  */
33 require_once($CFG->libdir.'/adminlib.php');
34 require_once($CFG->dirroot.'/user/selector/lib.php');
36 // Classes for producing tables with one row per capability ====================
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
42  * admin/roles/manage.php, override.php and check.php.
43  *
44  * An ajaxy search UI shown at the top, if JavaScript is on.
45  */
46 abstract class capability_table_base {
47     /** The context this table relates to. */
48     protected $context;
50     /** The capabilities to display. Initialised as fetch_context_capabilities($context). */
51     protected $capabilities = array();
53     /** Added as an id="" attribute to the table on output. */
54     protected $id;
56     /** Added to the class="" attribute on output. */
57     protected $classes = array('rolecap');
59     /** Default number of capabilities in the table for the search UI to be shown. */
60     const NUM_CAPS_FOR_SEARCH = 12;
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     }
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     }
82     /**
83      * Display the table.
84      */
85     public function display() {
86         if (count($this->capabilities) > capability_table_base::NUM_CAPS_FOR_SEARCH) {
87             global $PAGE;
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));
90         }
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";
96     /// Loop over capabilities.
97         $contextlevel = 0;
98         $component = '';
99         foreach ($this->capabilities as $capability) {
100             if ($this->skip_row($capability)) {
101                 continue;
102             }
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;
111         /// Start the row.
112             echo '<tr class="' . implode(' ', array_unique(array_merge(array('rolecap'),
113                     $this->get_row_classes($capability)))) . '">';
115         /// Table cell for the capability name.
116             echo '<th scope="row" class="name"><span class="cap-desc">' . get_capability_docs_link($capability) .
117                     '<span class="cap-name">' . $capability->name . '</span></span></th>';
119         /// Add the cells specific to this table.
120             $this->add_row_cells($capability);
122         /// End the row.
123             echo "</tr>\n";
124         }
126     /// End of the table.
127         echo "</tbody>\n</table>\n";
128     }
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>';
139     }
141     /** For subclasses to override, output header cells, after the initial capability one. */
142     protected abstract function add_header_cells();
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();
147     /**
148      * For subclasses to override. Allows certain capabilties
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     }
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     }
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);
180 /**
181  * Subclass of capability_table_base for use on the Check permissions page.
182  *
183  * We have one additional column, Allowed, which contains yes/no.
184  */
185 class check_capability_table extends capability_table_base {
186     protected $user;
187     protected $fullname;
188     protected $contextname;
189     protected $stryes;
190     protected $strno;
191     private $hascap;
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;
205         $this->stryes = get_string('yes');
206         $this->strno = get_string('no');
207     }
209     protected function add_header_cells() {
210         echo '<th>' . get_string('allowed', 'role') . '</th>';
211     }
213     protected function num_extra_columns() {
214         return 1;
215     }
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     }
226     protected function add_row_cells($capability) {
227         global $OUTPUT;
228         if ($this->hascap) {
229             $result = $this->stryes;
230         } else {
231             $result = $this->strno;
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>';
238     }
242 /**
243  * Subclass of capability_table_base for use on the Permissions page.
244  */
245 class 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();
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;
261         parent::__construct($context, 'permissions');
262         $this->contextname = $contextname;
263         $this->allowoverrides = $allowoverrides;
264         $this->allowsafeoverrides = $allowsafeoverrides;
265         $this->overridableroles = $overridableroles;
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);
273     }
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     }
281     protected function num_extra_columns() {
282         return 3;
283     }
285     protected function add_row_cells($capability) {
286         global $OUTPUT, $PAGE;
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;
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         }
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);
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         }
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         }
340         $risks = $this->get_risks($capability);
342         echo '<td>' . $risks . '</td>';
343         echo '<td>' . $neededroles . '</td>';
344         echo '<td>' . $forbiddenroles . '</td>';
345     }
347     protected function get_risks($capability) {
348         global $OUTPUT;
350         $allrisks = get_all_risks();
351         $risksurl = new moodle_url(get_docs_url(s(get_string('risks', 'role'))));
353         $return = '';
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         }
365         return $return;
366     }
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  */
376 abstract class capability_table_with_risks extends capability_table_base {
377     protected $allrisks;
378     protected $allpermissions; // We don't need perms ourselves, but all our subclasses do.
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.
382     /** The capabilities to highlight as default/inherited. */
383     protected $parentpermissions;
384     protected $displaypermissions;
385     protected $permissions;
386     protected $changed;
387     protected $roleid;
389     public function __construct($context, $id, $roleid) {
390         parent::__construct($context, $id);
392         $this->allrisks = get_all_risks();
393         $this->risksurl = get_docs_url(s(get_string('risks', 'role')));
395         $this->allpermissions = array(
396             CAP_INHERIT => 'inherit',
397             CAP_ALLOW => 'allow',
398             CAP_PREVENT => 'prevent' ,
399             CAP_PROHIBIT => 'prohibit',
400         );
402         $this->strperms = array();
403         foreach ($this->allpermissions as $permname) {
404             $this->strperms[$permname] =  get_string($permname, 'role');
405         }
407         $this->roleid = $roleid;
408         $this->load_current_permissions();
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     }
419     protected function load_current_permissions() {
420         global $DB;
422     /// Load the overrides/definition in this context.
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         }
429     }
431     protected abstract function load_parent_permissions();
433     /**
434      * Update $this->permissions based on submitted data, while making a list of
435      * changed capabilities in $this->changed.
436      */
437     public function read_submitted_permissions() {
438         $this->changed = array();
440         foreach ($this->capabilities as $cap) {
441             if ($cap->locked || $this->skip_row($cap)) {
442             /// The user is not allowed to change the permission for this capability
443                 continue;
444             }
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             }
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     }
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         }
471     /// Force accessinfo refresh for users visiting this context.
472         mark_context_dirty($this->context->path);
473     }
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();
483     }
485     protected function add_header_cells() {
486         global $OUTPUT;
487         echo '<th colspan="' . count($this->displaypermissions) . '" scope="col">' .
488                 get_string('permission', 'role') . ' ' . $OUTPUT->help_icon('permission', 'role') . '</th>';
489         echo '<th class="risk" colspan="' . count($this->allrisks) . '" scope="col">' . get_string('risks','role') . '</th>';
490     }
492     protected function num_extra_columns() {
493         return count($this->displaypermissions) + count($this->allrisks);
494     }
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     }
506     protected abstract function add_permission_cells($capability);
508     protected function add_row_cells($capability) {
509         $this->add_permission_cells($capability);
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     }
520     /**
521      * Print a risk icon, as a link to the Risks page on Moodle Docs.
522      *
523      * @param string $type the type of risk, will be one of the keys from the
524      *      get_all_risks array. Must start with 'risk'.
525      */
526     function get_risk_icon($type) {
527         global $OUTPUT;
528         if (!isset($this->riskicons[$type])) {
529             $iconurl = $OUTPUT->pix_url('i/' . str_replace('risk', 'risk_', $type));
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')));
533         }
534         return $this->riskicons[$type];
535     }
538 /**
539  * As well as tracking the permissions information about the role we are creating
540  * or editing, we also track the other information about the role. (This class is
541  * starting to be more and more like a formslib form in some respects.)
542  */
543 class define_role_table_advanced extends capability_table_with_risks {
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;
550     protected $disabled = '';
552     public function __construct($context, $roleid) {
553         $this->roleid = $roleid;
554         parent::__construct($context, 'defineroletable', $roleid);
555         $this->displaypermissions = $this->allpermissions;
556         $this->strperms[$this->allpermissions[CAP_INHERIT]] = get_string('notset', 'role');
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         );
566     }
568     protected function load_current_permissions() {
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             }
574             $contextlevels = get_role_contextlevels($this->roleid);
575             // Put the contextlevels in the array keys, as well as the values.
576             if (!empty($contextlevels)) {
577                 $this->contextlevels = array_combine($contextlevels, $contextlevels);
578             } else {
579                 $this->contextlevels = array();
580             }
581         } else {
582             $this->role = new stdClass;
583             $this->role->name = '';
584             $this->role->shortname = '';
585             $this->role->description = '';
586             $this->role->archetype = '';
587             $this->contextlevels = array();
588         }
589         parent::load_current_permissions();
590     }
592     public function read_submitted_permissions() {
593         global $DB;
594         $this->errors = array();
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             }
603         }
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         }
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         }
623         // Description.
624         $description = optional_param('description', null, PARAM_RAW);
625         if (!is_null($description)) {
626             $this->role->description = $description;
627         }
629         // Legacy type.
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;
635             } else {
636                 $this->role->archetype = '';
637             }
638         }
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             }
650         }
652         // Now read the permissions for each capability.
653         parent::read_submitted_permissions();
654     }
656     public function is_submission_valid() {
657         return empty($this->errors);
658     }
660     /**
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.
663      */
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     }
671     public function get_role_name() {
672         return $this->role->name;
673     }
675     public function get_role_id() {
676         return $this->role->id;
677     }
679     public function get_archetype() {
680         return $this->role->archetype;
681     }
683     protected function load_parent_permissions() {
684         $this->parentpermissions = get_default_capabilities($this->role->archetype);
685     }
687     public function save_changes() {
688         global $DB;
690         if (!$this->roleid) {
691             // Creating role
692             $this->role->id = create_role($this->role->name, $this->role->shortname, $this->role->description, $this->role->archetype);
693             $this->roleid = $this->role->id; // Needed to make the parent::save_changes(); call work.
694         } else {
695             // Updating role
696             $DB->update_record('role', $this->role);
697         }
699         // Assignable contexts.
700         set_role_contextlevels($this->role->id, $this->contextlevels);
702         // Permissions.
703         parent::save_changes();
704     }
706     protected function get_name_field($id) {
707         return '<input type="text" id="' . $id . '" name="' . $id . '" maxlength="254" value="' . s($this->role->name) . '" />';
708     }
710     protected function get_shortname_field($id) {
711         return '<input type="text" id="' . $id . '" name="' . $id . '" maxlength="254" value="' . s($this->role->shortname) . '" />';
712     }
714     protected function get_description_field($id) {
715         return print_textarea(true, 10, 50, 50, 10, 'description', $this->role->description, 0, true);
716     }
718     protected function get_archetype_field($id) {
719         global $OUTPUT;
720         $options = array();
721         $options[''] = get_string('none');
722         foreach(get_role_archetypes() as $type) {
723             $options[$type] = get_string('archetype'.$type, 'role');
724         }
725         return html_writer::select($options, 'archetype', $this->role->archetype, false);
726     }
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) {
736                 $output .= '<input type="hidden" name="contextlevel' . $cl . '" value="0" />';
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     }
745     protected function print_field($name, $caption, $field) {
746         global $OUTPUT;
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])) {
765             echo $OUTPUT->error_text($this->errors[$name]);
766         }
767         echo $field;
768         echo '</div>';
769         echo '</div>';
770     }
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     }
779     public function display() {
780         global $OUTPUT;
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'));
786         $this->print_field('menuarchetype', get_string('archetype', 'role').'&nbsp;'.$OUTPUT->help_icon('archetype', 'role'), $this->get_archetype_field('archetype'));
787         $this->print_field('', get_string('maybeassignedin', 'role'), $this->get_assignable_levels_control());
788         echo "</div>";
790         $this->print_show_hide_advanced_button();
792         // Now the permissions table.
793         parent::display();
794     }
796     protected function add_permission_cells($capability) {
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) {
806                 $checked = 'checked="checked" ';
807             }
808             echo '<td class="' . $permname . $extraclass . '">';
809             echo '<label><input type="radio" name="' . $capability->name .
810                     '" value="' . $perm . '" ' . $checked . '/> ';
811             echo '<span class="note">' . $strperm . '</span>';
812             echo '</label></td>';
813         }
814     }
817 class define_role_table_basic extends define_role_table_advanced {
818     protected $stradvmessage;
819     protected $strallow;
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     }
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     }
834     protected function add_permission_cells($capability) {
835         $perm = $this->permissions[$capability->name];
836         $permname = $this->allpermissions[$perm];
837         $defaultperm = $this->allpermissions[$this->parentpermissions[$capability->name]];
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 .
846                     '" value="' . CAP_ALLOW . '" ' . $checked . '/> ' . $this->strallow . '</label>';
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     }
854 class 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]);
858         $this->disabled = 'disabled="disabled" ';
859     }
861     public function save_changes() {
862         throw new moodle_exception('invalidaccess');
863     }
865     protected function get_name_field($id) {
866         return strip_tags(format_string($this->role->name));
867     }
869     protected function get_shortname_field($id) {
870         return $this->role->shortname;
871     }
873     protected function get_description_field($id) {
874         return format_text($this->role->description, FORMAT_HTML);
875     }
877     protected function get_archetype_field($id) {
878         if (empty($this->role->archetype)) {
879             return get_string('none');
880         } else {
881             return get_string('archetype'.$this->role->archetype, 'role');
882         }
883     }
885     protected function print_show_hide_advanced_button() {
886         // Do nothing.
887     }
889     protected function add_permission_cells($capability) {
890         $perm = $this->permissions[$capability->name];
891         $permname = $this->allpermissions[$perm];
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>';
901     }
904 class override_permissions_table_advanced extends capability_table_with_risks {
905     protected $strnotset;
906     protected $haslockedcapabilities = false;
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) {
921         parent::__construct($context, 'overriderolestable', $roleid);
922         $this->displaypermissions = $this->allpermissions;
923         $this->strnotset = get_string('notset', 'role');
925     /// Determine which capabilities should be locked.
926         if ($safeoverridesonly) {
927             foreach ($this->capabilities as $capid => $cap) {
928                 if (!is_safe_capability($cap)) {
929                     $this->capabilities[$capid]->locked = true;
930                     $this->haslockedcapabilities = true;
931                 }
932             }
933         }
934     }
936     protected function load_parent_permissions() {
937         global $DB;
939     /// Get the capabilities from the parent context, so that can be shown in the interface.
940         $parentcontext = get_context_instance_by_id(get_parent_contextid($this->context));
941         $this->parentpermissions = role_context_capabilities($this->roleid, $parentcontext);
942     }
944     public function has_locked_capabilities() {
945         return $this->haslockedcapabilities;
946     }
948     protected function add_permission_cells($capability) {
949         $disabled = '';
950         if ($capability->locked || $this->parentpermissions[$capability->name] == CAP_PROHIBIT) {
951             $disabled = ' disabled="disabled"';
952         }
954     /// One cell for each possible permission.
955         foreach ($this->displaypermissions as $perm => $permname) {
956             $strperm = $this->strperms[$permname];
957             $extraclass = '';
958             if ($perm != CAP_INHERIT && $perm == $this->parentpermissions[$capability->name]) {
959                 $extraclass = ' capcurrent';
960             }
961             $checked = '';
962             if ($this->permissions[$capability->name] == $perm) {
963                 $checked = 'checked="checked" ';
964             }
965             echo '<td class="' . $permname . $extraclass . '">';
966             echo '<label><input type="radio" name="' . $capability->name .
967                     '" value="' . $perm . '" ' . $checked . $disabled . '/> ';
968             if ($perm == CAP_INHERIT) {
969                 $inherited = $this->parentpermissions[$capability->name];
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     }
983 // User selectors for managing role assignments ================================
985 /**
986  * Base class to avoid duplicating code.
987  */
988 abstract class role_assign_user_selector_base extends user_selector_base {
989     const MAX_USERS_PER_PAGE = 100;
991     protected $roleid;
992     protected $context;
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     }
1010     protected function get_options() {
1011         global $CFG;
1012         $options = parent::get_options();
1013         $options['file'] = $CFG->admin . '/roles/lib.php';
1014         $options['roleid'] = $this->roleid;
1015         $options['contextid'] = $this->context->id;
1016         return $options;
1017     }
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
1023  * some CONTEXT_BLOCK).
1024  *
1025  * This returns only enrolled users in this context.
1026  */
1027 class potential_assignees_below_course extends role_assign_user_selector_base {
1028     public function find_users($search) {
1029         global $DB;
1031         list($enrolsql, $eparams) = get_enrolled_sql($this->context);
1033         // Now we have to go to the database.
1034         list($wherecondition, $params) = $this->search_sql($search, 'u');
1035         $params = array_merge($params, $eparams);
1037         if ($wherecondition) {
1038             $wherecondition = ' AND ' . $wherecondition;
1039         }
1041         $fields      = 'SELECT ' . $this->required_fields_sql('u');
1042         $countfields = 'SELECT COUNT(u.id)';
1044         $sql   = " FROM {user} u
1045                   WHERE u.id IN ($enrolsql) $wherecondition
1046                         AND u.id NOT IN (
1047                            SELECT u.id
1048                              FROM {role_assignments} r, {user} u
1049                             WHERE r.contextid = :contextid
1050                                   AND u.id = r.userid
1051                                   AND r.roleid = :roleid)";
1052         $order = ' ORDER BY lastname ASC, firstname ASC';
1054         $params['contextid'] = $this->context->id;
1055         $params['roleid'] = $this->roleid;
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         }
1065         // If not, show them.
1066         $availableusers = $DB->get_records_sql($fields . $sql . $order, $params);
1068         if (empty($availableusers)) {
1069             return array();
1070         }
1072         if ($search) {
1073             $groupname = get_string('potusersmatching', 'role', $search);
1074         } else {
1075             $groupname = get_string('potusers', 'role');
1076         }
1078         return array($groupname => $availableusers);
1079     }
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  */
1087 class potential_assignees_course_and_above extends role_assign_user_selector_base {
1088     public function find_users($search) {
1089         global $DB;
1091         list($wherecondition, $params) = $this->search_sql($search, '');
1093         $fields      = 'SELECT ' . $this->required_fields_sql('');
1094         $countfields = 'SELECT COUNT(1)';
1096         $sql = " FROM {user}
1097                 WHERE $wherecondition
1098                       AND id NOT IN (
1099                          SELECT u.id
1100                            FROM {role_assignments} r, {user} u
1101                           WHERE r.contextid = :contextid
1102                                 AND u.id = r.userid
1103                                 AND r.roleid = :roleid)";
1104         $order = ' ORDER BY lastname ASC, firstname ASC';
1106         $params['contextid'] = $this->context->id;
1107         $params['roleid'] = $this->roleid;
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         }
1116         $availableusers = $DB->get_records_sql($fields . $sql . $order, $params);
1118         if (empty($availableusers)) {
1119             return array();
1120         }
1122         if ($search) {
1123             $groupname = get_string('potusersmatching', 'role', $search);
1124         } else {
1125             $groupname = get_string('potusers', 'role');
1126         }
1128         return array($groupname => $availableusers);
1129     }
1132 /**
1133  * User selector subclass for the list of users who already have the role in
1134  * question on the assign roles page.
1135  */
1136 class existing_role_holders extends role_assign_user_selector_base {
1138     public function __construct($name, $options) {
1139         parent::__construct($name, $options);
1140     }
1142     public function find_users($search) {
1143         global $DB;
1145         list($wherecondition, $params) = $this->search_sql($search, 'u');
1146         list($ctxcondition, $ctxparams) = $DB->get_in_or_equal(get_parent_contexts($this->context, true), SQL_PARAMS_NAMED, 'ctx00');
1147         $params = array_merge($params, $ctxparams);
1148         $params['roleid'] = $this->roleid;
1150         $sql = "SELECT ra.id as raid," . $this->required_fields_sql('u') . ",ra.contextid,ra.component
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
1157                     ra.roleid = :roleid
1158                 ORDER BY ctx.depth DESC, ra.component, u.lastname, u.firstname";
1159         $contextusers = $DB->get_records_sql($sql, $params);
1161         // No users at all.
1162         if (empty($contextusers)) {
1163             return array();
1164         }
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;
1172         $dummyuser->component = '';
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             }
1200             if ($user->component !== '') {
1201                 // bad luck, you can tweak only manual role assignments
1202                 $user->disabled = true;
1203             }
1204             unset($user->component);
1205             $currentgroup[$user->id] = $user;
1206         }
1208         return $results;
1209     }
1211     protected function this_con_group_name($search, $numusers) {
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         }
1220         $contexttype = get_contextlevel_name($this->context->contextlevel);
1221         if ($search) {
1222             $a = new stdClass;
1223             $a->search = $search;
1224             $a->contexttype = $contexttype;
1225             if ($numusers) {
1226                 return get_string('usersinthisxmatching', 'role', $a);
1227             } else {
1228                 return get_string('noneinthisxmatching', 'role', $a);
1229             }
1230         } else {
1231             if ($numusers) {
1232                 return get_string('usersinthisx', 'role', $contexttype);
1233             } else {
1234                 return get_string('noneinthisx', 'role', $contexttype);
1235             }
1236         }
1237     }
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         }
1250     }
1253 /**
1254  * Base class for managing the data in the grid of checkboxes on the role allow
1255  * allow/overrides/switch editing pages (allow.php).
1256  */
1257 abstract class role_allow_role_page {
1258     protected $tablename;
1259     protected $targetcolname;
1260     protected $roles;
1261     protected $allowed = null;
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      */
1267     public function __construct($tablename, $targetcolname) {
1268         $this->tablename = $tablename;
1269         $this->targetcolname = $targetcolname;
1270         $this->load_required_roles();
1271     }
1273     /**
1274      * Load information about all the roles we will need information about.
1275      */
1276     protected function load_required_roles() {
1277     /// Get all roles
1278         $this->roles = get_all_roles();
1279         role_fix_names($this->roles, get_context_instance(CONTEXT_SYSTEM), ROLENAME_ORIGINAL);
1280     }
1282     /**
1283      * Update the data with the new settings submitted by the user.
1284      */
1285     public function process_submission() {
1286         global $DB;
1287     /// Delete all records, then add back the ones that should be allowed.
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     }
1298     /**
1299      * Set one allow in the database.
1300      * @param integer $fromroleid
1301      * @param integer $targetroleid
1302      */
1303     protected abstract function set_allow($fromroleid, $targetroleid);
1305     /**
1306      * Load the current allows from the database.
1307      */
1308     public function load_current_settings() {
1309         global $DB;
1310     /// Load the current settings
1311         $this->allowed = array();
1312         foreach ($this->roles as $role) {
1313             // Make an array $role->id => false. This is probably too clever for its own good.
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     }
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     }
1331     /**
1332      * @return object a $table structure that can be passed to print_table, containing
1333      * one cell for each checkbox.
1334      */
1335     public function get_table() {
1336         $table = new html_table();
1337         $table->tablealign = 'center';
1338         $table->cellpadding = 5;
1339         $table->cellspacing = 0;
1340         $table->width = '90%';
1341         $table->align = array('left');
1342         $table->rotateheaders = true;
1343         $table->head = array('&#xa0;');
1344         $table->colclasses = array('');
1346     /// Add role name headers.
1347         foreach ($this->roles as $targetrole) {
1348             $table->head[] = $targetrole->localname;
1349             $table->align[] = 'left';
1350             if ($this->is_allowed_target($targetrole->id)) {
1351                 $table->colclasses[] = '';
1352             } else {
1353                 $table->colclasses[] = 'dimmed_text';
1354             }
1355         }
1357     /// Now the rest of the table.
1358         foreach ($this->roles as $fromrole) {
1359             $row = array($fromrole->localname);
1360             foreach ($this->roles as $targetrole) {
1361                 $checked = '';
1362                 $disabled = '';
1363                 if ($this->allowed[$fromrole->id][$targetrole->id]) {
1364                     $checked = 'checked="checked" ';
1365                 }
1366                 if (!$this->is_allowed_target($targetrole->id)) {
1367                     $disabled = 'disabled="disabled" ';
1368                 }
1369                 $name = 's_' . $fromrole->id . '_' . $targetrole->id;
1370                 $tooltip = $this->get_cell_tooltip($fromrole, $targetrole);
1371                 $row[] = '<input type="checkbox" name="' . $name . '" id="' . $name .
1372                         '" title="' . $tooltip . '" value="1" ' . $checked . $disabled . '/>' .
1373                         '<label for="' . $name . '" class="accesshide">' . $tooltip . '</label>';
1374             }
1375             $table->data[] = $row;
1376         }
1378         return $table;
1379     }
1381     /**
1382      * Snippet of text displayed above the table, telling the admin what to do.
1383      * @return unknown_type
1384      */
1385     public abstract function get_intro_text();
1388 /**
1389  * Subclass of role_allow_role_page for the Allow assigns tab.
1390  */
1391 class role_allow_assign_page extends role_allow_role_page {
1392     public function __construct() {
1393         parent::__construct('role_allow_assign', 'allowassign');
1394     }
1396     protected function set_allow($fromroleid, $targetroleid) {
1397         allow_assign($fromroleid, $targetroleid);
1398     }
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     }
1407     public function get_intro_text() {
1408         return get_string('configallowassign', 'admin');
1409     }
1412 /**
1413  * Subclass of role_allow_role_page for the Allow overrides tab.
1414  */
1415 class role_allow_override_page extends role_allow_role_page {
1416     public function __construct() {
1417         parent::__construct('role_allow_override', 'allowoverride');
1418     }
1420     protected function set_allow($fromroleid, $targetroleid) {
1421         allow_override($fromroleid, $targetroleid);
1422     }
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     }
1431     public function get_intro_text() {
1432         return get_string('configallowoverride2', 'admin');
1433     }
1436 /**
1437  * Subclass of role_allow_role_page for the Allow switches tab.
1438  */
1439 class role_allow_switch_page extends role_allow_role_page {
1440     protected $allowedtargetroles;
1442     public function __construct() {
1443         parent::__construct('role_allow_switch', 'allowswitch');
1444     }
1446     protected function load_required_roles() {
1447         global $DB;
1448         parent::load_required_roles();
1449         $this->allowedtargetroles = $DB->get_records_menu('role', NULL, 'id');
1450     }
1452     protected function set_allow($fromroleid, $targetroleid) {
1453         allow_switch($fromroleid, $targetroleid);
1454     }
1456     protected function is_allowed_target($targetroleid) {
1457         return isset($this->allowedtargetroles[$targetroleid]);
1458     }
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     }
1467     public function get_intro_text() {
1468         return get_string('configallowswitch', 'admin');
1469     }
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  *
1479  * @param stdClass $context a context.
1480  * @param string $name passed to user selector constructor.
1481  * @param array $options to user selector constructor.
1482  * @return user_selector_base an appropriate user selector.
1483  */
1484 function 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         }
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;
1500 class 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     }
1511     public function find_users($search) {
1512         global $CFG, $DB;
1513         list($wherecondition, $params) = $this->search_sql($search, '');
1515         $fields      = 'SELECT ' . $this->required_fields_sql('');
1516         $countfields = 'SELECT COUNT(1)';
1518         $sql = " FROM {user}
1519                 WHERE $wherecondition AND mnethostid = :localmnet";
1520         $order = ' ORDER BY lastname ASC, firstname ASC';
1521         $params['localmnet'] = $CFG->mnet_localhost_id; // it could be dangerous to make remote users admins and also this could lead to other problems
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         }
1531         $availableusers = $DB->get_records_sql($fields . $sql . $order, $params);
1533         if (empty($availableusers)) {
1534             return array();
1535         }
1537         if ($search) {
1538             $groupname = get_string('potusersmatching', 'role', $search);
1539         } else {
1540             $groupname = get_string('potusers', 'role');
1541         }
1543         return array($groupname => $availableusers);
1544     }
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     }
1554 class 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     }
1564     public function find_users($search) {
1565         global $DB, $CFG;
1566         list($wherecondition, $params) = $this->search_sql($search, '');
1568         $fields      = 'SELECT ' . $this->required_fields_sql('');
1569         $countfields = 'SELECT COUNT(1)';
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';
1580         $availableusers = $DB->get_records_sql($fields . $sql . $order, $params);
1582         if (empty($availableusers)) {
1583             return array();
1584         }
1586         if ($search) {
1587             $groupname = get_string('extusersmatching', 'role', $search);
1588         } else {
1589             $groupname = get_string('extusers', 'role');
1590         }
1592         return array($groupname => $availableusers);
1593     }
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     }