MDL-63701 editor_atto: Add support for removal of context users
authorMihail Geshoski <mihail@moodle.com>
Thu, 1 Nov 2018 06:20:04 +0000 (14:20 +0800)
committerMihail Geshoski <mihail@moodle.com>
Thu, 1 Nov 2018 06:20:04 +0000 (14:20 +0800)
This issue is part of the MDL-62560 Epic.

lib/editor/atto/classes/privacy/provider.php
lib/editor/atto/tests/privacy_provider.php

index 44effe0..6e664e6 100644 (file)
@@ -31,6 +31,8 @@ use \core_privacy\local\request\writer;
 use \core_privacy\local\request\helper;
 use \core_privacy\local\request\deletion_criteria;
 use \core_privacy\local\metadata\collection;
+use \core_privacy\local\request\userlist;
+use \core_privacy\local\request\approved_userlist;
 
 /**
  * Privacy Subsystem implementation for editor_atto.
@@ -41,9 +43,10 @@ use \core_privacy\local\metadata\collection;
 class provider implements
         // The Atto editor stores user provided data.
         \core_privacy\local\metadata\provider,
-
         // The Atto editor provides data directly to core.
-        \core_privacy\local\request\plugin\provider {
+        \core_privacy\local\request\plugin\provider,
+        // The Atto editor is capable of determining which users have data within it.
+        \core_privacy\local\request\core_userlist_provider {
 
     /**
      * Returns information about how editor_atto stores its data.
@@ -85,6 +88,25 @@ class provider implements
         return $contextlist;
     }
 
+    /**
+     * Get the list of users within a specific 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();
+
+        $params = [
+            'contextid' => $context->id
+        ];
+
+        $sql = "SELECT userid
+                  FROM {editor_atto_autosave}
+                 WHERE contextid = :contextid";
+
+        $userlist->add_from_sql('userid', $sql, $params);
+    }
+
     /**
      * Export all user data for the specified user, in the specified contexts.
      *
@@ -148,6 +170,24 @@ 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();
+        $userids = $userlist->get_userids();
+
+        list($useridsql, $useridsqlparams) = $DB->get_in_or_equal($userids, SQL_PARAMS_NAMED);
+        $params = ['contextid' => $context->id] + $useridsqlparams;
+
+        $DB->delete_records_select('editor_atto_autosave', "contextid = :contextid AND userid {$useridsql}",
+            $params);
+    }
+
     /**
      * Delete all user data for the specified user, in the specified contexts.
      *
index 456d7a6..3103ebe 100644 (file)
@@ -28,6 +28,7 @@ defined('MOODLE_INTERNAL') || die();
 use \core_privacy\local\request\writer;
 use \core_privacy\local\request\approved_contextlist;
 use \editor_atto\privacy\provider;
+use \core_privacy\local\request\approved_userlist;
 
 /**
  * Unit tests for the editor_atto implementation of the privacy API.
@@ -326,6 +327,186 @@ class editor_atto_privacy_testcase extends \core_privacy\tests\provider_testcase
         ]));
     }
 
+    /**
+     * Test that user data with different contexts is fetched.
+     */
+    public function test_get_users_in_context() {
+        $this->resetAfterTest();
+
+        $component = 'editor_atto';
+
+        // Create editor drafts in:
+        // - the system; and
+        // - a course; and
+        // - current user context; and
+        // - another user.
+
+        $systemcontext = \context_system::instance();
+        // Create a course.
+        $course = $this->getDataGenerator()->create_course();
+        $coursecontext = \context_course::instance($course->id);
+
+        // Create a user.
+        $user = $this->getDataGenerator()->create_user();
+        $usercontext = \context_user::instance($user->id);
+        $this->setUser($user);
+
+        // Add a fake inline image to the original post.
+        $this->create_editor_draft($usercontext, $user->id,
+                'id_user_intro', 'text for test user at own context');
+        $this->create_editor_draft($systemcontext, $user->id,
+            'id_system_intro', 'text for test user at system context', 2);
+        $this->create_editor_draft($systemcontext, $user->id,
+            'id_system_description', 'text for test user at system context', 4);
+        $this->create_editor_draft($coursecontext, $user->id,
+            'id_course_intro', 'text for test user at course context');
+
+        // Create user2.
+        $user2 = $this->getDataGenerator()->create_user();
+        $this->setUser($user2);
+
+        $this->create_editor_draft($coursecontext, $user2->id,
+            'id_course_description', 'text for test user2 at course context');
+
+        // The list of users in usercontext should return user.
+        $userlist = new \core_privacy\local\request\userlist($usercontext, $component);
+        provider::get_users_in_context($userlist);
+        $this->assertCount(1, $userlist);
+        $this->assertTrue(in_array($user->id, $userlist->get_userids()));
+
+        // The list of users in systemcontext should return user.
+        $userlist = new \core_privacy\local\request\userlist($systemcontext, $component);
+        provider::get_users_in_context($userlist);
+        $this->assertCount(1, $userlist);
+        $this->assertTrue(in_array($user->id, $userlist->get_userids()));
+
+        // The list of users in coursecontext should return user and user2.
+        $userlist = new \core_privacy\local\request\userlist($coursecontext, $component);
+        provider::get_users_in_context($userlist);
+        $this->assertCount(2, $userlist);
+        $this->assertTrue(in_array($user->id, $userlist->get_userids()));
+        $this->assertTrue(in_array($user2->id, $userlist->get_userids()));
+    }
+
+    /**
+     * Test that data for users in approved userlist is deleted.
+     */
+    public function test_delete_data_for_users() {
+        $this->resetAfterTest();
+
+        $component = 'editor_atto';
+
+        // Create editor drafts in:
+        // - the system; and
+        // - a course; and
+        // - current user context; and
+        // - another user.
+
+        $systemcontext = \context_system::instance();
+        // Create a course.
+        $course = $this->getDataGenerator()->create_course();
+        $coursecontext = \context_course::instance($course->id);
+
+        // Create a user.
+        $user = $this->getDataGenerator()->create_user();
+        $usercontext = \context_user::instance($user->id);
+        $this->setUser($user);
+
+        // Add a fake inline image to the original post.
+        $this->create_editor_draft($usercontext, $user->id,
+            'id_user_intro', 'text for test user at own context');
+        $this->create_editor_draft($usercontext, $user->id,
+            'id_user_description', 'text for test user at own context');
+        $this->create_editor_draft($systemcontext, $user->id,
+            'id_system_intro', 'text for test user at system context', 2);
+        $this->create_editor_draft($systemcontext, $user->id,
+            'id_system_description', 'text for test user at system context', 4);
+        $this->create_editor_draft($coursecontext, $user->id,
+            'id_course_intro', 'text for test user at course context');
+        $this->create_editor_draft($coursecontext, $user->id,
+            'id_course_description', 'text for test user at course context');
+
+        // Create some data as the other user too.
+        $otheruser = $this->getDataGenerator()->create_user();
+        $otherusercontext = \context_user::instance($otheruser->id);
+        $this->setUser($otheruser);
+
+        $this->create_editor_draft($otherusercontext, $otheruser->id,
+            'id_user_intro', 'text for other user at own context');
+        $this->create_editor_draft($otherusercontext, $otheruser->id,
+            'id_user_description', 'text for other user at own context');
+        $this->create_editor_draft($systemcontext, $otheruser->id,
+            'id_system_intro', 'text for other user at system context');
+        $this->create_editor_draft($systemcontext, $otheruser->id,
+            'id_system_description', 'text for other user at system context');
+        $this->create_editor_draft($coursecontext, $otheruser->id,
+            'id_course_intro', 'text for other user at course context');
+        $this->create_editor_draft($coursecontext, $otheruser->id,
+            'id_course_description', 'text for other user at course context');
+
+        // The list of users for usercontext should return user.
+        $userlist1 = new \core_privacy\local\request\userlist($usercontext, $component);
+        provider::get_users_in_context($userlist1);
+        $this->assertCount(1, $userlist1);
+        $this->assertTrue(in_array($user->id, $userlist1->get_userids()));
+
+        // The list of users for otherusercontext should return otheruser.
+        $userlist2 = new \core_privacy\local\request\userlist($otherusercontext, $component);
+        provider::get_users_in_context($userlist2);
+        $this->assertCount(1, $userlist2);
+        $this->assertTrue(in_array($otheruser->id, $userlist2->get_userids()));
+
+        // Add userlist1 to the approved user list.
+        $approvedlist = new approved_userlist($usercontext, $component, $userlist1->get_userids());
+        // Delete user data using delete_data_for_user for usercontext.
+        provider::delete_data_for_users($approvedlist);
+
+        // Re-fetch users in usercontext - The user list should now be empty.
+        $userlist1 = new \core_privacy\local\request\userlist($usercontext, $component);
+        provider::get_users_in_context($userlist1);
+        $this->assertCount(0, $userlist1);
+        // Re-fetch users in otherusercontext - The user list should not be empty (otheruser).
+        $userlist2 = new \core_privacy\local\request\userlist($otherusercontext, $component);
+        provider::get_users_in_context($userlist2);
+        $this->assertCount(1, $userlist2);
+        $this->assertTrue(in_array($otheruser->id, $userlist2->get_userids()));
+
+        // The list of users for systemcontext should return user and otheruser.
+        $userlist3 = new \core_privacy\local\request\userlist($systemcontext, $component);
+        provider::get_users_in_context($userlist3);
+        $this->assertCount(2, $userlist3);
+        $this->assertTrue(in_array($user->id, $userlist3->get_userids()));
+        $this->assertTrue(in_array($otheruser->id, $userlist3->get_userids()));
+
+        // Add $userlist3 to the approved user list in the system context.
+        $approvedlist = new approved_userlist($systemcontext, $component, $userlist3->get_userids());
+        // Delete user and otheruser data using delete_data_for_user.
+        provider::delete_data_for_users($approvedlist);
+
+        // Re-fetch users in systemcontext - The user list should be empty.
+        $userlist3 = new \core_privacy\local\request\userlist($systemcontext, $component);
+        provider::get_users_in_context($userlist3);
+        $this->assertCount(0, $userlist3);
+
+        // The list of users for coursecontext should return user and otheruser.
+        $userlist4 = new \core_privacy\local\request\userlist($coursecontext, $component);
+        provider::get_users_in_context($userlist4);
+        $this->assertCount(2, $userlist4);
+        $this->assertTrue(in_array($user->id, $userlist4->get_userids()));
+        $this->assertTrue(in_array($otheruser->id, $userlist4->get_userids()));
+
+        // Add user to the approved user list in the course context.
+        $approvedlist = new approved_userlist($coursecontext, $component, [$user->id]);
+        // Delete user data using delete_data_for_user.
+        provider::delete_data_for_users($approvedlist);
+
+        // Re-fetch users in coursecontext - The user list should return otheruser.
+        $userlist4 = new \core_privacy\local\request\userlist($coursecontext, $component);
+        provider::get_users_in_context($userlist4);
+        $this->assertCount(1, $userlist4);
+        $this->assertTrue(in_array($otheruser->id, $userlist4->get_userids()));
+    }
+
     /**
      * Test fetch and delete when another user has editted a draft in your
      * user context. Edge case.