source: moodle/trunk/fuentes/admin/roles/classes/define_role_table_advanced.php @ 136

Last change on this file since 136 was 136, checked in by mabarracus, 4 years ago

Ported code to xenial

File size: 23.2 KB
Line 
1<?php
2// This file is part of Moodle - http://moodle.org/
3//
4// Moodle is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// Moodle is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
16
17/**
18 * Advanced role definition form.
19 *
20 * @package    core_role
21 * @copyright  1999 onwards Martin Dougiamas (http://dougiamas.com)
22 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23 */
24
25defined('MOODLE_INTERNAL') || die();
26
27
28/**
29 * As well as tracking the permissions information about the role we are creating
30 * or editing, we also track the other information about the role. (This class is
31 * starting to be more and more like a formslib form in some respects.)
32 */
33class core_role_define_role_table_advanced extends core_role_capability_table_with_risks {
34    /** @var stdClass Used to store other information (besides permissions) about the role we are creating/editing. */
35    protected $role;
36    /** @var array Used to store errors found when validating the data. */
37    protected $errors;
38    protected $contextlevels;
39    protected $allcontextlevels;
40    protected $disabled = '';
41
42    protected $allowassign;
43    protected $allowoverride;
44    protected $allowswitch;
45
46    public function __construct($context, $roleid) {
47        $this->roleid = $roleid;
48        parent::__construct($context, 'defineroletable', $roleid);
49        $this->displaypermissions = $this->allpermissions;
50        $this->strperms[$this->allpermissions[CAP_INHERIT]] = get_string('notset', 'core_role');
51
52        $this->allcontextlevels = array();
53        $levels = context_helper::get_all_levels();
54        foreach ($levels as $level => $classname) {
55            $this->allcontextlevels[$level] = context_helper::get_level_name($level);
56        }
57    }
58
59    protected function load_current_permissions() {
60        global $DB;
61        if ($this->roleid) {
62            if (!$this->role = $DB->get_record('role', array('id' => $this->roleid))) {
63                throw new moodle_exception('invalidroleid');
64            }
65            $contextlevels = get_role_contextlevels($this->roleid);
66            // Put the contextlevels in the array keys, as well as the values.
67            if (!empty($contextlevels)) {
68                $this->contextlevels = array_combine($contextlevels, $contextlevels);
69            } else {
70                $this->contextlevels = array();
71            }
72            $this->allowassign = array_keys($this->get_allow_roles_list('assign'));
73            $this->allowoverride = array_keys($this->get_allow_roles_list('override'));
74            $this->allowswitch = array_keys($this->get_allow_roles_list('switch'));
75
76        } else {
77            $this->role = new stdClass;
78            $this->role->name = '';
79            $this->role->shortname = '';
80            $this->role->description = '';
81            $this->role->archetype = '';
82            $this->contextlevels = array();
83            $this->allowassign = array();
84            $this->allowoverride = array();
85            $this->allowswitch = array();
86        }
87        parent::load_current_permissions();
88    }
89
90    public function read_submitted_permissions() {
91        global $DB;
92        $this->errors = array();
93
94        // Role short name. We clean this in a special way. We want to end up
95        // with only lowercase safe ASCII characters.
96        $shortname = optional_param('shortname', null, PARAM_RAW);
97        if (!is_null($shortname)) {
98            $this->role->shortname = $shortname;
99            $this->role->shortname = core_text::specialtoascii($this->role->shortname);
100            $this->role->shortname = core_text::strtolower(clean_param($this->role->shortname, PARAM_ALPHANUMEXT));
101            if (empty($this->role->shortname)) {
102                $this->errors['shortname'] = get_string('errorbadroleshortname', 'core_role');
103            }
104        }
105        if ($DB->record_exists_select('role', 'shortname = ? and id <> ?', array($this->role->shortname, $this->roleid))) {
106            $this->errors['shortname'] = get_string('errorexistsroleshortname', 'core_role');
107        }
108
109        // Role name.
110        $name = optional_param('name', null, PARAM_TEXT);
111        if (!is_null($name)) {
112            $this->role->name = $name;
113            // Hack: short names of standard roles are equal to archetypes, empty name means localised via lang packs.
114            $archetypes = get_role_archetypes();
115            if (!isset($archetypes[$shortname]) and html_is_blank($this->role->name)) {
116                $this->errors['name'] = get_string('errorbadrolename', 'core_role');
117            }
118        }
119        if ($this->role->name !== '' and $DB->record_exists_select('role', 'name = ? and id <> ?', array($this->role->name, $this->roleid))) {
120            $this->errors['name'] = get_string('errorexistsrolename', 'core_role');
121        }
122
123        // Description.
124        $description = optional_param('description', null, PARAM_RAW);
125        if (!is_null($description)) {
126            $this->role->description = $description;
127        }
128
129        // Legacy type.
130        $archetype = optional_param('archetype', null, PARAM_RAW);
131        if (isset($archetype)) {
132            $archetypes = get_role_archetypes();
133            if (isset($archetypes[$archetype])) {
134                $this->role->archetype = $archetype;
135            } else {
136                $this->role->archetype = '';
137            }
138        }
139
140        // Assignable context levels.
141        foreach ($this->allcontextlevels as $cl => $notused) {
142            $assignable = optional_param('contextlevel' . $cl, null, PARAM_BOOL);
143            if (!is_null($assignable)) {
144                if ($assignable) {
145                    $this->contextlevels[$cl] = $cl;
146                } else {
147                    unset($this->contextlevels[$cl]);
148                }
149            }
150        }
151
152        // Allowed roles.
153        $allow = optional_param_array('allowassign', null, PARAM_INT);
154        if (!is_null($allow)) {
155            $this->allowassign = $allow;
156        }
157        $allow = optional_param_array('allowoverride', null, PARAM_INT);
158        if (!is_null($allow)) {
159            $this->allowoverride = $allow;
160        }
161        $allow = optional_param_array('allowswitch', null, PARAM_INT);
162        if (!is_null($allow)) {
163            $this->allowswitch = $allow;
164        }
165
166        // Now read the permissions for each capability.
167        parent::read_submitted_permissions();
168    }
169
170    public function is_submission_valid() {
171        return empty($this->errors);
172    }
173
174    /**
175     * Call this after the table has been initialised,
176     * this resets everything to that role.
177     *
178     * @param int $roleid role id or 0 for no role
179     * @param array $options array with following keys:
180     *      'name', 'shortname', 'description', 'permissions', 'archetype',
181     *      'contextlevels', 'allowassign', 'allowoverride', 'allowswitch'
182     */
183    public function force_duplicate($roleid, array $options) {
184        global $DB;
185
186        if ($roleid == 0) {
187            // This means reset to nothing == remove everything.
188
189            if ($options['shortname']) {
190                $this->role->shortname = '';
191            }
192
193            if ($options['name']) {
194                $this->role->name = '';
195            }
196
197            if ($options['description']) {
198                $this->role->description = '';
199            }
200
201            if ($options['archetype']) {
202                $this->role->archetype = '';
203            }
204
205            if ($options['contextlevels']) {
206                $this->contextlevels = array();
207            }
208
209            if ($options['allowassign']) {
210                $this->allowassign = array();
211            }
212            if ($options['allowoverride']) {
213                $this->allowoverride = array();
214            }
215            if ($options['allowswitch']) {
216                $this->allowswitch = array();
217            }
218
219            if ($options['permissions']) {
220                foreach ($this->capabilities as $capid => $cap) {
221                    $this->permissions[$cap->name] = CAP_INHERIT;
222                }
223            }
224
225            return;
226        }
227
228        $role = $DB->get_record('role', array('id'=>$roleid), '*', MUST_EXIST);
229
230        if ($options['shortname']) {
231            $this->role->shortname = $role->shortname;
232        }
233
234        if ($options['name']) {
235            $this->role->name = $role->name;
236        }
237
238        if ($options['description']) {
239            $this->role->description = $role->description;
240        }
241
242        if ($options['archetype']) {
243            $this->role->archetype = $role->archetype;
244        }
245
246        if ($options['contextlevels']) {
247            $this->contextlevels = array();
248            $levels = get_role_contextlevels($roleid);
249            foreach ($levels as $cl) {
250                $this->contextlevels[$cl] = $cl;
251            }
252        }
253
254        if ($options['allowassign']) {
255            $this->allowassign = array_keys($this->get_allow_roles_list('assign', $roleid));
256        }
257        if ($options['allowoverride']) {
258            $this->allowoverride = array_keys($this->get_allow_roles_list('override', $roleid));
259        }
260        if ($options['allowswitch']) {
261            $this->allowswitch = array_keys($this->get_allow_roles_list('switch', $roleid));
262        }
263
264        if ($options['permissions']) {
265            $this->permissions = $DB->get_records_menu('role_capabilities',
266                array('roleid' => $roleid, 'contextid' => context_system::instance()->id),
267                '', 'capability,permission');
268
269            foreach ($this->capabilities as $capid => $cap) {
270                if (!isset($this->permissions[$cap->name])) {
271                    $this->permissions[$cap->name] = CAP_INHERIT;
272                }
273            }
274        }
275    }
276
277    /**
278     * Change the role definition to match given archetype.
279     *
280     * @param string $archetype
281     * @param array $options array with following keys:
282     *      'name', 'shortname', 'description', 'permissions', 'archetype',
283     *      'contextlevels', 'allowassign', 'allowoverride', 'allowswitch'
284     */
285    public function force_archetype($archetype, array $options) {
286        $archetypes = get_role_archetypes();
287        if (!isset($archetypes[$archetype])) {
288            throw new coding_exception('Unknown archetype: '.$archetype);
289        }
290
291        if ($options['shortname']) {
292            $this->role->shortname = '';
293        }
294
295        if ($options['name']) {
296            $this->role->name = '';
297        }
298
299        if ($options['description']) {
300            $this->role->description = '';
301        }
302
303        if ($options['archetype']) {
304            $this->role->archetype = $archetype;
305        }
306
307        if ($options['contextlevels']) {
308            $this->contextlevels = array();
309            $defaults = get_default_contextlevels($archetype);
310            foreach ($defaults as $cl) {
311                $this->contextlevels[$cl] = $cl;
312            }
313        }
314
315        if ($options['allowassign']) {
316            $this->allowassign = get_default_role_archetype_allows('assign', $archetype);
317        }
318        if ($options['allowoverride']) {
319            $this->allowoverride = get_default_role_archetype_allows('override', $archetype);
320        }
321        if ($options['allowswitch']) {
322            $this->allowswitch = get_default_role_archetype_allows('switch', $archetype);
323        }
324
325        if ($options['permissions']) {
326            $defaultpermissions = get_default_capabilities($archetype);
327            foreach ($this->permissions as $k => $v) {
328                if (isset($defaultpermissions[$k])) {
329                    $this->permissions[$k] = $defaultpermissions[$k];
330                    continue;
331                }
332                $this->permissions[$k] = CAP_INHERIT;
333            }
334        }
335    }
336
337    /**
338     * Change the role definition to match given preset.
339     *
340     * @param string $xml
341     * @param array $options array with following keys:
342     *      'name', 'shortname', 'description', 'permissions', 'archetype',
343     *      'contextlevels', 'allowassign', 'allowoverride', 'allowswitch'
344     */
345    public function force_preset($xml, array $options) {
346        if (!$info = core_role_preset::parse_preset($xml)) {
347            throw new coding_exception('Invalid role preset');
348        }
349
350        if ($options['shortname']) {
351            if (isset($info['shortname'])) {
352                $this->role->shortname = $info['shortname'];
353            }
354        }
355
356        if ($options['name']) {
357            if (isset($info['name'])) {
358                $this->role->name = $info['name'];
359            }
360        }
361
362        if ($options['description']) {
363            if (isset($info['description'])) {
364                $this->role->description = $info['description'];
365            }
366        }
367
368        if ($options['archetype']) {
369            if (isset($info['archetype'])) {
370                $this->role->archetype = $info['archetype'];
371            }
372        }
373
374        if ($options['contextlevels']) {
375            if (isset($info['contextlevels'])) {
376                $this->contextlevels = $info['contextlevels'];
377            }
378        }
379
380        foreach (array('assign', 'override', 'switch') as $type) {
381            if ($options['allow'.$type]) {
382                if (isset($info['allow'.$type])) {
383                    $this->{'allow'.$type} = $info['allow'.$type];
384                }
385            }
386        }
387
388        if ($options['permissions']) {
389            foreach ($this->permissions as $k => $v) {
390                // Note: do not set everything else to CAP_INHERIT here
391                //       because the xml file might not contain all capabilities.
392                if (isset($info['permissions'][$k])) {
393                    $this->permissions[$k] = $info['permissions'][$k];
394                }
395            }
396        }
397    }
398
399    public function get_role_name() {
400        return $this->role->name;
401    }
402
403    public function get_role_id() {
404        return $this->role->id;
405    }
406
407    public function get_archetype() {
408        return $this->role->archetype;
409    }
410
411    protected function load_parent_permissions() {
412        $this->parentpermissions = get_default_capabilities($this->role->archetype);
413    }
414
415    public function save_changes() {
416        global $DB;
417
418        if (!$this->roleid) {
419            // Creating role.
420            $this->role->id = create_role($this->role->name, $this->role->shortname, $this->role->description, $this->role->archetype);
421            $this->roleid = $this->role->id; // Needed to make the parent::save_changes(); call work.
422        } else {
423            // Updating role.
424            $DB->update_record('role', $this->role);
425        }
426
427        // Assignable contexts.
428        set_role_contextlevels($this->role->id, $this->contextlevels);
429
430        // Set allowed roles.
431        $this->save_allow('assign');
432        $this->save_allow('override');
433        $this->save_allow('switch');
434
435        // Permissions.
436        parent::save_changes();
437    }
438
439    protected function save_allow($type) {
440        global $DB;
441
442        $current = array_keys($this->get_allow_roles_list($type));
443        $wanted = $this->{'allow'.$type};
444
445        $addfunction = 'allow_'.$type;
446        $deltable = 'role_allow_'.$type;
447        $field = 'allow'.$type;
448
449        foreach ($current as $roleid) {
450            if (!in_array($roleid, $wanted)) {
451                $DB->delete_records($deltable, array('roleid'=>$this->roleid, $field=>$roleid));
452                continue;
453            }
454            $key = array_search($roleid, $wanted);
455            unset($wanted[$key]);
456        }
457
458        foreach ($wanted as $roleid) {
459            if ($roleid == -1) {
460                $roleid = $this->roleid;
461            }
462            $addfunction($this->roleid, $roleid);
463        }
464    }
465
466    protected function get_name_field($id) {
467        return '<input type="text" id="' . $id . '" name="' . $id . '" maxlength="254" value="' . s($this->role->name) . '" />';
468    }
469
470    protected function get_shortname_field($id) {
471        return '<input type="text" id="' . $id . '" name="' . $id . '" maxlength="254" value="' . s($this->role->shortname) . '" />';
472    }
473
474    protected function get_description_field($id) {
475        return print_textarea(true, 10, 50, 50, 10, 'description', $this->role->description, 0, true);
476    }
477
478    protected function get_archetype_field($id) {
479        $options = array();
480        $options[''] = get_string('none');
481        foreach (get_role_archetypes() as $type) {
482            $options[$type] = get_string('archetype'.$type, 'role');
483        }
484        return html_writer::select($options, 'archetype', $this->role->archetype, false);
485    }
486
487    protected function get_assignable_levels_control() {
488        $output = '';
489        foreach ($this->allcontextlevels as $cl => $clname) {
490            $extraarguments = $this->disabled;
491            if (in_array($cl, $this->contextlevels)) {
492                $extraarguments .= 'checked="checked" ';
493            }
494            if (!$this->disabled) {
495                $output .= '<input type="hidden" name="contextlevel' . $cl . '" value="0" />';
496            }
497            $output .= '<input type="checkbox" id="cl' . $cl . '" name="contextlevel' . $cl .
498                '" value="1" ' . $extraarguments . '/> ';
499            $output .= '<label for="cl' . $cl . '">' . $clname . "</label><br />\n";
500        }
501        return $output;
502    }
503
504    /**
505     * Returns an array of roles of the allowed type.
506     *
507     * @param string $type Must be one of: assign, switch, or override.
508     * @param int $roleid (null means current role)
509     * @return array
510     */
511    protected function get_allow_roles_list($type, $roleid = null) {
512        global $DB;
513
514        if ($type !== 'assign' and $type !== 'switch' and $type !== 'override') {
515            debugging('Invalid role allowed type specified', DEBUG_DEVELOPER);
516            return array();
517        }
518
519        if ($roleid === null) {
520            $roleid = $this->roleid;
521        }
522
523        if (empty($roleid)) {
524            return array();
525        }
526
527        $sql = "SELECT r.*
528                  FROM {role} r
529                  JOIN {role_allow_{$type}} a ON a.allow{$type} = r.id
530                 WHERE a.roleid = :roleid
531              ORDER BY r.sortorder ASC";
532        return $DB->get_records_sql($sql, array('roleid'=>$roleid));
533    }
534
535    /**
536     * Returns an array of roles with the allowed type.
537     *
538     * @param string $type Must be one of: assign, switch, or override.
539     * @return array Am array of role names with the allowed type
540     */
541    protected function get_allow_role_control($type) {
542        if ($type !== 'assign' and $type !== 'switch' and $type !== 'override') {
543            debugging('Invalid role allowed type specified', DEBUG_DEVELOPER);
544            return '';
545        }
546
547        $property = 'allow'.$type;
548        $selected = $this->$property;
549
550        $options = array();
551        foreach (role_get_names(null, ROLENAME_ALIAS) as $role) {
552            $options[$role->id] = $role->localname;
553        }
554        if ($this->roleid == 0) {
555            $options[-1] = get_string('thisnewrole', 'core_role');
556        }
557        return html_writer::select($options, 'allow'.$type.'[]', $selected, false, array('multiple'=>'multiple', 'size'=>10));
558    }
559
560    /**
561     * Returns information about the risks associated with a role.
562     *
563     * @return string
564     */
565    protected function get_role_risks_info() {
566        return '';
567    }
568
569    protected function print_field($name, $caption, $field) {
570        global $OUTPUT;
571        // Attempt to generate HTML like formslib.
572        echo '<div class="fitem">';
573        echo '<div class="fitemtitle">';
574        if ($name) {
575            echo '<label for="' . $name . '">';
576        }
577        echo $caption;
578        if ($name) {
579            echo "</label>\n";
580        }
581        echo '</div>';
582        if (isset($this->errors[$name])) {
583            $extraclass = ' error';
584        } else {
585            $extraclass = '';
586        }
587        echo '<div class="felement' . $extraclass . '">';
588        echo $field;
589        if (isset($this->errors[$name])) {
590            echo $OUTPUT->error_text($this->errors[$name]);
591        }
592        echo '</div>';
593        echo '</div>';
594    }
595
596    protected function print_show_hide_advanced_button() {
597        echo '<p class="definenotice">' . get_string('highlightedcellsshowdefault', 'core_role') . ' </p>';
598        echo '<div class="advancedbutton">';
599        echo '<input type="submit" name="toggleadvanced" value="' . get_string('hideadvanced', 'form') . '" />';
600        echo '</div>';
601    }
602
603    public function display() {
604        global $OUTPUT;
605        // Extra fields at the top of the page.
606        echo '<div class="topfields clearfix">';
607        $this->print_field('shortname', get_string('roleshortname', 'core_role').'&nbsp;'.$OUTPUT->help_icon('roleshortname', 'core_role'), $this->get_shortname_field('shortname'));
608        $this->print_field('name', get_string('customrolename', 'core_role').'&nbsp;'.$OUTPUT->help_icon('customrolename', 'core_role'), $this->get_name_field('name'));
609        $this->print_field('edit-description', get_string('customroledescription', 'core_role').'&nbsp;'.$OUTPUT->help_icon('customroledescription', 'core_role'),
610            $this->get_description_field('description'));
611        $this->print_field('menuarchetype', get_string('archetype', 'core_role').'&nbsp;'.$OUTPUT->help_icon('archetype', 'core_role'), $this->get_archetype_field('archetype'));
612        $this->print_field('', get_string('maybeassignedin', 'core_role'), $this->get_assignable_levels_control());
613        $this->print_field('menuallowassign', get_string('allowassign', 'core_role'), $this->get_allow_role_control('assign'));
614        $this->print_field('menuallowoverride', get_string('allowoverride', 'core_role'), $this->get_allow_role_control('override'));
615        $this->print_field('menuallowswitch', get_string('allowswitch', 'core_role'), $this->get_allow_role_control('switch'));
616        if ($risks = $this->get_role_risks_info()) {
617            $this->print_field('', get_string('rolerisks', 'core_role'), $risks);
618        }
619        echo "</div>";
620
621        $this->print_show_hide_advanced_button();
622
623        // Now the permissions table.
624        parent::display();
625    }
626
627    protected function add_permission_cells($capability) {
628        // One cell for each possible permission.
629        foreach ($this->displaypermissions as $perm => $permname) {
630            $strperm = $this->strperms[$permname];
631            $extraclass = '';
632            if ($perm == $this->parentpermissions[$capability->name]) {
633                $extraclass = ' capdefault';
634            }
635            $checked = '';
636            if ($this->permissions[$capability->name] == $perm) {
637                $checked = 'checked="checked" ';
638            }
639            echo '<td class="' . $permname . $extraclass . '">';
640            echo '<label><input type="radio" name="' . $capability->name .
641                '" value="' . $perm . '" ' . $checked . '/> ';
642            echo '<span class="note">' . $strperm . '</span>';
643            echo '</label></td>';
644        }
645    }
646}
Note: See TracBrowser for help on using the repository browser.