diff --git a/course/classes/privacy/provider.php b/course/classes/privacy/provider.php index 3c83403ee31..436ed8a2790 100644 --- a/course/classes/privacy/provider.php +++ b/course/classes/privacy/provider.php @@ -29,8 +29,10 @@ defined('MOODLE_INTERNAL') || die(); use \core_privacy\local\metadata\collection; use \core_privacy\local\request\contextlist; use \core_privacy\local\request\approved_contextlist; -use \core_privacy\local\request\writer; +use \core_privacy\local\request\approved_userlist; use \core_privacy\local\request\transform; +use \core_privacy\local\request\userlist; +use \core_privacy\local\request\writer; /** * Privacy class for requesting user data. @@ -41,6 +43,7 @@ use \core_privacy\local\request\transform; class provider implements \core_privacy\local\metadata\provider, \core_privacy\local\request\context_aware_provider, + \core_privacy\local\request\core_userlist_provider, \core_privacy\local\request\plugin\provider, \core_privacy\local\request\user_preference_provider { @@ -75,6 +78,21 @@ 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 (!is_a($context, \context_course::class)) { + return; + } + + \core_completion\privacy\provider::add_course_completion_users_to_userlist($userlist); + } + /** * Export all user data for the specified user, in the specified contexts. * @@ -218,4 +236,19 @@ class provider implements } } } + + /** + * 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) { + // Delete course completion data. + \core_completion\privacy\provider::delete_completion_by_approved_userlist($userlist, $context->instanceid); + } + } } diff --git a/course/tests/privacy_test.php b/course/tests/privacy_test.php index 3f4eceedbe8..25f60f490c4 100644 --- a/course/tests/privacy_test.php +++ b/course/tests/privacy_test.php @@ -50,6 +50,36 @@ class core_course_privacy_testcase extends \core_privacy\tests\provider_testcase $this->assertEquals($this->coursecontext->id, $contextlist->current()->id); } + /** + * Test fetching users within a context. + */ + public function test_get_users_in_context() { + $this->resetAfterTest(); + $component = 'core_course'; + + $user1 = $this->getDataGenerator()->create_user(); + $user2 = $this->getDataGenerator()->create_user(); + $user3 = $this->getDataGenerator()->create_user(); + + // User1 and user2 complete course. + $this->create_course_completion(); + $this->complete_course($user1); + $this->complete_course($user2); + + // User3 is enrolled but has not completed course. + $this->getDataGenerator()->enrol_user($user3->id, $this->course->id, 'student'); + + // Ensure only users that have course completion are returned. + $userlist = new \core_privacy\local\request\userlist($this->coursecontext, $component); + \core_course\privacy\provider::get_users_in_context($userlist); + $expected = [$user1->id, $user2->id]; + $actual = $userlist->get_userids(); + sort($expected); + sort($actual); + $this->assertCount(2, $actual); + $this->assertEquals($expected, $actual); + } + /** * Test that user data is exported. */ @@ -175,4 +205,42 @@ class core_course_privacy_testcase extends \core_privacy\tests\provider_testcase $records = $DB->get_records('course_completion_crit_compl'); $this->assertCount(1, $records); } + + /** + * Test deleting data within a context for an approved userlist. + */ + public function test_delete_data_for_users() { + global $DB; + $this->resetAfterTest(); + + $component = 'core_course'; + $user1 = $this->getDataGenerator()->create_user(); + $user2 = $this->getDataGenerator()->create_user(); + $user3 = $this->getDataGenerator()->create_user(); + + $this->create_course_completion(); + $this->complete_course($user1); + $this->complete_course($user2); + $this->complete_course($user3); + + // Ensure records exist for all users before delete. + $records = $DB->get_records('course_modules_completion'); + $this->assertCount(3, $records); + $records = $DB->get_records('course_completion_crit_compl'); + $this->assertCount(3, $records); + + $approveduserids = [$user1->id, $user3->id]; + $approvedlist = new \core_privacy\local\request\approved_userlist($this->coursecontext, $component, $approveduserids); + \core_course\privacy\provider::delete_data_for_users($approvedlist); + + // Ensure content is only deleted for approved userlist. + $records = $DB->get_records('course_modules_completion'); + $this->assertCount(1, $records); + $record = reset($records); + $this->assertEquals($user2->id, $record->userid); + $records = $DB->get_records('course_completion_crit_compl'); + $this->assertCount(1, $records); + $record = reset($records); + $this->assertEquals($user2->id, $record->userid); + } }