diff --git a/enrol/paypal/classes/privacy/provider.php b/enrol/paypal/classes/privacy/provider.php index cc003e3f94e..f699af1e768 100644 --- a/enrol/paypal/classes/privacy/provider.php +++ b/enrol/paypal/classes/privacy/provider.php @@ -29,8 +29,9 @@ defined('MOODLE_INTERNAL') || die(); use core_privacy\local\metadata\collection; use core_privacy\local\request\approved_contextlist; +use core_privacy\local\request\approved_userlist; use core_privacy\local\request\contextlist; -use core_privacy\local\request\helper; +use core_privacy\local\request\userlist; use core_privacy\local\request\writer; /** @@ -40,8 +41,14 @@ use core_privacy\local\request\writer; * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class provider implements + // Transactions store user data. \core_privacy\local\metadata\provider, - \core_privacy\local\request\plugin\provider { + + // The paypal enrolment plugin contains user's transactions. + \core_privacy\local\request\plugin\provider, + + // This plugin is capable of determining which users have data within it. + \core_privacy\local\request\core_userlist_provider { /** * Returns meta data about this system. @@ -126,6 +133,30 @@ class provider implements return $contextlist; } + /** + * Get the list of users who have data within a context. + * + * @param userlist $userlist The userlist containing the list of users who have data in this context/plugin combination. + */ + public static function get_users_in_context(userlist $userlist) { + $context = $userlist->get_context(); + + if (!$context instanceof \context_course) { + return; + } + + // Values of ep.receiver_email and ep.business are already normalised to lowercase characters by PayPal, + // therefore there is no need to use LOWER() on them in the following query. + $sql = "SELECT u.id + FROM {enrol_paypal} ep + JOIN {enrol} e ON ep.instanceid = e.id + JOIN {user} u ON ep.userid = u.id OR LOWER(u.email) = ep.receiver_email OR LOWER(u.email) = ep.business + WHERE e.courseid = :courseid"; + $params = ['courseid' => $context->instanceid]; + + $userlist->add_from_sql('id', $sql, $params); + } + /** * Export all user data for the specified user, in the specified contexts. * @@ -283,4 +314,37 @@ class provider implements $params = $inparams + ['receiver_email' => \core_text::strtolower($user->email)]; $DB->set_field_select('enrol_paypal', 'receiver_email', '', $select, $params); } + + /** + * Delete multiple users within a single context. + * + * @param approved_userlist $userlist The approved context and user information to delete information for. + */ + public static function delete_data_for_users(approved_userlist $userlist) { + global $DB; + + $context = $userlist->get_context(); + + if ($context->contextlevel != CONTEXT_COURSE) { + return; + } + + $userids = $userlist->get_userids(); + + list($usersql, $userparams) = $DB->get_in_or_equal($userids, SQL_PARAMS_NAMED); + + $params = ['courseid' => $context->instanceid] + $userparams; + + $select = "courseid = :courseid AND userid $usersql"; + $DB->delete_records_select('enrol_paypal', $select, $params); + + // We do not want to delete the payment record when the user is just the receiver of payment. + // In that case, we just delete the receiver's info from the transaction record. + + $select = "courseid = :courseid AND business IN (SELECT LOWER(email) FROM {user} WHERE id $usersql)"; + $DB->set_field_select('enrol_paypal', 'business', '', $select, $params); + + $select = "courseid = :courseid AND receiver_email IN (SELECT LOWER(email) FROM {user} WHERE id $usersql)"; + $DB->set_field_select('enrol_paypal', 'receiver_email', '', $select, $params); + } } diff --git a/enrol/paypal/tests/privacy_provider_test.php b/enrol/paypal/tests/privacy_provider_test.php index 262d7cc0cca..f1077871309 100644 --- a/enrol/paypal/tests/privacy_provider_test.php +++ b/enrol/paypal/tests/privacy_provider_test.php @@ -663,4 +663,184 @@ class enrol_paypal_privacy_provider_testcase extends \core_privacy\tests\provide ]; $DB->insert_record('enrol_paypal', $paypaldata); } + + /** + * Test for provider::get_users_in_context(). + */ + public function test_get_users_in_context() { + $coursecontext1 = context_course::instance($this->course1->id); + $coursecontext2 = context_course::instance($this->course2->id); + $coursecontext3 = context_course::instance($this->course3->id); + + $userlist1 = new \core_privacy\local\request\userlist($coursecontext1, 'enrol_paypal'); + provider::get_users_in_context($userlist1); + $this->assertEquals( + [ + $this->businessuser1->id, + $this->businessuser2->id, + $this->receiveruser1->id, + $this->student1->id, + $this->student12->id + ], + $userlist1->get_userids(), + '', 0.0, 10, true + ); + + $userlist2 = new \core_privacy\local\request\userlist($coursecontext2, 'enrol_paypal'); + provider::get_users_in_context($userlist2); + $this->assertEquals( + [ + $this->businessuser1->id, + $this->businessuser2->id, + $this->receiveruser2->id, + $this->student2->id, + $this->student12->id + ], + $userlist2->get_userids(), + '', 0.0, 10, true + ); + + $userlist3 = new \core_privacy\local\request\userlist($coursecontext3, 'enrol_paypal'); + provider::get_users_in_context($userlist3); + $this->assertEquals( + [ + $this->businessuser3->id, + $this->receiveruser3->id, + $this->student3->id + ], + $userlist3->get_userids(), + '', 0.0, 10, true + ); + } + + /** + * Test for provider::delete_data_for_users(). + */ + public function test_delete_data_for_users() { + global $DB; + + $coursecontext1 = context_course::instance($this->course1->id); + + // Before deletion, we should have 2 PayPal transactions (1 of them for student12) in course1 + // and 3 PayPal transactions (1 of them for student12) in course2. + // Student12 is enrolled in course1 and course2. + // There is 1 transaction in course1 and 2 transactions in course2 under the name of businessuser1. + $this->assertEquals( + 2, + $DB->count_records('enrol_paypal', ['courseid' => $this->course1->id]) + ); + $this->assertEquals( + [$this->course1->id, $this->course2->id], + $DB->get_fieldset_select('enrol_paypal', 'courseid', 'userid = ?', [$this->student12->id]), + '', 0.0, 10, true + ); + $this->assertEquals( + [$this->course1->id, $this->course2->id, $this->course2->id], + $DB->get_fieldset_select('enrol_paypal', 'courseid', 'business = ?', + [\core_text::strtolower($this->businessuser1->email)]), + '', 0.0, 10, true + ); + $this->assertEquals( + 3, + $DB->count_records('enrol_paypal', ['courseid' => $this->course2->id]) + ); + + // Delete data of student12 and businessuser1 in course1. + $approveduserlist = new \core_privacy\local\request\approved_userlist($coursecontext1, 'enrol_paypal', + [$this->student12->id, $this->businessuser1->id]); + provider::delete_data_for_users($approveduserlist); + + // After deletion, PayPal transactions for student12 in course1 should have been deleted. + // Now, we should have 1 PayPal transaction (which is not related to student12) in course1. + // There should still be 3 PayPal transactions (1 of them for student12) in course2. + // Student12 is not enrolled in course1 anymore, but s/he is still enrolled in course2. + // There is no transaction in course1 under the name of businessuser1, but the 2 transactions in course2 + // that were under his/her name are intact. + $this->assertEquals( + 1, + $DB->count_records('enrol_paypal', ['courseid' => $this->course1->id]) + ); + $this->assertEquals( + [$this->course2->id], + $DB->get_fieldset_select('enrol_paypal', 'courseid', 'userid = ?', [$this->student12->id]), + '', 0.0, 10, true + ); + $this->assertEquals( + [$this->course2->id, $this->course2->id], + $DB->get_fieldset_select('enrol_paypal', 'courseid', 'business = ?', + [\core_text::strtolower($this->businessuser1->email)]), + '', 0.0, 10, true + ); + $this->assertEquals( + 3, + $DB->count_records('enrol_paypal', ['courseid' => $this->course2->id]) + ); + } + + /** + * Test for provider::delete_data_for_users() for business user deletion. + */ + public function test_delete_data_for_users_business() { + global $DB; + + $coursecontext1 = context_course::instance($this->course1->id); + + // Before deletion, there are 3 transactions under the name of businessuser1 and one of them is in course1. + $this->assertEquals(3, $DB->count_records('enrol_paypal', ['business' => $this->businessuser1->email])); + $transactions = $DB->get_records('enrol_paypal', [ + 'courseid' => $this->course1->id, + 'business' => $this->businessuser1->email + ]); + $this->assertCount(1, $transactions); + $transaction = reset($transactions); + + // Delete data of businessuser1 in course1. + $approveduserlist = new \core_privacy\local\request\approved_userlist($coursecontext1, 'enrol_paypal', + [$this->businessuser1->id]); + provider::delete_data_for_users($approveduserlist); + + // After deletion, there should be 2 transactions under the name of businessuser1 and none of them should be in course1. + $this->assertEquals(2, $DB->count_records('enrol_paypal', ['business' => $this->businessuser1->email])); + $this->assertEquals(0, $DB->count_records('enrol_paypal', [ + 'courseid' => $this->course1->id, + 'business' => $this->businessuser1->email + ])); + + // Also, the transaction in course1 that was under the name of businessuser1 should still exist, + // but it should not be under the name of businessuser1 anymore. + $newtransaction = $DB->get_record('enrol_paypal', ['id' => $transaction->id]); + $this->assertEquals('', $newtransaction->business); + } + + /** + * Test for provider::delete_data_for_users() for receiver user deletion. + */ + public function test_delete_data_for_users_receiver() { + global $DB; + + $coursecontext1 = context_course::instance($this->course1->id); + + // Before deletion, there are 2 transactions under the name of receiveruser1 and both of them are in course1. + $this->assertEquals(2, $DB->count_records('enrol_paypal', ['receiver_email' => $this->receiveruser1->email])); + $transactions = $DB->get_records('enrol_paypal', [ + 'courseid' => $this->course1->id, + 'receiver_email' => $this->receiveruser1->email + ]); + $this->assertCount(2, $transactions); + + // Delete data of receiveruser1 in course1. + $approveduserlist = new \core_privacy\local\request\approved_userlist($coursecontext1, 'enrol_paypal', + [$this->receiveruser1->id]); + provider::delete_data_for_users($approveduserlist); + + // After deletion, there should be no transaction under the name of receiveruser1. + $this->assertEquals(0, $DB->count_records('enrol_paypal', ['receiver_email' => $this->receiveruser1->email])); + + // Also, the transactions in course1 that were under the name of receiveruser1 should still exist, + // but they should not be under the name of receiveruser1 anymore. + foreach ($transactions as $transaction) { + $newtransaction = $DB->get_record('enrol_paypal', ['id' => $transaction->id]); + $this->assertEquals('', $newtransaction->receiver_email); + } + } }