From 87abd9b0aa4dbac6a2f5345c38ed9b4210f426d3 Mon Sep 17 00:00:00 2001 From: Shamim Rezaie Date: Wed, 25 Apr 2018 03:35:17 +1000 Subject: [PATCH] MDL-62117 enrol_paypal: Add implementation of Privacy API --- enrol/paypal/classes/privacy/provider.php | 284 +++++++++ enrol/paypal/lang/en/enrol_paypal.php | 28 + enrol/paypal/tests/privacy_provider_test.php | 583 +++++++++++++++++++ 3 files changed, 895 insertions(+) create mode 100644 enrol/paypal/classes/privacy/provider.php create mode 100644 enrol/paypal/tests/privacy_provider_test.php diff --git a/enrol/paypal/classes/privacy/provider.php b/enrol/paypal/classes/privacy/provider.php new file mode 100644 index 00000000000..08ff5bba28f --- /dev/null +++ b/enrol/paypal/classes/privacy/provider.php @@ -0,0 +1,284 @@ +. + +/** + * Privacy Subsystem implementation for enrol_paypal. + * + * @package enrol_paypal + * @category privacy + * @copyright 2018 Shamim Rezaie + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace enrol_paypal\privacy; + +defined('MOODLE_INTERNAL') || die(); + +use core_privacy\local\metadata\collection; +use core_privacy\local\request\approved_contextlist; +use core_privacy\local\request\contextlist; +use core_privacy\local\request\helper; +use core_privacy\local\request\writer; + +/** + * Privacy Subsystem implementation for enrol_paypal. + * + * @copyright 2018 Shamim Rezaie + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class provider implements + \core_privacy\local\metadata\provider, + \core_privacy\local\request\plugin\provider { + + /** + * Returns meta data about this system. + * + * @param collection $collection The initialised collection to add items to. + * @return collection A listing of user data stored through this system. + */ + public static function get_metadata(collection $collection) : collection { + $collection->add_external_location_link( + 'paypal.com', + [ + 'os0' => 'privacy:metadata:enrol_paypal:paypal_com:os0', + 'custom' => 'privacy:metadata:enrol_paypal:paypal_com:custom', + 'first_name' => 'privacy:metadata:enrol_paypal:paypal_com:first_name', + 'last_name' => 'privacy:metadata:enrol_paypal:paypal_com:last_name', + 'address' => 'privacy:metadata:enrol_paypal:paypal_com:address', + 'city' => 'privacy:metadata:enrol_paypal:paypal_com:city', + 'email' => 'privacy:metadata:enrol_paypal:paypal_com:email', + 'country' => 'privacy:metadata:enrol_paypal:paypal_com:country', + ], + 'privacy:metadata:enrol_paypal:paypal_com' + ); + + // The enrol_paypal has a DB table that contains user data. + $collection->add_database_table( + 'enrol_paypal', + [ + 'business' => 'privacy:metadata:enrol_paypal:enrol_paypal:business', + 'receiver_email' => 'privacy:metadata:enrol_paypal:enrol_paypal:receiver_email', + 'receiver_id' => 'privacy:metadata:enrol_paypal:enrol_paypal:receiver_id', + 'item_name' => 'privacy:metadata:enrol_paypal:enrol_paypal:item_name', + 'courseid' => 'privacy:metadata:enrol_paypal:enrol_paypal:courseid', + 'userid' => 'privacy:metadata:enrol_paypal:enrol_paypal:userid', + 'instanceid' => 'privacy:metadata:enrol_paypal:enrol_paypal:instanceid', + 'memo' => 'privacy:metadata:enrol_paypal:enrol_paypal:memo', + 'tax' => 'privacy:metadata:enrol_paypal:enrol_paypal:tax', + 'option_selection1_x' => 'privacy:metadata:enrol_paypal:enrol_paypal:option_selection1_x', + 'payment_status' => 'privacy:metadata:enrol_paypal:enrol_paypal:payment_status', + 'pending_reason' => 'privacy:metadata:enrol_paypal:enrol_paypal:pending_reason', + 'reason_code' => 'privacy:metadata:enrol_paypal:enrol_paypal:reason_code', + 'txn_id' => 'privacy:metadata:enrol_paypal:enrol_paypal:txn_id', + 'parent_txn_id' => 'privacy:metadata:enrol_paypal:enrol_paypal:parent_txn_id', + 'payment_type' => 'privacy:metadata:enrol_paypal:enrol_paypal:payment_type', + 'timeupdated' => 'privacy:metadata:enrol_paypal:enrol_paypal:timeupdated' + ], + 'privacy:metadata:enrol_paypal:enrol_paypal' + ); + + return $collection; + } + + /** + * Get the list of contexts that contain user information for the specified user. + * + * @param int $userid The user to search. + * @return contextlist The contextlist containing the list of contexts used in this plugin. + */ + public static function get_contexts_for_userid(int $userid) : contextlist { + $contextlist = new contextlist(); + + // 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 ctx.id + FROM {enrol_paypal} ep + JOIN {enrol} e ON ep.instanceid = e.id + JOIN {context} ctx ON e.courseid = ctx.instanceid AND ctx.contextlevel = :contextcourse + LEFT JOIN {user} u1 ON LOWER(u1.email) = ep.receiver_email + LEFT JOIN {user} u2 ON LOWER(u2.email) = ep.business + WHERE ep.userid = :userid + OR u1.id = :receiverid + OR u2.id = :businessid"; + $params = [ + 'contextcourse' => CONTEXT_COURSE, + 'userid' => $userid, + 'receiverid' => $userid, + 'businessid' => $userid, + ]; + + $contextlist->add_from_sql($sql, $params); + + return $contextlist; + } + + /** + * Export all user data for the specified user, in the specified contexts. + * + * @param approved_contextlist $contextlist The approved contexts to export information for. + */ + public static function export_user_data(approved_contextlist $contextlist) { + global $DB; + + if (empty($contextlist->count())) { + return; + } + + $user = $contextlist->get_user(); + + list($contextsql, $contextparams) = $DB->get_in_or_equal($contextlist->get_contextids(), SQL_PARAMS_NAMED); + + // 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 ep.* + FROM {enrol_paypal} ep + JOIN {enrol} e ON ep.instanceid = e.id + JOIN {context} ctx ON e.courseid = ctx.instanceid AND ctx.contextlevel = :contextcourse + LEFT JOIN {user} u1 ON LOWER(u1.email) = ep.receiver_email + LEFT JOIN {user} u2 ON LOWER(u2.email) = ep.business + WHERE ctx.id {$contextsql} + AND (ep.userid = :userid + OR u1.id = :receiverid + OR u2.id = :businessid) + ORDER BY e.courseid"; + + $params = [ + 'contextcourse' => CONTEXT_COURSE, + 'userid' => $user->id, + 'receiverid' => $user->id, + 'businessid' => $user->id, + ]; + $params += $contextparams; + + // Reference to the course seen in the last iteration of the loop. By comparing this with the current record, and + // because we know the results are ordered, we know when we've moved to the PayPal transactions for a new course + // and therefore when we can export the complete data for the last course. + $lastcourseid = null; + + $strtransactions = get_string('transactions', 'enrol_paypal'); + $transactions = []; + $paypalrecords = $DB->get_recordset_sql($sql, $params); + foreach ($paypalrecords as $paypalrecord) { + if ($lastcourseid != $paypalrecord->courseid) { + if (!empty($transactions)) { + $coursecontext = \context_course::instance($paypalrecord->courseid); + writer::with_context($coursecontext)->export_data( + [$strtransactions], + (object) ['transactions' => $transactions] + ); + } + $transactions = []; + } + + $transaction = (object) [ + 'receiver_id' => $paypalrecord->receiver_id, + 'item_name' => $paypalrecord->item_name, + 'userid' => $paypalrecord->userid, + 'memo' => $paypalrecord->memo, + 'tax' => $paypalrecord->tax, + 'option_name1' => $paypalrecord->option_name1, + 'option_selection1_x' => $paypalrecord->option_selection1_x, + 'option_name2' => $paypalrecord->option_name2, + 'option_selection2_x' => $paypalrecord->option_selection2_x, + 'payment_status' => $paypalrecord->payment_status, + 'pending_reason' => $paypalrecord->pending_reason, + 'reason_code' => $paypalrecord->reason_code, + 'txn_id' => $paypalrecord->txn_id, + 'parent_txn_id' => $paypalrecord->parent_txn_id, + 'payment_type' => $paypalrecord->payment_type, + 'timeupdated' => \core_privacy\local\request\transform::datetime($paypalrecord->timeupdated), + ]; + if ($paypalrecord->userid == $user->id) { + $transaction->userid = $paypalrecord->userid; + } + if ($paypalrecord->business == \core_text::strtolower($user->email)) { + $transaction->business = $paypalrecord->business; + } + if ($paypalrecord->receiver_email == \core_text::strtolower($user->email)) { + $transaction->receiver_email = $paypalrecord->receiver_email; + } + + $transactions[] = $paypalrecord; + + $lastcourseid = $paypalrecord->courseid; + } + $paypalrecords->close(); + + // The data for the last activity won't have been written yet, so make sure to write it now! + if (!empty($transactions)) { + $coursecontext = \context_course::instance($paypalrecord->courseid); + writer::with_context($coursecontext)->export_data( + [$strtransactions], + (object) ['transactions' => $transactions] + ); + } + } + + /** + * Delete all data for all users in the specified context. + * + * @param \context $context The specific context to delete data for. + */ + public static function delete_data_for_all_users_in_context(\context $context) { + global $DB; + + if (!$context instanceof \context_course) { + return; + } + + $DB->delete_records('enrol_paypal', array('courseid' => $context->instanceid)); + } + + /** + * Delete all user data for the specified user, in the specified contexts. + * + * @param approved_contextlist $contextlist The approved contexts and user information to delete information for. + */ + public static function delete_data_for_user(approved_contextlist $contextlist) { + global $DB; + + if (empty($contextlist->count())) { + return; + } + + $user = $contextlist->get_user(); + + $contexts = $contextlist->get_contexts(); + $courseids = []; + foreach ($contexts as $context) { + if ($context instanceof \context_course) { + $courseids[] = $context->instanceid; + } + } + + list($insql, $inparams) = $DB->get_in_or_equal($courseids, SQL_PARAMS_NAMED); + + $select = "userid = :userid AND courseid $insql"; + $params = $inparams + ['userid' => $user->id]; + $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 = "business = :business AND courseid $insql"; + $params = $inparams + ['business' => \core_text::strtolower($user->email)]; + $DB->set_field_select('enrol_paypal', 'business', '', $select, $params); + + $select = "receiver_email = :receiver_email AND courseid $insql"; + $params = $inparams + ['receiver_email' => \core_text::strtolower($user->email)]; + $DB->set_field_select('enrol_paypal', 'receiver_email', '', $select, $params); + } +} diff --git a/enrol/paypal/lang/en/enrol_paypal.php b/enrol/paypal/lang/en/enrol_paypal.php index 6aa32ec584e..a4697502897 100644 --- a/enrol/paypal/lang/en/enrol_paypal.php +++ b/enrol/paypal/lang/en/enrol_paypal.php @@ -56,7 +56,35 @@ $string['paypal:unenrolself'] = 'Unenrol self from the course'; $string['paypalaccepted'] = 'PayPal payments accepted'; $string['pluginname'] = 'PayPal'; $string['pluginname_desc'] = 'The PayPal module allows you to set up paid courses. If the cost for any course is zero, then students are not asked to pay for entry. There is a site-wide cost that you set here as a default for the whole site and then a course setting that you can set for each course individually. The course cost overrides the site cost.'; +$string['privacy:metadata:enrol_paypal:enrol_paypal'] = 'Information about the PayPal transactions for PayPal enrolments.'; +$string['privacy:metadata:enrol_paypal:enrol_paypal:business'] = 'Email address or PayPal account ID of the payment recipient (that is, the merchant).'; +$string['privacy:metadata:enrol_paypal:enrol_paypal:courseid'] = 'The ID of the course that is sold.'; +$string['privacy:metadata:enrol_paypal:enrol_paypal:instanceid'] = 'The ID of the enrolment instance in the course.'; +$string['privacy:metadata:enrol_paypal:enrol_paypal:item_name'] = 'The full name of the course that its enrolment has been sold.'; +$string['privacy:metadata:enrol_paypal:enrol_paypal:memo'] = 'A note that was entered by the buyer in PayPal website payments note field.'; +$string['privacy:metadata:enrol_paypal:enrol_paypal:option_selection1_x'] = 'Full name of the buyer.'; +$string['privacy:metadata:enrol_paypal:enrol_paypal:parent_txn_id'] = 'In the case of a refund, reversal, or canceled reversal, this would be the transaction ID of the original transaction.'; +$string['privacy:metadata:enrol_paypal:enrol_paypal:payment_status'] = 'The status of the payment.'; +$string['privacy:metadata:enrol_paypal:enrol_paypal:payment_type'] = 'Holds whether the payment was funded with an eCheck (echeck), or was funded with PayPal balance, credit card, or instant transfer (instant).'; +$string['privacy:metadata:enrol_paypal:enrol_paypal:pending_reason'] = 'The reason why payment status is pending (if that is).'; +$string['privacy:metadata:enrol_paypal:enrol_paypal:reason_code'] = 'The reason why payment status is Reversed, Refunded, Canceled_Reversal, or Denied (if the status is one of them).'; +$string['privacy:metadata:enrol_paypal:enrol_paypal:receiver_email'] = 'Primary email address of the payment recipient (that is, the merchant).'; +$string['privacy:metadata:enrol_paypal:enrol_paypal:receiver_id'] = 'Unique PayPal account ID of the payment recipient (i.e., the merchant).'; +$string['privacy:metadata:enrol_paypal:enrol_paypal:tax'] = 'Amount of tax charged on payment.'; +$string['privacy:metadata:enrol_paypal:enrol_paypal:timeupdated'] = 'The time of Moodle being notified by PayPal about the payment.'; +$string['privacy:metadata:enrol_paypal:enrol_paypal:txn_id'] = 'The merchant\'s original transaction identification number for the payment from the buyer, against which the case was registered.'; +$string['privacy:metadata:enrol_paypal:enrol_paypal:userid'] = 'The ID of the user who bought the course enrolment.'; +$string['privacy:metadata:enrol_paypal:paypal_com'] = 'The PayPal enrolment plugin transmits user data from Moodle to the PayPal website.'; +$string['privacy:metadata:enrol_paypal:paypal_com:address'] = 'Address of the user who is buying the course.'; +$string['privacy:metadata:enrol_paypal:paypal_com:city'] = 'City of the user who is buying the course.'; +$string['privacy:metadata:enrol_paypal:paypal_com:country'] = 'Country of the user who is buying the course.'; +$string['privacy:metadata:enrol_paypal:paypal_com:custom'] = 'A hyphen-separated string that contains ID of the user (the buyer), ID of the course, ID of the enrolment instance.'; +$string['privacy:metadata:enrol_paypal:paypal_com:email'] = 'Email address of the user who is buying the course.'; +$string['privacy:metadata:enrol_paypal:paypal_com:first_name'] = 'First name of the user who is buying the course.'; +$string['privacy:metadata:enrol_paypal:paypal_com:last_name'] = 'Last name of the user who is buying the course.'; +$string['privacy:metadata:enrol_paypal:paypal_com:os0'] = 'Full name of the buyer.'; $string['sendpaymentbutton'] = 'Send payment via PayPal'; $string['status'] = 'Allow PayPal enrolments'; $string['status_desc'] = 'Allow users to use PayPal to enrol into a course by default.'; +$string['transactions'] = 'PayPal transactions'; $string['unenrolselfconfirm'] = 'Do you really want to unenrol yourself from course "{$a}"?'; diff --git a/enrol/paypal/tests/privacy_provider_test.php b/enrol/paypal/tests/privacy_provider_test.php new file mode 100644 index 00000000000..35266839362 --- /dev/null +++ b/enrol/paypal/tests/privacy_provider_test.php @@ -0,0 +1,583 @@ +. + +/** + * Privacy provider tests. + * + * @package enrol_paypal + * @category test + * @copyright 2018 Shamim Rezaie + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +use core_privacy\local\metadata\collection; +use enrol_paypal\privacy\provider; +use core_privacy\local\request\writer; + +/** + * Class enrol_paypal_privacy_provider_testcase. + * + * @copyright 2018 Shamim Rezaie + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class enrol_paypal_privacy_provider_testcase extends \core_privacy\tests\provider_testcase { + + /** @var stdClass A user whose email address matches the business field in some of the PayPal transactions. */ + protected $businessuser1; + + /** @var stdClass A user whose email address matches the business field in some of the PayPal transactions. */ + protected $businessuser2; + + /** @var stdClass A user whose email address matches the receiver_email field in some of the PayPal transactions. */ + protected $receiveruser1; + + /** @var stdClass A user whose email address matches the receiver_email field in some of the PayPal transactions. */ + protected $receiveruser2; + + /** @var stdClass A user who is not enrolled in any course. */ + protected $student0; + + /** @var stdClass A student who is only enrolled in course1. */ + protected $student1; + + /** @var stdClass A student who is only enrolled in course2 with 2 transaction histories in the course. */ + protected $student2; + + /** @var stdClass A student who is enrolled in both course1 and course2. */ + protected $student12; + + /** @var stdClass A test course with 2 enrolments for student1 and student12. */ + protected $course1; + + /** @var stdClass A test course with 2 enrolments for student2 and student12. */ + protected $course2; + + protected function setUp() { + global $DB; + + $this->resetAfterTest(); + + $studentrole = $DB->get_record('role', array('shortname' => 'student')); + + $generator = $this->getDataGenerator(); + + // Create seller accounts. + $this->businessuser1 = $generator->create_user(['email' => 'busines1@domain.invalid']); + $this->businessuser2 = $generator->create_user(['email' => 'busines2@domain.invalid']); + $this->receiveruser1 = $generator->create_user(['email' => 'receiver1@domain.invalid']); + $this->receiveruser2 = $generator->create_user(['email' => 'receiver2@domain.invalid']); + + // Create courses. + $this->course1 = $generator->create_course(); + $this->course2 = $generator->create_course(); + + // Create enrolment instances. + $paypalplugin = enrol_get_plugin('paypal'); + + $enrolinstanceid = $paypalplugin->add_instance($this->course1, + ['roleid' => $studentrole->id, 'courseid' => $this->course1->id]); + $enrolinstance1 = $DB->get_record('enrol', array('id' => $enrolinstanceid)); + + $enrolinstanceid = $paypalplugin->add_instance($this->course2, + ['roleid' => $studentrole->id, 'courseid' => $this->course2->id]); + $enrolinstance2 = $DB->get_record('enrol', array('id' => $enrolinstanceid)); + + // Create students. + $this->student0 = $generator->create_user(); // This user will not be enrolled in any course. + $this->student1 = $generator->create_user(); + $this->student2 = $generator->create_user(); + $this->student12 = $generator->create_user(); + + // Enrol student1 in course1. + $paypalplugin->enrol_user($enrolinstance1, $this->student1->id, $studentrole->id); + $paypaldata = [ + 'business' => $this->businessuser1->email, + 'receiver_email' => $this->receiveruser1->email, + 'receiver_id' => 'SELLERSID', + 'item_name' => $this->course1->fullname, + 'courseid' => $this->course1->id, + 'userid' => $this->student1->id, + 'instanceid' => $enrolinstance1->id, + 'payment_status' => 'Completed', + 'txn_id' => 'STUDENT1-IN-COURSE1-00', + 'payment_type' => 'instant', + 'timeupdated' => time(), + ]; + $DB->insert_record('enrol_paypal', $paypaldata); + + // Enrol student2 in course2. + $paypalplugin->enrol_user($enrolinstance2, $this->student2->id, $studentrole->id); + // This user has 2 transaction histories. + // Here is the first one. + $paypaldata = [ + 'business' => $this->businessuser1->email, + 'receiver_email' => $this->receiveruser2->email, + 'receiver_id' => 'SELLERSID', + 'item_name' => $this->course2->fullname, + 'courseid' => $this->course2->id, + 'userid' => $this->student2->id, + 'instanceid' => $enrolinstance2->id, + 'payment_status' => 'Completed', + 'txn_id' => 'STUDENT2-IN-COURSE2-00', + 'payment_type' => 'instant', + 'timeupdated' => time() - 86400, // Yesterday. + ]; + $DB->insert_record('enrol_paypal', $paypaldata); + // And now, the second one. + $paypaldata = [ + 'business' => $this->businessuser1->email, + 'receiver_email' => $this->receiveruser2->email, + 'receiver_id' => 'SELLERSID', + 'item_name' => $this->course2->fullname, + 'courseid' => $this->course2->id, + 'userid' => $this->student2->id, + 'instanceid' => $enrolinstance2->id, + 'payment_status' => 'Completed', + 'txn_id' => 'STUDENT2-IN-COURSE2-01', + 'payment_type' => 'instant', + 'timeupdated' => time(), + ]; + $DB->insert_record('enrol_paypal', $paypaldata); + + // Enrol student12 in course1 and course2. + // First in course1. + $paypalplugin->enrol_user($enrolinstance1, $this->student12->id, $studentrole->id); + $paypaldata = [ + 'business' => $this->businessuser2->email, + 'receiver_email' => $this->receiveruser1->email, + 'receiver_id' => 'SELLERSID', + 'item_name' => $this->course1->fullname, + 'courseid' => $this->course1->id, + 'userid' => $this->student12->id, + 'instanceid' => $enrolinstance1->id, + 'payment_status' => 'Completed', + 'txn_id' => 'STUDENT12-IN-COURSE1-00', + 'payment_type' => 'instant', + 'timeupdated' => time(), + ]; + $DB->insert_record('enrol_paypal', $paypaldata); + // Then in course2. + $paypalplugin->enrol_user($enrolinstance2, $this->student12->id, $studentrole->id); + $paypaldata = [ + 'business' => $this->businessuser2->email, + 'receiver_email' => $this->receiveruser2->email, + 'receiver_id' => 'SELLERSID', + 'item_name' => $this->course2->fullname, + 'courseid' => $this->course2->id, + 'userid' => $this->student12->id, + 'instanceid' => $enrolinstance2->id, + 'payment_status' => 'Completed', + 'txn_id' => 'STUDENT12-IN-COURSE2-00', + 'payment_type' => 'instant', + 'timeupdated' => time(), + ]; + $DB->insert_record('enrol_paypal', $paypaldata); + } + + /** + * Test for provider::get_metadata(). + */ + public function test_get_metadata() { + $collection = new collection('enrol_paypal'); + $newcollection = provider::get_metadata($collection); + $itemcollection = $newcollection->get_collection(); + $this->assertCount(2, $itemcollection); + + $location = reset($itemcollection); + $this->assertEquals('paypal.com', $location->get_name()); + $this->assertEquals('privacy:metadata:enrol_paypal:paypal_com', $location->get_summary()); + + $privacyfields = $location->get_privacy_fields(); + $this->assertArrayHasKey('os0', $privacyfields); + $this->assertArrayHasKey('custom', $privacyfields); + $this->assertArrayHasKey('first_name', $privacyfields); + $this->assertArrayHasKey('last_name', $privacyfields); + $this->assertArrayHasKey('address', $privacyfields); + $this->assertArrayHasKey('city', $privacyfields); + $this->assertArrayHasKey('email', $privacyfields); + $this->assertArrayHasKey('country', $privacyfields); + + $table = next($itemcollection); + $this->assertEquals('enrol_paypal', $table->get_name()); + $this->assertEquals('privacy:metadata:enrol_paypal:enrol_paypal', $table->get_summary()); + + $privacyfields = $table->get_privacy_fields(); + $this->assertArrayHasKey('business', $privacyfields); + $this->assertArrayHasKey('receiver_email', $privacyfields); + $this->assertArrayHasKey('receiver_id', $privacyfields); + $this->assertArrayHasKey('item_name', $privacyfields); + $this->assertArrayHasKey('courseid', $privacyfields); + $this->assertArrayHasKey('userid', $privacyfields); + $this->assertArrayHasKey('instanceid', $privacyfields); + $this->assertArrayHasKey('memo', $privacyfields); + $this->assertArrayHasKey('tax', $privacyfields); + $this->assertArrayHasKey('option_selection1_x', $privacyfields); + $this->assertArrayHasKey('payment_status', $privacyfields); + $this->assertArrayHasKey('pending_reason', $privacyfields); + $this->assertArrayHasKey('reason_code', $privacyfields); + $this->assertArrayHasKey('txn_id', $privacyfields); + $this->assertArrayHasKey('parent_txn_id', $privacyfields); + $this->assertArrayHasKey('payment_type', $privacyfields); + $this->assertArrayHasKey('timeupdated', $privacyfields); + } + + /** + * Test for provider::get_contexts_for_userid(). + */ + public function test_get_contexts_for_userid() { + $coursecontext1 = context_course::instance($this->course1->id); + $coursecontext2 = context_course::instance($this->course2->id); + + // Student1 is only enrolled in 1 course. + $contextlist = provider::get_contexts_for_userid($this->student1->id); + $this->assertCount(1, $contextlist); + + $contextids = $contextlist->get_contextids(); + $this->assertEquals([$coursecontext1->id], $contextids); + + // Student12 is enrolled in 2 course. + $contextlist = provider::get_contexts_for_userid($this->student12->id); + $this->assertCount(2, $contextlist); + + $contextids = $contextlist->get_contextids(); + $this->assertContains($coursecontext1->id, $contextids); + $this->assertContains($coursecontext2->id, $contextids); + } + + /** + * Test for provider::export_user_data(). + */ + public function test_export_user_data() { + $coursecontext1 = context_course::instance($this->course1->id); + + $this->setUser($this->student1); + + // Export all of the data for the context. + $this->export_context_data_for_user($this->student1->id, $coursecontext1, 'enrol_paypal'); + $writer = writer::with_context($coursecontext1); + $this->assertTrue($writer->has_any_data()); + + $data = $writer->get_data([get_string('transactions', 'enrol_paypal')]); + + } + + /** + * Test for provider::export_user_data() when user is not enrolled. + */ + public function test_export_user_data_not_enrolled() { + $coursecontext1 = context_course::instance($this->course1->id); + + $this->setUser($this->student2); + + // Export all of the data for the context. + $this->export_context_data_for_user($this->student2->id, $coursecontext1, 'enrol_paypal'); + $writer = writer::with_context($coursecontext1); + $this->assertFalse($writer->has_any_data()); + } + + /** + * Test for provider::export_user_data() when user has no enrolment. + */ + public function test_export_user_data_no_enrolment() { + $coursecontext1 = context_course::instance($this->course1->id); + + $this->setUser($this->student0); + + // Export all of the data for the context. + $this->export_context_data_for_user($this->student0->id, $coursecontext1, 'enrol_paypal'); + $writer = writer::with_context($coursecontext1); + $this->assertFalse($writer->has_any_data()); + } + + public function test_export_user_data_multiple_paypal_history() { + $coursecontext2 = context_course::instance($this->course2->id); + + $this->setUser($this->student2); + // Export all of the data for the context. + $this->export_context_data_for_user($this->student2->id, $coursecontext2, 'enrol_paypal'); + $writer = writer::with_context($coursecontext2); + $this->assertTrue($writer->has_any_data()); + + $data = $writer->get_data([get_string('transactions', 'enrol_paypal')]); + $this->assertCount(2, $data->transactions); + $this->assertEquals( + ['STUDENT2-IN-COURSE2-00', 'STUDENT2-IN-COURSE2-01'], + array_column($data->transactions, 'txn_id'), + '', 0.0, 10, true + ); + } + + /** + * Test for provider::delete_data_for_all_users_in_context(). + */ + public function test_delete_data_for_all_users_in_context() { + global $DB; + + $coursecontext1 = context_course::instance($this->course1->id); + $this->setUser($this->student1); + + // Before deletion, we should have 2 PayPal transactions in course1 and 3 PayPal transactions in course2. + $this->assertEquals( + 2, + $DB->count_records('enrol_paypal', ['courseid' => $this->course1->id]) + ); + $this->assertEquals( + 3, + $DB->count_records('enrol_paypal', ['courseid' => $this->course2->id]) + ); + + // Delete data based on context. + provider::delete_data_for_all_users_in_context($coursecontext1); + + // After deletion, PayPal transactions in course1 should have been deleted. + $this->assertEquals( + 0, + $DB->count_records('enrol_paypal', ['courseid' => $this->course1->id]) + ); + $this->assertEquals( + 3, + $DB->count_records('enrol_paypal', ['courseid' => $this->course2->id]) + ); + } + + /** + * Test for provider::delete_data_for_all_users_in_context() when there is multiple transaction histories for a user. + */ + public function test_delete_data_for_all_users_in_context_multiple_transactions() { + global $DB; + + $coursecontext2 = context_course::instance($this->course2->id); + $this->setUser($this->student2); + + // Before deletion, we should have 2 PayPal transactions in course1 and 3 PayPal transactions in course2. + $this->assertEquals( + 2, + $DB->count_records('enrol_paypal', ['courseid' => $this->course1->id]) + ); + $this->assertEquals( + 3, + $DB->count_records('enrol_paypal', ['courseid' => $this->course2->id]) + ); + + // Delete data based on context. + provider::delete_data_for_all_users_in_context($coursecontext2); + + // After deletion, PayPal transactions in course2 should have been deleted. + $this->assertEquals( + 2, + $DB->count_records('enrol_paypal', ['courseid' => $this->course1->id]) + ); + $this->assertEquals( + 0, + $DB->count_records('enrol_paypal', ['courseid' => $this->course2->id]) + ); + } + + /** + * Test for provider::delete_data_for_user() when student is enrolled in multiple courses and deleting from one of them. + */ + public function test_delete_data_for_user_from_single_context() { + global $DB; + + $coursecontext1 = context_course::instance($this->course1->id); + + $this->setUser($this->student12); + + // 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. + $this->assertEquals( + 2, + $DB->count_records('enrol_paypal', ['courseid' => $this->course1->id]) + ); + $this->assertEquals( + 1, + $DB->count_records('enrol_paypal', ['courseid' => $this->course1->id, 'userid' => $this->student12->id]) + ); + $this->assertEquals( + 3, + $DB->count_records('enrol_paypal', ['courseid' => $this->course2->id]) + ); + $this->assertEquals( + 1, + $DB->count_records('enrol_paypal', ['courseid' => $this->course2->id, 'userid' => $this->student12->id]) + ); + + // Delete data for user. + $contextlist = new \core_privacy\local\request\approved_contextlist($this->student12, 'enrol_paypal', + [$coursecontext1->id]); + provider::delete_data_for_user($contextlist); + + // After deletion, PayPal transactions for student12 in course1 should have been deleted. + $this->assertEquals( + 1, + $DB->count_records('enrol_paypal', ['courseid' => $this->course1->id]) + ); + $this->assertEquals( + 0, + $DB->count_records('enrol_paypal', ['courseid' => $this->course1->id, 'userid' => $this->student12->id]) + ); + $this->assertEquals( + 3, + $DB->count_records('enrol_paypal', ['courseid' => $this->course2->id]) + ); + $this->assertEquals( + 1, + $DB->count_records('enrol_paypal', ['courseid' => $this->course2->id, 'userid' => $this->student12->id]) + ); + } + + /** + * Test for provider::delete_data_for_user() when student is enrolled in multiple courses and deleting from all of them. + */ + public function test_delete_data_for_user_from_multiple_context() { + global $DB; + + $coursecontext1 = context_course::instance($this->course1->id); + $coursecontext2 = context_course::instance($this->course2->id); + + $this->setUser($this->student12); + + // 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. + $this->assertEquals( + 2, + $DB->count_records('enrol_paypal', ['courseid' => $this->course1->id]) + ); + $this->assertEquals( + 1, + $DB->count_records('enrol_paypal', ['courseid' => $this->course1->id, 'userid' => $this->student12->id]) + ); + $this->assertEquals( + 3, + $DB->count_records('enrol_paypal', ['courseid' => $this->course2->id]) + ); + $this->assertEquals( + 1, + $DB->count_records('enrol_paypal', ['courseid' => $this->course2->id, 'userid' => $this->student12->id]) + ); + + // Delete data for user. + $contextlist = new \core_privacy\local\request\approved_contextlist($this->student12, 'enrol_paypal', + [$coursecontext1->id, $coursecontext2->id]); + provider::delete_data_for_user($contextlist); + + // After deletion, PayPal enrolment data for student12 in both course1 and course2 should have been deleted. + $this->assertEquals( + 1, + $DB->count_records('enrol_paypal', ['courseid' => $this->course1->id]) + ); + $this->assertEquals( + 0, + $DB->count_records('enrol_paypal', ['courseid' => $this->course1->id, 'userid' => $this->student12->id]) + ); + $this->assertEquals( + 2, + $DB->count_records('enrol_paypal', ['courseid' => $this->course2->id]) + ); + $this->assertEquals( + 0, + $DB->count_records('enrol_paypal', ['courseid' => $this->course2->id, 'userid' => $this->student12->id]) + ); + } + + /** + * Test for provider::delete_data_for_user() when user is not enrolled, but is the receiver of the payment. + */ + public function test_delete_data_for_user_for_business_user() { + global $DB; + + $coursecontext1 = context_course::instance($this->course1->id); + + $this->setUser($this->businessuser1); + + // Before deletion, we should have 5 PayPal enrolments. + // 3 of which paid to businessuser1 and 2 of which paid to businessuser2. + $this->assertEquals( + 3, + $DB->count_records('enrol_paypal', ['business' => $this->businessuser1->email]) + ); + $this->assertEquals( + 2, + $DB->count_records('enrol_paypal', ['business' => $this->businessuser2->email]) + ); + + // Delete data for user in $coursecontext1. + $contextlist = new \core_privacy\local\request\approved_contextlist($this->businessuser1, 'enrol_paypal', + [$coursecontext1->id]); + provider::delete_data_for_user($contextlist); + + // After deletion, PayPal enrolment data for businessuser1 in course1 should have been deleted. + $this->assertEquals( + 0, + $DB->count_records('enrol_paypal', ['courseid' => $this->course1->id, 'business' => $this->businessuser1->email]) + ); + $this->assertEquals( + 2, + $DB->count_records('enrol_paypal', ['business' => $this->businessuser1->email]) + ); + $this->assertEquals( + 1, + $DB->count_records('enrol_paypal', ['courseid' => $this->course1->id, 'business' => '']) + ); + $this->assertEquals( + 2, + $DB->count_records('enrol_paypal', ['business' => $this->businessuser2->email]) + ); + } + + /** + * Test for provider::delete_data_for_user() when user is not enrolled, but is the receiver of the payment. + */ + public function test_delete_data_for_user_for_receiver_user() { + global $DB; + + $coursecontext1 = context_course::instance($this->course1->id); + + $this->setUser($this->receiveruser1); + + // Before deletion, we should have 5 PayPal enrolments. + // 2 of which paid to receiveruser1 and 3 of which paid to receiveruser2. + $this->assertEquals( + 2, + $DB->count_records('enrol_paypal', ['receiver_email' => $this->receiveruser1->email]) + ); + $this->assertEquals( + 3, + $DB->count_records('enrol_paypal', ['receiver_email' => $this->receiveruser2->email]) + ); + + // Delete data for user. + $contextlist = new \core_privacy\local\request\approved_contextlist($this->receiveruser1, 'enrol_paypal', + [$coursecontext1->id]); + provider::delete_data_for_user($contextlist); + + // After deletion, PayPal enrolment data for receiveruser1 in course1 should have been deleted. + $this->assertEquals( + 0, + $DB->count_records('enrol_paypal', ['receiver_email' => $this->receiveruser1->email]) + ); + $this->assertEquals( + 2, + $DB->count_records('enrol_paypal', ['receiver_email' => '']) + ); + $this->assertEquals( + 3, + $DB->count_records('enrol_paypal', ['receiver_email' => $this->receiveruser2->email]) + ); + } +}