MDL-63634 enrol_paypal: support removal of multiple users in a context
authorShamim Rezaie <shamim@moodle.com>
Fri, 12 Oct 2018 15:00:14 +0000 (02:00 +1100)
committerShamim Rezaie <shamim@moodle.com>
Fri, 26 Oct 2018 08:35:47 +0000 (19:35 +1100)
This issue is a part of the MDL-62560 Epic.

enrol/paypal/classes/privacy/provider.php
enrol/paypal/tests/privacy_provider_test.php

index cc003e3..f699af1 100644 (file)
@@ -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);
+    }
 }
index 262d7cc..f107787 100644 (file)
@@ -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);
+        }
+    }
 }