MDL-65584 blog: safer deletion of associations in privacy provider.
[moodle.git] / blog / tests / privacy_test.php
1 <?php
2 // This file is part of Moodle - http://moodle.org/
3 //
4 // Moodle is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // Moodle is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
17 /**
18  * Data provider tests.
19  *
20  * @package    core_blog
21  * @category   test
22  * @copyright  2018 Frédéric Massart
23  * @author     Frédéric Massart <fred@branchup.tech>
24  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
25  */
27 defined('MOODLE_INTERNAL') || die();
28 global $CFG;
30 use core_privacy\tests\provider_testcase;
31 use core_privacy\local\request\approved_contextlist;
32 use core_privacy\local\request\transform;
33 use core_privacy\local\request\writer;
34 use core_blog\privacy\provider;
36 require_once($CFG->dirroot . '/blog/locallib.php');
37 require_once($CFG->dirroot . '/comment/lib.php');
39 /**
40  * Data provider testcase class.
41  *
42  * @package    core_blog
43  * @category   test
44  * @copyright  2018 Frédéric Massart
45  * @author     Frédéric Massart <fred@branchup.tech>
46  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
47  */
48 class core_blog_privacy_testcase extends provider_testcase {
50     public function setUp() {
51         $this->resetAfterTest();
52     }
54     public function test_get_contexts_for_userid() {
55         $dg = $this->getDataGenerator();
56         $c1 = $dg->create_course();
57         $c2 = $dg->create_course();
58         $c3 = $dg->create_course();
59         $cm1a = $dg->create_module('page', ['course' => $c1]);
60         $cm1b = $dg->create_module('page', ['course' => $c1]);
61         $cm2a = $dg->create_module('page', ['course' => $c2]);
62         $u1 = $dg->create_user();
63         $u2 = $dg->create_user();
64         $u1ctx = context_user::instance($u1->id);
66         // Blog share a table with notes, so throw data in there and make sure it doesn't get reported.
67         $dg->get_plugin_generator('core_notes')->create_instance(['userid' => $u1->id, 'courseid' => $c3->id]);
69         $this->assertEmpty(provider::get_contexts_for_userid($u1->id)->get_contextids());
70         $this->assertEmpty(provider::get_contexts_for_userid($u2->id)->get_contextids());
72         // Gradually create blog posts for user 1. First system one.
73         $this->create_post(['userid' => $u1->id]);
74         $contextids = provider::get_contexts_for_userid($u1->id)->get_contextids();
75         $this->assertCount(1, $contextids);
76         $this->assertEquals($u1ctx->id, $contextids[0]);
77         $this->assertEmpty(provider::get_contexts_for_userid($u2->id)->get_contextids());
79         // Create a blog post associated with c1.
80         $post = $this->create_post(['userid' => $u1->id, 'courseid' => $c1->id]);
81         $entry = new blog_entry($post->id);
82         $entry->add_association(context_course::instance($c1->id)->id);
83         $contextids = provider::get_contexts_for_userid($u1->id)->get_contextids();
84         $this->assertCount(2, $contextids);
85         $this->assertTrue(in_array($u1ctx->id, $contextids));
86         $this->assertTrue(in_array(context_course::instance($c1->id)->id, $contextids));
87         $this->assertEmpty(provider::get_contexts_for_userid($u2->id)->get_contextids());
89         // Create a blog post associated with cm2a.
90         $post = $this->create_post(['userid' => $u1->id, 'courseid' => $c2->id]);
91         $entry = new blog_entry($post->id);
92         $entry->add_association(context_module::instance($cm2a->cmid)->id);
93         $contextids = provider::get_contexts_for_userid($u1->id)->get_contextids();
94         $this->assertCount(3, $contextids);
95         $this->assertTrue(in_array($u1ctx->id, $contextids));
96         $this->assertTrue(in_array(context_course::instance($c1->id)->id, $contextids));
97         $this->assertTrue(in_array(context_module::instance($cm2a->cmid)->id, $contextids));
98         $this->assertEmpty(provider::get_contexts_for_userid($u2->id)->get_contextids());
100         // User 2 comments on u1's post.
101         $comment = $this->get_comment_object($u1ctx, $post->id);
102         $this->setUser($u2);
103         $comment->add('Hello, it\'s me!');
104         $contextids = provider::get_contexts_for_userid($u1->id)->get_contextids();
105         $this->assertCount(3, $contextids);
106         $this->assertTrue(in_array($u1ctx->id, $contextids));
107         $this->assertTrue(in_array(context_course::instance($c1->id)->id, $contextids));
108         $this->assertTrue(in_array(context_module::instance($cm2a->cmid)->id, $contextids));
109         $contextids = provider::get_contexts_for_userid($u2->id)->get_contextids();
110         $this->assertCount(1, $contextids);
111         $this->assertTrue(in_array($u1ctx->id, $contextids));
112     }
114     public function test_get_contexts_for_userid_with_one_associated_post_only() {
115         $dg = $this->getDataGenerator();
116         $c1 = $dg->create_course();
117         $u1 = $dg->create_user();
118         $u1ctx = context_user::instance($u1->id);
120         $this->assertEmpty(provider::get_contexts_for_userid($u1->id)->get_contextids());
122         // Create a blog post associated with c1. It should always return both the course and user context.
123         $post = $this->create_post(['userid' => $u1->id, 'courseid' => $c1->id]);
124         $entry = new blog_entry($post->id);
125         $entry->add_association(context_course::instance($c1->id)->id);
126         $contextids = provider::get_contexts_for_userid($u1->id)->get_contextids();
127         $this->assertCount(2, $contextids);
128         $this->assertTrue(in_array($u1ctx->id, $contextids));
129         $this->assertTrue(in_array(context_course::instance($c1->id)->id, $contextids));
130     }
132     /**
133      * Test that user IDs are returned for a specificed course or module context.
134      */
135     public function test_get_users_in_context_course_and_module() {
136         $user1 = $this->getDataGenerator()->create_user();
137         $user2 = $this->getDataGenerator()->create_user();
138         $course = $this->getDataGenerator()->create_course();
139         $c1ctx = context_course::instance($course->id);
141         $post = $this->create_post(['userid' => $user1->id, 'courseid' => $course->id]);
142         $entry = new blog_entry($post->id);
143         $entry->add_association($c1ctx->id);
145         // Add a comment from user 2.
146         $comment = $this->get_comment_object(context_user::instance($user1->id), $entry->id);
147         $this->setUser($user2);
148         $comment->add('Nice blog post');
150         $userlist = new \core_privacy\local\request\userlist($c1ctx, 'core_blog');
151         provider::get_users_in_context($userlist);
152         $userids = $userlist->get_userids();
153         $this->assertCount(2, $userids);
155         // Add an association for a module.
156         $cm1a = $this->getDataGenerator()->create_module('page', ['course' => $course]);
157         $cm1ctx = context_module::instance($cm1a->cmid);
159         $post2 = $this->create_post(['userid' => $user2->id, 'courseid' => $course->id]);
160         $entry2 = new blog_entry($post2->id);
161         $entry2->add_association($cm1ctx->id);
163         $userlist = new \core_privacy\local\request\userlist($cm1ctx, 'core_blog');
164         provider::get_users_in_context($userlist);
165         $userids = $userlist->get_userids();
166         $this->assertCount(1, $userids);
167     }
169     /**
170      * Test that user IDs are returned for a specificed user context.
171      */
172     public function test_get_users_in_context_user_context() {
173         $user1 = $this->getDataGenerator()->create_user();
174         $user2 = $this->getDataGenerator()->create_user();
175         $u1ctx = context_user::instance($user1->id);
177         $post = $this->create_post(['userid' => $user1->id]);
178         $entry = new blog_entry($post->id);
180         // Add a comment from user 2.
181         $comment = $this->get_comment_object($u1ctx, $entry->id);
182         $this->setUser($user2);
183         $comment->add('Another nice blog post');
185         $userlist = new \core_privacy\local\request\userlist($u1ctx, 'core_blog');
186         provider::get_users_in_context($userlist);
187         $userids = $userlist->get_userids();
188         $this->assertCount(2, $userids);
189     }
191     /**
192      * Test that user IDs are returned for a specificed user context for an external blog.
193      */
194     public function test_get_users_in_context_external_blog() {
195         $user1 = $this->getDataGenerator()->create_user();
196         $u1ctx = context_user::instance($user1->id);
197         $extu1 = $this->create_external_blog(['userid' => $user1->id]);
199         $userlist = new \core_privacy\local\request\userlist($u1ctx, 'core_blog');
200         provider::get_users_in_context($userlist);
201         $userids = $userlist->get_userids();
202         $this->assertCount(1, $userids);
203     }
205     public function test_delete_data_for_user() {
206         global $DB;
208         $dg = $this->getDataGenerator();
209         $c1 = $dg->create_course();
210         $c2 = $dg->create_course();
211         $cm1a = $dg->create_module('page', ['course' => $c1]);
212         $cm1b = $dg->create_module('page', ['course' => $c1]);
213         $cm2a = $dg->create_module('page', ['course' => $c2]);
214         $u1 = $dg->create_user();
215         $u2 = $dg->create_user();
216         $u3 = $dg->create_user();
218         $c1ctx = context_course::instance($c1->id);
219         $c2ctx = context_course::instance($c2->id);
220         $cm1actx = context_module::instance($cm1a->cmid);
221         $cm1bctx = context_module::instance($cm1b->cmid);
222         $cm2actx = context_module::instance($cm2a->cmid);
223         $u1ctx = context_user::instance($u1->id);
224         $u2ctx = context_user::instance($u2->id);
226         // Blog share a table with notes, so throw data in there and make sure it doesn't get deleted.
227         $this->assertFalse($DB->record_exists('post', ['courseid' => $c1->id, 'userid' => $u1->id, 'module' => 'notes']));
228         $dg->get_plugin_generator('core_notes')->create_instance(['userid' => $u1->id, 'courseid' => $c1->id]);
229         $this->assertTrue($DB->record_exists('post', ['courseid' => $c1->id, 'userid' => $u1->id, 'module' => 'notes']));
231         // Create two external blogs.
232         $extu1 = $this->create_external_blog(['userid' => $u1->id]);
233         $extu2 = $this->create_external_blog(['userid' => $u2->id]);
235         // Create a set of posts.
236         $entry = new blog_entry($this->create_post(['userid' => $u1->id])->id);
237         $commentedon = $entry;
238         $entry = new blog_entry($this->create_post(['userid' => $u2->id])->id);
240         // Two course associations for u1.
241         $entry = new blog_entry($this->create_post(['userid' => $u1->id, 'courseid' => $c1->id])->id);
242         $entry->add_association($c1ctx->id);
243         $entry = new blog_entry($this->create_post(['userid' => $u1->id, 'courseid' => $c1->id])->id);
244         $entry->add_association($c1ctx->id);
246         // Two module associations with cm1a, and 1 with cm1b for u1.
247         $entry = new blog_entry($this->create_post(['userid' => $u1->id, 'courseid' => $c1->id])->id);
248         $entry->add_association($cm1actx->id);
249         $entry = new blog_entry($this->create_post(['userid' => $u1->id, 'courseid' => $c1->id])->id);
250         $entry->add_association($cm1actx->id);
251         $entry = new blog_entry($this->create_post(['userid' => $u1->id, 'courseid' => $c1->id])->id);
252         $entry->add_association($cm1bctx->id);
254         // One association for u2 in c1, cm1a and cm2a.
255         $entry = new blog_entry($this->create_post(['userid' => $u2->id, 'courseid' => $c1->id])->id);
256         $entry->add_association($c1ctx->id);
257         $entry = new blog_entry($this->create_post(['userid' => $u2->id, 'courseid' => $c1->id])->id);
258         $entry->add_association($cm1actx->id);
259         $entry = new blog_entry($this->create_post(['userid' => $u2->id, 'courseid' => $c2->id])->id);
260         $entry->add_association($cm2actx->id);
262         // One association for u1 in c2 and cm2a.
263         $entry = new blog_entry($this->create_post(['userid' => $u1->id, 'courseid' => $c2->id])->id);
264         $entry->add_association($c2ctx->id);
265         $entry = new blog_entry($this->create_post(['userid' => $u1->id, 'courseid' => $c2->id])->id);
266         $entry->add_association($cm2actx->id);
268         // Add comments.
269         $comment = $this->get_comment_object($u1ctx, $commentedon->id);
270         $this->setUser($u1);
271         $comment->add('Hello, it\'s me!');
272         $comment->add('I was wondering...');
273         $this->setUser($u2);
274         $comment->add('If after all these years');
275         $this->setUser($u3);
276         $comment->add('You\'d like to meet');
278         // Assert current setup.
279         $this->assertCount(6, provider::get_contexts_for_userid($u1->id)->get_contextids());
280         $this->assertCount(9, $DB->get_records('post', ['userid' => $u1->id]));
281         $this->assertCount(5, provider::get_contexts_for_userid($u2->id)->get_contextids());
282         $this->assertCount(4, $DB->get_records('post', ['userid' => $u2->id]));
283         $this->assertCount(1, $DB->get_records('blog_external', ['userid' => $u1->id]));
284         $this->assertCount(1, $DB->get_records('blog_external', ['userid' => $u2->id]));
285         $this->assertCount(2, $DB->get_records('comments', ['userid' => $u1->id]));
286         $this->assertCount(1, $DB->get_records('comments', ['userid' => $u2->id]));
287         $this->assertCount(1, $DB->get_records('comments', ['userid' => $u3->id]));
289         // Delete for u1 in cm1a.
290         $appctxs = new approved_contextlist($u1, 'core_blog', [$cm1actx->id]);
291         provider::delete_data_for_user($appctxs);
292         $contextids = provider::get_contexts_for_userid($u1->id)->get_contextids();
293         $this->assertCount(5, $contextids);
294         $this->assertFalse(in_array($cm1actx->id, $contextids));
295         $this->assertCount(9, $DB->get_records('post', ['userid' => $u1->id]));
296         $this->assertCount(5, provider::get_contexts_for_userid($u2->id)->get_contextids());
297         $this->assertCount(4, $DB->get_records('post', ['userid' => $u2->id]));
298         $this->assertCount(1, $DB->get_records('blog_external', ['userid' => $u1->id]));
299         $this->assertCount(1, $DB->get_records('blog_external', ['userid' => $u2->id]));
300         $this->assertTrue($DB->record_exists('post', ['courseid' => $c1->id, 'userid' => $u1->id, 'module' => 'notes']));
302         // Delete for u1 in c1.
303         $appctxs = new approved_contextlist($u1, 'core_blog', [$c1ctx->id]);
304         provider::delete_data_for_user($appctxs);
305         $contextids = provider::get_contexts_for_userid($u1->id)->get_contextids();
306         $this->assertCount(4, $contextids);
307         $this->assertFalse(in_array($c1ctx->id, $contextids));
308         $this->assertCount(9, $DB->get_records('post', ['userid' => $u1->id]));
309         $this->assertCount(5, provider::get_contexts_for_userid($u2->id)->get_contextids());
310         $this->assertCount(4, $DB->get_records('post', ['userid' => $u2->id]));
311         $this->assertCount(1, $DB->get_records('blog_external', ['userid' => $u1->id]));
312         $this->assertCount(1, $DB->get_records('blog_external', ['userid' => $u2->id]));
313         $this->assertTrue($DB->record_exists('post', ['courseid' => $c1->id, 'userid' => $u1->id, 'module' => 'notes']));
315         // Delete for u1 in c2.
316         $appctxs = new approved_contextlist($u1, 'core_blog', [$c2ctx->id]);
317         provider::delete_data_for_user($appctxs);
318         $contextids = provider::get_contexts_for_userid($u1->id)->get_contextids();
319         $this->assertCount(3, $contextids);
320         $this->assertFalse(in_array($c2ctx->id, $contextids));
321         $this->assertCount(9, $DB->get_records('post', ['userid' => $u1->id]));
322         $this->assertCount(5, provider::get_contexts_for_userid($u2->id)->get_contextids());
323         $this->assertCount(4, $DB->get_records('post', ['userid' => $u2->id]));
324         $this->assertCount(1, $DB->get_records('blog_external', ['userid' => $u1->id]));
325         $this->assertCount(1, $DB->get_records('blog_external', ['userid' => $u2->id]));
326         $this->assertTrue($DB->record_exists('post', ['courseid' => $c1->id, 'userid' => $u1->id, 'module' => 'notes']));
328         // Delete for u1 in another user's context, shouldn't do anything.
329         provider::delete_data_for_user(new approved_contextlist($u1, 'core_blog', [$u2ctx->id]));
330         $contextids = provider::get_contexts_for_userid($u1->id)->get_contextids();
331         $this->assertCount(3, $contextids);
332         $this->assertFalse(in_array($c2ctx->id, $contextids));
333         $this->assertCount(9, $DB->get_records('post', ['userid' => $u1->id]));
334         $this->assertCount(5, provider::get_contexts_for_userid($u2->id)->get_contextids());
335         $this->assertCount(4, $DB->get_records('post', ['userid' => $u2->id]));
336         $this->assertCount(1, $DB->get_records('blog_external', ['userid' => $u1->id]));
337         $this->assertCount(1, $DB->get_records('blog_external', ['userid' => $u2->id]));
338         $this->assertTrue($DB->record_exists('post', ['courseid' => $c1->id, 'userid' => $u1->id, 'module' => 'notes']));
339         $this->assertCount(2, $DB->get_records('comments', ['userid' => $u1->id]));
340         $this->assertCount(1, $DB->get_records('comments', ['userid' => $u2->id]));
342         // Delete for u2 in u1 context.
343         provider::delete_data_for_user(new approved_contextlist($u2, 'core_blog', [$u1ctx->id]));
344         $contextids = provider::get_contexts_for_userid($u1->id)->get_contextids();
345         $this->assertCount(3, $contextids);
346         $this->assertFalse(in_array($c2ctx->id, $contextids));
347         $this->assertCount(9, $DB->get_records('post', ['userid' => $u1->id]));
348         $this->assertCount(4, provider::get_contexts_for_userid($u2->id)->get_contextids());
349         $this->assertCount(4, $DB->get_records('post', ['userid' => $u2->id]));
350         $this->assertCount(1, $DB->get_records('blog_external', ['userid' => $u1->id]));
351         $this->assertCount(1, $DB->get_records('blog_external', ['userid' => $u2->id]));
352         $this->assertTrue($DB->record_exists('post', ['courseid' => $c1->id, 'userid' => $u1->id, 'module' => 'notes']));
353         $this->assertCount(2, $DB->get_records('comments', ['userid' => $u1->id]));
354         $this->assertCount(0, $DB->get_records('comments', ['userid' => $u2->id]));
355         $this->assertCount(1, $DB->get_records('comments', ['userid' => $u3->id]));
357         // Delete for u1 in their context.
358         $appctxs = new approved_contextlist($u1, 'core_blog', [$u1ctx->id]);
359         provider::delete_data_for_user($appctxs);
360         $contextids = provider::get_contexts_for_userid($u1->id)->get_contextids();
361         $this->assertCount(0, $contextids);
362         $this->assertCount(1, $DB->get_records('post', ['userid' => $u1->id]));
363         $this->assertCount(4, provider::get_contexts_for_userid($u2->id)->get_contextids());
364         $this->assertCount(4, $DB->get_records('post', ['userid' => $u2->id]));
365         $this->assertCount(0, $DB->get_records('blog_external', ['userid' => $u1->id]));
366         $this->assertCount(1, $DB->get_records('blog_external', ['userid' => $u2->id]));
367         $this->assertCount(0, $DB->get_records('comments', ['userid' => $u1->id]));
368         $this->assertCount(0, $DB->get_records('comments', ['userid' => $u2->id]));
369         $this->assertCount(0, $DB->get_records('comments', ['userid' => $u3->id]));
370         $this->assertTrue($DB->record_exists('post', ['courseid' => $c1->id, 'userid' => $u1->id, 'module' => 'notes']));
371     }
373     /**
374      * Test provider delete_data_for_user with a context that contains no entries
375      *
376      * @return void
377      */
378     public function test_delete_data_for_user_empty_context() {
379         global $DB;
381         $user = $this->getDataGenerator()->create_user();
382         $course = $this->getDataGenerator()->create_course();
383         $context = context_course::instance($course->id);
385         // Create a blog entry for user, associated with course.
386         $entry = new blog_entry($this->create_post(['userid' => $user->id, 'courseid' => $course->id])->id);
387         $entry->add_association($context->id);
389         // Generate list of contexts for user.
390         $contexts = provider::get_contexts_for_userid($user->id);
391         $this->assertContains($context->id, $contexts->get_contextids());
393         // Now delete the blog entry.
394         $entry->delete();
396         // Try to delete user data using contexts obtained prior to entry deletion.
397         $contextlist = new approved_contextlist($user, 'core_blog', $contexts->get_contextids());
398         provider::delete_data_for_user($contextlist);
400         // Sanity check to ensure blog_associations is really empty.
401         $this->assertEmpty($DB->get_records('blog_association', ['contextid' => $context->id]));
402     }
404     public function test_delete_data_for_all_users_in_context() {
405         global $DB;
407         $dg = $this->getDataGenerator();
408         $c1 = $dg->create_course();
409         $c2 = $dg->create_course();
410         $cm1a = $dg->create_module('page', ['course' => $c1]);
411         $cm1b = $dg->create_module('page', ['course' => $c1]);
412         $cm2a = $dg->create_module('page', ['course' => $c2]);
413         $u1 = $dg->create_user();
414         $u2 = $dg->create_user();
416         $c1ctx = context_course::instance($c1->id);
417         $c2ctx = context_course::instance($c2->id);
418         $cm1actx = context_module::instance($cm1a->cmid);
419         $cm1bctx = context_module::instance($cm1b->cmid);
420         $cm2actx = context_module::instance($cm2a->cmid);
421         $u1ctx = context_user::instance($u1->id);
423         // Create two external blogs.
424         $extu1 = $this->create_external_blog(['userid' => $u1->id]);
425         $extu2 = $this->create_external_blog(['userid' => $u2->id]);
427         // Create a set of posts.
428         $entry = new blog_entry($this->create_post(['userid' => $u1->id])->id);
429         $entry = new blog_entry($this->create_post(['userid' => $u2->id])->id);
431         // Course associations for u1 and u2.
432         $entry = new blog_entry($this->create_post(['userid' => $u1->id, 'courseid' => $c1->id])->id);
433         $entry->add_association($c1ctx->id);
434         $entry = new blog_entry($this->create_post(['userid' => $u1->id, 'courseid' => $c1->id])->id);
435         $entry->add_association($c1ctx->id);
436         $entry = new blog_entry($this->create_post(['userid' => $u2->id, 'courseid' => $c1->id])->id);
437         $entry->add_association($c1ctx->id);
439         // Module associations for u1 and u2.
440         $entry = new blog_entry($this->create_post(['userid' => $u1->id, 'courseid' => $c1->id])->id);
441         $entry->add_association($cm1actx->id);
442         $entry = new blog_entry($this->create_post(['userid' => $u1->id, 'courseid' => $c1->id])->id);
443         $entry->add_association($cm1actx->id);
444         $entry = new blog_entry($this->create_post(['userid' => $u1->id, 'courseid' => $c1->id])->id);
445         $entry->add_association($cm1bctx->id);
446         $entry = new blog_entry($this->create_post(['userid' => $u2->id, 'courseid' => $c1->id])->id);
447         $entry->add_association($cm1actx->id);
449         // Foreign associations for u1, u2.
450         $entry = new blog_entry($this->create_post(['userid' => $u1->id, 'courseid' => $c2->id])->id);
451         $entry->add_association($c2ctx->id);
452         $entry = new blog_entry($this->create_post(['userid' => $u2->id, 'courseid' => $c2->id])->id);
453         $entry->add_association($c2ctx->id);
454         $entry = new blog_entry($this->create_post(['userid' => $u1->id, 'courseid' => $cm2a->id])->id);
455         $entry->add_association($cm2actx->id);
457         // Validate what we've got.
458         $contextids = provider::get_contexts_for_userid($u1->id)->get_contextids();
459         $this->assertCount(8, $DB->get_records('post', ['userid' => $u1->id]));
460         $this->assertCount(6, $contextids);
461         $this->assertTrue(in_array($c1ctx->id, $contextids));
462         $this->assertTrue(in_array($c2ctx->id, $contextids));
463         $this->assertTrue(in_array($cm1actx->id, $contextids));
464         $this->assertTrue(in_array($cm1bctx->id, $contextids));
465         $this->assertTrue(in_array($cm2actx->id, $contextids));
466         $this->assertTrue(in_array($u1ctx->id, $contextids));
467         $contextids = provider::get_contexts_for_userid($u2->id)->get_contextids();
468         $this->assertCount(4, $DB->get_records('post', ['userid' => $u2->id]));
469         $this->assertCount(4, $contextids);
470         $this->assertTrue(in_array($c1ctx->id, $contextids));
471         $this->assertTrue(in_array($c2ctx->id, $contextids));
472         $this->assertTrue(in_array($cm1actx->id, $contextids));
474         $this->assertCount(1, $DB->get_records('blog_external', ['userid' => $u1->id]));
475         $this->assertCount(1, $DB->get_records('blog_external', ['userid' => $u2->id]));
477         // Delete cm1a context.
478         provider::delete_data_for_all_users_in_context($cm1actx);
479         $contextids = provider::get_contexts_for_userid($u1->id)->get_contextids();
480         $this->assertCount(8, $DB->get_records('post', ['userid' => $u1->id]));
481         $this->assertCount(5, $contextids);
482         $this->assertTrue(in_array($c1ctx->id, $contextids));
483         $this->assertTrue(in_array($c2ctx->id, $contextids));
484         $this->assertFalse(in_array($cm1actx->id, $contextids));
485         $this->assertTrue(in_array($cm1bctx->id, $contextids));
486         $this->assertTrue(in_array($cm2actx->id, $contextids));
487         $this->assertTrue(in_array($u1ctx->id, $contextids));
488         $contextids = provider::get_contexts_for_userid($u2->id)->get_contextids();
489         $this->assertCount(4, $DB->get_records('post', ['userid' => $u2->id]));
490         $this->assertCount(3, $contextids);
491         $this->assertTrue(in_array($c1ctx->id, $contextids));
492         $this->assertTrue(in_array($c2ctx->id, $contextids));
493         $this->assertFalse(in_array($cm1actx->id, $contextids));
495         $this->assertCount(1, $DB->get_records('blog_external', ['userid' => $u1->id]));
496         $this->assertCount(1, $DB->get_records('blog_external', ['userid' => $u2->id]));
498         // Delete c1 context.
499         provider::delete_data_for_all_users_in_context($c1ctx);
500         $contextids = provider::get_contexts_for_userid($u1->id)->get_contextids();
501         $this->assertCount(8, $DB->get_records('post', ['userid' => $u1->id]));
502         $this->assertCount(4, $contextids);
503         $this->assertFalse(in_array($c1ctx->id, $contextids));
504         $this->assertTrue(in_array($c2ctx->id, $contextids));
505         $this->assertFalse(in_array($cm1actx->id, $contextids));
506         $this->assertTrue(in_array($cm1bctx->id, $contextids));
507         $this->assertTrue(in_array($cm2actx->id, $contextids));
508         $this->assertTrue(in_array($u1ctx->id, $contextids));
509         $contextids = provider::get_contexts_for_userid($u2->id)->get_contextids();
510         $this->assertCount(4, $DB->get_records('post', ['userid' => $u2->id]));
511         $this->assertCount(2, $contextids);
512         $this->assertFalse(in_array($c1ctx->id, $contextids));
513         $this->assertTrue(in_array($c2ctx->id, $contextids));
514         $this->assertFalse(in_array($cm1actx->id, $contextids));
516         $this->assertCount(1, $DB->get_records('blog_external', ['userid' => $u1->id]));
517         $this->assertCount(1, $DB->get_records('blog_external', ['userid' => $u2->id]));
519         // Delete u1 context.
520         provider::delete_data_for_all_users_in_context($u1ctx);
521         $contextids = provider::get_contexts_for_userid($u1->id)->get_contextids();
522         $this->assertCount(0, $DB->get_records('post', ['userid' => $u1->id]));
523         $this->assertCount(0, $contextids);
524         $this->assertFalse(in_array($c1ctx->id, $contextids));
525         $this->assertFalse(in_array($c2ctx->id, $contextids));
526         $this->assertFalse(in_array($cm1actx->id, $contextids));
527         $this->assertFalse(in_array($cm1bctx->id, $contextids));
528         $this->assertFalse(in_array($cm2actx->id, $contextids));
529         $this->assertFalse(in_array($u1ctx->id, $contextids));
530         $contextids = provider::get_contexts_for_userid($u2->id)->get_contextids();
531         $this->assertCount(4, $DB->get_records('post', ['userid' => $u2->id]));
532         $this->assertCount(2, $contextids);
533         $this->assertFalse(in_array($c1ctx->id, $contextids));
534         $this->assertTrue(in_array($c2ctx->id, $contextids));
535         $this->assertFalse(in_array($cm1actx->id, $contextids));
537         $this->assertCount(0, $DB->get_records('blog_external', ['userid' => $u1->id]));
538         $this->assertCount(1, $DB->get_records('blog_external', ['userid' => $u2->id]));
539     }
541     public function test_export_data_for_user() {
542         global $DB;
543         $dg = $this->getDataGenerator();
545         $c1 = $dg->create_course();
546         $cm1a = $dg->create_module('page', ['course' => $c1]);
547         $cm1b = $dg->create_module('page', ['course' => $c1]);
548         $u1 = $dg->create_user();
549         $u2 = $dg->create_user();
550         $c1ctx = context_course::instance($c1->id);
551         $cm1actx = context_module::instance($cm1a->cmid);
552         $cm1bctx = context_module::instance($cm1b->cmid);
553         $u1ctx = context_user::instance($u1->id);
554         $u2ctx = context_user::instance($u2->id);
556         // System entries.
557         $e1 = new blog_entry($this->create_post(['userid' => $u1->id, 'subject' => 'Hello world!',
558             'publishstate' => 'public'])->id);
559         $e2 = new blog_entry($this->create_post(['userid' => $u1->id, 'subject' => 'Hi planet!',
560             'publishstate' => 'draft'])->id);
561         $e3 = new blog_entry($this->create_post(['userid' => $u2->id, 'subject' => 'Ignore me'])->id);
563         // Create a blog entry associated with contexts.
564         $e4 = new blog_entry($this->create_post(['userid' => $u1->id, 'courseid' => $c1->id, 'subject' => 'Course assoc'])->id);
565         $e4->add_association($c1ctx->id);
566         $e4b = new blog_entry($this->create_post(['userid' => $u1->id, 'courseid' => $c1->id, 'subject' => 'Course assoc 2'])->id);
567         $e4b->add_association($c1ctx->id);
568         $e5 = new blog_entry($this->create_post(['userid' => $u1->id, 'courseid' => $c1->id, 'subject' => 'Module assoc',
569             'publishstate' => 'public'])->id);
570         $e5->add_association($cm1actx->id);
571         $e5b = new blog_entry($this->create_post(['userid' => $u1->id, 'courseid' => $c1->id, 'subject' => 'C/CM assoc'])->id);
572         $e5b->add_association($c1ctx->id);
573         $e5b->add_association($cm1actx->id);
574         $e6 = new blog_entry($this->create_post(['userid' => $u2->id, 'courseid' => $c1->id, 'subject' => 'Module assoc'])->id);
575         $e6->add_association($cm1actx->id);
577         // External blogs.
578         $ex1 = $this->create_external_blog(['userid' => $u1->id, 'url' => 'https://moodle.org', 'name' => 'Moodle RSS']);
579         $ex2 = $this->create_external_blog(['userid' => $u1->id, 'url' => 'https://example.com', 'name' => 'Example']);
580         $ex3 = $this->create_external_blog(['userid' => $u2->id, 'url' => 'https://example.com', 'name' => 'Ignore me']);
582         // Attach tags.
583         core_tag_tag::set_item_tags('core', 'post', $e1->id, $u1ctx, ['Beer', 'Golf']);
584         core_tag_tag::set_item_tags('core', 'blog_external', $ex1->id, $u1ctx, ['Car', 'Golf']);
585         core_tag_tag::set_item_tags('core', 'post', $e3->id, $u2ctx, ['ITG']);
586         core_tag_tag::set_item_tags('core', 'blog_external', $ex3->id, $u2ctx, ['DDR']);
587         core_tag_tag::set_item_tags('core', 'dontfindme', $e1->id, $u1ctx, ['Lone tag']);
589         // Attach comments.
590         $comment = $this->get_comment_object($u1ctx, $e1->id);
591         $this->setUser($u1);
592         $comment->add('Hello, it\'s me!');
593         $this->setUser($u2);
594         $comment->add('I was wondering if after');
595         $this->setUser($u1);
596         $comment = $this->get_comment_object($u2ctx, $e3->id);
597         $comment->add('All these years');
599         // Blog share a table with notes, so throw some data in there, it should not be exported.
600         $note = $dg->get_plugin_generator('core_notes')->create_instance(['userid' => $u1->id, 'courseid' => $c1->id,
601             'subject' => 'ABC']);
603         // Validate module associations.
604         $contextlist = new approved_contextlist($u1, 'core_blog', [$cm1actx->id]);
605         provider::export_user_data($contextlist);
606         $writer = writer::with_context($cm1actx);
607         $assocs = $writer->get_data([get_string('privacy:path:blogassociations', 'core_blog')]);
608         $this->assertCount(2, $assocs->associations);
609         $this->assertTrue(in_array('Module assoc', $assocs->associations));
610         $this->assertTrue(in_array('C/CM assoc', $assocs->associations));
612         // Validate course associations.
613         $contextlist = new approved_contextlist($u1, 'core_blog', [$c1ctx->id]);
614         provider::export_user_data($contextlist);
615         $writer = writer::with_context($c1ctx);
616         $assocs = $writer->get_data([get_string('privacy:path:blogassociations', 'core_blog')]);
617         $this->assertCount(3, $assocs->associations);
618         $this->assertTrue(in_array('Course assoc', $assocs->associations));
619         $this->assertTrue(in_array('Course assoc 2', $assocs->associations));
620         $this->assertTrue(in_array('C/CM assoc', $assocs->associations));
622         // Confirm we're not exporting for another user.
623         $contextlist = new approved_contextlist($u2, 'core_blog', [$u2ctx->id]);
624         $writer = writer::with_context($u1ctx);
625         $this->assertFalse($writer->has_any_data());
627         // Now export user context for u2.
628         $this->setUser($u2);
629         $contextlist = new approved_contextlist($u2, 'core_blog', [$u1ctx->id]);
630         provider::export_user_data($contextlist);
631         $writer = writer::with_context($u1ctx);
632         $data = $writer->get_data([get_string('blog', 'core_blog'), get_string('externalblogs', 'core_blog'),
633             $ex1->name . " ({$ex1->id})"]);
634         $this->assertEmpty($data);
635         $data = $writer->get_data([get_string('blog', 'core_blog'), get_string('blogentries', 'core_blog'),
636             $e2->subject . " ({$e2->id})"]);
637         $this->assertEmpty($data);
638         $data = $writer->get_data([get_string('blog', 'core_blog'), get_string('blogentries', 'core_blog'),
639             $e1->subject . " ({$e1->id})"]);
640         $this->assertEmpty($data);
641         $data = $writer->get_data([get_string('blog', 'core_blog'), get_string('blogentries', 'core_blog'),
642             $e1->subject . " ({$e1->id})", get_string('commentsubcontext', 'core_comment')]);
643         $this->assertNotEmpty($data);
644         $this->assertCount(1, $data->comments);
645         $comment = array_shift($data->comments);
646         $this->assertEquals('I was wondering if after', strip_tags($comment->content));
648         // Now export user context data.
649         $this->setUser($u1);
650         $contextlist = new approved_contextlist($u1, 'core_blog', [$u1ctx->id]);
651         writer::reset();
652         provider::export_user_data($contextlist);
653         $writer = writer::with_context($u1ctx);
655         // Check external blogs.
656         $externals = [$ex1, $ex2];
657         foreach ($externals as $ex) {
658             $data = $writer->get_data([get_string('blog', 'core_blog'), get_string('externalblogs', 'core_blog'),
659                 $ex->name . " ({$ex->id})"]);
660             $this->assertNotEmpty($data);
661             $this->assertEquals($data->name, $ex->name);
662             $this->assertEquals($data->description, $ex->description);
663             $this->assertEquals($data->url, $ex->url);
664             $this->assertEquals($data->filtertags, $ex->filtertags);
665             $this->assertEquals($data->modified, transform::datetime($ex->timemodified));
666             $this->assertEquals($data->lastfetched, transform::datetime($ex->timefetched));
667         }
669         // Check entries.
670         $entries = [$e1, $e2, $e4, $e4b, $e5, $e5b];
671         $associations = [
672             $e1->id => null,
673             $e2->id => null,
674             $e4->id => $c1ctx->get_context_name(),
675             $e4b->id => $c1ctx->get_context_name(),
676             $e5->id => $cm1actx->get_context_name(),
677             $e5b->id => [$c1ctx->get_context_name(), $cm1actx->get_context_name()],
678         ];
679         foreach ($entries as $e) {
680             $path = [get_string('blog', 'core_blog'), get_string('blogentries', 'core_blog'), $e->subject . " ({$e->id})"];
681             $data = $writer->get_data($path);
682             $this->assertNotEmpty($data);
683             $this->assertEquals($data->subject, $e->subject);
684             $this->assertEquals($data->summary, $e->summary);
685             $this->assertEquals($data->publishstate, provider::transform_publishstate($e->publishstate));
686             $this->assertEquals($data->created, transform::datetime($e->created));
687             $this->assertEquals($data->lastmodified, transform::datetime($e->lastmodified));
689             // We attached comments and tags to this entry.
690             $commentpath = array_merge($path, [get_string('commentsubcontext', 'core_comment')]);
691             if ($e->id == $e1->id) {
692                 $tagdata = $writer->get_related_data($path, 'tags');
693                 $this->assertEquals(['Beer', 'Golf'], $tagdata, '', 0, 10, true);
695                 $comments = $writer->get_data($commentpath);
696                 $this->assertCount(2, $comments->comments);
698                 $c0 = strip_tags($comments->comments[0]->content);
699                 $c1 = strip_tags($comments->comments[1]->content);
700                 $expectedcomments = [
701                     'Hello, it\'s me!',
702                     'I was wondering if after',
703                 ];
705                 $this->assertNotFalse(array_search($c0, $expectedcomments));
706                 $this->assertNotFalse(array_search($c1, $expectedcomments));
707                 $this->assertNotEquals($c0, $c1);
709             } else {
710                 $tagdata = $writer->get_related_data($path, 'tags');
711                 $this->assertEmpty($tagdata);
712                 $comments = $writer->get_data($commentpath);
713                 $this->assertEmpty($comments);
714             }
716             if (isset($associations[$e->id])) {
717                 $assocs = $associations[$e->id];
718                 if (is_array($assocs)) {
719                     $this->assertCount(count($assocs), $data->associations);
720                     foreach ($assocs as $v) {
721                         $this->assertTrue(in_array($v, $data->associations));
722                     }
723                 } else {
724                     $this->assertCount(1, $data->associations);
725                     $this->assertTrue(in_array($assocs, $data->associations));
726                 }
727             }
728         }
730         // The note was not exported.
731         $path = [get_string('blog', 'core_blog'), get_string('blogentries', 'core_blog'), "ABC ($note->id)"];
732         $this->assertEmpty($writer->get_data($path));
734     }
736     /**
737      * Test that deleting of blog information in a user context works as desired.
738      */
739     public function test_delete_data_for_users_user_context() {
740         global $DB;
742         $u1 = $this->getDataGenerator()->create_user();
743         $u2 = $this->getDataGenerator()->create_user();
744         $u3 = $this->getDataGenerator()->create_user();
745         $u4 = $this->getDataGenerator()->create_user();
746         $u5 = $this->getDataGenerator()->create_user();
748         $u1ctx = context_user::instance($u1->id);
750         $post = $this->create_post(['userid' => $u1->id]);
751         $entry = new blog_entry($post->id);
753         $comment = $this->get_comment_object($u1ctx, $entry->id);
754         $this->setUser($u1);
755         $comment->add('Hello, I created the blog');
756         $this->setUser($u2);
757         $comment->add('User 2 making a comment.');
758         $this->setUser($u3);
759         $comment->add('User 3 here.');
760         $this->setUser($u4);
761         $comment->add('User 4 is nice.');
762         $this->setUser($u5);
763         $comment->add('User 5 for the win.');
765         // This will only delete the comments made by user 4 and 5.
766         $this->assertCount(5, $DB->get_records('comments', ['contextid' => $u1ctx->id]));
767         $userlist = new \core_privacy\local\request\approved_userlist($u1ctx, 'core_blog', [$u4->id, $u5->id]);
768         provider::delete_data_for_users($userlist);
769         $this->assertCount(3, $DB->get_records('comments', ['contextid' => $u1ctx->id]));
770         $this->assertCount(1, $DB->get_records('post', ['userid' => $u1->id]));
772         // As the owner of the post is here everything will be deleted.
773         $userlist = new \core_privacy\local\request\approved_userlist($u1ctx, 'core_blog', [$u1->id, $u2->id]);
774         provider::delete_data_for_users($userlist);
775         $this->assertEmpty($DB->get_records('comments', ['contextid' => $u1ctx->id]));
776         $this->assertEmpty($DB->get_records('post', ['userid' => $u1->id]));
777     }
779     /**
780      * Test that deleting of an external blog in a user context works as desired.
781      */
782     public function test_delete_data_for_users_external_blog() {
783         global $DB;
785         $u1 = $this->getDataGenerator()->create_user();
786         $u2 = $this->getDataGenerator()->create_user();
788         $u1ctx = context_user::instance($u1->id);
789         $u2ctx = context_user::instance($u2->id);
791         $post = $this->create_external_blog(['userid' => $u1->id, 'url' => 'https://moodle.org', 'name' => 'Moodle RSS']);
792         $post2 = $this->create_external_blog(['userid' => $u2->id, 'url' => 'https://moodle.com', 'name' => 'Some other thing']);
794         // Check that we have two external blogs created.
795         $this->assertCount(2, $DB->get_records('blog_external'));
796         // This will only delete the external blog for user 1.
797         $userlist = new \core_privacy\local\request\approved_userlist($u1ctx, 'core_blog', [$u1->id, $u2->id]);
798         provider::delete_data_for_users($userlist);
799         $this->assertCount(1, $DB->get_records('blog_external'));
800     }
802     public function test_delete_data_for_users_course_and_module_context() {
803         global $DB;
805         $u1 = $this->getDataGenerator()->create_user();
806         $u2 = $this->getDataGenerator()->create_user();
807         $u3 = $this->getDataGenerator()->create_user();
808         $u4 = $this->getDataGenerator()->create_user();
809         $u5 = $this->getDataGenerator()->create_user();
811         $course = $this->getDataGenerator()->create_course();
812         $module = $this->getDataGenerator()->create_module('page', ['course' => $course]);
814         $u1ctx = context_user::instance($u1->id);
815         $u3ctx = context_user::instance($u3->id);
816         $c1ctx = context_course::instance($course->id);
817         $cm1ctx = context_module::instance($module->cmid);
819         // Blog with course association.
820         $post1 = $this->create_post(['userid' => $u1->id, 'courseid' => $course->id]);
821         $entry1 = new blog_entry($post1->id);
822         $entry1->add_association($c1ctx->id);
824         // Blog with module association.
825         $post2 = $this->create_post(['userid' => $u3->id, 'courseid' => $course->id]);
826         $entry2 = new blog_entry($post2->id);
827         $entry2->add_association($cm1ctx->id);
829         $comment = $this->get_comment_object($u1ctx, $entry1->id);
830         $this->setUser($u1);
831         $comment->add('Hello, I created the blog');
832         $this->setUser($u2);
833         $comment->add('comment on first course blog');
834         $this->setUser($u4);
835         $comment->add('user 4 on course blog');
837         $comment = $this->get_comment_object($u3ctx, $entry2->id);
838         $this->setUser($u3);
839         $comment->add('Hello, I created the module blog');
840         $this->setUser($u2);
841         $comment->add('I am commenting on both');
842         $this->setUser($u5);
843         $comment->add('User 5 for modules');
845         $this->assertCount(6, $DB->get_records('comments', ['component' => 'blog']));
846         $this->assertCount(2, $DB->get_records('post', ['courseid' => $course->id]));
847         $this->assertCount(2, $DB->get_records('blog_association'));
849         // When using the course or module context we are only removing the blog associations and the comments.
850         $userlist = new \core_privacy\local\request\approved_userlist($c1ctx, 'core_blog', [$u2->id, $u1->id, $u5->id]);
851         provider::delete_data_for_users($userlist);
852         // Only one of the blog_associations should be removed. Everything else should be as before.
853         $this->assertCount(6, $DB->get_records('comments', ['component' => 'blog']));
854         $this->assertCount(2, $DB->get_records('post', ['courseid' => $course->id]));
855         $this->assertCount(1, $DB->get_records('blog_association'));
857         $userlist = new \core_privacy\local\request\approved_userlist($cm1ctx, 'core_blog', [$u2->id, $u1->id, $u3->id]);
858         provider::delete_data_for_users($userlist);
859         // Now we've removed the other association.
860         $this->assertCount(6, $DB->get_records('comments', ['component' => 'blog']));
861         $this->assertCount(2, $DB->get_records('post', ['courseid' => $course->id]));
862         $this->assertEmpty($DB->get_records('blog_association'));
863     }
865     /**
866      * Create a blog post.
867      *
868      * @param array $params The params.
869      * @return stdClass
870      */
871     protected function create_post(array $params) {
872         global $DB;
873         $post = new stdClass();
874         $post->module = 'blog';
875         $post->courseid = 0;
876         $post->subject = 'the test post';
877         $post->summary = 'test post summary text';
878         $post->summaryformat = FORMAT_PLAIN;
879         $post->publishstate = 'site';
880         $post->created = time() - HOURSECS;
881         $post->lastmodified = time();
882         foreach ($params as $key => $value) {
883             $post->{$key} = $value;
884         }
886         $post->id = $DB->insert_record('post', $post);
887         return $post;
888     }
890     /**
891      * Create an extenral blog.
892      *
893      * @param array $params The params.
894      * @return stdClass
895      */
896     protected function create_external_blog(array $params) {
897         global $DB;
898         $post = new stdClass();
899         $post->name = 'test external';
900         $post->description = 'the description';
901         $post->url = 'http://example.com';
902         $post->filtertags = 'a, c, b';
903         $post->timefetched = time() - HOURSECS;
904         $post->timemodified = time();
905         foreach ($params as $key => $value) {
906             $post->{$key} = $value;
907         }
908         $post->id = $DB->insert_record('blog_external', $post);
909         return $post;
910     }
912     /**
913      * Get the comment area.
914      *
915      * @param context $context The context.
916      * @param int $itemid The item ID.
917      * @param string $component The component.
918      * @param string $area The area.
919      * @return comment
920      */
921     protected function get_comment_object(context $context, $itemid) {
922         $args = new stdClass();
923         $args->context = $context;
924         $args->course = get_course(SITEID);
925         $args->area = 'format_blog';
926         $args->itemid = $itemid;
927         $args->component = 'blog';
928         $comment = new comment($args);
929         $comment->set_post_permission(true);
930         return $comment;
931     }