456d7a63546ba01e30f9eaf143ecbdb874ce96dd
[moodle.git] / lib / editor / atto / tests / privacy_provider.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  * Unit tests for the editor_atto implementation of the privacy API.
19  *
20  * @package    editor_atto
21  * @category   test
22  * @copyright  2018 Andrew Nicols <andrew@nicols.co.uk>
23  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24  */
26 defined('MOODLE_INTERNAL') || die();
28 use \core_privacy\local\request\writer;
29 use \core_privacy\local\request\approved_contextlist;
30 use \editor_atto\privacy\provider;
32 /**
33  * Unit tests for the editor_atto implementation of the privacy API.
34  *
35  * @copyright  2018 Andrew Nicols <andrew@nicols.co.uk>
36  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
37  */
38 class editor_atto_privacy_testcase extends \core_privacy\tests\provider_testcase {
39     /**
40      * One test to check fetch and export of all drafts.
41      */
42     public function test_fetch_and_exports_drafts() {
43         global $USER;
44         $this->resetAfterTest();
46         // Create editor drafts in:
47         // - the system; and
48         // - a course; and
49         // - current user context; and
50         // - another user.
52         $systemcontext = \context_system::instance();
53         $course = $this->getDataGenerator()->create_course();
54         $coursecontext = \context_course::instance($course->id);
56         $usercontextids = [];
57         $user = $this->getDataGenerator()->create_user();
58         $this->setUser($user);
60         $usercontext = \context_user::instance($user->id);
61         $usercontextids[] = $usercontext->id;
62         $usercontextids[] = $systemcontext->id;
63         $usercontextids[] = $coursecontext->id;
65         // Add a fake inline image to the original post.
67         $userdraftintro = $this->create_editor_draft($usercontext, $user->id,
68                 'id_user_intro', 'text for test user at own context');
69         $userdraftdescription = $this->create_editor_draft($usercontext, $user->id,
70                 'id_user_description', 'text for test user at own context');
71         $systemuserdraftintro = $this->create_editor_draft($systemcontext, $user->id,
72                 'id_system_intro', 'text for test user at system context', 2);
73         $systemuserdraftdescription = $this->create_editor_draft($systemcontext, $user->id,
74                 'id_system_description', 'text for test user at system context', 4);
75         $coursedraftintro = $this->create_editor_draft($coursecontext, $user->id,
76                 'id_course_intro', 'text for test user at course context');
77         $coursedraftdescription = $this->create_editor_draft($coursecontext, $user->id,
78                 'id_course_description', 'text for test user at course context');
80         // Create some data as the other user too.
81         $otherusercontextids = [];
82         $otheruser = $this->getDataGenerator()->create_user();
83         $this->setUser($otheruser);
85         $otherusercontext = \context_user::instance($otheruser->id);
86         $otherusercontextids[] = $otherusercontext->id;
87         $otherusercontextids[] = $systemcontext->id;
88         $otherusercontextids[] = $coursecontext->id;
90         $otheruserdraftintro = $this->create_editor_draft($otherusercontext, $otheruser->id,
91                 'id_user_intro', 'text for other user at own context');
92         $otheruserdraftdescription = $this->create_editor_draft($otherusercontext, $otheruser->id,
93                 'id_user_description', 'text for other user at own context');
94         $systemotheruserdraftintro = $this->create_editor_draft($systemcontext, $otheruser->id,
95                 'id_system_intro', 'text for other user at system context');
96         $systemotheruserdraftdescription = $this->create_editor_draft($systemcontext, $otheruser->id,
97                 'id_system_description', 'text for other user at system context');
98         $courseotheruserdraftintro = $this->create_editor_draft($coursecontext, $otheruser->id,
99                 'id_course_intro', 'text for other user at course context');
100         $courseotheruserdraftdescription = $this->create_editor_draft($coursecontext, $otheruser->id,
101                 'id_course_description', 'text for other user at course context');
103         // Test as the original user.
104         // Get all context data for the original user.
105         $this->setUser($user);
106         $contextlist = provider::get_contexts_for_userid($user->id);
108         // There are three contexts in the list.
109         $this->assertCount(3, $contextlist);
111         // Check the list against the expected list of contexts.
112         foreach ($contextlist as $context) {
113             $this->assertContains($context->id, $usercontextids);
114         }
116         // Export the data for the system context.
117         // There should be two.
118         $this->export_context_data_for_user($user->id, $systemcontext, 'editor_atto');
119         $writer = \core_privacy\local\request\writer::with_context($systemcontext);
120         $this->assertTrue($writer->has_any_data());
122         $subcontextbase = [get_string('autosaves', 'editor_atto')];
124         // There should be an intro and description.
125         $intro = $writer->get_data(array_merge($subcontextbase, [$systemuserdraftintro->id]));
126         $fs = get_file_storage();
127         $this->assertEquals(
128                 format_text($systemuserdraftintro->drafttext, FORMAT_HTML, provider::get_filter_options()),
129                 $intro->drafttext
130             );
131         $this->assertCount(2, $writer->get_files(array_merge($subcontextbase, [$systemuserdraftintro->id])));
133         $description = $writer->get_data(array_merge($subcontextbase, [$systemuserdraftdescription->id]));
134         $this->assertEquals(
135                 format_text($systemuserdraftdescription->drafttext, FORMAT_HTML, provider::get_filter_options()),
136                 $description->drafttext
137             );
138         $this->assertCount(4, $writer->get_files(array_merge($subcontextbase, [$systemuserdraftdescription->id])));
139     }
141     /**
142      * Test delete_for_all_users_in_context.
143      */
144     public function test_delete_for_all_users_in_context() {
145         global $USER, $DB;
146         $this->resetAfterTest();
148         // Create editor drafts in:
149         // - the system; and
150         // - a course; and
151         // - current user context; and
152         // - another user.
154         $systemcontext = \context_system::instance();
155         $course = $this->getDataGenerator()->create_course();
156         $coursecontext = \context_course::instance($course->id);
158         $usercontextids = [];
159         $user = $this->getDataGenerator()->create_user();
160         $this->setUser($user);
162         $usercontext = \context_user::instance($user->id);
163         $usercontextids[] = $usercontext->id;
164         $usercontextids[] = $systemcontext->id;
165         $usercontextids[] = $coursecontext->id;
167         // Add a fake inline image to the original post.
169         $userdraftintro = $this->create_editor_draft($usercontext, $user->id,
170                 'id_user_intro', 'text for test user at own context');
171         $userdraftdescription = $this->create_editor_draft($usercontext, $user->id,
172                 'id_user_description', 'text for test user at own context');
173         $systemuserdraftintro = $this->create_editor_draft($systemcontext, $user->id,
174                 'id_system_intro', 'text for test user at system context', 2);
175         $systemuserdraftdescription = $this->create_editor_draft($systemcontext, $user->id,
176                 'id_system_description', 'text for test user at system context', 4);
177         $coursedraftintro = $this->create_editor_draft($coursecontext, $user->id,
178                 'id_course_intro', 'text for test user at course context');
179         $coursedraftdescription = $this->create_editor_draft($coursecontext, $user->id,
180                 'id_course_description', 'text for test user at course context');
182         // Create some data as the other user too.
183         $otherusercontextids = [];
184         $otheruser = $this->getDataGenerator()->create_user();
185         $this->setUser($otheruser);
187         $otherusercontext = \context_user::instance($otheruser->id);
188         $otherusercontextids[] = $otherusercontext->id;
189         $otherusercontextids[] = $systemcontext->id;
190         $otherusercontextids[] = $coursecontext->id;
192         $otheruserdraftintro = $this->create_editor_draft($otherusercontext, $otheruser->id,
193                 'id_user_intro', 'text for other user at own context');
194         $otheruserdraftdescription = $this->create_editor_draft($otherusercontext, $otheruser->id,
195                 'id_user_description', 'text for other user at own context');
196         $systemotheruserdraftintro = $this->create_editor_draft($systemcontext, $otheruser->id,
197                 'id_system_intro', 'text for other user at system context');
198         $systemotheruserdraftdescription = $this->create_editor_draft($systemcontext, $otheruser->id,
199                 'id_system_description', 'text for other user at system context');
200         $courseotheruserdraftintro = $this->create_editor_draft($coursecontext, $otheruser->id,
201                 'id_course_intro', 'text for other user at course context');
202         $courseotheruserdraftdescription = $this->create_editor_draft($coursecontext, $otheruser->id,
203                 'id_course_description', 'text for other user at course context');
205         // Test deletion of the user context.
206         $this->assertCount(2, $DB->get_records('editor_atto_autosave', ['contextid' => $usercontext->id]));
207         provider::delete_data_for_all_users_in_context($usercontext);
208         $this->assertCount(0, $DB->get_records('editor_atto_autosave', ['contextid' => $usercontext->id]));
210         // No other contexts should be removed.
211         $this->assertCount(2, $DB->get_records('editor_atto_autosave', ['contextid' => $otherusercontext->id]));
212         $this->assertCount(4, $DB->get_records('editor_atto_autosave', ['contextid' => $systemcontext->id]));
213         $this->assertCount(4, $DB->get_records('editor_atto_autosave', ['contextid' => $coursecontext->id]));
215         // Test deletion of the course contexts.
216         provider::delete_data_for_all_users_in_context($coursecontext);
217         $this->assertCount(0, $DB->get_records('editor_atto_autosave', ['contextid' => $coursecontext->id]));
218         $this->assertCount(2, $DB->get_records('editor_atto_autosave', ['contextid' => $otherusercontext->id]));
219         $this->assertCount(4, $DB->get_records('editor_atto_autosave', ['contextid' => $systemcontext->id]));
221         // Test deletion of the system contexts.
222         provider::delete_data_for_all_users_in_context($systemcontext);
223         $this->assertCount(0, $DB->get_records('editor_atto_autosave', ['contextid' => $systemcontext->id]));
224         $this->assertCount(2, $DB->get_records('editor_atto_autosave', ['contextid' => $otherusercontext->id]));
225     }
227     /**
228      * Test delete_for_all_users_in_context.
229      */
230     public function test_delete_for_user_in_contexts() {
231         global $USER, $DB;
232         $this->resetAfterTest();
234         // Create editor drafts in:
235         // - the system; and
236         // - a course; and
237         // - current user context; and
238         // - another user.
240         $systemcontext = \context_system::instance();
241         $course = $this->getDataGenerator()->create_course();
242         $coursecontext = \context_course::instance($course->id);
244         $usercontextids = [];
245         $user = $this->getDataGenerator()->create_user();
246         $this->setUser($user);
248         $usercontext = \context_user::instance($user->id);
249         $usercontextids[] = $usercontext->id;
250         $usercontextids[] = $systemcontext->id;
251         $usercontextids[] = $coursecontext->id;
253         // Add a fake inline image to the original post.
255         $userdraftintro = $this->create_editor_draft($usercontext, $user->id,
256                 'id_user_intro', 'text for test user at own context');
257         $userdraftdescription = $this->create_editor_draft($usercontext, $user->id,
258                 'id_user_description', 'text for test user at own context');
259         $systemuserdraftintro = $this->create_editor_draft($systemcontext, $user->id,
260                 'id_system_intro', 'text for test user at system context', 2);
261         $systemuserdraftdescription = $this->create_editor_draft($systemcontext, $user->id,
262                 'id_system_description', 'text for test user at system context', 4);
263         $coursedraftintro = $this->create_editor_draft($coursecontext, $user->id,
264                 'id_course_intro', 'text for test user at course context');
265         $coursedraftdescription = $this->create_editor_draft($coursecontext, $user->id,
266                 'id_course_description', 'text for test user at course context');
268         // Create some data as the other user too.
269         $otherusercontextids = [];
270         $otheruser = $this->getDataGenerator()->create_user();
271         $this->setUser($otheruser);
273         $otherusercontext = \context_user::instance($otheruser->id);
274         $otherusercontextids[] = $otherusercontext->id;
275         $otherusercontextids[] = $systemcontext->id;
276         $otherusercontextids[] = $coursecontext->id;
278         $otheruserdraftintro = $this->create_editor_draft($otherusercontext, $otheruser->id,
279                 'id_user_intro', 'text for other user at own context');
280         $otheruserdraftdescription = $this->create_editor_draft($otherusercontext, $otheruser->id,
281                 'id_user_description', 'text for other user at own context');
282         $systemotheruserdraftintro = $this->create_editor_draft($systemcontext, $otheruser->id,
283                 'id_system_intro', 'text for other user at system context');
284         $systemotheruserdraftdescription = $this->create_editor_draft($systemcontext, $otheruser->id,
285                 'id_system_description', 'text for other user at system context');
286         $courseotheruserdraftintro = $this->create_editor_draft($coursecontext, $otheruser->id,
287                 'id_course_intro', 'text for other user at course context');
288         $courseotheruserdraftdescription = $this->create_editor_draft($coursecontext, $otheruser->id,
289                 'id_course_description', 'text for other user at course context');
291         // Test deletion of all data for user in usercontext only.
292         $contextlist = new \core_privacy\tests\request\approved_contextlist(
293             \core_user::get_user($user->id),
294             'editor_atto',
295             [$usercontext->id]
296         );
297         provider::delete_data_for_user($contextlist);
298         $this->assertCount(0, $DB->get_records('editor_atto_autosave', ['contextid' => $usercontext->id]));
300         // No other contexts should be removed.
301         $this->assertCount(2, $DB->get_records('editor_atto_autosave', ['contextid' => $otherusercontext->id]));
302         $this->assertCount(4, $DB->get_records('editor_atto_autosave', ['contextid' => $systemcontext->id]));
303         $this->assertCount(4, $DB->get_records('editor_atto_autosave', ['contextid' => $coursecontext->id]));
305         // Test deletion of all data for user in course and system.
306         $contextlist = new \core_privacy\tests\request\approved_contextlist(
307             \core_user::get_user($user->id),
308             'editor_atto',
309             [$coursecontext->id, $systemcontext->id]
310         );
311         provider::delete_data_for_user($contextlist);
312         $this->assertCount(0, $DB->get_records('editor_atto_autosave', ['contextid' => $usercontext->id]));
313         $this->assertCount(2, $DB->get_records('editor_atto_autosave', ['contextid' => $otherusercontext->id]));
314         $this->assertCount(2, $DB->get_records('editor_atto_autosave', ['contextid' => $systemcontext->id]));
315         $this->assertCount(2, $DB->get_records('editor_atto_autosave', ['contextid' => $coursecontext->id]));
317         // Data for the other user should remain.
318         $this->assertCount(2, $DB->get_records('editor_atto_autosave', [
319             'contextid' => $coursecontext->id,
320             'userid' => $otheruser->id,
321         ]));
323         $this->assertCount(2, $DB->get_records('editor_atto_autosave', [
324             'contextid' => $systemcontext->id,
325             'userid' => $otheruser->id,
326         ]));
327     }
329     /**
330      * Test fetch and delete when another user has editted a draft in your
331      * user context. Edge case.
332      */
333     public function test_another_user_edits_you() {
334         global $USER, $DB;
335         $this->resetAfterTest();
337         $user = $this->getDataGenerator()->create_user();
338         $usercontext = \context_user::instance($user->id);
339         $otheruser = $this->getDataGenerator()->create_user();
340         $otherusercontext = \context_user::instance($otheruser->id);
341         $this->setUser($user);
343         $userdraftintro = $this->create_editor_draft($usercontext, $otheruser->id,
344                 'id_user_intro', 'text for test user at other context');
346         // Test as the owning user.
347         $this->setUser($user);
348         $contextlist = provider::get_contexts_for_userid($user->id);
349         $contexts = $contextlist->get_contexts();
350         $this->assertCount(1, $contexts);
351         $firstcontext = reset($contexts);
352         $this->assertEquals($usercontext, $firstcontext);
354         // Should have the data.
355         $this->export_context_data_for_user($user->id, $usercontext, 'editor_atto');
356         $writer = \core_privacy\local\request\writer::with_context($usercontext);
357         $this->assertTrue($writer->has_any_data());
359         $subcontext = [
360             get_string('autosaves', 'editor_atto'),
361             $userdraftintro->id,
362         ];
363         $data = $writer->get_data($subcontext);
364         $this->assertEquals(\core_privacy\local\request\transform::user($otheruser->id), $data->author);
366         $contextlist = new \core_privacy\tests\request\approved_contextlist(
367             \core_user::get_user($user->id),
368             'editor_atto',
369             [$usercontext->id]
370         );
373         // Deleting for this context should _not_ delete as the user does not own this draft (crazy edge case, remember).
374         provider::delete_data_for_user($contextlist);
375         $records = $DB->get_records('editor_atto_autosave');
376         $this->assertNotEmpty($records);
377         $this->assertCount(1, $records);
378         $firstrecord = reset($records);
379         $this->assertEquals($userdraftintro->id, $firstrecord->id);
380     }
382     /**
383      * Test fetch and delete when you have edited another user's context.
384      */
385     public function test_another_you_edit_different_user() {
386         global $USER, $DB;
387         $this->resetAfterTest();
389         $user = $this->getDataGenerator()->create_user();
390         $usercontext = \context_user::instance($user->id);
391         $otheruser = $this->getDataGenerator()->create_user();
392         $otherusercontext = \context_user::instance($otheruser->id);
393         $this->setUser($user);
395         $userdraftintro = $this->create_editor_draft($otherusercontext, $user->id,
396                 'id_user_intro', 'text for other user you just edited.');
398         // Test as the context owner.
399         $this->setUser($user);
400         $contextlist = provider::get_contexts_for_userid($user->id);
401         $contexts = $contextlist->get_contexts();
402         $this->assertCount(1, $contexts);
403         $firstcontext = reset($contexts);
404         $this->assertEquals($otherusercontext, $firstcontext);
406         // Should have the data.
407         $this->export_context_data_for_user($user->id, $otherusercontext, 'editor_atto');
408         $writer = \core_privacy\local\request\writer::with_context($otherusercontext);
409         $this->assertTrue($writer->has_any_data());
411         $subcontext = [
412             get_string('autosaves', 'editor_atto'),
413             $userdraftintro->id,
414         ];
415         $data = $writer->get_data($subcontext);
416         $this->assertFalse(isset($data->author));
418         $contextlist = new \core_privacy\tests\request\approved_contextlist(
419             \core_user::get_user($user->id),
420             'editor_atto',
421             [$otherusercontext->id]
422         );
423         provider::delete_data_for_user($contextlist);
424         $this->assertEmpty($DB->get_records('editor_atto_autosave'));
425     }
427    /**
428      * Create an editor draft.
429      *
430      * @param   \context    $context The context to create the draft for.
431      * @param   int         $userid The ID to create the draft for.
432      * @param   string      $elementid The elementid for the editor.
433      * @param   string      $text The text to write.
434      * @param   int         $filecount The number of files to create.
435      * @return  \stdClass   The editor draft.
436      */
437     protected function create_editor_draft(\context $context, $userid, $elementid, $text, $filecount = 0) {
438         global $DB;
440         $draftid = file_get_unused_draft_itemid();
441         $fs = get_file_storage();
443         for ($i = 0; $i < $filecount; $i++) {
444             $fs->create_file_from_string([
445                     'contextid' => $context->id,
446                     'component' => 'user',
447                     'filearea'  => 'draft',
448                     'itemid'    => $draftid,
449                     'filepath'  => '/',
450                     'filename'  => "example_{$i}.txt",
451                 ],
452             "Awesome example of a text file with id {$i} for {$context->id} and {$elementid}");
453         }
455         $id = $DB->insert_record('editor_atto_autosave', (object) [
456                 'elementid' => $elementid,
457                 'contextid' => $context->id,
458                 'userid' => $userid,
459                 'drafttext' => $text,
460                 'draftid' => $draftid,
461                 'pageinstance' => 'example_page_instance_' . rand(1, 1000),
462                 'timemodified' => time(),
464                 // Page hash doesn't matter for our purposes.
465                 'pagehash' => sha1("{$userid}/{$context->id}/{$elementid}/{$draftid}"),
466             ]);
468         return $DB->get_record('editor_atto_autosave', ['id' => $id]);
469     }