diff --git a/admin/tool/monitor/classes/privacy/provider.php b/admin/tool/monitor/classes/privacy/provider.php index b2174ef3a51..d8ca913346e 100644 --- a/admin/tool/monitor/classes/privacy/provider.php +++ b/admin/tool/monitor/classes/privacy/provider.php @@ -27,7 +27,9 @@ 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\approved_userlist; use \core_privacy\local\request\transform; +use \core_privacy\local\request\userlist; use \core_privacy\local\request\writer; use \tool_monitor\subscription_manager; use \tool_monitor\rule_manager; @@ -39,7 +41,10 @@ use \tool_monitor\rule_manager; * @copyright 2018 Adrian Greeve * @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 { +class provider implements + \core_privacy\local\metadata\provider, + \core_privacy\local\request\core_userlist_provider, + \core_privacy\local\request\plugin\provider { /** * Get information about the user data stored by this plugin. @@ -101,6 +106,40 @@ class provider implements \core_privacy\local\metadata\provider, \core_privacy\l 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_user::class)) { + return; + } + + $params = [ + 'contextid' => $context->id, + 'contextuser' => CONTEXT_USER, + ]; + + $sql = "SELECT mr.userid + FROM {context} ctx + JOIN {tool_monitor_rules} mr ON ctx.instanceid = mr.userid + AND ctx.contextlevel = :contextuser + WHERE ctx.id = :contextid"; + + $userlist->add_from_sql('userid', $sql, $params); + + $sql = "SELECT ms.userid + FROM {context} ctx + LEFT JOIN {tool_monitor_subscriptions} ms ON ctx.instanceid = ms.userid + AND ctx.contextlevel = :contextuser + WHERE ctx.id = :contextid"; + + $userlist->add_from_sql('userid', $sql, $params); + } + /** * Export all event monitor information for the list of contexts and this user. * @@ -142,6 +181,22 @@ class provider implements \core_privacy\local\metadata\provider, \core_privacy\l static::delete_user_data($contextlist->get_user()->id); } + /** + * 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) { + $context = $userlist->get_context(); + $userids = $userlist->get_userids(); + $userid = reset($userids); + + // Only delete data for user context, which should be a single user. + if ($context->contextlevel == CONTEXT_USER && count($userids) == 1 && $userid == $context->instanceid) { + static::delete_user_data($userid); + } + } + /** * This does the deletion of user data for the event monitor. * diff --git a/admin/tool/monitor/tests/privacy_test.php b/admin/tool/monitor/tests/privacy_test.php index 5c5f11c5c99..8993b7731ed 100644 --- a/admin/tool/monitor/tests/privacy_test.php +++ b/admin/tool/monitor/tests/privacy_test.php @@ -27,6 +27,7 @@ defined('MOODLE_INTERNAL') || die(); use \tool_monitor\privacy\provider; use \core_privacy\local\request\approved_contextlist; +use \core_privacy\local\request\approved_userlist; /** * Privacy test for the event monitor @@ -128,6 +129,55 @@ class tool_monitor_privacy_testcase extends advanced_testcase { $this->assertEquals($usercontext2->id, $contextlist->get_contextids()[0]); } + /** + * Check that the correct userlist is returned if there is any user data for this context. + */ + public function test_get_users_in_context() { + $component = 'tool_monitor'; + $user = $this->getDataGenerator()->create_user(); + $user2 = $this->getDataGenerator()->create_user(); + $usercontext = \context_user::instance($user->id); + $usercontext2 = \context_user::instance($user2->id); + + $userlist = new \core_privacy\local\request\userlist($usercontext, $component); + provider::get_users_in_context($userlist); + $this->assertEmpty($userlist); + + $userlist = new \core_privacy\local\request\userlist($usercontext2, $component); + provider::get_users_in_context($userlist); + $this->assertEmpty($userlist); + + $monitorgenerator = $this->getDataGenerator()->get_plugin_generator('tool_monitor'); + + // Create a rule with user. + $this->setUser($user); + $rule = $monitorgenerator->create_rule(); + $userlist = new \core_privacy\local\request\userlist($usercontext, $component); + provider::get_users_in_context($userlist); + + // Check that we only get back user. + $userids = $userlist->get_userids(); + $this->assertCount(1, $userlist); + $this->assertEquals($user->id, $userids[0]); + + // Create a subscription with user2. + $this->setUser($user2); + + $record = new stdClass(); + $record->courseid = 0; + $record->userid = $user2->id; + $record->ruleid = $rule->id; + + $subscription = $monitorgenerator->create_subscription($record); + $userlist = new \core_privacy\local\request\userlist($usercontext2, $component); + provider::get_users_in_context($userlist); + + // Check that user2 is returned for just subscribing to a rule. + $userids = $userlist->get_userids(); + $this->assertCount(1, $userlist); + $this->assertEquals($user2->id, $userids[0]); + } + /** * Test that user data is exported correctly. */ @@ -286,4 +336,80 @@ class tool_monitor_privacy_testcase extends advanced_testcase { $this->assertEquals($user2->id, $dbsubs[$subscription2->id]->userid); $this->assertEquals($user2->id, $dbsubs[$subscription3->id]->userid); } + + /** + * Test deleting user data for an approved userlist in a context. + */ + public function test_delete_data_for_users() { + global $DB; + + $component = 'tool_monitor'; + $user = $this->getDataGenerator()->create_user(); + $user2 = $this->getDataGenerator()->create_user(); + $usercontext = \context_user::instance($user->id); + $usercontext2 = \context_user::instance($user2->id); + $monitorgenerator = $this->getDataGenerator()->get_plugin_generator('tool_monitor'); + + $this->setUser($user); + // Need to give user one the ability to manage rules. + $this->assign_user_capability('tool/monitor:managerules', \context_system::instance()); + + $rulerecord = (object)['name' => 'privacy rule']; + $rule = $monitorgenerator->create_rule($rulerecord); + + $secondrulerecord = (object)['name' => 'privacy rule2']; + $rule2 = $monitorgenerator->create_rule($secondrulerecord); + + $subscription = (object)['ruleid' => $rule->id, 'userid' => $user->id]; + $subscription = $monitorgenerator->create_subscription($subscription); + + // Have user 2 subscribe to the second rule created by user 1. + $subscription2 = (object)['ruleid' => $rule2->id, 'userid' => $user2->id]; + $subscription2 = $monitorgenerator->create_subscription($subscription2); + + $this->setUser($user2); + $thirdrulerecord = (object)['name' => 'privacy rule for second user']; + $rule3 = $monitorgenerator->create_rule($thirdrulerecord); + + $subscription3 = (object)['ruleid' => $rule3->id, 'userid' => $user2->id]; + $subscription3 = $monitorgenerator->create_subscription($subscription3); + + // Get all of the monitor rules, ensure all exist. + $dbrules = $DB->get_records('tool_monitor_rules'); + $this->assertCount(3, $dbrules); + + // Delete for user2 in first user's context, should have no effect. + $approveduserids = [$user2->id]; + $approvedlist = new approved_userlist($usercontext, $component, $approveduserids); + provider::delete_data_for_users($approvedlist); + + $dbrules = $DB->get_records('tool_monitor_rules'); + $this->assertCount(3, $dbrules); + + // Delete for user in usercontext. + $approveduserids = [$user->id]; + $approvedlist = new approved_userlist($usercontext, $component, $approveduserids); + provider::delete_data_for_users($approvedlist); + + // Only the rules for user 1 that does not have any more subscriptions should be deleted (the first rule). + $dbrules = $DB->get_records('tool_monitor_rules'); + $this->assertCount(2, $dbrules); + $this->assertEquals($user->id, $dbrules[$rule2->id]->userid); + $this->assertEquals($user2->id, $dbrules[$rule3->id]->userid); + + // There should be two subscriptions left, both for user 2. + $dbsubs = $DB->get_records('tool_monitor_subscriptions'); + $this->assertCount(2, $dbsubs); + $this->assertEquals($user2->id, $dbsubs[$subscription2->id]->userid); + $this->assertEquals($user2->id, $dbsubs[$subscription3->id]->userid); + + // Delete for user2 in context 2. + $approveduserids = [$user2->id]; + $approvedlist = new approved_userlist($usercontext2, $component, $approveduserids); + provider::delete_data_for_users($approvedlist); + + // There should be no subscriptions left. + $dbsubs = $DB->get_records('tool_monitor_subscriptions'); + $this->assertEmpty($dbsubs); + } }