See Release Notes
Long Term Support Release
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 * Privacy Subsystem implementation for core_role. 19 * 20 * @package core_role 21 * @copyright 2018 Carlos Escobedo <carlos@moodle.com> 22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 */ 24 25 namespace core_role\privacy; 26 27 defined('MOODLE_INTERNAL') || die(); 28 29 use \core_privacy\local\metadata\collection; 30 use \core_privacy\local\request\contextlist; 31 use \core_privacy\local\request\approved_contextlist; 32 use \core_privacy\local\request\transform; 33 use \core_privacy\local\request\writer; 34 use \core_privacy\local\request\userlist; 35 use \core_privacy\local\request\approved_userlist; 36 37 /** 38 * Privacy provider for core_role. 39 * 40 * @copyright 2018 Carlos Escobedo <carlos@moodle.com> 41 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 42 */ 43 class provider implements 44 \core_privacy\local\metadata\provider, 45 \core_privacy\local\request\subsystem\provider, 46 \core_privacy\local\request\subsystem\plugin_provider, 47 \core_privacy\local\request\user_preference_provider, 48 \core_privacy\local\request\core_userlist_provider { 49 50 /** 51 * Get information about the user data stored by this plugin. 52 * 53 * @param collection $collection An object for storing metadata. 54 * @return collection The metadata. 55 */ 56 public static function get_metadata(collection $collection) : collection { 57 $rolecapabilities = [ 58 'roleid' => 'privacy:metadata:role_capabilities:roleid', 59 'capability' => 'privacy:metadata:role_capabilities:capability', 60 'permission' => 'privacy:metadata:role_capabilities:permission', 61 'timemodified' => 'privacy:metadata:role_capabilities:timemodified', 62 'modifierid' => 'privacy:metadata:role_capabilities:modifierid' 63 ]; 64 $roleassignments = [ 65 'roleid' => 'privacy:metadata:role_assignments:roleid', 66 'userid' => 'privacy:metadata:role_assignments:userid', 67 'timemodified' => 'privacy:metadata:role_assignments:timemodified', 68 'modifierid' => 'privacy:metadata:role_assignments:modifierid', 69 'component' => 'privacy:metadata:role_assignments:component', 70 'itemid' => 'privacy:metadata:role_assignments:itemid' 71 ]; 72 $collection->add_database_table('role_capabilities', $rolecapabilities, 73 'privacy:metadata:role_capabilities:tableexplanation'); 74 $collection->add_database_table('role_assignments', $roleassignments, 75 'privacy:metadata:role_assignments:tableexplanation'); 76 77 $collection->add_user_preference('definerole_showadvanced', 78 'privacy:metadata:preference:showadvanced'); 79 80 return $collection; 81 } 82 /** 83 * Export all user preferences for the plugin. 84 * 85 * @param int $userid The userid of the user whose data is to be exported. 86 */ 87 public static function export_user_preferences(int $userid) { 88 $showadvanced = get_user_preferences('definerole_showadvanced', null, $userid); 89 if ($showadvanced !== null) { 90 writer::export_user_preference('core_role', 91 'definerole_showadvanced', 92 transform::yesno($showadvanced), 93 get_string('privacy:metadata:preference:showadvanced', 'core_role') 94 ); 95 } 96 } 97 /** 98 * Return all contexts for this userid. 99 * 100 * @param int $userid The user ID. 101 * @return contextlist The list of context IDs. 102 */ 103 public static function get_contexts_for_userid(int $userid) : contextlist { 104 global $DB; 105 106 $contextlist = new contextlist(); 107 108 // The role_capabilities table contains user data. 109 $contexts = [ 110 CONTEXT_SYSTEM, 111 CONTEXT_USER, 112 CONTEXT_COURSECAT, 113 CONTEXT_COURSE, 114 CONTEXT_MODULE, 115 CONTEXT_BLOCK 116 ]; 117 list($insql, $inparams) = $DB->get_in_or_equal($contexts, SQL_PARAMS_NAMED); 118 $sql = "SELECT ctx.id 119 FROM {context} ctx 120 JOIN {role_capabilities} rc 121 ON rc.contextid = ctx.id 122 AND ((ctx.contextlevel {$insql} AND rc.modifierid = :modifierid) 123 OR (ctx.contextlevel = :contextlevel AND ctx.instanceid = :userid))"; 124 $params = [ 125 'modifierid' => $userid, 126 'contextlevel' => CONTEXT_USER, 127 'userid' => $userid 128 ]; 129 $params += $inparams; 130 131 $contextlist->add_from_sql($sql, $params); 132 133 // The role_assignments table contains user data. 134 $contexts = [ 135 CONTEXT_SYSTEM, 136 CONTEXT_USER, 137 CONTEXT_COURSECAT, 138 CONTEXT_COURSE, 139 CONTEXT_MODULE, 140 CONTEXT_BLOCK 141 ]; 142 list($insql, $inparams) = $DB->get_in_or_equal($contexts, SQL_PARAMS_NAMED); 143 $params = [ 144 'userid' => $userid, 145 'modifierid' => $userid 146 ]; 147 $params += $inparams; 148 $sql = "SELECT ctx.id 149 FROM {role_assignments} ra 150 JOIN {context} ctx 151 ON ctx.id = ra.contextid 152 AND ctx.contextlevel {$insql} 153 WHERE (ra.userid = :userid 154 OR ra.modifierid = :modifierid) 155 AND ra.component != 'tool_cohortroles'"; 156 $contextlist->add_from_sql($sql, $params); 157 158 return $contextlist; 159 } 160 161 /** 162 * Get the list of users within a specific context. 163 * 164 * @param userlist $userlist The userlist containing the list of users who have data in this context/plugin combination. 165 */ 166 public static function get_users_in_context(userlist $userlist) { 167 168 if (empty($userlist)) { 169 return; 170 } 171 172 $context = $userlist->get_context(); 173 174 // Include users who created or modified role capabilities. 175 $sql = "SELECT modifierid as userid 176 FROM {role_capabilities} 177 WHERE contextid = :contextid"; 178 179 $params = [ 180 'contextid' => $context->id 181 ]; 182 183 $userlist->add_from_sql('userid', $sql, $params); 184 185 // Include users that have a role assigned to them. 186 $sql = "SELECT userid 187 FROM {role_assignments} 188 WHERE contextid = :contextid"; 189 190 $userlist->add_from_sql('userid', $sql, $params); 191 192 // Include users who created or modified the role assignment. 193 // Differentiate and exclude special cases where tool_cohortroles adds records through the 194 // "Assign user roles to cohort" feature into the role_assignments table. 195 // These records should be separately processed in tool_cohortroles. 196 $sql = "SELECT modifierid as userid 197 FROM {role_assignments} 198 WHERE contextid = :contextid 199 AND component != 'tool_cohortroles'"; 200 201 $userlist->add_from_sql('userid', $sql, $params); 202 } 203 204 /** 205 * Export all user data for the specified user, in the specified contexts. 206 * 207 * @param approved_contextlist $contextlist The list of approved contexts for a user. 208 */ 209 public static function export_user_data(approved_contextlist $contextlist) { 210 global $DB; 211 212 if (empty($contextlist)) { 213 return; 214 } 215 216 $rolesnames = self::get_roles_name(); 217 $userid = $contextlist->get_user()->id; 218 $ctxfields = \context_helper::get_preload_record_columns_sql('ctx'); 219 list($insql, $inparams) = $DB->get_in_or_equal($contextlist->get_contextids(), SQL_PARAMS_NAMED); 220 221 // Role Assignments export data. 222 $contexts = [ 223 CONTEXT_SYSTEM, 224 CONTEXT_USER, 225 CONTEXT_COURSECAT, 226 CONTEXT_COURSE, 227 CONTEXT_MODULE, 228 CONTEXT_BLOCK 229 ]; 230 list($inctxsql, $ctxparams) = $DB->get_in_or_equal($contexts, SQL_PARAMS_NAMED); 231 $sql = "SELECT ra.id, ra.contextid, ra.roleid, ra.userid, ra.timemodified, ra.modifierid, $ctxfields 232 FROM {role_assignments} ra 233 JOIN {context} ctx 234 ON ctx.id = ra.contextid 235 AND ctx.contextlevel {$inctxsql} 236 AND (ra.userid = :userid OR ra.modifierid = :modifierid) 237 AND ra.component != 'tool_cohortroles' 238 JOIN {role} r 239 ON r.id = ra.roleid 240 WHERE ctx.id {$insql}"; 241 $params = ['userid' => $userid, 'modifierid' => $userid]; 242 $params += $inparams; 243 $params += $ctxparams; 244 $assignments = $DB->get_recordset_sql($sql, $params); 245 foreach ($assignments as $assignment) { 246 \context_helper::preload_from_record($assignment); 247 $alldata[$assignment->contextid][$rolesnames[$assignment->roleid]][] = (object)[ 248 'timemodified' => transform::datetime($assignment->timemodified), 249 'userid' => transform::user($assignment->userid), 250 'modifierid' => transform::user($assignment->modifierid) 251 ]; 252 } 253 $assignments->close(); 254 if (!empty($alldata)) { 255 array_walk($alldata, function($roledata, $contextid) { 256 $context = \context::instance_by_id($contextid); 257 array_walk($roledata, function($data, $rolename) use ($context) { 258 writer::with_context($context)->export_data( 259 [get_string('privacy:metadata:role_assignments', 'core_role'), $rolename], 260 (object)$data); 261 }); 262 }); 263 unset($alldata); 264 } 265 266 // Role Capabilities export data. 267 $strpermissions = self::get_permissions_name(); 268 $contexts = [ 269 CONTEXT_SYSTEM, 270 CONTEXT_USER, 271 CONTEXT_COURSECAT, 272 CONTEXT_COURSE, 273 CONTEXT_MODULE, 274 CONTEXT_BLOCK 275 ]; 276 list($inctxsql, $ctxparams) = $DB->get_in_or_equal($contexts, SQL_PARAMS_NAMED); 277 $sql = "SELECT rc.id, rc.contextid, rc.capability, rc.permission, rc.timemodified, rc.roleid, $ctxfields 278 FROM {context} ctx 279 JOIN {role_capabilities} rc 280 ON rc.contextid = ctx.id 281 AND ((ctx.contextlevel {$inctxsql} AND rc.modifierid = :modifierid) 282 OR (ctx.contextlevel = :contextlevel AND ctx.instanceid = :userid)) 283 WHERE ctx.id {$insql}"; 284 $params = [ 285 'modifierid' => $userid, 286 'contextlevel' => CONTEXT_USER, 287 'userid' => $userid 288 ]; 289 $params += $inparams; 290 $params += $ctxparams; 291 $capabilities = $DB->get_recordset_sql($sql, $params); 292 foreach ($capabilities as $capability) { 293 \context_helper::preload_from_record($capability); 294 $alldata[$capability->contextid][$rolesnames[$capability->roleid]][] = (object)[ 295 'timemodified' => transform::datetime($capability->timemodified), 296 'capability' => $capability->capability, 297 'permission' => $strpermissions[$capability->permission] 298 ]; 299 } 300 $capabilities->close(); 301 if (!empty($alldata)) { 302 array_walk($alldata, function($capdata, $contextid) { 303 $context = \context::instance_by_id($contextid); 304 array_walk($capdata, function($data, $rolename) use ($context) { 305 writer::with_context($context)->export_data( 306 [get_string('privacy:metadata:role_capabilities', 'core_role'), $rolename], 307 (object)$data); 308 }); 309 }); 310 } 311 } 312 /** 313 * Exports the data relating to tool_cohortroles component on role assignments by 314 * Assign user roles to cohort feature. 315 * 316 * @param int $userid The user ID. 317 */ 318 public static function export_user_role_to_cohort(int $userid) { 319 global $DB; 320 321 $rolesnames = self::get_roles_name(); 322 $sql = "SELECT ra.id, ra.contextid, ra.roleid, ra.userid, ra.timemodified, ra.modifierid, r.id as roleid 323 FROM {role_assignments} ra 324 JOIN {context} ctx 325 ON ctx.id = ra.contextid 326 AND ctx.contextlevel = :contextlevel 327 AND ra.component = 'tool_cohortroles' 328 JOIN {role} r 329 ON r.id = ra.roleid 330 WHERE ctx.instanceid = :instanceid 331 OR ra.userid = :userid"; 332 $params = ['userid' => $userid, 'instanceid' => $userid, 'contextlevel' => CONTEXT_USER]; 333 $assignments = $DB->get_recordset_sql($sql, $params); 334 foreach ($assignments as $assignment) { 335 $alldata[$assignment->contextid][$rolesnames[$assignment->roleid]][] = (object)[ 336 'timemodified' => transform::datetime($assignment->timemodified), 337 'userid' => transform::user($assignment->userid), 338 'modifierid' => transform::user($assignment->modifierid) 339 ]; 340 } 341 $assignments->close(); 342 if (!empty($alldata)) { 343 array_walk($alldata, function($roledata, $contextid) { 344 $context = \context::instance_by_id($contextid); 345 array_walk($roledata, function($data, $rolename) use ($context) { 346 writer::with_context($context)->export_related_data( 347 [get_string('privacy:metadata:role_cohortroles', 'core_role'), $rolename], 'cohortroles', 348 (object)$data); 349 }); 350 }); 351 } 352 } 353 /** 354 * Delete all user data for this context. 355 * 356 * @param \context $context The context to delete data for. 357 */ 358 public static function delete_data_for_all_users_in_context(\context $context) { 359 global $DB; 360 361 // Don't remove data from role_capabilities. 362 // Because this data affects the whole Moodle, there are override capabilities. 363 // Don't belong to the modifier user. 364 365 // Remove data from role_assignments. 366 $DB->delete_records('role_assignments', ['contextid' => $context->id]); 367 } 368 369 /** 370 * Delete multiple users within a single context. 371 * 372 * @param approved_userlist $userlist The approved context and user information to delete information for. 373 */ 374 public static function delete_data_for_users(approved_userlist $userlist) { 375 global $DB; 376 377 // Don't remove data from role_capabilities. 378 // Because this data affects the whole Moodle, there are override capabilities. 379 // Don't belong to the modifier user. 380 $context = $userlist->get_context(); 381 $userids = $userlist->get_userids(); 382 383 if (empty($userids)) { 384 return; 385 } 386 list($usersql, $userparams) = $DB->get_in_or_equal($userids, SQL_PARAMS_NAMED); 387 $params = ['contextid' => $context->id] + $userparams; 388 389 // Remove data from role_assignments. 390 $DB->delete_records_select('role_assignments', 391 "contextid = :contextid AND userid {$usersql}", $params); 392 } 393 394 /** 395 * Delete all user data for this user only. 396 * 397 * @param approved_contextlist $contextlist The list of approved contexts for a user. 398 */ 399 public static function delete_data_for_user(approved_contextlist $contextlist) { 400 global $DB; 401 402 // Don't remove data from role_capabilities. 403 // Because this data affects the whole Moodle, there are override capabilities. 404 // Don't belong to the modifier user. 405 406 // Remove data from role_assignments. 407 if (empty($contextlist->count())) { 408 return; 409 } 410 $userid = $contextlist->get_user()->id; 411 $contextids = $contextlist->get_contextids(); 412 413 list($contextsql, $contextparams) = $DB->get_in_or_equal($contextids, SQL_PARAMS_NAMED); 414 $params = ['userid' => $userid] + $contextparams; 415 416 // Only delete the roles assignments where the user is assigned in all contexts. 417 $DB->delete_records_select('role_assignments', 418 "userid = :userid AND contextid {$contextsql}", $params); 419 } 420 /** 421 * Delete user entries in role_assignments related to the feature 422 * Assign user roles to cohort feature. 423 * 424 * @param int $userid The user ID. 425 */ 426 public static function delete_user_role_to_cohort(int $userid) { 427 global $DB; 428 429 // Delete entries where userid is a mentor by tool_cohortroles. 430 $DB->delete_records('role_assignments', ['userid' => $userid, 'component' => 'tool_cohortroles']); 431 } 432 /** 433 * Get all the localised roles name in a simple array. 434 * 435 * @return array Array of name of the roles by roleid. 436 */ 437 protected static function get_roles_name() { 438 $roles = role_fix_names(get_all_roles(), \context_system::instance(), ROLENAME_ORIGINAL); 439 $rolesnames = array(); 440 foreach ($roles as $role) { 441 $rolesnames[$role->id] = $role->localname; 442 } 443 return $rolesnames; 444 } 445 /** 446 * Get all the permissions name in a simple array. 447 * 448 * @return array Array of permissions name. 449 */ 450 protected static function get_permissions_name() { 451 $strpermissions = array( 452 CAP_INHERIT => get_string('inherit', 'role'), 453 CAP_ALLOW => get_string('allow', 'role'), 454 CAP_PREVENT => get_string('prevent', 'role'), 455 CAP_PROHIBIT => get_string('prohibit', 'role') 456 ); 457 return $strpermissions; 458 } 459 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body