MDL-63497 block_html: Add support for removal of context users
authorMichael Hawkins <michaelh@moodle.com>
Wed, 12 Sep 2018 05:17:37 +0000 (13:17 +0800)
committerDavid Monllao <davidm@moodle.com>
Mon, 22 Oct 2018 10:48:32 +0000 (12:48 +0200)
This issue is a part of the MDL-62560 Epic.

blocks/html/classes/privacy/provider.php
blocks/html/tests/privacy_provider_test.php

index 21131fe..e7a080f 100644 (file)
@@ -26,7 +26,9 @@ namespace block_html\privacy;
 
 defined('MOODLE_INTERNAL') || die();
 
+use \core_privacy\local\request\userlist;
 use \core_privacy\local\request\approved_contextlist;
+use \core_privacy\local\request\approved_userlist;
 use \core_privacy\local\request\writer;
 use \core_privacy\local\request\helper;
 use \core_privacy\local\request\deletion_criteria;
@@ -42,6 +44,9 @@ class provider implements
         // The block_html block stores user provided data.
         \core_privacy\local\metadata\provider,
 
+        // This plugin is capable of determining which users have data within it.
+        \core_privacy\local\request\core_userlist_provider,
+
         // The block_html block provides data directly to core.
         \core_privacy\local\request\plugin\provider {
 
@@ -87,6 +92,32 @@ 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_block::class)) {
+            return;
+        }
+
+        $params = [
+            'contextid'    => $context->id,
+            'contextuser' => CONTEXT_USER,
+        ];
+
+        $sql = "SELECT bpc.instanceid AS userid
+                  FROM {context} c
+                  JOIN {block_instances} bi ON bi.id = c.instanceid AND bi.blockname = 'html'
+                  JOIN {context} bpc ON bpc.id = bi.parentcontextid AND bpc.contextlevel = :contextuser
+                 WHERE c.id = :contextid";
+
+        $userlist->add_from_sql('userid', $sql, $params);
+    }
+
     /**
      * Export all user data for the specified user, in the specified contexts.
      *
@@ -164,6 +195,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) {
+        $context = $userlist->get_context();
+
+        if ($context instanceof \context_block && ($blockinstance = static::get_instance_from_context($context))) {
+            blocks_delete_instance($blockinstance);
+        }
+    }
+
     /**
      * Delete all user data for the specified user, in the specified contexts.
      *
index 94d38ca..f5bc2be 100644 (file)
@@ -27,6 +27,7 @@ defined('MOODLE_INTERNAL') || die();
 
 use \core_privacy\local\request\writer;
 use \core_privacy\local\request\approved_contextlist;
+use \core_privacy\local\request\approved_userlist;
 use \block_html\privacy\provider;
 
 /**
@@ -341,4 +342,96 @@ class block_html_privacy_testcase extends \core_privacy\tests\provider_testcase
             $this->assertTrue(isset($contexts[$context->id]));
         }
     }
+
+    /**
+     * Test that only users with a user context HTML block are fetched.
+     */
+    public function test_get_users_in_context() {
+        $this->resetAfterTest();
+
+        $component = 'block_html';
+        $title = 'Block title';
+        $content = 'Block content';
+        $blockformat = FORMAT_PLAIN;
+
+        // Create a user with a user context HTML block.
+        $user1 = $this->getDataGenerator()->create_user();
+        $this->setUser($user1);
+
+        $userblock = $this->create_user_block($title, $content, $blockformat);
+        $usercontext = \context_block::instance($userblock->instance->id);
+
+        // Create a user with a course context HTML block.
+        $user2 = $this->getDataGenerator()->create_user();
+        $this->setUser($user2);
+
+        $course = $this->getDataGenerator()->create_course();
+        $courseblock = $this->create_course_block($course, $title, $content, $blockformat);
+        $coursecontext = \context_block::instance($courseblock->instance->id);
+
+        // Ensure only the user with a user context HTML block is returned.
+        $userlist = new \core_privacy\local\request\userlist($usercontext, $component);
+        \block_html\privacy\provider::get_users_in_context($userlist);
+
+        $this->assertCount(1, $userlist);
+
+        $expected = [$user1->id];
+        $actual = $userlist->get_userids();
+
+        $this->assertEquals($expected, $actual);
+
+        // Ensure passing the course context returns no users.
+        $userlist = new \core_privacy\local\request\userlist($coursecontext, $component);
+        \mod_forum\privacy\provider::get_users_in_context($userlist);
+        $this->assertEmpty($userlist);
+    }
+
+    /**
+     * Test that data for users in approved userlist is deleted.
+     */
+    public function test_delete_data_for_users() {
+        $this->resetAfterTest();
+
+        $component = 'block_html';
+        $title = 'Block title';
+        $content = 'Block content';
+        $blockformat = FORMAT_PLAIN;
+
+        // Create 2 user swith a user context HTML blocks.
+        $user1 = $this->getDataGenerator()->create_user();
+        $this->setUser($user1);
+
+        $block1 = $this->create_user_block($title, $content, $blockformat);
+        $context1 = \context_block::instance($block1->instance->id);
+
+        $user2 = $this->getDataGenerator()->create_user();
+        $this->setUser($user2);
+        $block2 = $this->create_user_block($title, $content, $blockformat);
+        $context2 = \context_block::instance($block2->instance->id);
+
+        // Create and populate the userlists.
+        $userlist1 = new \core_privacy\local\request\userlist($context1, $component);
+        \block_html\privacy\provider::get_users_in_context($userlist1);
+        $userlist2 = new \core_privacy\local\request\userlist($context2, $component);
+        \block_html\privacy\provider::get_users_in_context($userlist2);
+
+        // Ensure both members are included.
+        $this->assertCount(1, $userlist1);
+        $this->assertCount(1, $userlist2);
+
+        // Convert $userlist1 into an approved_contextlist.
+        $approvedlist = new approved_userlist($context1, 'block_html', $userlist1->get_userids());
+
+        // Delete using delete_data_for_user.
+        provider::delete_data_for_users($approvedlist);
+
+        // Re-fetch users in the contexts - only the first one should now be empty.
+        $userlist1 = new \core_privacy\local\request\userlist($context1, $component);
+        \block_html\privacy\provider::get_users_in_context($userlist1);
+        $this->assertCount(0, $userlist1);
+
+        $userlist2 = new \core_privacy\local\request\userlist($context2, $component);
+        \block_html\privacy\provider::get_users_in_context($userlist2);
+        $this->assertCount(1, $userlist2);
+    }
 }