MDL-63666 logging: Add support for removal of context users.
authorAdrian Greeve <abgreeve@gmail.com>
Wed, 17 Oct 2018 03:51:52 +0000 (11:51 +0800)
committerMihail Geshoski <mihail@moodle.com>
Fri, 2 Nov 2018 00:57:43 +0000 (08:57 +0800)
This creates a new interface for the logging subplugins to
implement for the users to be deleted separately.

admin/tool/log/classes/local/privacy/logstore_userlist_provider.php [new file with mode: 0644]
admin/tool/log/classes/local/privacy/moodle_database_export_and_delete.php
admin/tool/log/classes/privacy/provider.php
admin/tool/log/store/database/classes/privacy/provider.php
admin/tool/log/store/database/tests/privacy_test.php
admin/tool/log/store/legacy/classes/privacy/provider.php
admin/tool/log/store/legacy/tests/privacy_test.php
admin/tool/log/store/standard/classes/privacy/provider.php
admin/tool/log/store/standard/tests/privacy_test.php

diff --git a/admin/tool/log/classes/local/privacy/logstore_userlist_provider.php b/admin/tool/log/classes/local/privacy/logstore_userlist_provider.php
new file mode 100644 (file)
index 0000000..c650678
--- /dev/null
@@ -0,0 +1,57 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Logstore userlist provider interface.
+ *
+ * @package    tool_log
+ * @copyright  2018 Adrian Greeve
+ * @author     Adrian Greeve <adriangreeve.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace tool_log\local\privacy;
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Logstore userlist provider interface.
+ *
+ * Logstore subplugins providers must implement this interface.
+ *
+ * @package    tool_log
+ * @copyright  2018 Adrian Greeve
+ * @author     Adrian Greeve <adriangreeve.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+interface logstore_userlist_provider extends \core_privacy\local\request\plugin\subplugin_provider {
+
+    /**
+     * Add user IDs that contain user information for the specified context.
+     *
+     * @param \core_privacy\local\request\userlist $userlist The userlist to add the users to.
+     * @return void
+     */
+    public static function add_userids_for_context(\core_privacy\local\request\userlist $userlist);
+
+
+    /**
+     * Delete all data for all users in the specified context.
+     *
+     * @param \core_privacy\local\request\approved_userlist $userlist The specific context and users to delete data for.
+     * @return void
+     */
+    public static function delete_data_for_all_users(\core_privacy\local\request\approved_userlist $userlist);
+}
index da973ea..24f4698 100644 (file)
@@ -120,4 +120,19 @@ trait moodle_database_export_and_delete {
         $db->delete_records_select($table, "userid = :userid AND contextid $insql", $params);
     }
 
+    /**
+     * Delete all user data for the specified users, in the specified context.
+     *
+     * @param \core_privacy\local\request\approved_userlist $contextlist The approved contexts and user information to delete information for.
+     */
+    public static function delete_data_for_all_users(\core_privacy\local\request\approved_userlist $userlist) {
+        list($db, $table) = static::get_database_and_table();
+        if (!$db || !$table) {
+            return;
+        }
+        list($insql, $inparams) = $db->get_in_or_equal($userlist->get_userids(), SQL_PARAMS_NAMED);
+        $params = array_merge($inparams, ['contextid' => $userlist->get_context()->id]);
+        $db->delete_records_select($table, "contextid = :contextid AND userid $insql", $params);
+    }
+
 }
index 252ba50..1c1c77d 100644 (file)
@@ -43,7 +43,8 @@ use tool_log\log\manager;
  */
 class provider implements
     \core_privacy\local\metadata\provider,
-    \core_privacy\local\request\subsystem\provider {
+    \core_privacy\local\request\subsystem\provider,
+    \core_privacy\local\request\core_userlist_provider {
 
     /**
      * Returns metadata.
@@ -68,6 +69,17 @@ class provider implements
         return $contextlist;
     }
 
+    /**
+     * Get the list of contexts that contain user information for the specified user.
+     *
+     * @param   \core_privacy\local\request\userlist    $userlist   The userlist containing the list of users who have data in
+     * this context/plugin combination.
+     */
+    public static function get_users_in_context(\core_privacy\local\request\userlist $userlist) {
+        $interface = \tool_log\local\privacy\logstore_userlist_provider::class;
+        static::call_subplugins_method_with_args('add_userids_for_context', [$userlist], $interface);
+    }
+
     /**
      * Export all user data for the specified user, in the specified contexts.
      *
@@ -97,16 +109,29 @@ class provider implements
         static::call_subplugins_method_with_args('delete_data_for_user', [$contextlist]);
     }
 
+    /**
+     * Delete multiple users within a single context.
+     *
+     * @param \core_privacy\local\request\approved_userlist $userlist The approved context and user information to delete
+     * information for.
+     */
+    public static function delete_data_for_users(\core_privacy\local\request\approved_userlist $userlist) {
+        $interface = \tool_log\local\privacy\logstore_userlist_provider::class;
+        static::call_subplugins_method_with_args('delete_data_for_all_users', [$userlist], $interface);
+    }
+
     /**
      * Invoke the subplugins method with arguments.
      *
      * @param string $method The method name.
      * @param array $args The arguments.
+     * @param string $interface The interface to use. By default uses the logstore_provider.
      * @return void
      */
-    protected static function call_subplugins_method_with_args($method, array $args = []) {
-        $interface = \tool_log\local\privacy\logstore_provider::class;
+    protected static function call_subplugins_method_with_args($method, array $args = [], string $interface = null) {
+        if (!isset($interface)) {
+            $interface = \tool_log\local\privacy\logstore_provider::class;
+        }
         \core_privacy\manager::plugintype_class_callback('logstore', $interface, $method, $args);
     }
-
 }
index b5e8eac..c9b31af 100644 (file)
@@ -40,7 +40,8 @@ use core_privacy\local\request\contextlist;
  */
 class provider implements
     \core_privacy\local\metadata\provider,
-    \tool_log\local\privacy\logstore_provider {
+    \tool_log\local\privacy\logstore_provider,
+    \tool_log\local\privacy\logstore_userlist_provider {
 
     use \tool_log\local\privacy\moodle_database_export_and_delete;
 
@@ -91,6 +92,38 @@ class provider implements
         $contextlist->add_from_sql($sql, []);
     }
 
+    /**
+     * Add user IDs that contain user information for the specified context.
+     *
+     * @param \core_privacy\local\request\userlist $userlist The userlist to add the users to.
+     * @return void
+     */
+    public static function add_userids_for_context(\core_privacy\local\request\userlist $userlist) {
+        list($db, $table) = static::get_database_and_table();
+        if (!$db || !$table) {
+            return;
+        }
+
+        $userids = [];
+        $records = $db->get_records($table, ['contextid' => $userlist->get_context()->id], '',
+                'id, userid, relateduserid, realuserid');
+        if (empty($records)) {
+            return;
+        }
+
+        foreach ($records as $record) {
+            $userids[] = $record->userid;
+            if (!empty($record->relateduserid)) {
+                $userids[] = $record->relateduserid;
+            }
+            if (!empty($record->realuserid)) {
+                $userids[] = $record->realuserid;
+            }
+        }
+        $userids = array_unique($userids);
+        $userlist->add_users($userids);
+    }
+
     /**
      * Get the database object.
      *
index 4a1e37e..a2c0565 100644 (file)
@@ -151,6 +151,55 @@ class logstore_database_privacy_testcase extends provider_testcase {
         $this->assert_contextlist_equals($this->get_contextlist_for_user($admin), [$sysctx, $c1ctx, $c2ctx]);
     }
 
+    /**
+     * Check that user IDs are returned for a given context.
+     */
+    public function test_add_userids_for_context() {
+        $admin = \core_user::get_user(2);
+        $u1 = $this->getDataGenerator()->create_user();
+        $u2 = $this->getDataGenerator()->create_user();
+        $u3 = $this->getDataGenerator()->create_user();
+        $u4 = $this->getDataGenerator()->create_user();
+
+        $c1 = $this->getDataGenerator()->create_course();
+
+        $sysctx = context_system::instance();
+        $c1ctx = context_course::instance($c1->id);
+
+        $this->enable_logging();
+        $manager = get_log_manager(true);
+
+        $userlist = new \core_privacy\local\request\userlist($sysctx, 'logstore_database');
+        $userids = $userlist->get_userids();
+        $this->assertEmpty($userids);
+        provider::add_userids_for_context($userlist);
+        $userids = $userlist->get_userids();
+        $this->assertEmpty($userids);
+        // User one should be added (userid).
+        $this->setUser($u1);
+        $e = \logstore_database\event\unittest_executed::create(['context' => $sysctx]);
+        $e->trigger();
+        // User two (userids) and three (relateduserid) should be added.
+        $this->setUser($u2);
+        $e = \logstore_database\event\unittest_executed::create(['context' => $sysctx, 'relateduserid' => $u3->id]);
+        $e->trigger();
+        // The admin user should be added (realuserid).
+        $this->setAdminUser();
+        \core\session\manager::loginas($u2->id, context_system::instance());
+        $e = \logstore_database\event\unittest_executed::create(['context' => $sysctx]);
+        $e->trigger();
+        // Set off an event in a different context. User 4 should not be returned below.
+        $this->setUser($u4);
+        $e = \logstore_database\event\unittest_executed::create(['context' => $c1ctx]);
+        $e->trigger();
+
+        provider::add_userids_for_context($userlist);
+        $userids = $userlist->get_userids();
+        $this->assertCount(4, $userids);
+        $expectedresult = [$admin->id, $u1->id, $u2->id, $u3->id];
+        $this->assertEmpty(array_diff($expectedresult, $userids));
+    }
+
     public function test_delete_data_for_user() {
         global $DB;
         $u1 = $this->getDataGenerator()->create_user();
@@ -233,6 +282,51 @@ class logstore_database_privacy_testcase extends provider_testcase {
         $this->assertEquals(1, $DB->count_records('logstore_standard_log', ['userid' => $u2->id]));
     }
 
+    /**
+     * Check that data is removed for the listed users in a given context.
+     */
+    public function test_delete_data_for_all_users() {
+        global $DB;
+
+        $u1 = $this->getDataGenerator()->create_user();
+        $u2 = $this->getDataGenerator()->create_user();
+        $u3 = $this->getDataGenerator()->create_user();
+        $u4 = $this->getDataGenerator()->create_user();
+
+        $course = $this->getDataGenerator()->create_course();
+        $sysctx = context_system::instance();
+        $c1ctx = context_course::instance($course->id);
+
+        $this->enable_logging();
+        $manager = get_log_manager(true);
+
+        $this->setUser($u1);
+        $e = \logstore_database\event\unittest_executed::create(['context' => $sysctx]);
+        $e->trigger();
+        $this->setUser($u2);
+        $e = \logstore_database\event\unittest_executed::create(['context' => $sysctx]);
+        $e->trigger();
+        $this->setUser($u3);
+        $e = \logstore_database\event\unittest_executed::create(['context' => $sysctx]);
+        $e->trigger();
+        $this->setUser($u4);
+        $e = \logstore_database\event\unittest_executed::create(['context' => $c1ctx]);
+        $e->trigger();
+
+        // Check that four records were created.
+        $this->assertEquals(4, $DB->count_records('logstore_standard_log'));
+
+        $userlist = new \core_privacy\local\request\approved_userlist($sysctx, 'logstore_database', [$u1->id, $u3->id]);
+        provider::delete_data_for_all_users($userlist);
+        // We should have a record for u2 and u4.
+        $this->assertEquals(2, $DB->count_records('logstore_standard_log'));
+
+        $records = $DB->get_records('logstore_standard_log', ['contextid' => $sysctx->id]);
+        $this->assertCount(1, $records);
+        $currentrecord = array_shift($records);
+        $this->assertEquals($u2->id, $currentrecord->userid);
+    }
+
     public function test_export_data_for_user() {
         $admin = \core_user::get_user(2);
         $u1 = $this->getDataGenerator()->create_user();
index 298d821..ebaacb3 100644 (file)
@@ -44,7 +44,8 @@ use tool_log\local\privacy\helper;
  */
 class provider implements
     \core_privacy\local\metadata\provider,
-    \tool_log\local\privacy\logstore_provider {
+    \tool_log\local\privacy\logstore_provider,
+    \tool_log\local\privacy\logstore_userlist_provider {
 
     /**
      * Returns metadata.
@@ -89,6 +90,22 @@ class provider implements
         $contextlist->add_from_sql($sql, $params);
     }
 
+    /**
+     * Add user IDs that contain user information for the specified context.
+     *
+     * @param \core_privacy\local\request\userlist $userlist The userlist to add the users to.
+     * @return void
+     */
+    public static function add_userids_for_context(\core_privacy\local\request\userlist $userlist) {
+        $context = $userlist->get_context();
+        list($insql, $params) = static::get_sql_where_from_contexts([$context]);
+
+        $sql = "SELECT l.userid
+                  FROM {log} l
+                 WHERE $insql";
+        $userlist->add_from_sql('userid', $sql, $params);
+    }
+
     /**
      * Export all user data for the specified user, in the specified contexts.
      *
@@ -168,6 +185,24 @@ class provider implements
         $DB->delete_records_select('log', "$sql AND userid = :userid", array_merge($params, ['userid' => $userid]));
     }
 
+
+    /**
+     * Delete all data for all users in the specified context.
+     *
+     * @param \core_privacy\local\request\approved_userlist $userlist The specific context and users to delete data for.
+     * @return void
+     */
+    public static function delete_data_for_all_users(\core_privacy\local\request\approved_userlist $userlist) {
+        global $DB;
+        list($sql, $params) = static::get_sql_where_from_contexts([$userlist->get_context()]);
+        if (empty($sql)) {
+            return;
+        }
+        list($usersql, $userparams) = $DB->get_in_or_equal($userlist->get_userids(), SQL_PARAMS_NAMED);
+        $params = array_merge($params, $userparams);
+        $DB->delete_records_select('log', "$sql AND userid $usersql", $params);
+    }
+
     /**
      * Get an SQL where statement from a list of contexts.
      *
index a60960a..9c7dfc7 100644 (file)
@@ -87,6 +87,71 @@ class logstore_legacy_privacy_testcase extends provider_testcase {
         $this->assert_contextlist_equals($this->get_contextlist_for_user($u3), [$sysctx]);
     }
 
+    /**
+     * Test returning user IDs for a given context.
+     */
+    public function test_add_userids_for_context() {
+        $u1 = $this->getDataGenerator()->create_user();
+        $u2 = $this->getDataGenerator()->create_user();
+        $u3 = $this->getDataGenerator()->create_user();
+        $course = $this->getDataGenerator()->create_course();
+        $module = $this->getDataGenerator()->create_module('url', ['course' => $course]);
+        $sysctx = context_system::instance();
+        $c1ctx = context_course::instance($course->id);
+        $cm1ctx = context_module::instance($module->cmid);
+
+        $userctx = context_user::instance($u1->id);
+
+        $this->enable_logging();
+        $manager = get_log_manager(true);
+
+        $this->setUser($u1);
+        $e = unittest_executed::create(['context' => $sysctx, 'other' => ['sample' => 1]]);
+        $e->trigger();
+        $this->setUser($u2);
+        $e = unittest_executed::create(['context' => $sysctx, 'other' => ['sample' => 2]]);
+        $e->trigger();
+        $e = unittest_executed::create(['context' => $sysctx, 'other' => ['sample' => 3]]);
+        $e->trigger();
+        $this->setUser($u3);
+        $e = unittest_executed::create(['context' => $c1ctx, 'other' => ['sample' => 4]]);
+        $e->trigger();
+        $this->setUser($u1);
+        $e = unittest_executed::create(['context' => $c1ctx, 'other' => ['sample' => 5]]);
+        $e->trigger();
+        $e = unittest_executed::create(['context' => $cm1ctx, 'other' => ['sample' => 6]]);
+        $e->trigger();
+        $this->setUser($u2);
+        $e = unittest_executed::create(['context' => $cm1ctx, 'other' => ['sample' => 7]]);
+        $e->trigger();
+        $this->setUser($u3);
+        $e = unittest_executed::create(['context' => $cm1ctx, 'other' => ['sample' => 8]]);
+        $e->trigger();
+
+        // Start with system and check that each of the contexts returns what we expected.
+        $userlist = new \core_privacy\local\request\userlist($sysctx, 'logstore_legacy');
+        provider::add_userids_for_context($userlist);
+        $systemuserids = $userlist->get_userids();
+        $this->assertCount(2, $systemuserids);
+        $this->assertNotFalse(array_search($u1->id, $systemuserids));
+        $this->assertNotFalse(array_search($u2->id, $systemuserids));
+        // Check the course context.
+        $userlist = new \core_privacy\local\request\userlist($c1ctx, 'logstore_legacy');
+        provider::add_userids_for_context($userlist);
+        $courseuserids = $userlist->get_userids();
+        $this->assertCount(2, $courseuserids);
+        $this->assertNotFalse(array_search($u1->id, $courseuserids));
+        $this->assertNotFalse(array_search($u3->id, $courseuserids));
+        // Check the module context.
+        $userlist = new \core_privacy\local\request\userlist($cm1ctx, 'logstore_legacy');
+        provider::add_userids_for_context($userlist);
+        $moduleuserids = $userlist->get_userids();
+        $this->assertCount(3, $moduleuserids);
+        $this->assertNotFalse(array_search($u1->id, $moduleuserids));
+        $this->assertNotFalse(array_search($u2->id, $moduleuserids));
+        $this->assertNotFalse(array_search($u3->id, $moduleuserids));
+    }
+
     public function test_delete_data_for_user() {
         global $DB;
 
@@ -241,6 +306,68 @@ class logstore_legacy_privacy_testcase extends provider_testcase {
         $this->assertEquals(0, $DB->count_records('log', ['userid' => $u2->id]));
     }
 
+    /**
+     * Test the deletion of data for users in a context.
+     */
+    public function test_delete_data_for_all_users() {
+        global $DB;
+
+        $u1 = $this->getDataGenerator()->create_user();
+        $u2 = $this->getDataGenerator()->create_user();
+        $u3 = $this->getDataGenerator()->create_user();
+        $course = $this->getDataGenerator()->create_course();
+        $module = $this->getDataGenerator()->create_module('url', ['course' => $course]);
+        $sysctx = context_system::instance();
+        $c1ctx = context_course::instance($course->id);
+        $cm1ctx = context_module::instance($module->cmid);
+
+        $userctx = context_user::instance($u1->id);
+
+        $this->enable_logging();
+        $manager = get_log_manager(true);
+
+        $this->setUser($u1);
+        $e = unittest_executed::create(['context' => $sysctx, 'other' => ['sample' => 1]]);
+        $e->trigger();
+        $this->setUser($u2);
+        $e = unittest_executed::create(['context' => $sysctx, 'other' => ['sample' => 2]]);
+        $e->trigger();
+        $e = unittest_executed::create(['context' => $sysctx, 'other' => ['sample' => 3]]);
+        $e->trigger();
+        $this->setUser($u3);
+        $e = unittest_executed::create(['context' => $c1ctx, 'other' => ['sample' => 4]]);
+        $e->trigger();
+        $this->setUser($u1);
+        $e = unittest_executed::create(['context' => $c1ctx, 'other' => ['sample' => 5]]);
+        $e->trigger();
+        $e = unittest_executed::create(['context' => $cm1ctx, 'other' => ['sample' => 6]]);
+        $e->trigger();
+        $this->setUser($u2);
+        $e = unittest_executed::create(['context' => $cm1ctx, 'other' => ['sample' => 7]]);
+        $e->trigger();
+        $this->setUser($u3);
+        $e = unittest_executed::create(['context' => $cm1ctx, 'other' => ['sample' => 8]]);
+        $e->trigger();
+
+        // System context deleting one user.
+        $this->assertEquals(3, $DB->count_records('log', ['cmid' => 0, 'course' => 0]));
+        $userlist = new \core_privacy\local\request\approved_userlist($sysctx, 'logstore_legacy', [$u2->id]);
+        provider::delete_data_for_all_users($userlist);
+        $this->assertEquals(1, $DB->count_records('log', ['cmid' => 0, 'course' => 0]));
+
+        // Course context deleting one user.
+        $this->assertEquals(2, $DB->count_records('log', ['cmid' => 0, 'course' => $course->id]));
+        $userlist = new \core_privacy\local\request\approved_userlist($c1ctx, 'logstore_legacy', [$u1->id]);
+        provider::delete_data_for_all_users($userlist);
+        $this->assertEquals(1, $DB->count_records('log', ['cmid' => 0, 'course' => $course->id]));
+
+        // Module context deleting two users.
+        $this->assertEquals(3, $DB->count_records('log', ['cmid' => $module->cmid, 'course' => $course->id]));
+        $userlist = new \core_privacy\local\request\approved_userlist($cm1ctx, 'logstore_legacy', [$u1->id, $u3->id]);
+        provider::delete_data_for_all_users($userlist);
+        $this->assertEquals(1, $DB->count_records('log', ['cmid' => $module->cmid, 'course' => $course->id]));
+    }
+
     public function test_export_data_for_user() {
         global $DB;
 
index 4ee8007..76a4e1f 100644 (file)
@@ -40,7 +40,8 @@ use core_privacy\local\request\contextlist;
  */
 class provider implements
     \core_privacy\local\metadata\provider,
-    \tool_log\local\privacy\logstore_provider {
+    \tool_log\local\privacy\logstore_provider,
+    \tool_log\local\privacy\logstore_userlist_provider {
 
     use \tool_log\local\privacy\moodle_database_export_and_delete;
 
@@ -86,6 +87,22 @@ class provider implements
         ]);
     }
 
+    /**
+     * Add user IDs that contain user information for the specified context.
+     *
+     * @param \core_privacy\local\request\userlist $userlist The userlist to add the users to.
+     * @return void
+     */
+    public static function add_userids_for_context(\core_privacy\local\request\userlist $userlist) {
+        $params = ['contextid' => $userlist->get_context()->id];
+        $sql = "SELECT userid, relateduserid, realuserid
+                  FROM {logstore_standard_log}
+                 WHERE contextid = :contextid";
+        $userlist->add_from_sql('userid', $sql, $params);
+        $userlist->add_from_sql('relateduserid', $sql, $params);
+        $userlist->add_from_sql('realuserid', $sql, $params);
+    }
+
     /**
      * Get the database object.
      *
index 5103b3a..d54f5f5 100644 (file)
@@ -109,6 +109,52 @@ class logstore_standard_privacy_testcase extends provider_testcase {
         $this->assert_contextlist_equals($this->get_contextlist_for_user($admin), [$sysctx, $c1ctx, $c2ctx]);
     }
 
+    public function test_add_userids_for_context() {
+        $admin = \core_user::get_user(2);
+        $u1 = $this->getDataGenerator()->create_user();
+        $u2 = $this->getDataGenerator()->create_user();
+        $u3 = $this->getDataGenerator()->create_user();
+        $u4 = $this->getDataGenerator()->create_user();
+
+        $c1 = $this->getDataGenerator()->create_course();
+
+        $sysctx = context_system::instance();
+        $c1ctx = context_course::instance($c1->id);
+
+        $this->enable_logging();
+        $manager = get_log_manager(true);
+
+        $userlist = new \core_privacy\local\request\userlist($sysctx, 'logstore_standard_log');
+        $userids = $userlist->get_userids();
+        $this->assertEmpty($userids);
+        provider::add_userids_for_context($userlist);
+        $userids = $userlist->get_userids();
+        $this->assertEmpty($userids);
+        // User one should be added (userid).
+        $this->setUser($u1);
+        $e = \logstore_standard\event\unittest_executed::create(['context' => $sysctx]);
+        $e->trigger();
+        // User two (userids) and three (relateduserid) should be added.
+        $this->setUser($u2);
+        $e = \logstore_standard\event\unittest_executed::create(['context' => $sysctx, 'relateduserid' => $u3->id]);
+        $e->trigger();
+        // The admin user should be added (realuserid).
+        $this->setAdminUser();
+        \core\session\manager::loginas($u2->id, context_system::instance());
+        $e = \logstore_standard\event\unittest_executed::create(['context' => $sysctx]);
+        $e->trigger();
+        // Set off an event in a different context. User 4 should not be returned below.
+        $this->setUser($u4);
+        $e = \logstore_standard\event\unittest_executed::create(['context' => $c1ctx]);
+        $e->trigger();
+
+        provider::add_userids_for_context($userlist);
+        $userids = $userlist->get_userids();
+        $this->assertCount(4, $userids);
+        $expectedresult = [$admin->id, $u1->id, $u2->id, $u3->id];
+        $this->assertEmpty(array_diff($expectedresult, $userids));
+    }
+
     public function test_delete_data_for_user() {
         global $DB;
         $u1 = $this->getDataGenerator()->create_user();
@@ -191,6 +237,48 @@ class logstore_standard_privacy_testcase extends provider_testcase {
         $this->assertEquals(1, $DB->count_records('logstore_standard_log', ['userid' => $u2->id]));
     }
 
+    public function test_delete_data_for_all_users() {
+        global $DB;
+
+        $u1 = $this->getDataGenerator()->create_user();
+        $u2 = $this->getDataGenerator()->create_user();
+        $u3 = $this->getDataGenerator()->create_user();
+        $u4 = $this->getDataGenerator()->create_user();
+
+        $course = $this->getDataGenerator()->create_course();
+        $sysctx = context_system::instance();
+        $c1ctx = context_course::instance($course->id);
+
+        $this->enable_logging();
+        $manager = get_log_manager(true);
+
+        $this->setUser($u1);
+        $e = \logstore_standard\event\unittest_executed::create(['context' => $sysctx]);
+        $e->trigger();
+        $this->setUser($u2);
+        $e = \logstore_standard\event\unittest_executed::create(['context' => $sysctx]);
+        $e->trigger();
+        $this->setUser($u3);
+        $e = \logstore_standard\event\unittest_executed::create(['context' => $sysctx]);
+        $e->trigger();
+        $this->setUser($u4);
+        $e = \logstore_standard\event\unittest_executed::create(['context' => $c1ctx]);
+        $e->trigger();
+
+        // Check that four records were created.
+        $this->assertEquals(4, $DB->count_records('logstore_standard_log'));
+
+        $userlist = new \core_privacy\local\request\approved_userlist($sysctx, 'logstore_standard_log', [$u1->id, $u3->id]);
+        provider::delete_data_for_all_users($userlist);
+        // We should have a record for u2 and u4.
+        $this->assertEquals(2, $DB->count_records('logstore_standard_log'));
+
+        $records = $DB->get_records('logstore_standard_log', ['contextid' => $sysctx->id]);
+        $this->assertCount(1, $records);
+        $currentrecord = array_shift($records);
+        $this->assertEquals($u2->id, $currentrecord->userid);
+    }
+
     public function test_export_data_for_user() {
         $admin = \core_user::get_user(2);
         $u1 = $this->getDataGenerator()->create_user();