Merge branch 'MDL-63690-master' of git://github.com/mihailges/moodle
authorAndrew Nicols <andrew@nicols.co.uk>
Wed, 31 Oct 2018 00:20:31 +0000 (08:20 +0800)
committerAndrew Nicols <andrew@nicols.co.uk>
Wed, 31 Oct 2018 00:20:31 +0000 (08:20 +0800)
blog/classes/privacy/provider.php
blog/tests/privacy_test.php
privacy/classes/local/request/userlist.php

index e3acab9..29c9c74 100644 (file)
@@ -49,7 +49,8 @@ require_once($CFG->dirroot . '/blog/locallib.php');
  */
 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.
@@ -164,6 +165,51 @@ 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) {
+        global $DB;
+        $context = $userlist->get_context();
+        if ($context->contextlevel == CONTEXT_COURSE || $context->contextlevel == CONTEXT_MODULE) {
+
+            $params = ['contextid' => $context->id];
+
+            $sql = "SELECT p.id, p.userid
+                      FROM {post} p
+                      JOIN {blog_association} ba ON ba.blogid = p.id AND ba.contextid = :contextid";
+
+            $posts = $DB->get_records_sql($sql, $params);
+            $userids = array_map(function($post) {
+                return $post->userid;
+            }, $posts);
+            $userlist->add_users($userids);
+
+            // Add any user's who posted on the blog.
+            list($insql, $inparams) = $DB->get_in_or_equal(array_keys($posts), SQL_PARAMS_NAMED);
+            \core_comment\privacy\provider::get_users_in_context_from_sql($userlist, 'c', 'blog', 'format_blog', null, $insql,
+                    $inparams);
+        } else if ($context->contextlevel == CONTEXT_USER) {
+            $params = ['userid' => $context->instanceid];
+
+            $sql = "SELECT userid
+                      FROM {blog_external}
+                     WHERE userid = :userid";
+            $userlist->add_from_sql('userid', $sql, $params);
+
+            $sql = "SELECT userid
+                      FROM {post}
+                     WHERE userid = :userid";
+            $userlist->add_from_sql('userid', $sql, $params);
+
+            // Add any user's who posted on the blog.
+            \core_comment\privacy\provider::get_users_in_context_from_sql($userlist, 'c', 'blog', 'format_blog', $context->id);
+        }
+    }
+
     /**
      * Export all user data for the specified user, in the specified contexts.
      *
@@ -417,6 +463,42 @@ 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(\core_privacy\local\request\approved_userlist $userlist) {
+        global $DB;
+
+        $context = $userlist->get_context();
+        $userids = $userlist->get_userids();
+
+        if ($context->contextlevel == CONTEXT_USER) {
+            // If one of the listed users matches this context then delete the blog, associations, and comments.
+            if (array_search($context->instanceid, $userids) !== false) {
+                self::delete_all_user_data($context);
+                \core_comment\privacy\provider::delete_comments_for_all_users($context, 'blog', 'format_blog');
+                return;
+            }
+            \core_comment\privacy\provider::delete_comments_for_users($userlist, 'blog', 'format_blog');
+        } else {
+            list($insql, $inparams) = $DB->get_in_or_equal($userids, SQL_PARAMS_NAMED);
+            $sql = "SELECT ba.id
+                      FROM {blog_association} ba
+                      JOIN {post} p ON p.id = ba.blogid
+                     WHERE ba.contextid = :contextid
+                       AND p.userid $insql";
+            $inparams['contextid'] = $context->id;
+            $associds = $DB->get_fieldset_sql($sql, $inparams);
+
+            if (!empty($associds)) {
+                list($insql, $inparams) = $DB->get_in_or_equal($associds, SQL_PARAMS_NAMED, 'param', true);
+                $DB->delete_records_select('blog_association', "id $insql", $inparams);
+            }
+        }
+    }
+
     /**
      * Helper method to delete all user data.
      *
index a71faa5..8db5bdd 100644 (file)
@@ -129,6 +129,79 @@ class core_blog_privacy_testcase extends provider_testcase {
         $this->assertTrue(in_array(context_course::instance($c1->id)->id, $contextids));
     }
 
+    /**
+     * Test that user IDs are returned for a specificed course or module context.
+     */
+    public function test_get_users_in_context_course_and_module() {
+        $user1 = $this->getDataGenerator()->create_user();
+        $user2 = $this->getDataGenerator()->create_user();
+        $course = $this->getDataGenerator()->create_course();
+        $c1ctx = context_course::instance($course->id);
+
+        $post = $this->create_post(['userid' => $user1->id, 'courseid' => $course->id]);
+        $entry = new blog_entry($post->id);
+        $entry->add_association($c1ctx->id);
+
+        // Add a comment from user 2.
+        $comment = $this->get_comment_object(context_user::instance($user1->id), $entry->id);
+        $this->setUser($user2);
+        $comment->add('Nice blog post');
+
+        $userlist = new \core_privacy\local\request\userlist($c1ctx, 'core_blog');
+        provider::get_users_in_context($userlist);
+        $userids = $userlist->get_userids();
+        $this->assertCount(2, $userids);
+
+        // Add an association for a module.
+        $cm1a = $this->getDataGenerator()->create_module('page', ['course' => $course]);
+        $cm1ctx = context_module::instance($cm1a->cmid);
+
+        $post2 = $this->create_post(['userid' => $user2->id, 'courseid' => $course->id]);
+        $entry2 = new blog_entry($post2->id);
+        $entry2->add_association($cm1ctx->id);
+
+        $userlist = new \core_privacy\local\request\userlist($cm1ctx, 'core_blog');
+        provider::get_users_in_context($userlist);
+        $userids = $userlist->get_userids();
+        $this->assertCount(1, $userids);
+    }
+
+    /**
+     * Test that user IDs are returned for a specificed user context.
+     */
+    public function test_get_users_in_context_user_context() {
+        $user1 = $this->getDataGenerator()->create_user();
+        $user2 = $this->getDataGenerator()->create_user();
+        $u1ctx = context_user::instance($user1->id);
+
+        $post = $this->create_post(['userid' => $user1->id]);
+        $entry = new blog_entry($post->id);
+
+        // Add a comment from user 2.
+        $comment = $this->get_comment_object($u1ctx, $entry->id);
+        $this->setUser($user2);
+        $comment->add('Another nice blog post');
+
+        $userlist = new \core_privacy\local\request\userlist($u1ctx, 'core_blog');
+        provider::get_users_in_context($userlist);
+        $userids = $userlist->get_userids();
+        $this->assertCount(2, $userids);
+    }
+
+    /**
+     * Test that user IDs are returned for a specificed user context for an external blog.
+     */
+    public function test_get_users_in_context_external_blog() {
+        $user1 = $this->getDataGenerator()->create_user();
+        $u1ctx = context_user::instance($user1->id);
+        $extu1 = $this->create_external_blog(['userid' => $user1->id]);
+
+        $userlist = new \core_privacy\local\request\userlist($u1ctx, 'core_blog');
+        provider::get_users_in_context($userlist);
+        $userids = $userlist->get_userids();
+        $this->assertCount(1, $userids);
+    }
+
     public function test_delete_data_for_user() {
         global $DB;
 
@@ -629,6 +702,135 @@ class core_blog_privacy_testcase extends provider_testcase {
 
     }
 
+    /**
+     * Test that deleting of blog information in a user context works as desired.
+     */
+    public function test_delete_data_for_users_user_context() {
+        global $DB;
+
+        $u1 = $this->getDataGenerator()->create_user();
+        $u2 = $this->getDataGenerator()->create_user();
+        $u3 = $this->getDataGenerator()->create_user();
+        $u4 = $this->getDataGenerator()->create_user();
+        $u5 = $this->getDataGenerator()->create_user();
+
+        $u1ctx = context_user::instance($u1->id);
+
+        $post = $this->create_post(['userid' => $u1->id]);
+        $entry = new blog_entry($post->id);
+
+        $comment = $this->get_comment_object($u1ctx, $entry->id);
+        $this->setUser($u1);
+        $comment->add('Hello, I created the blog');
+        $this->setUser($u2);
+        $comment->add('User 2 making a comment.');
+        $this->setUser($u3);
+        $comment->add('User 3 here.');
+        $this->setUser($u4);
+        $comment->add('User 4 is nice.');
+        $this->setUser($u5);
+        $comment->add('User 5 for the win.');
+
+        // This will only delete the comments made by user 4 and 5.
+        $this->assertCount(5, $DB->get_records('comments', ['contextid' => $u1ctx->id]));
+        $userlist = new \core_privacy\local\request\approved_userlist($u1ctx, 'core_blog', [$u4->id, $u5->id]);
+        provider::delete_data_for_users($userlist);
+        $this->assertCount(3, $DB->get_records('comments', ['contextid' => $u1ctx->id]));
+        $this->assertCount(1, $DB->get_records('post', ['userid' => $u1->id]));
+
+        // As the owner of the post is here everything will be deleted.
+        $userlist = new \core_privacy\local\request\approved_userlist($u1ctx, 'core_blog', [$u1->id, $u2->id]);
+        provider::delete_data_for_users($userlist);
+        $this->assertEmpty($DB->get_records('comments', ['contextid' => $u1ctx->id]));
+        $this->assertEmpty($DB->get_records('post', ['userid' => $u1->id]));
+    }
+
+    /**
+     * Test that deleting of an external blog in a user context works as desired.
+     */
+    public function test_delete_data_for_users_external_blog() {
+        global $DB;
+
+        $u1 = $this->getDataGenerator()->create_user();
+        $u2 = $this->getDataGenerator()->create_user();
+
+        $u1ctx = context_user::instance($u1->id);
+        $u2ctx = context_user::instance($u2->id);
+
+        $post = $this->create_external_blog(['userid' => $u1->id, 'url' => 'https://moodle.org', 'name' => 'Moodle RSS']);
+        $post2 = $this->create_external_blog(['userid' => $u2->id, 'url' => 'https://moodle.com', 'name' => 'Some other thing']);
+
+        // Check that we have two external blogs created.
+        $this->assertCount(2, $DB->get_records('blog_external'));
+        // This will only delete the external blog for user 1.
+        $userlist = new \core_privacy\local\request\approved_userlist($u1ctx, 'core_blog', [$u1->id, $u2->id]);
+        provider::delete_data_for_users($userlist);
+        $this->assertCount(1, $DB->get_records('blog_external'));
+    }
+
+    public function test_delete_data_for_users_course_and_module_context() {
+        global $DB;
+
+        $u1 = $this->getDataGenerator()->create_user();
+        $u2 = $this->getDataGenerator()->create_user();
+        $u3 = $this->getDataGenerator()->create_user();
+        $u4 = $this->getDataGenerator()->create_user();
+        $u5 = $this->getDataGenerator()->create_user();
+
+        $course = $this->getDataGenerator()->create_course();
+        $module = $this->getDataGenerator()->create_module('page', ['course' => $course]);
+
+        $u1ctx = context_user::instance($u1->id);
+        $u3ctx = context_user::instance($u3->id);
+        $c1ctx = context_course::instance($course->id);
+        $cm1ctx = context_module::instance($module->cmid);
+
+        // Blog with course association.
+        $post1 = $this->create_post(['userid' => $u1->id, 'courseid' => $course->id]);
+        $entry1 = new blog_entry($post1->id);
+        $entry1->add_association($c1ctx->id);
+
+        // Blog with module association.
+        $post2 = $this->create_post(['userid' => $u3->id, 'courseid' => $course->id]);
+        $entry2 = new blog_entry($post2->id);
+        $entry2->add_association($cm1ctx->id);
+
+        $comment = $this->get_comment_object($u1ctx, $entry1->id);
+        $this->setUser($u1);
+        $comment->add('Hello, I created the blog');
+        $this->setUser($u2);
+        $comment->add('comment on first course blog');
+        $this->setUser($u4);
+        $comment->add('user 4 on course blog');
+
+        $comment = $this->get_comment_object($u3ctx, $entry2->id);
+        $this->setUser($u3);
+        $comment->add('Hello, I created the module blog');
+        $this->setUser($u2);
+        $comment->add('I am commenting on both');
+        $this->setUser($u5);
+        $comment->add('User 5 for modules');
+
+        $this->assertCount(6, $DB->get_records('comments', ['component' => 'blog']));
+        $this->assertCount(2, $DB->get_records('post', ['courseid' => $course->id]));
+        $this->assertCount(2, $DB->get_records('blog_association'));
+
+        // When using the course or module context we are only removing the blog associations and the comments.
+        $userlist = new \core_privacy\local\request\approved_userlist($c1ctx, 'core_blog', [$u2->id, $u1->id, $u5->id]);
+        provider::delete_data_for_users($userlist);
+        // Only one of the blog_associations should be removed. Everything else should be as before.
+        $this->assertCount(6, $DB->get_records('comments', ['component' => 'blog']));
+        $this->assertCount(2, $DB->get_records('post', ['courseid' => $course->id]));
+        $this->assertCount(1, $DB->get_records('blog_association'));
+
+        $userlist = new \core_privacy\local\request\approved_userlist($cm1ctx, 'core_blog', [$u2->id, $u1->id, $u3->id]);
+        provider::delete_data_for_users($userlist);
+        // Now we've removed the other association.
+        $this->assertCount(6, $DB->get_records('comments', ['component' => 'blog']));
+        $this->assertCount(2, $DB->get_records('post', ['courseid' => $course->id]));
+        $this->assertEmpty($DB->get_records('blog_association'));
+    }
+
     /**
      * Create a blog post.
      *
index 0664f14..171ccc0 100644 (file)
@@ -80,12 +80,13 @@ class userlist extends userlist_base {
     public function add_users(array $userids) : userlist {
         global $DB;
 
-        list($useridsql, $useridparams) = $DB->get_in_or_equal($userids, SQL_PARAMS_NAMED);
-        $sql = "SELECT DISTINCT u.id
-                  FROM {user} u
-                 WHERE u.id {$useridsql}";
-        $this->add_from_sql('id', $sql, $useridparams);
-
+        if (!empty($userids)) {
+            list($useridsql, $useridparams) = $DB->get_in_or_equal($userids, SQL_PARAMS_NAMED);
+            $sql = "SELECT DISTINCT u.id
+                      FROM {user} u
+                     WHERE u.id {$useridsql}";
+            $this->add_from_sql('id', $sql, $useridparams);
+        }
         return $this;
     }