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_payment. 19 * 20 * @package core_payment 21 * @category privacy 22 * @copyright 2020 Shamim Rezaie <shamim@moodle.com> 23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 */ 25 26 namespace core_payment\privacy; 27 28 use core_privacy\local\metadata\collection; 29 use core_privacy\local\request\approved_contextlist; 30 use core_privacy\local\request\approved_userlist; 31 use core_privacy\local\request\contextlist; 32 use core_privacy\local\request\transform; 33 use core_privacy\local\request\userlist; 34 use core_privacy\local\request\writer; 35 use core_payment\helper as payment_helper; 36 37 /** 38 * Privacy Subsystem implementation for core_payment. 39 * 40 * @copyright 2020 Shamim Rezaie <shamim@moodle.com> 41 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 42 */ 43 class provider implements 44 // This component has data. 45 // We need to return all payment information where the user is 46 // listed in the payment.userid field. 47 // We may also need to fetch this informtion from individual plugins in some cases. 48 // e.g. to fetch the full and other gateway-specific meta-data. 49 \core_privacy\local\metadata\provider, 50 51 // This is a subsysytem which provides information to core. 52 \core_privacy\local\request\subsystem\provider, 53 54 // This is a subsysytem which provides information to plugins. 55 \core_privacy\local\request\subsystem\plugin_provider, 56 57 // This plugin is capable of determining which users have data within it. 58 \core_privacy\local\request\core_userlist_provider, 59 60 // This plugin is capable of determining which users have data within it for the plugins it provides data to. 61 \core_privacy\local\request\shared_userlist_provider 62 { 63 64 /** 65 * Returns meta data about this system. 66 * 67 * @param collection $collection The initialised collection to add items to. 68 * @return collection A listing of user data stored through this system. 69 */ 70 public static function get_metadata(collection $collection): collection { 71 // The 'payments' table contains data about payments. 72 $collection->add_database_table('payments', [ 73 'userid' => 'privacy:metadata:database:payments:userid', 74 'amount' => 'privacy:metadata:database:payments:amount', 75 'currency' => 'privacy:metadata:database:payments:currency', 76 'gateway' => 'privacy:metadata:database:payments:gateway', 77 'timecreated' => 'privacy:metadata:database:payments:timecreated', 78 'timemodified' => 'privacy:metadata:database:payments:timemodified', 79 ], 'privacy:metadata:database:payments'); 80 81 return $collection; 82 } 83 84 /** 85 * Get the list of users who have data within a context. 86 * 87 * @param int $userid The user to search. 88 * @return contextlist The contextlist containing the list of contexts used in this plugin. 89 */ 90 public static function get_contexts_for_userid(int $userid): contextlist { 91 global $DB; 92 93 $contextids = []; 94 $payments = $DB->get_recordset('payments', ['userid' => $userid]); 95 foreach ($payments as $payment) { 96 $contextids[] = \core_privacy\manager::component_class_callback( 97 $payment->component, 98 consumer_provider::class, 99 'get_contextid_for_payment', 100 [$payment->paymentarea, $payment->itemid] 101 ) ?: SYSCONTEXTID; 102 } 103 $payments->close(); 104 105 $contextlist = new contextlist(); 106 107 if (!empty($contextids)) { 108 [$insql, $inparams] = $DB->get_in_or_equal(array_unique($contextids), SQL_PARAMS_NAMED); 109 $contextlist->add_from_sql("SELECT id FROM {context} WHERE id {$insql}", $inparams); 110 } 111 112 return $contextlist; 113 } 114 115 /** 116 * Get the list of users who have data within a context. 117 * 118 * @param userlist $userlist The userlist containing the list of users who have data in this context/plugin combination. 119 */ 120 public static function get_users_in_context(userlist $userlist) { 121 global $DB; 122 123 $providers = static::get_consumer_providers(); 124 125 foreach ($providers as $provider) { 126 $provider::get_users_in_context($userlist); 127 } 128 129 // Orphaned payments. 130 $context = $userlist->get_context(); 131 if ($context instanceof \context_system) { 132 [$notinsql, $notinparams] = $DB->get_in_or_equal($providers, SQL_PARAMS_NAMED, 'param', false); 133 $sql = "SELECT p.userid 134 FROM {payments} p 135 WHERE component $notinsql"; 136 137 $userlist->add_from_sql('userid', $sql, $notinparams); 138 } 139 } 140 141 /** 142 * Export all user data for the specified user, in the specified contexts. 143 * 144 * @param approved_contextlist $contextlist The approved contexts to export information for. 145 */ 146 public static function export_user_data(approved_contextlist $contextlist) { 147 global $DB; 148 149 $providers = static::get_consumer_providers(); 150 151 foreach ($providers as $provider) { 152 $provider::export_user_data($contextlist); 153 } 154 155 // Orphaned payments. 156 if (in_array(SYSCONTEXTID, $contextlist->get_contextids())) { 157 [$notinsql, $notinparams] = $DB->get_in_or_equal($providers, SQL_PARAMS_NAMED, 'param', false); 158 $params = ['userid' => $contextlist->get_user()->id] + $notinparams; 159 $orphanedpayments = $DB->get_records_sql( 160 "SELECT * 161 FROM {payments} 162 WHERE userid = :userid AND component $notinsql", 163 $params 164 ); 165 166 foreach ($orphanedpayments as $payment) { 167 static::export_payment_data_for_user_in_context( 168 \context_system::instance(), 169 [''], 170 $payment->userid, 171 $payment->component, 172 $payment->paymentarea, 173 $payment->itemid 174 ); 175 } 176 } 177 } 178 179 /** 180 * Delete all data for all users in the specified context. 181 * 182 * @param context $context The specific context to delete data for. 183 */ 184 public static function delete_data_for_all_users_in_context(\context $context) { 185 global $DB; 186 187 $providers = static::get_consumer_providers(); 188 189 foreach ($providers as $provider) { 190 $provider::delete_data_for_all_users_in_context($context); 191 } 192 193 // Orphaned payments. 194 if ($context instanceof \context_system) { 195 [$notinsql, $params] = $DB->get_in_or_equal($providers, SQL_PARAMS_NAMED, 'param', false); 196 $paymentsql = "SELECT id FROM {payments} WHERE component $notinsql"; 197 198 static::delete_data_for_payment_sql($paymentsql, $params); 199 } 200 } 201 202 /** 203 * Delete all user data for the specified user, in the specified contexts. 204 * 205 * @param approved_contextlist $contextlist The approved contexts and user information to delete information for. 206 */ 207 public static function delete_data_for_user(approved_contextlist $contextlist) { 208 global $DB; 209 210 $providers = static::get_consumer_providers(); 211 212 foreach ($providers as $provider) { 213 $provider::delete_data_for_user($contextlist); 214 } 215 216 // Orphaned payments. 217 if (in_array(SYSCONTEXTID, $contextlist->get_contextids())) { 218 [$notinsql, $notinparams] = $DB->get_in_or_equal($providers, SQL_PARAMS_NAMED, 'param', false); 219 $paymentsql = "SELECT id 220 FROM {payments} 221 WHERE userid = :userid AND component $notinsql"; 222 $paymentparams = ['userid' => $contextlist->get_user()->id] + $notinparams; 223 224 static::delete_data_for_payment_sql($paymentsql, $paymentparams); 225 } 226 } 227 228 /** 229 * Delete multiple users within a single context. 230 * 231 * @param approved_userlist $userlist The approved context and user information to delete information for. 232 */ 233 public static function delete_data_for_users(approved_userlist $userlist) { 234 global $DB; 235 236 $providers = static::get_consumer_providers(); 237 238 foreach ($providers as $provider) { 239 $provider::delete_data_for_users($userlist); 240 } 241 242 // Orphaned payments. 243 if ($userlist->get_context() instanceof \context_system) { 244 [$notinsql, $notinparams] = $DB->get_in_or_equal($providers, SQL_PARAMS_NAMED, 'param', false); 245 [$usersql, $userparams] = $DB->get_in_or_equal($userlist->get_userids(), SQL_PARAMS_NAMED); 246 247 $paymentsql = "SELECT id 248 FROM {payments} 249 WHERE component $notinsql AND userid $usersql"; 250 $paymentparams = $notinparams + $userparams; 251 252 static::delete_data_for_payment_sql($paymentsql, $paymentparams); 253 } 254 } 255 256 /** 257 * Returns the list of plugins that use the payment subsystem and implement the consumer_provider interface. 258 * 259 * @return string[] provider class names 260 */ 261 private static function get_consumer_providers(): array { 262 $providers = []; 263 foreach (array_keys(\core_component::get_plugin_types()) as $plugintype) { 264 $potentialproviders = \core_component::get_plugin_list_with_class($plugintype, 'privacy\provider'); 265 foreach ($potentialproviders as $potentialprovider) { 266 if (is_a($potentialprovider, consumer_provider::class, true)) { 267 $providers[] = $potentialprovider; 268 } 269 } 270 } 271 return $providers; 272 } 273 274 /** 275 * Export all user data for the specified user, in the specified context. 276 * 277 * @param \context $context The context that the payment belongs to 278 * @param string[] $subpath Sub-path to be used during export 279 * @param int $userid User id 280 * @param string $component Component name 281 * @param string $paymentarea Payment area 282 * @param int $itemid An internal identifier that is used by the component 283 */ 284 public static function export_payment_data_for_user_in_context(\context $context, array $subpath, int $userid, 285 string $component, string $paymentarea, int $itemid) { 286 global $DB; 287 288 $payments = $DB->get_records('payments', [ 289 'component' => $component, 290 'paymentarea' => $paymentarea, 291 'itemid' => $itemid, 292 'userid' => $userid, 293 ]); 294 295 foreach ($payments as $payment) { 296 $data = (object) [ 297 'userid' => transform::user($payment->userid), 298 'amount' => payment_helper::get_cost_as_string($payment->amount, $payment->currency), 299 'timecreated' => transform::datetime($payment->timecreated), 300 'timemodified' => transform::datetime($payment->timemodified), 301 ]; 302 $subcontext = array_merge( 303 [get_string('payments', 'payment')], 304 $subpath, 305 ['payment-' . $payment->id] 306 ); 307 writer::with_context($context)->export_data( 308 $subcontext, 309 $data 310 ); 311 \core_privacy\manager::component_class_callback( 312 'paygw_' . $payment->gateway, 313 paygw_provider::class, 314 'export_payment_data', 315 [$context, $subcontext, $payment] 316 ); 317 } 318 } 319 320 /** 321 * Delete all user data related to the given payments. 322 * 323 * @param string $paymentsql SQL query that selects payment.id field for the payments 324 * @param array $paymentparams Array of parameters for $paymentsql 325 */ 326 public static function delete_data_for_payment_sql(string $paymentsql, array $paymentparams) { 327 global $DB; 328 329 \core_privacy\manager::plugintype_class_callback( 330 'paygw', 331 paygw_provider::class, 332 'delete_data_for_payment_sql', 333 [$paymentsql, $paymentparams] 334 ); 335 336 $DB->delete_records_subquery('payments', 'id', 'id', $paymentsql, $paymentparams); 337 } 338 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body