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 * Capabilities table with risks.
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 * This subclass is the bases for both the define roles and override roles
29 * pages. As well as adding the risks columns, this also provides generic
30 * facilities for showing a certain number of permissions columns, and
31 * recording the current and submitted permissions for each capability.
32 */
33abstract class core_role_capability_table_with_risks extends core_role_capability_table_base {
34    protected $allrisks;
35    protected $allpermissions; // We don't need perms ourselves, but all our subclasses do.
36    protected $strperms; // Language string cache.
37    protected $risksurl; // URL in moodledocs about risks.
38    /** @var array The capabilities to highlight as default/inherited. */
39    protected $parentpermissions;
40    protected $displaypermissions;
41    protected $permissions;
42    protected $changed;
43    protected $roleid;
44
45    public function __construct($context, $id, $roleid) {
46        parent::__construct($context, $id);
47
48        $this->allrisks = get_all_risks();
49        $this->risksurl = get_docs_url(s(get_string('risks', 'core_role')));
50
51        $this->allpermissions = array(
52            CAP_INHERIT => 'inherit',
53            CAP_ALLOW => 'allow',
54            CAP_PREVENT => 'prevent' ,
55            CAP_PROHIBIT => 'prohibit',
56        );
57
58        $this->strperms = array();
59        foreach ($this->allpermissions as $permname) {
60            $this->strperms[$permname] =  get_string($permname, 'core_role');
61        }
62
63        $this->roleid = $roleid;
64        $this->load_current_permissions();
65
66        // Fill in any blank permissions with an explicit CAP_INHERIT, and init a locked field.
67        foreach ($this->capabilities as $capid => $cap) {
68            if (!isset($this->permissions[$cap->name])) {
69                $this->permissions[$cap->name] = CAP_INHERIT;
70            }
71            $this->capabilities[$capid]->locked = false;
72        }
73    }
74
75    protected function load_current_permissions() {
76        global $DB;
77
78        // Load the overrides/definition in this context.
79        if ($this->roleid) {
80            $this->permissions = $DB->get_records_menu('role_capabilities', array('roleid' => $this->roleid,
81                'contextid' => $this->context->id), '', 'capability,permission');
82        } else {
83            $this->permissions = array();
84        }
85    }
86
87    protected abstract function load_parent_permissions();
88
89    /**
90     * Update $this->permissions based on submitted data, while making a list of
91     * changed capabilities in $this->changed.
92     */
93    public function read_submitted_permissions() {
94        $this->changed = array();
95
96        foreach ($this->capabilities as $cap) {
97            if ($cap->locked || $this->skip_row($cap)) {
98                // The user is not allowed to change the permission for this capability.
99                continue;
100            }
101
102            $permission = optional_param($cap->name, null, PARAM_PERMISSION);
103            if (is_null($permission)) {
104                // A permission was not specified in submitted data.
105                continue;
106            }
107
108            // If the permission has changed, update $this->permissions and
109            // Record the fact there is data to save.
110            if ($this->permissions[$cap->name] != $permission) {
111                $this->permissions[$cap->name] = $permission;
112                $this->changed[] = $cap->name;
113            }
114        }
115    }
116
117    /**
118     * Save the new values of any permissions that have been changed.
119     */
120    public function save_changes() {
121        // Set the permissions.
122        foreach ($this->changed as $changedcap) {
123            assign_capability($changedcap, $this->permissions[$changedcap],
124                $this->roleid, $this->context->id, true);
125        }
126    }
127
128    public function display() {
129        $this->load_parent_permissions();
130        foreach ($this->capabilities as $cap) {
131            if (!isset($this->parentpermissions[$cap->name])) {
132                $this->parentpermissions[$cap->name] = CAP_INHERIT;
133            }
134        }
135        parent::display();
136    }
137
138    protected function add_header_cells() {
139        global $OUTPUT;
140        echo '<th colspan="' . count($this->displaypermissions) . '" scope="col">' .
141            get_string('permission', 'core_role') . ' ' . $OUTPUT->help_icon('permission', 'core_role') . '</th>';
142        echo '<th class="risk" colspan="' . count($this->allrisks) . '" scope="col">' . get_string('risks', 'core_role') . '</th>';
143    }
144
145    protected function num_extra_columns() {
146        return count($this->displaypermissions) + count($this->allrisks);
147    }
148
149    protected function get_row_classes($capability) {
150        $rowclasses = array();
151        foreach ($this->allrisks as $riskname => $risk) {
152            if ($risk & (int)$capability->riskbitmask) {
153                $rowclasses[] = $riskname;
154            }
155        }
156        return $rowclasses;
157    }
158
159    protected abstract function add_permission_cells($capability);
160
161    protected function add_row_cells($capability) {
162        $cells = $this->add_permission_cells($capability);
163        // One cell for each possible risk.
164        foreach ($this->allrisks as $riskname => $risk) {
165            $cells .= '<td class="risk ' . str_replace('risk', '', $riskname) . '">';
166            if ($risk & (int)$capability->riskbitmask) {
167                $cells .= $this->get_risk_icon($riskname);
168            }
169            $cells .= '</td>';
170        }
171        return $cells;
172    }
173
174    /**
175     * Print a risk icon, as a link to the Risks page on Moodle Docs.
176     *
177     * @param string $type the type of risk, will be one of the keys from the
178     *      get_all_risks array. Must start with 'risk'.
179     */
180    public function get_risk_icon($type) {
181        global $OUTPUT;
182
183        $alt = get_string("{$type}short", "admin");
184        $title = get_string($type, "admin");
185
186        $text = $OUTPUT->pix_icon('i/' . str_replace('risk', 'risk_', $type), $alt, 'moodle', [
187                'title' => $title,
188            ]);
189        $action = new popup_action('click', $this->risksurl, 'docspopup');
190        $riskicon = $OUTPUT->action_link($this->risksurl, $text, $action);
191
192        return $riskicon;
193    }
194}
195