From 38839b0afbb56fc103be1573bb375c3f560aa374 Mon Sep 17 00:00:00 2001 From: Michael Hawkins Date: Wed, 3 Oct 2018 10:15:37 +0800 Subject: [PATCH] MDL-63501 mod_scorm: Add support for removal of context users This issue is a part of the MDL-62560 Epic. --- mod/scorm/classes/privacy/provider.php | 67 +++++++++++++++++++++++ mod/scorm/tests/privacy_test.php | 75 +++++++++++++++++++++++++- 2 files changed, 141 insertions(+), 1 deletion(-) diff --git a/mod/scorm/classes/privacy/provider.php b/mod/scorm/classes/privacy/provider.php index 3d6c1c74879..bbb40b578ac 100644 --- a/mod/scorm/classes/privacy/provider.php +++ b/mod/scorm/classes/privacy/provider.php @@ -28,9 +28,11 @@ 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\transform; +use core_privacy\local\request\userlist; use core_privacy\local\request\writer; /** @@ -41,6 +43,7 @@ use core_privacy\local\request\writer; */ class provider implements \core_privacy\local\metadata\provider, + \core_privacy\local\request\core_userlist_provider, \core_privacy\local\request\plugin\provider { /** @@ -103,6 +106,36 @@ 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_module::class)) { + return; + } + + $sql = "SELECT ss.userid + FROM {%s} ss + JOIN {modules} m + ON m.name = 'scorm' + JOIN {course_modules} cm + ON cm.instance = ss.scormid + AND cm.module = m.id + JOIN {context} ctx + ON ctx.instanceid = cm.id + AND ctx.contextlevel = :modlevel + WHERE ctx.id = :contextid"; + + $params = ['modlevel' => CONTEXT_MODULE, 'contextid' => $context->id]; + + $userlist->add_from_sql('userid', sprintf($sql, 'scorm_scoes_track'), $params); + $userlist->add_from_sql('userid', sprintf($sql, 'scorm_aicc_session'), $params); + } + /** * Export all user data for the specified user, in the specified contexts. * @@ -290,6 +323,40 @@ class provider implements static::delete_data('scorm_aicc_session', $sql, $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 (!is_a($context, \context_module::class)) { + return; + } + + // Prepare SQL to gather all completed IDs. + $userids = $userlist->get_userids(); + list($insql, $inparams) = $DB->get_in_or_equal($userids, SQL_PARAMS_NAMED); + + $sql = "SELECT ss.id + FROM {%s} ss + JOIN {modules} m + ON m.name = 'scorm' + JOIN {course_modules} cm + ON cm.instance = ss.scormid + AND cm.module = m.id + JOIN {context} ctx + ON ctx.instanceid = cm.id + WHERE ctx.id = :contextid + AND ss.userid $insql"; + $params = array_merge($inparams, ['contextid' => $context->id]); + + static::delete_data('scorm_scoes_track', $sql, $params); + static::delete_data('scorm_aicc_session', $sql, $params); + } + /** * Delete data from $tablename with the IDs returned by $sql query. * diff --git a/mod/scorm/tests/privacy_test.php b/mod/scorm/tests/privacy_test.php index 00811b78d1f..e7566713e0f 100644 --- a/mod/scorm/tests/privacy_test.php +++ b/mod/scorm/tests/privacy_test.php @@ -27,6 +27,7 @@ defined('MOODLE_INTERNAL') || die(); use mod_scorm\privacy\provider; use core_privacy\local\request\approved_contextlist; +use core_privacy\local\request\approved_userlist; use core_privacy\local\request\writer; use core_privacy\tests\provider_testcase; @@ -68,6 +69,28 @@ class mod_scorm_testcase extends provider_testcase { $this->assertContains($this->context->id, $contextlist->get_contextids()); } + /** + * Test getting the user IDs for the context related to this plugin. + */ + public function test_get_users_in_context() { + $this->resetAfterTest(true); + $this->setAdminUser(); + $this->scorm_setup_test_scenario_data(); + $component = 'mod_scorm'; + + $userlist = new \core_privacy\local\request\userlist($this->context, $component); + provider::get_users_in_context($userlist); + + // Students 1 and 2 have attempts in the SCORM context, student 0 does not. + $this->assertCount(2, $userlist); + + $expected = [$this->student1->id, $this->student2->id]; + $actual = $userlist->get_userids(); + sort($expected); + sort($actual); + $this->assertEquals($expected, $actual); + } + /** * Test that data is exported correctly for this plugin. */ @@ -196,9 +219,59 @@ class mod_scorm_testcase extends provider_testcase { $this->assertEquals(2, $count); } + /** + * Test for provider::delete_data_for_users(). + */ + public function test_delete_data_for_users() { + global $DB; + $component = 'mod_scorm'; + + $this->resetAfterTest(true); + $this->setAdminUser(); + $this->scorm_setup_test_scenario_data(); + + // Before deletion, we should have 8 entries in the scorm_scoes_track table. + $count = $DB->count_records('scorm_scoes_track'); + $this->assertEquals(8, $count); + // Before deletion, we should have 4 entries in the scorm_aicc_session table. + $count = $DB->count_records('scorm_aicc_session'); + $this->assertEquals(4, $count); + + // Delete only student 1's data, retain student 2's data. + $approveduserids = [$this->student1->id]; + $approvedlist = new approved_userlist($this->context, $component, $approveduserids); + provider::delete_data_for_users($approvedlist); + + // After deletion, the scorm_scoes_track entries for the first student should have been deleted. + $count = $DB->count_records('scorm_scoes_track', ['userid' => $this->student1->id]); + $this->assertEquals(0, $count); + $count = $DB->count_records('scorm_scoes_track'); + $this->assertEquals(4, $count); + + // After deletion, the scorm_aicc_session entries for the first student should have been deleted. + $count = $DB->count_records('scorm_aicc_session', ['userid' => $this->student1->id]); + $this->assertEquals(0, $count); + $count = $DB->count_records('scorm_aicc_session'); + $this->assertEquals(2, $count); + + // Confirm that the SCORM hasn't been removed. + $scormcount = $DB->get_records('scorm'); + $this->assertCount(1, (array) $scormcount); + + // Delete scoes_track for student0 (nothing has to be removed). + $approveduserids = [$this->student0->id]; + $approvedlist = new approved_userlist($this->context, $component, $approveduserids); + provider::delete_data_for_users($approvedlist); + + $count = $DB->count_records('scorm_scoes_track'); + $this->assertEquals(4, $count); + $count = $DB->count_records('scorm_aicc_session'); + $this->assertEquals(2, $count); + } + /** * Helper function to setup 3 users and 2 SCORM attempts for student1 and student2. - * $this->student0 is always created withot any attempt. + * $this->student0 is always created without any attempt. */ protected function scorm_setup_test_scenario_data() { global $DB;