MDL-61943 core_role: Add privacy implementation for core_role
[moodle.git] / admin / roles / classes / privacy / provider.php
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  * Privacy Subsystem implementation for core_role.
18  *
19  * @package    core_role
20  * @copyright  2018 Carlos Escobedo <carlos@moodle.com>
21  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
22  */
23 namespace core_role\privacy;
24 defined('MOODLE_INTERNAL') || die();
26 use \core_privacy\local\metadata\collection;
27 use \core_privacy\local\request\contextlist;
28 use \core_privacy\local\request\approved_contextlist;
29 use \core_privacy\local\request\transform;
30 use \core_privacy\local\request\writer;
32 /**
33  * Privacy provider for core_role.
34  *
35  * @copyright  2018 Carlos Escobedo <carlos@moodle.com>
36  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
37  */
38 class provider implements
39     \core_privacy\local\metadata\provider,
40     \core_privacy\local\request\subsystem\provider,
41     \core_privacy\local\request\subsystem\plugin_provider,
42     \core_privacy\local\request\user_preference_provider {
44     /**
45      * Get information about the user data stored by this plugin.
46      *
47      * @param  collection $collection An object for storing metadata.
48      * @return collection The metadata.
49      */
50     public static function get_metadata(collection $collection) : collection {
51         $rolecapabilities = [
52             'roleid' => 'privacy:metadata:role_capabilities:roleid',
53             'capability' => 'privacy:metadata:role_capabilities:capability',
54             'permission' => 'privacy:metadata:role_capabilities:permission',
55             'timemodified' => 'privacy:metadata:role_capabilities:timemodified',
56             'modifierid' => 'privacy:metadata:role_capabilities:modifierid'
57         ];
58         $roleassignments = [
59             'roleid' => 'privacy:metadata:role_assignments:roleid',
60             'userid' => 'privacy:metadata:role_assignments:userid',
61             'timemodified' => 'privacy:metadata:role_assignments:timemodified',
62             'modifierid' => 'privacy:metadata:role_assignments:modifierid',
63             'component' => 'privacy:metadata:role_assignments:component',
64             'itemid' => 'privacy:metadata:role_assignments:itemid'
65         ];
66         $collection->add_database_table('role_capabilities', $rolecapabilities,
67             'privacy:metadata:role_capabilities:tableexplanation');
68         $collection->add_database_table('role_assignments', $roleassignments,
69             'privacy:metadata:role_assignments:tableexplanation');
71         $collection->add_user_preference('definerole_showadvanced',
72             'privacy:metadata:preference:showadvanced');
74         return $collection;
75     }
76     /**
77      * Export all user preferences for the plugin.
78      *
79      * @param   int         $userid The userid of the user whose data is to be exported.
80      */
81     public static function export_user_preferences(int $userid) {
82         $showadvanced = get_user_preferences('definerole_showadvanced', null, $userid);
83         if ($showadvanced !== null) {
84             writer::export_user_preference('core_role',
85                 'definerole_showadvanced',
86                 transform::yesno($showadvanced),
87                 get_string('privacy:metadata:preference:showadvanced', 'core_role')
88             );
89         }
90     }
91     /**
92      * Return all contexts for this userid.
93      *
94      * @param  int $userid The user ID.
95      * @return contextlist The list of context IDs.
96      */
97     public static function get_contexts_for_userid(int $userid) : contextlist {
98         global $DB;
100         $contextlist = new contextlist();
102         // The role_capabilities table contains user data.
103         $contexts = [
104             CONTEXT_SYSTEM,
105             CONTEXT_USER,
106             CONTEXT_COURSECAT,
107             CONTEXT_COURSE,
108             CONTEXT_MODULE,
109             CONTEXT_BLOCK
110         ];
111         list($insql, $inparams) = $DB->get_in_or_equal($contexts, SQL_PARAMS_NAMED);
112         $sql = "SELECT ctx.id
113                   FROM {context} ctx
114                   JOIN {role_capabilities} rc
115                     ON rc.contextid = ctx.id
116                    AND ((ctx.contextlevel {$insql} AND rc.modifierid = :modifierid)
117                     OR (ctx.contextlevel = :contextlevel AND ctx.instanceid = :userid))";
118         $params = [
119             'modifierid' => $userid,
120             'contextlevel' => CONTEXT_USER,
121             'userid' => $userid
122          ];
123         $params += $inparams;
125         $contextlist->add_from_sql($sql, $params);
127         // The role_assignments table contains user data.
128         $contexts = [
129             CONTEXT_SYSTEM,
130             CONTEXT_USER,
131             CONTEXT_COURSECAT,
132             CONTEXT_COURSE,
133             CONTEXT_MODULE,
134             CONTEXT_BLOCK
135         ];
136         list($insql, $inparams) = $DB->get_in_or_equal($contexts, SQL_PARAMS_NAMED);
137         $params = [
138             'userid' => $userid,
139             'modifierid' => $userid
140          ];
141         $params += $inparams;
142         $sql = "SELECT ctx.id
143                   FROM {role_assignments} ra
144                   JOIN {context} ctx
145                     ON ctx.id = ra.contextid
146                    AND ctx.contextlevel {$insql}
147                  WHERE (ra.userid = :userid
148                     OR ra.modifierid = :modifierid)
149                    AND ra.component != 'tool_cohortroles'";
150         $contextlist->add_from_sql($sql, $params);
152         return $contextlist;
153     }
154     /**
155      * Export all user data for the specified user, in the specified contexts.
156      *
157      * @param  approved_contextlist $contextlist The list of approved contexts for a user.
158      */
159     public static function export_user_data(approved_contextlist $contextlist) {
160         global $DB;
162         if (empty($contextlist)) {
163              return;
164         }
166         $rolesnames = self::get_roles_name();
167         $userid = $contextlist->get_user()->id;
168         $ctxfields = \context_helper::get_preload_record_columns_sql('ctx');
169         list($insql, $inparams) = $DB->get_in_or_equal($contextlist->get_contextids(), SQL_PARAMS_NAMED);
171         // Role Assignments export data.
172         $contexts = [
173             CONTEXT_SYSTEM,
174             CONTEXT_USER,
175             CONTEXT_COURSECAT,
176             CONTEXT_COURSE,
177             CONTEXT_MODULE,
178             CONTEXT_BLOCK
179         ];
180         list($inctxsql, $ctxparams) = $DB->get_in_or_equal($contexts, SQL_PARAMS_NAMED);
181         $sql = "SELECT ra.id, ra.contextid, ra.roleid, ra.userid, ra.timemodified, ra.modifierid, $ctxfields
182                   FROM {role_assignments} ra
183                   JOIN {context} ctx
184                     ON ctx.id = ra.contextid
185                    AND ctx.contextlevel {$inctxsql}
186                    AND (ra.userid = :userid OR ra.modifierid = :modifierid)
187                    AND ra.component != 'tool_cohortroles'
188                   JOIN {role} r
189                     ON r.id = ra.roleid
190                  WHERE ctx.id {$insql}";
191         $params = ['userid' => $userid, 'modifierid' => $userid];
192         $params += $inparams;
193         $params += $ctxparams;
194         $assignments = $DB->get_recordset_sql($sql, $params);
195         foreach ($assignments as $assignment) {
196             \context_helper::preload_from_record($assignment);
197             $alldata[$assignment->contextid][$rolesnames[$assignment->roleid]][] = (object)[
198                 'timemodified' => transform::datetime($assignment->timemodified),
199                 'userid' => transform::user($assignment->userid),
200                 'modifierid' => transform::user($assignment->modifierid)
201             ];
202         }
203         $assignments->close();
204         if (!empty($alldata)) {
205             array_walk($alldata, function($roledata, $contextid) {
206                 $context = \context::instance_by_id($contextid);
207                 array_walk($roledata, function($data, $rolename) use ($context) {
208                     writer::with_context($context)->export_data(
209                             [get_string('privacy:metadata:role_assignments', 'core_role'), $rolename],
210                             (object)$data);
211                 });
212             });
213             unset($alldata);
214         }
216         // Role Capabilities export data.
217         $strpermissions = self::get_permissions_name();
218         $contexts = [
219             CONTEXT_SYSTEM,
220             CONTEXT_USER,
221             CONTEXT_COURSECAT,
222             CONTEXT_COURSE,
223             CONTEXT_MODULE,
224             CONTEXT_BLOCK
225         ];
226         list($inctxsql, $ctxparams) = $DB->get_in_or_equal($contexts, SQL_PARAMS_NAMED);
227         $sql = "SELECT rc.id, rc.contextid, rc.capability, rc.permission, rc.timemodified, rc.roleid, $ctxfields
228                   FROM {context} ctx
229                   JOIN {role_capabilities} rc
230                     ON rc.contextid = ctx.id
231                    AND ((ctx.contextlevel {$inctxsql} AND rc.modifierid = :modifierid)
232                     OR (ctx.contextlevel = :contextlevel AND ctx.instanceid = :userid))
233                  WHERE ctx.id {$insql}";
234         $params = [
235             'modifierid' => $userid,
236             'contextlevel' => CONTEXT_USER,
237             'userid' => $userid
238          ];
239         $params += $inparams;
240         $params += $ctxparams;
241         $capabilities = $DB->get_recordset_sql($sql, $params);
242         foreach ($capabilities as $capability) {
243             \context_helper::preload_from_record($capability);
244             $alldata[$capability->contextid][$rolesnames[$capability->roleid]][] = (object)[
245                 'timemodified' => transform::datetime($capability->timemodified),
246                 'capability' => $capability->capability,
247                 'permission' => $strpermissions[$capability->permission]
248             ];
249         }
250         $capabilities->close();
251         if (!empty($alldata)) {
252             array_walk($alldata, function($capdata, $contextid) {
253                 $context = \context::instance_by_id($contextid);
254                 array_walk($capdata, function($data, $rolename) use ($context) {
255                     writer::with_context($context)->export_data(
256                             [get_string('privacy:metadata:role_capabilities', 'core_role'), $rolename],
257                             (object)$data);
258                 });
259             });
260         }
261     }
262     /**
263      * Exports the data relating to tool_cohortroles component on role assignments by
264      * Assign user roles to cohort feature.
265      *
266      * @param  int $userid The user ID.
267      */
268     public static function export_user_role_to_cohort(int $userid) {
269         global $DB;
271         $rolesnames = self::get_roles_name();
272         $sql = "SELECT ra.id, ra.contextid, ra.roleid, ra.userid, ra.timemodified, ra.modifierid, r.id as roleid
273                   FROM {role_assignments} ra
274                   JOIN {context} ctx
275                     ON ctx.id = ra.contextid
276                    AND ctx.contextlevel = :contextlevel
277                    AND ra.component = 'tool_cohortroles'
278                   JOIN {role} r
279                     ON r.id = ra.roleid
280                  WHERE ctx.instanceid = :instanceid
281                     OR ra.userid = :userid";
282         $params = ['userid' => $userid, 'instanceid' => $userid, 'contextlevel' => CONTEXT_USER];
283         $assignments = $DB->get_recordset_sql($sql, $params);
284         foreach ($assignments as $assignment) {
285             $alldata[$assignment->contextid][$rolesnames[$assignment->roleid]][] = (object)[
286                 'timemodified' => transform::datetime($assignment->timemodified),
287                 'userid' => transform::user($assignment->userid),
288                 'modifierid' => transform::user($assignment->modifierid)
289             ];
290         }
291         $assignments->close();
292         if (!empty($alldata)) {
293             array_walk($alldata, function($roledata, $contextid) {
294                 $context = \context::instance_by_id($contextid);
295                 array_walk($roledata, function($data, $rolename) use ($context) {
296                     writer::with_context($context)->export_related_data(
297                             [get_string('privacy:metadata:role_cohortroles', 'core_role'), $rolename], 'cohortroles',
298                             (object)$data);
299                 });
300             });
301         }
302     }
303     /**
304      * Delete all user data for this context.
305      *
306      * @param  \context $context The context to delete data for.
307      */
308     public static function delete_data_for_all_users_in_context(\context $context) {
309         global $DB;
311         // Don't remove data from role_capabilities.
312         // Because this data affects the whole Moodle, there are override capabilities.
313         // Don't belong to the modifier user.
315         // Remove data from role_assignments.
316         if (empty($context)) {
317             return;
318         }
319         $DB->delete_records('role_assignments', ['contextid' => $context->id]);
320     }
321     /**
322      * Delete all user data for this user only.
323      *
324      * @param  approved_contextlist $contextlist The list of approved contexts for a user.
325      */
326     public static function delete_data_for_user(approved_contextlist $contextlist) {
327         global $DB;
329         // Don't remove data from role_capabilities.
330         // Because this data affects the whole Moodle, there are override capabilities.
331         // Don't belong to the modifier user.
333         // Remove data from role_assignments.
334         if (empty($contextlist->count())) {
335             return;
336         }
337         $userid = $contextlist->get_user()->id;
338         foreach ($contextlist->get_contexts() as $context) {
339             // Only delete the roles assignments where the user is assigned in all contexts.
340             $DB->delete_records('role_assignments', ['userid' => $userid, 'contextid' => $context->id]);
341         }
342     }
343     /**
344      * Delete user entries in role_assignments related to the feature
345      * Assign user roles to cohort feature.
346      *
347      * @param  int $userid The user ID.
348      */
349     public static function delete_user_role_to_cohort(int $userid) {
350         global $DB;
352         // Delete entries where userid is a mentor by tool_cohortroles.
353         $DB->delete_records('role_assignments', ['userid' => $userid, 'component' => 'tool_cohortroles']);
354     }
355     /**
356      * Get all the localised roles name in a simple array.
357      *
358      * @return array Array of name of the roles by roleid.
359      */
360     protected static function get_roles_name() {
361         $roles = role_fix_names(get_all_roles(), \context_system::instance(), ROLENAME_ORIGINAL);
362         $rolesnames = array();
363         foreach ($roles as $role) {
364             $rolesnames[$role->id] = $role->localname;
365         }
366         return $rolesnames;
367     }
368     /**
369      * Get all the permissions name in a simple array.
370      *
371      * @return array Array of permissions name.
372      */
373     protected static function get_permissions_name() {
374         $strpermissions = array(
375             CAP_INHERIT => get_string('inherit', 'role'),
376             CAP_ALLOW => get_string('allow', 'role'),
377             CAP_PREVENT => get_string('prevent', 'role'),
378             CAP_PROHIBIT => get_string('prohibit', 'role')
379         );
380         return $strpermissions;
381     }