Differences Between: [Versions 402 and 403]
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 namespace core_external\privacy; 18 19 use context; 20 use context_user; 21 use core_privacy\local\metadata\collection; 22 use core_privacy\local\request\approved_contextlist; 23 use core_privacy\local\request\transform; 24 use core_privacy\local\request\writer; 25 use core_privacy\local\request\userlist; 26 use core_privacy\local\request\approved_userlist; 27 28 /** 29 * Data provider class. 30 * 31 * @package core_external 32 * @copyright 2018 Frédéric Massart 33 * @author Frédéric Massart <fred@branchup.tech> 34 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 35 */ 36 class provider implements 37 \core_privacy\local\metadata\provider, 38 \core_privacy\local\request\core_userlist_provider, 39 \core_privacy\local\request\subsystem\provider { 40 41 /** 42 * Returns metadata. 43 * 44 * @param collection $collection The initialised collection to add items to. 45 * @return collection A listing of user data stored through this system. 46 */ 47 public static function get_metadata(collection $collection) : collection { 48 49 $collection->add_database_table('external_tokens', [ 50 'token' => 'privacy:metadata:tokens:token', 51 'privatetoken' => 'privacy:metadata:tokens:privatetoken', 52 'tokentype' => 'privacy:metadata:tokens:tokentype', 53 'userid' => 'privacy:metadata:tokens:userid', 54 'creatorid' => 'privacy:metadata:tokens:creatorid', 55 'iprestriction' => 'privacy:metadata:tokens:iprestriction', 56 'validuntil' => 'privacy:metadata:tokens:validuntil', 57 'timecreated' => 'privacy:metadata:tokens:timecreated', 58 'lastaccess' => 'privacy:metadata:tokens:lastaccess', 59 'name' => 'privacy:metadata:tokens:name', 60 ], 'privacy:metadata:tokens'); 61 62 $collection->add_database_table('external_services_users', [ 63 'userid' => 'privacy:metadata:serviceusers:userid', 64 'iprestriction' => 'privacy:metadata:serviceusers:iprestriction', 65 'validuntil' => 'privacy:metadata:serviceusers:validuntil', 66 'timecreated' => 'privacy:metadata:serviceusers:timecreated', 67 ], 'privacy:metadata:serviceusers'); 68 69 return $collection; 70 } 71 72 /** 73 * Get the list of contexts that contain user information for the specified user. 74 * 75 * @param int $userid The user to search. 76 * @return \core_privacy\local\request\contextlist $contextlist The contextlist containing the list of contexts 77 * used in this plugin. 78 */ 79 public static function get_contexts_for_userid(int $userid): \core_privacy\local\request\contextlist { 80 $contextlist = new \core_privacy\local\request\contextlist(); 81 82 $sql = " 83 SELECT ctx.id 84 FROM {external_tokens} t 85 JOIN {context} ctx 86 ON ctx.instanceid = t.userid 87 AND ctx.contextlevel = :userlevel 88 WHERE t.userid = :userid1 89 OR t.creatorid = :userid2"; 90 $contextlist->add_from_sql($sql, ['userlevel' => CONTEXT_USER, 'userid1' => $userid, 'userid2' => $userid]); 91 92 $sql = " 93 SELECT ctx.id 94 FROM {external_services_users} su 95 JOIN {context} ctx 96 ON ctx.instanceid = su.userid 97 AND ctx.contextlevel = :userlevel 98 WHERE su.userid = :userid"; 99 $contextlist->add_from_sql($sql, ['userlevel' => CONTEXT_USER, 'userid' => $userid]); 100 101 return $contextlist; 102 } 103 104 /** 105 * Get the list of users within a specific context. 106 * 107 * @param userlist $userlist The userlist containing the list of users who have data in this context/plugin combination. 108 */ 109 public static function get_users_in_context(userlist $userlist) { 110 global $DB; 111 112 $context = $userlist->get_context(); 113 114 if (!$context instanceof \context_user) { 115 return; 116 } 117 118 $userid = $context->instanceid; 119 120 $hasdata = false; 121 $hasdata = $hasdata || $DB->record_exists_select('external_tokens', 'userid = ? OR creatorid = ?', [$userid, $userid]); 122 $hasdata = $hasdata || $DB->record_exists('external_services_users', ['userid' => $userid]); 123 124 if ($hasdata) { 125 $userlist->add_user($userid); 126 } 127 } 128 129 /** 130 * Export all user data for the specified user, in the specified contexts. 131 * 132 * @param approved_contextlist $contextlist The approved contexts to export information for. 133 */ 134 public static function export_user_data(approved_contextlist $contextlist) { 135 global $DB; 136 137 $userid = $contextlist->get_user()->id; 138 $contexts = array_reduce($contextlist->get_contexts(), function($carry, $context) use ($userid) { 139 if ($context->contextlevel == CONTEXT_USER) { 140 if ($context->instanceid == $userid) { 141 $carry['has_mine'] = true; 142 } else { 143 $carry['others'][] = $context->instanceid; 144 } 145 } 146 return $carry; 147 }, [ 148 'has_mine' => false, 149 'others' => [] 150 ]); 151 152 $path = [get_string('services', 'core_external')]; 153 154 // Exporting my stuff. 155 if ($contexts['has_mine']) { 156 157 $data = []; 158 159 // Exporting my tokens. 160 $sql = " 161 SELECT t.*, s.name as externalservicename 162 FROM {external_tokens} t 163 JOIN {external_services} s 164 ON s.id = t.externalserviceid 165 WHERE t.userid = :userid 166 ORDER BY t.id"; 167 $recordset = $DB->get_recordset_sql($sql, ['userid' => $userid]); 168 foreach ($recordset as $record) { 169 if (!isset($data['tokens'])) { 170 $data['tokens'] = []; 171 } 172 $data['tokens'][] = static::transform_token($record); 173 } 174 $recordset->close(); 175 176 // Exporting the services I have access to. 177 $sql = " 178 SELECT su.*, s.name as externalservicename 179 FROM {external_services_users} su 180 JOIN {external_services} s 181 ON s.id = su.externalserviceid 182 WHERE su.userid = :userid 183 ORDER BY su.id"; 184 $recordset = $DB->get_recordset_sql($sql, ['userid' => $userid]); 185 foreach ($recordset as $record) { 186 if (!isset($data['services_user'])) { 187 $data['services_user'] = []; 188 } 189 $data['services_user'][] = [ 190 'external_service' => $record->externalservicename, 191 'ip_restriction' => $record->iprestriction, 192 'valid_until' => $record->validuntil ? transform::datetime($record->validuntil) : null, 193 'created_on' => transform::datetime($record->timecreated), 194 ]; 195 } 196 $recordset->close(); 197 198 if (!empty($data)) { 199 writer::with_context(context_user::instance($userid))->export_data($path, (object) $data); 200 }; 201 } 202 203 // Exporting the tokens I created. 204 if (!empty($contexts['others'])) { 205 list($insql, $inparams) = $DB->get_in_or_equal($contexts['others'], SQL_PARAMS_NAMED); 206 $sql = " 207 SELECT t.*, s.name as externalservicename 208 FROM {external_tokens} t 209 JOIN {external_services} s 210 ON s.id = t.externalserviceid 211 WHERE t.userid $insql 212 AND t.creatorid = :userid1 213 AND t.userid <> :userid2 214 ORDER BY t.userid, t.id"; 215 $params = array_merge($inparams, ['userid1' => $userid, 'userid2' => $userid]); 216 $recordset = $DB->get_recordset_sql($sql, $params); 217 static::recordset_loop_and_export($recordset, 'userid', [], function($carry, $record) { 218 $carry[] = static::transform_token($record); 219 return $carry; 220 }, function($userid, $data) use ($path) { 221 writer::with_context(context_user::instance($userid))->export_related_data($path, 'created_by_you', (object) [ 222 'tokens' => $data 223 ]); 224 }); 225 } 226 } 227 228 /** 229 * Delete all data for all users in the specified context. 230 * 231 * @param context $context The specific context to delete data for. 232 */ 233 public static function delete_data_for_all_users_in_context(context $context) { 234 if ($context->contextlevel != CONTEXT_USER) { 235 return; 236 } 237 static::delete_user_data($context->instanceid); 238 } 239 240 /** 241 * Delete multiple users within a single context. 242 * 243 * @param approved_userlist $userlist The approved context and user information to delete information for. 244 */ 245 public static function delete_data_for_users(approved_userlist $userlist) { 246 247 $context = $userlist->get_context(); 248 249 if ($context instanceof \context_user) { 250 static::delete_user_data($context->instanceid); 251 } 252 } 253 254 /** 255 * Delete all user data for the specified user, in the specified contexts. 256 * 257 * @param approved_contextlist $contextlist The approved contexts and user information to delete information for. 258 */ 259 public static function delete_data_for_user(approved_contextlist $contextlist) { 260 $userid = $contextlist->get_user()->id; 261 foreach ($contextlist as $context) { 262 if ($context->contextlevel == CONTEXT_USER && $context->instanceid == $userid) { 263 static::delete_user_data($context->instanceid); 264 break; 265 } 266 } 267 } 268 269 /** 270 * Delete user data. 271 * 272 * @param int $userid The user ID. 273 * @return void 274 */ 275 protected static function delete_user_data($userid) { 276 global $DB; 277 $DB->delete_records('external_tokens', ['userid' => $userid]); 278 $DB->delete_records('external_services_users', ['userid' => $userid]); 279 } 280 281 /** 282 * Transform a token entry. 283 * 284 * @param object $record The token record. 285 * @return array 286 */ 287 protected static function transform_token($record) { 288 $notexportedstr = get_string('privacy:request:notexportedsecurity', 'core_external'); 289 return [ 290 'external_service' => $record->externalservicename, 291 'token' => $notexportedstr, 292 'private_token' => $record->privatetoken ? $notexportedstr : null, 293 'ip_restriction' => $record->iprestriction, 294 'valid_until' => $record->validuntil ? transform::datetime($record->validuntil) : null, 295 'created_on' => transform::datetime($record->timecreated), 296 'last_access' => $record->lastaccess ? transform::datetime($record->lastaccess) : null, 297 'name' => $record->name, 298 ]; 299 } 300 301 /** 302 * Loop and export from a recordset. 303 * 304 * @param \moodle_recordset $recordset The recordset. 305 * @param string $splitkey The record key to determine when to export. 306 * @param mixed $initial The initial data to reduce from. 307 * @param callable $reducer The function to return the dataset, receives current dataset, and the current record. 308 * @param callable $export The function to export the dataset, receives the last value from $splitkey and the dataset. 309 * @return void 310 */ 311 protected static function recordset_loop_and_export(\moodle_recordset $recordset, $splitkey, $initial, 312 callable $reducer, callable $export) { 313 314 $data = $initial; 315 $lastid = null; 316 317 foreach ($recordset as $record) { 318 if ($lastid && $record->{$splitkey} != $lastid) { 319 $export($lastid, $data); 320 $data = $initial; 321 } 322 $data = $reducer($data, $record); 323 $lastid = $record->{$splitkey}; 324 } 325 $recordset->close(); 326 327 if (!empty($lastid)) { 328 $export($lastid, $data); 329 } 330 } 331 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body