Proyectos de Subversion Moodle

Rev

Rev 1 | | Comparar con el anterior | Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 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/>.
16
 
17
/**
18
 * Unit Tests for the Moodle Content Writer.
19
 *
20
 * @package     core_privacy
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
 */
25
 
26
defined('MOODLE_INTERNAL') || die();
27
 
28
global $CFG;
29
 
30
use \core_privacy\local\request\writer;
31
use \core_privacy\local\request\moodle_content_writer;
32
 
33
/**
34
 * Tests for the \core_privacy API's moodle_content_writer functionality.
35
 *
36
 * @copyright   2018 Andrew Nicols <andrew@nicols.co.uk>
37
 * @license     http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
38
 * @coversDefaultClass \core_privacy\local\request\moodle_content_writer
39
 */
40
class moodle_content_writer_test extends advanced_testcase {
41
 
42
    /**
43
     * Test that exported data is saved correctly within the system context.
44
     *
45
     * @dataProvider export_data_provider
46
     * @param   \stdClass  $data Data
47
     * @covers ::export_data
48
     */
11 efrain 49
    public function test_export_data($data): void {
1 efrain 50
        $context = \context_system::instance();
51
        $subcontext = [];
52
 
53
        $writer = $this->get_writer_instance()
54
            ->set_context($context)
55
            ->export_data($subcontext, $data);
56
 
57
        $fileroot = $this->fetch_exported_content($writer);
58
 
59
        $contextpath = $this->get_context_path($context, $subcontext, 'data.json');
60
        $this->assertTrue($fileroot->hasChild($contextpath));
61
 
62
        $json = $fileroot->getChild($contextpath)->getContent();
63
        $expanded = json_decode($json);
64
        $this->assertEquals($data, $expanded);
65
    }
66
 
67
    /**
68
     * Test that exported data is saved correctly for context/subcontext.
69
     *
70
     * @dataProvider export_data_provider
71
     * @param   \stdClass  $data Data
72
     * @covers ::export_data
73
     */
11 efrain 74
    public function test_export_data_different_context($data): void {
1 efrain 75
        $context = \context_user::instance(\core_user::get_user_by_username('admin')->id);
76
        $subcontext = ['sub', 'context'];
77
 
78
        $writer = $this->get_writer_instance()
79
            ->set_context($context)
80
            ->export_data($subcontext, $data);
81
 
82
        $fileroot = $this->fetch_exported_content($writer);
83
 
84
        $contextpath = $this->get_context_path($context, $subcontext, 'data.json');
85
        $this->assertTrue($fileroot->hasChild($contextpath));
86
 
87
        $json = $fileroot->getChild($contextpath)->getContent();
88
        $expanded = json_decode($json);
89
        $this->assertEquals($data, $expanded);
90
    }
91
 
92
    /**
93
     * Test that exported is saved within the correct directory locations.
94
     *
95
     * @covers ::export_data
96
     */
11 efrain 97
    public function test_export_data_writes_to_multiple_context(): void {
1 efrain 98
        $subcontext = ['sub', 'context'];
99
 
100
        $systemcontext = \context_system::instance();
101
        $systemdata = (object) [
102
            'belongsto' => 'system',
103
        ];
104
        $usercontext = \context_user::instance(\core_user::get_user_by_username('admin')->id);
105
        $userdata = (object) [
106
            'belongsto' => 'user',
107
        ];
108
 
109
        $writer = $this->get_writer_instance();
110
 
111
        $writer
112
            ->set_context($systemcontext)
113
            ->export_data($subcontext, $systemdata);
114
 
115
        $writer
116
            ->set_context($usercontext)
117
            ->export_data($subcontext, $userdata);
118
 
119
        $fileroot = $this->fetch_exported_content($writer);
120
 
121
        $contextpath = $this->get_context_path($systemcontext, $subcontext, 'data.json');
122
        $this->assertTrue($fileroot->hasChild($contextpath));
123
 
124
        $json = $fileroot->getChild($contextpath)->getContent();
125
        $expanded = json_decode($json);
126
        $this->assertEquals($systemdata, $expanded);
127
 
128
        $contextpath = $this->get_context_path($usercontext, $subcontext, 'data.json');
129
        $this->assertTrue($fileroot->hasChild($contextpath));
130
 
131
        $json = $fileroot->getChild($contextpath)->getContent();
132
        $expanded = json_decode($json);
133
        $this->assertEquals($userdata, $expanded);
134
    }
135
 
136
    /**
137
     * Test that multiple writes to the same location cause the latest version to be written.
138
     *
139
     * @covers ::export_data
140
     */
11 efrain 141
    public function test_export_data_multiple_writes_same_context(): void {
1 efrain 142
        $subcontext = ['sub', 'context'];
143
 
144
        $systemcontext = \context_system::instance();
145
        $originaldata = (object) [
146
            'belongsto' => 'system',
147
        ];
148
 
149
        $newdata = (object) [
150
            'abc' => 'def',
151
        ];
152
 
153
        $writer = $this->get_writer_instance();
154
 
155
        $writer
156
            ->set_context($systemcontext)
157
            ->export_data($subcontext, $originaldata);
158
 
159
        $writer
160
            ->set_context($systemcontext)
161
            ->export_data($subcontext, $newdata);
162
 
163
        $fileroot = $this->fetch_exported_content($writer);
164
 
165
        $contextpath = $this->get_context_path($systemcontext, $subcontext, 'data.json');
166
        $this->assertTrue($fileroot->hasChild($contextpath));
167
 
168
        $json = $fileroot->getChild($contextpath)->getContent();
169
        $expanded = json_decode($json);
170
        $this->assertEquals($newdata, $expanded);
171
    }
172
 
173
    /**
174
     * Data provider for exporting user data.
175
     */
176
    public function export_data_provider() {
177
        return [
178
            'basic' => [
179
                (object) [
180
                    'example' => (object) [
181
                        'key' => 'value',
182
                    ],
183
                ],
184
            ],
185
        ];
186
    }
187
 
188
    /**
189
     * Test that metadata can be set.
190
     *
191
     * @dataProvider export_metadata_provider
192
     * @param   string  $key Key
193
     * @param   string  $value Value
194
     * @param   string  $description Description
195
     * @covers ::export_metadata
196
     */
11 efrain 197
    public function test_export_metadata($key, $value, $description): void {
1 efrain 198
        $context = \context_system::instance();
199
        $subcontext = ['a', 'b', 'c'];
200
 
201
        $writer = $this->get_writer_instance()
202
            ->set_context($context)
203
            ->export_metadata($subcontext, $key, $value, $description);
204
 
205
        $fileroot = $this->fetch_exported_content($writer);
206
 
207
        $contextpath = $this->get_context_path($context, $subcontext, 'metadata.json');
208
        $this->assertTrue($fileroot->hasChild($contextpath));
209
 
210
        $json = $fileroot->getChild($contextpath)->getContent();
211
        $expanded = json_decode($json);
212
        $this->assertTrue(isset($expanded->$key));
213
        $this->assertEquals($value, $expanded->$key->value);
214
        $this->assertEquals($description, $expanded->$key->description);
215
    }
216
 
217
    /**
218
     * Test that metadata can be set additively.
219
     *
220
     * @covers ::export_metadata
221
     */
11 efrain 222
    public function test_export_metadata_additive(): void {
1 efrain 223
        $context = \context_system::instance();
224
        $subcontext = [];
225
 
226
        $writer = $this->get_writer_instance();
227
 
228
        $writer
229
            ->set_context($context)
230
            ->export_metadata($subcontext, 'firstkey', 'firstvalue', 'firstdescription');
231
 
232
        $writer
233
            ->set_context($context)
234
            ->export_metadata($subcontext, 'secondkey', 'secondvalue', 'seconddescription');
235
 
236
        $fileroot = $this->fetch_exported_content($writer);
237
 
238
        $contextpath = $this->get_context_path($context, $subcontext, 'metadata.json');
239
        $this->assertTrue($fileroot->hasChild($contextpath));
240
 
241
        $json = $fileroot->getChild($contextpath)->getContent();
242
        $expanded = json_decode($json);
243
 
244
        $this->assertTrue(isset($expanded->firstkey));
245
        $this->assertEquals('firstvalue', $expanded->firstkey->value);
246
        $this->assertEquals('firstdescription', $expanded->firstkey->description);
247
 
248
        $this->assertTrue(isset($expanded->secondkey));
249
        $this->assertEquals('secondvalue', $expanded->secondkey->value);
250
        $this->assertEquals('seconddescription', $expanded->secondkey->description);
251
    }
252
 
253
    /**
254
     * Test that metadata can be set additively.
255
     *
256
     * @covers ::export_metadata
257
     */
11 efrain 258
    public function test_export_metadata_to_multiple_contexts(): void {
1 efrain 259
        $systemcontext = \context_system::instance();
260
        $usercontext = \context_user::instance(\core_user::get_user_by_username('admin')->id);
261
        $subcontext = [];
262
 
263
        $writer = $this->get_writer_instance();
264
 
265
        $writer
266
            ->set_context($systemcontext)
267
            ->export_metadata($subcontext, 'firstkey', 'firstvalue', 'firstdescription')
268
            ->export_metadata($subcontext, 'secondkey', 'secondvalue', 'seconddescription');
269
 
270
        $writer
271
            ->set_context($usercontext)
272
            ->export_metadata($subcontext, 'firstkey', 'alternativevalue', 'alternativedescription')
273
            ->export_metadata($subcontext, 'thirdkey', 'thirdvalue', 'thirddescription');
274
 
275
        $fileroot = $this->fetch_exported_content($writer);
276
 
277
        $systemcontextpath = $this->get_context_path($systemcontext, $subcontext, 'metadata.json');
278
        $this->assertTrue($fileroot->hasChild($systemcontextpath));
279
 
280
        $json = $fileroot->getChild($systemcontextpath)->getContent();
281
        $expanded = json_decode($json);
282
 
283
        $this->assertTrue(isset($expanded->firstkey));
284
        $this->assertEquals('firstvalue', $expanded->firstkey->value);
285
        $this->assertEquals('firstdescription', $expanded->firstkey->description);
286
        $this->assertTrue(isset($expanded->secondkey));
287
        $this->assertEquals('secondvalue', $expanded->secondkey->value);
288
        $this->assertEquals('seconddescription', $expanded->secondkey->description);
289
        $this->assertFalse(isset($expanded->thirdkey));
290
 
291
        $usercontextpath = $this->get_context_path($usercontext, $subcontext, 'metadata.json');
292
        $this->assertTrue($fileroot->hasChild($usercontextpath));
293
 
294
        $json = $fileroot->getChild($usercontextpath)->getContent();
295
        $expanded = json_decode($json);
296
 
297
        $this->assertTrue(isset($expanded->firstkey));
298
        $this->assertEquals('alternativevalue', $expanded->firstkey->value);
299
        $this->assertEquals('alternativedescription', $expanded->firstkey->description);
300
        $this->assertFalse(isset($expanded->secondkey));
301
        $this->assertTrue(isset($expanded->thirdkey));
302
        $this->assertEquals('thirdvalue', $expanded->thirdkey->value);
303
        $this->assertEquals('thirddescription', $expanded->thirdkey->description);
304
    }
305
 
306
    /**
307
     * Data provider for exporting user metadata.
308
     *
309
     * return   array
310
     */
311
    public function export_metadata_provider() {
312
        return [
313
            'basic' => [
314
                'key',
315
                'value',
316
                'This is a description',
317
            ],
318
            'valuewithspaces' => [
319
                'key',
320
                'value has mixed',
321
                'This is a description',
322
            ],
323
            'encodedvalue' => [
324
                'key',
325
                base64_encode('value has mixed'),
326
                'This is a description',
327
            ],
328
        ];
329
    }
330
 
331
    /**
332
     * Exporting a single stored_file should cause that file to be output in the files directory.
333
     *
334
     * @covers ::export_area_files
335
     */
11 efrain 336
    public function test_export_area_files(): void {
1 efrain 337
        $this->resetAfterTest();
338
        $context = \context_system::instance();
339
        $fs = get_file_storage();
340
 
341
        // Add two files to core_privacy::tests::0.
342
        $files = [];
343
        $file = (object) [
344
            'component' => 'core_privacy',
345
            'filearea' => 'tests',
346
            'itemid' => 0,
347
            'path' => '/',
348
            'name' => 'a.txt',
349
            'content' => 'Test file 0',
350
        ];
351
        $files[] = $file;
352
 
353
        $file = (object) [
354
            'component' => 'core_privacy',
355
            'filearea' => 'tests',
356
            'itemid' => 0,
357
            'path' => '/sub/',
358
            'name' => 'b.txt',
359
            'content' => 'Test file 1',
360
        ];
361
        $files[] = $file;
362
 
363
        // One with a different itemid.
364
        $file = (object) [
365
            'component' => 'core_privacy',
366
            'filearea' => 'tests',
367
            'itemid' => 1,
368
            'path' => '/',
369
            'name' => 'c.txt',
370
            'content' => 'Other',
371
        ];
372
        $files[] = $file;
373
 
374
        // One with a different filearea.
375
        $file = (object) [
376
            'component' => 'core_privacy',
377
            'filearea' => 'alternative',
378
            'itemid' => 0,
379
            'path' => '/',
380
            'name' => 'd.txt',
381
            'content' => 'Alternative',
382
        ];
383
        $files[] = $file;
384
 
385
        // One with a different component.
386
        $file = (object) [
387
            'component' => 'core',
388
            'filearea' => 'tests',
389
            'itemid' => 0,
390
            'path' => '/',
391
            'name' => 'e.txt',
392
            'content' => 'Other tests',
393
        ];
394
        $files[] = $file;
395
 
396
        foreach ($files as $file) {
397
            $record = [
398
                'contextid' => $context->id,
399
                'component' => $file->component,
400
                'filearea'  => $file->filearea,
401
                'itemid'    => $file->itemid,
402
                'filepath'  => $file->path,
403
                'filename'  => $file->name,
404
            ];
405
 
406
            $file->namepath = '/' . $file->filearea . '/' . ($file->itemid ?: '') . $file->path . $file->name;
407
            $file->storedfile = $fs->create_file_from_string($record, $file->content);
408
        }
409
 
410
        $writer = $this->get_writer_instance()
411
            ->set_context($context)
412
            ->export_area_files([], 'core_privacy', 'tests', 0);
413
 
414
        $fileroot = $this->fetch_exported_content($writer);
415
 
416
        $firstfiles = array_slice($files, 0, 2);
417
        foreach ($firstfiles as $file) {
418
            $contextpath = $this->get_context_path($context, ['_files'], $file->namepath);
419
            $this->assertTrue($fileroot->hasChild($contextpath));
420
            $this->assertEquals($file->content, $fileroot->getChild($contextpath)->getContent());
421
        }
422
 
423
        $otherfiles = array_slice($files, 2);
424
        foreach ($otherfiles as $file) {
425
            $contextpath = $this->get_context_path($context, ['_files'], $file->namepath);
426
            $this->assertFalse($fileroot->hasChild($contextpath));
427
        }
428
    }
429
 
430
    /**
431
     * Exporting a single stored_file should cause that file to be output in the files directory.
432
     *
433
     * @dataProvider    export_file_provider
434
     * @param   string  $filearea File area
435
     * @param   int     $itemid Item ID
436
     * @param   string  $filepath File path
437
     * @param   string  $filename File name
438
     * @param   string  $content Content
439
     *
440
     * @covers ::export_file
441
     */
11 efrain 442
    public function test_export_file($filearea, $itemid, $filepath, $filename, $content): void {
1 efrain 443
        $this->resetAfterTest();
444
        $context = \context_system::instance();
445
        $filenamepath = '/' . $filearea . '/' . ($itemid ? '_' . $itemid : '') . $filepath . $filename;
446
 
447
        $filerecord = array(
448
            'contextid' => $context->id,
449
            'component' => 'core_privacy',
450
            'filearea'  => $filearea,
451
            'itemid'    => $itemid,
452
            'filepath'  => $filepath,
453
            'filename'  => $filename,
454
        );
455
 
456
        $fs = get_file_storage();
457
        $file = $fs->create_file_from_string($filerecord, $content);
458
 
459
        $writer = $this->get_writer_instance()
460
            ->set_context($context)
461
            ->export_file([], $file);
462
 
463
        $fileroot = $this->fetch_exported_content($writer);
464
 
465
        $contextpath = $this->get_context_path($context, ['_files'], $filenamepath);
466
        $this->assertTrue($fileroot->hasChild($contextpath));
467
        $this->assertEquals($content, $fileroot->getChild($contextpath)->getContent());
468
    }
469
 
470
    /**
471
     * Data provider for the test_export_file function.
472
     *
473
     * @return  array
474
     */
475
    public function export_file_provider() {
476
        return [
477
            'basic' => [
478
                'intro',
479
                0,
480
                '/',
481
                'testfile.txt',
482
                'An example file content',
483
            ],
484
            'longpath' => [
485
                'attachments',
486
                '12',
487
                '/path/within/a/path/within/a/path/',
488
                'testfile.txt',
489
                'An example file content',
490
            ],
491
            'pathwithspaces' => [
492
                'intro',
493
                0,
494
                '/path with/some spaces/',
495
                'testfile.txt',
496
                'An example file content',
497
            ],
498
            'filewithspaces' => [
499
                'submission_attachments',
500
                1,
501
                '/path with/some spaces/',
502
                'test file.txt',
503
                'An example file content',
504
            ],
505
            'image' => [
506
                'intro',
507
                0,
508
                '/',
509
                'logo.png',
510
                file_get_contents(__DIR__ . '/fixtures/logo.png'),
511
            ],
512
            'UTF8' => [
513
                'submission_content',
514
                2,
515
                '/Žluťoučký/',
516
                'koníček.txt',
517
                'koníček',
518
            ],
519
            'EUC-JP' => [
520
                'intro',
521
                0,
522
                '/言語設定/',
523
                '言語設定.txt',
524
                '言語設定',
525
            ],
526
        ];
527
    }
528
 
529
    /**
530
     * User preferences can be exported against a user.
531
     *
532
     * @dataProvider    export_user_preference_provider
533
     * @param   string      $component  Component
534
     * @param   string      $key Key
535
     * @param   string      $value Value
536
     * @param   string      $desc Description
537
     * @covers ::export_user_preference
538
     */
11 efrain 539
    public function test_export_user_preference_context_user($component, $key, $value, $desc): void {
1 efrain 540
        $admin = \core_user::get_user_by_username('admin');
541
 
542
        $writer = $this->get_writer_instance();
543
 
544
        $context = \context_user::instance($admin->id);
545
        $writer = $this->get_writer_instance()
546
            ->set_context($context)
547
            ->export_user_preference($component, $key, $value, $desc);
548
 
549
        $fileroot = $this->fetch_exported_content($writer);
550
 
551
        $contextpath = $this->get_context_path($context, [get_string('userpreferences')], "{$component}.json");
552
        $this->assertTrue($fileroot->hasChild($contextpath));
553
 
554
        $json = $fileroot->getChild($contextpath)->getContent();
555
        $expanded = json_decode($json);
556
        $this->assertTrue(isset($expanded->$key));
557
        $data = $expanded->$key;
558
        $this->assertEquals($value, $data->value);
559
        $this->assertEquals($desc, $data->description);
560
    }
561
 
562
    /**
563
     * User preferences can be exported against a course category.
564
     *
565
     * @dataProvider    export_user_preference_provider
566
     * @param   string      $component  Component
567
     * @param   string      $key Key
568
     * @param   string      $value Value
569
     * @param   string      $desc Description
570
     * @covers ::export_user_preference
571
     */
11 efrain 572
    public function test_export_user_preference_context_coursecat($component, $key, $value, $desc): void {
1 efrain 573
        global $DB;
574
 
575
        $categories = $DB->get_records('course_categories');
576
        $firstcategory = reset($categories);
577
 
578
        $context = \context_coursecat::instance($firstcategory->id);
579
        $writer = $this->get_writer_instance()
580
            ->set_context($context)
581
            ->export_user_preference($component, $key, $value, $desc);
582
 
583
        $fileroot = $this->fetch_exported_content($writer);
584
 
585
        $contextpath = $this->get_context_path($context, [get_string('userpreferences')], "{$component}.json");
586
        $this->assertTrue($fileroot->hasChild($contextpath));
587
 
588
        $json = $fileroot->getChild($contextpath)->getContent();
589
        $expanded = json_decode($json);
590
        $this->assertTrue(isset($expanded->$key));
591
        $data = $expanded->$key;
592
        $this->assertEquals($value, $data->value);
593
        $this->assertEquals($desc, $data->description);
594
    }
595
 
596
    /**
597
     * User preferences can be exported against a course.
598
     *
599
     * @dataProvider    export_user_preference_provider
600
     * @param   string      $component  Component
601
     * @param   string      $key Key
602
     * @param   string      $value Value
603
     * @param   string      $desc Description
604
     * @covers ::export_user_preference
605
     */
11 efrain 606
    public function test_export_user_preference_context_course($component, $key, $value, $desc): void {
1 efrain 607
        global $DB;
608
 
609
        $this->resetAfterTest();
610
 
611
        $course = $this->getDataGenerator()->create_course();
612
 
613
        $context = \context_course::instance($course->id);
614
        $writer = $this->get_writer_instance()
615
            ->set_context($context)
616
            ->export_user_preference($component, $key, $value, $desc);
617
 
618
        $fileroot = $this->fetch_exported_content($writer);
619
 
620
        $contextpath = $this->get_context_path($context, [get_string('userpreferences')], "{$component}.json");
621
        $this->assertTrue($fileroot->hasChild($contextpath));
622
 
623
        $json = $fileroot->getChild($contextpath)->getContent();
624
        $expanded = json_decode($json);
625
        $this->assertTrue(isset($expanded->$key));
626
        $data = $expanded->$key;
627
        $this->assertEquals($value, $data->value);
628
        $this->assertEquals($desc, $data->description);
629
    }
630
 
631
    /**
632
     * User preferences can be exported against a module context.
633
     *
634
     * @dataProvider    export_user_preference_provider
635
     * @param   string      $component  Component
636
     * @param   string      $key Key
637
     * @param   string      $value Value
638
     * @param   string      $desc Description
639
     * @covers ::export_user_preference
640
     */
11 efrain 641
    public function test_export_user_preference_context_module($component, $key, $value, $desc): void {
1 efrain 642
        global $DB;
643
 
644
        $this->resetAfterTest();
645
 
646
        $course = $this->getDataGenerator()->create_course();
647
        $forum = $this->getDataGenerator()->create_module('forum', ['course' => $course->id]);
648
 
649
        $context = \context_module::instance($forum->cmid);
650
        $writer = $this->get_writer_instance()
651
            ->set_context($context)
652
            ->export_user_preference($component, $key, $value, $desc);
653
 
654
        $fileroot = $this->fetch_exported_content($writer);
655
 
656
        $contextpath = $this->get_context_path($context, [get_string('userpreferences')], "{$component}.json");
657
        $this->assertTrue($fileroot->hasChild($contextpath));
658
 
659
        $json = $fileroot->getChild($contextpath)->getContent();
660
        $expanded = json_decode($json);
661
        $this->assertTrue(isset($expanded->$key));
662
        $data = $expanded->$key;
663
        $this->assertEquals($value, $data->value);
664
        $this->assertEquals($desc, $data->description);
665
    }
666
 
667
    /**
668
     * User preferences can not be exported against a block context.
669
     *
670
     * @dataProvider    export_user_preference_provider
671
     * @param   string      $component  Component
672
     * @param   string      $key Key
673
     * @param   string      $value Value
674
     * @param   string      $desc Description
675
     * @covers ::export_user_preference
676
     */
11 efrain 677
    public function test_export_user_preference_context_block($component, $key, $value, $desc): void {
1 efrain 678
        global $DB;
679
 
680
        $blocks = $DB->get_records('block_instances');
681
        $block = reset($blocks);
682
 
683
        $context = \context_block::instance($block->id);
684
        $writer = $this->get_writer_instance()
685
            ->set_context($context)
686
            ->export_user_preference($component, $key, $value, $desc);
687
 
688
        $fileroot = $this->fetch_exported_content($writer);
689
 
690
        $contextpath = $this->get_context_path($context, [get_string('userpreferences')], "{$component}.json");
691
        $this->assertTrue($fileroot->hasChild($contextpath));
692
 
693
        $json = $fileroot->getChild($contextpath)->getContent();
694
        $expanded = json_decode($json);
695
        $this->assertTrue(isset($expanded->$key));
696
        $data = $expanded->$key;
697
        $this->assertEquals($value, $data->value);
698
        $this->assertEquals($desc, $data->description);
699
    }
700
 
701
    /**
702
     * Writing user preferences for two different blocks with the same name and
703
     * same parent context should generate two different context paths and export
704
     * files.
705
     *
706
     * @covers ::export_user_preference
707
     */
11 efrain 708
    public function test_export_user_preference_context_block_multiple_instances(): void {
1 efrain 709
        $this->resetAfterTest();
710
 
711
        $generator = $this->getDataGenerator();
712
        $course = $generator->create_course();
713
        $coursecontext = context_course::instance($course->id);
714
        $block1 = $generator->create_block('online_users', ['parentcontextid' => $coursecontext->id]);
715
        $block2 = $generator->create_block('online_users', ['parentcontextid' => $coursecontext->id]);
716
        $block1context = context_block::instance($block1->id);
717
        $block2context = context_block::instance($block2->id);
718
        $component = 'block';
719
        $desc = 'test preference';
720
        $block1key = 'block1key';
721
        $block1value = 'block1value';
722
        $block2key = 'block2key';
723
        $block2value = 'block2value';
724
        $writer = $this->get_writer_instance();
725
 
726
        // Confirm that we have two different block contexts with the same name
727
        // and the same parent context id.
728
        $this->assertNotEquals($block1context->id, $block2context->id);
729
        $this->assertEquals($block1context->get_context_name(), $block2context->get_context_name());
730
        $this->assertEquals($block1context->get_parent_context()->id, $block2context->get_parent_context()->id);
731
 
732
        $retrieveexport = function($context) use ($writer, $component) {
733
            $fileroot = $this->fetch_exported_content($writer);
734
 
735
            $contextpath = $this->get_context_path($context, [get_string('userpreferences')], "{$component}.json");
736
            $this->assertTrue($fileroot->hasChild($contextpath));
737
 
738
            $json = $fileroot->getChild($contextpath)->getContent();
739
            return json_decode($json);
740
        };
741
 
742
        $writer->set_context($block1context)
743
            ->export_user_preference($component, $block1key, $block1value, $desc);
744
        $writer->set_context($block2context)
745
            ->export_user_preference($component, $block2key, $block2value, $desc);
746
 
747
        $block1export = $retrieveexport($block1context);
748
        $block2export = $retrieveexport($block2context);
749
 
750
        // Confirm that the exports didn't write to the same file.
751
        $this->assertTrue(isset($block1export->$block1key));
752
        $this->assertTrue(isset($block2export->$block2key));
753
        $this->assertFalse(isset($block1export->$block2key));
754
        $this->assertFalse(isset($block2export->$block1key));
755
        $this->assertEquals($block1value, $block1export->$block1key->value);
756
        $this->assertEquals($block2value, $block2export->$block2key->value);
757
    }
758
 
759
    /**
760
     * User preferences can be exported against the system.
761
     *
762
     * @dataProvider    export_user_preference_provider
763
     * @param   string      $component  Component
764
     * @param   string      $key Key
765
     * @param   string      $value Value
766
     * @param   string      $desc Description
767
     *
768
     * @covers ::export_user_preference
769
     */
11 efrain 770
    public function test_export_user_preference_context_system($component, $key, $value, $desc): void {
1 efrain 771
        $context = \context_system::instance();
772
        $writer = $this->get_writer_instance()
773
            ->set_context($context)
774
            ->export_user_preference($component, $key, $value, $desc);
775
 
776
        $fileroot = $this->fetch_exported_content($writer);
777
 
778
        $contextpath = $this->get_context_path($context, [get_string('userpreferences')], "{$component}.json");
779
        $this->assertTrue($fileroot->hasChild($contextpath));
780
 
781
        $json = $fileroot->getChild($contextpath)->getContent();
782
        $expanded = json_decode($json);
783
        $this->assertTrue(isset($expanded->$key));
784
        $data = $expanded->$key;
785
        $this->assertEquals($value, $data->value);
786
        $this->assertEquals($desc, $data->description);
787
    }
788
 
789
    /**
790
     * User preferences can be exported against the system.
791
     *
792
     * @covers ::export_user_preference
793
     */
11 efrain 794
    public function test_export_multiple_user_preference_context_system(): void {
1 efrain 795
        $context = \context_system::instance();
796
        $writer = $this->get_writer_instance();
797
        $component = 'core_privacy';
798
 
799
        $writer
800
            ->set_context($context)
801
            ->export_user_preference($component, 'key1', 'val1', 'desc1')
802
            ->export_user_preference($component, 'key2', 'val2', 'desc2');
803
 
804
        $fileroot = $this->fetch_exported_content($writer);
805
 
806
        $contextpath = $this->get_context_path($context, [get_string('userpreferences')], "{$component}.json");
807
        $this->assertTrue($fileroot->hasChild($contextpath));
808
 
809
        $json = $fileroot->getChild($contextpath)->getContent();
810
        $expanded = json_decode($json);
811
 
812
        $this->assertTrue(isset($expanded->key1));
813
        $data = $expanded->key1;
814
        $this->assertEquals('val1', $data->value);
815
        $this->assertEquals('desc1', $data->description);
816
 
817
        $this->assertTrue(isset($expanded->key2));
818
        $data = $expanded->key2;
819
        $this->assertEquals('val2', $data->value);
820
        $this->assertEquals('desc2', $data->description);
821
    }
822
 
823
    /**
824
     * User preferences can be exported against the system.
825
     *
826
     * @covers ::export_user_preference
827
     */
11 efrain 828
    public function test_export_user_preference_replace(): void {
1 efrain 829
        $context = \context_system::instance();
830
        $writer = $this->get_writer_instance();
831
        $component = 'core_privacy';
832
        $key = 'key';
833
 
834
        $writer
835
            ->set_context($context)
836
            ->export_user_preference($component, $key, 'val1', 'desc1');
837
 
838
        $writer
839
            ->set_context($context)
840
            ->export_user_preference($component, $key, 'val2', 'desc2');
841
 
842
        $fileroot = $this->fetch_exported_content($writer);
843
 
844
        $contextpath = $this->get_context_path($context, [get_string('userpreferences')], "{$component}.json");
845
        $this->assertTrue($fileroot->hasChild($contextpath));
846
 
847
        $json = $fileroot->getChild($contextpath)->getContent();
848
        $expanded = json_decode($json);
849
 
850
        $this->assertTrue(isset($expanded->$key));
851
        $data = $expanded->$key;
852
        $this->assertEquals('val2', $data->value);
853
        $this->assertEquals('desc2', $data->description);
854
    }
855
 
856
    /**
857
     * Provider for various user preferences.
858
     *
859
     * @return  array
860
     */
861
    public function export_user_preference_provider() {
862
        return [
863
            'basic' => [
864
                'core_privacy',
865
                'onekey',
866
                'value',
867
                'description',
868
            ],
869
            'encodedvalue' => [
870
                'core_privacy',
871
                'donkey',
872
                base64_encode('value'),
873
                'description',
874
            ],
875
            'long description' => [
876
                'core_privacy',
877
                'twokey',
878
                'value',
879
                'This is a much longer description which actually states what this is used for. Blah blah blah.',
880
            ],
881
        ];
882
    }
883
 
884
    /**
885
     * Test that exported data is human readable.
886
     *
887
     * @dataProvider unescaped_unicode_export_provider
888
     * @param string $text
889
     * @covers ::export_data
890
     */
11 efrain 891
    public function test_export_data_unescaped_unicode($text): void {
1 efrain 892
        $context = \context_system::instance();
893
        $subcontext = [];
894
        $data = (object) ['key' => $text];
895
 
896
        $writer = $this->get_writer_instance()
897
                ->set_context($context)
898
                ->export_data($subcontext, $data);
899
 
900
        $fileroot = $this->fetch_exported_content($writer);
901
 
902
        $contextpath = $this->get_context_path($context, $subcontext, 'data.json');
903
 
904
        $json = $fileroot->getChild($contextpath)->getContent();
905
        $this->assertMatchesRegularExpression("/$text/", $json);
906
 
907
        $expanded = json_decode($json);
908
        $this->assertEquals($data, $expanded);
909
    }
910
 
911
    /**
912
     * Test that exported metadata is human readable.
913
     *
914
     * @dataProvider unescaped_unicode_export_provider
915
     * @param string $text
916
     * @covers ::export_metadata
917
     */
11 efrain 918
    public function test_export_metadata_unescaped_unicode($text): void {
1 efrain 919
        $context = \context_system::instance();
920
        $subcontext = ['a', 'b', 'c'];
921
 
922
        $writer = $this->get_writer_instance()
923
                ->set_context($context)
924
                ->export_metadata($subcontext, $text, $text, $text);
925
 
926
        $fileroot = $this->fetch_exported_content($writer);
927
 
928
        $contextpath = $this->get_context_path($context, $subcontext, 'metadata.json');
929
 
930
        $json = $fileroot->getChild($contextpath)->getContent();
931
        $this->assertMatchesRegularExpression("/$text.*$text.*$text/is", $json);
932
 
933
        $expanded = json_decode($json);
934
        $this->assertTrue(isset($expanded->$text));
935
        $this->assertEquals($text, $expanded->$text->value);
936
        $this->assertEquals($text, $expanded->$text->description);
937
    }
938
 
939
    /**
940
     * Test that exported related data is human readable.
941
     *
942
     * @dataProvider unescaped_unicode_export_provider
943
     * @param string $text
944
     * @covers ::export_related_data
945
     */
11 efrain 946
    public function test_export_related_data_unescaped_unicode($text): void {
1 efrain 947
        $context = \context_system::instance();
948
        $subcontext = [];
949
        $data = (object) ['key' => $text];
950
 
951
        $writer = $this->get_writer_instance()
952
                ->set_context($context)
953
                ->export_related_data($subcontext, 'name', $data);
954
 
955
        $fileroot = $this->fetch_exported_content($writer);
956
 
957
        $contextpath = $this->get_context_path($context, $subcontext, 'name.json');
958
 
959
        $json = $fileroot->getChild($contextpath)->getContent();
960
        $this->assertMatchesRegularExpression("/$text/", $json);
961
 
962
        $expanded = json_decode($json);
963
        $this->assertEquals($data, $expanded);
964
    }
965
 
966
    /**
967
     * Test that exported related data name is properly cleaned
968
     *
969
     * @covers ::export_related_data
970
     */
11 efrain 971
    public function test_export_related_data_clean_name(): void {
1 efrain 972
        $context = \context_system::instance();
973
        $subcontext = [];
974
        $data = (object) ['foo' => 'bar'];
975
 
976
        $name = 'Bad/chars:>';
977
 
978
        $writer = $this->get_writer_instance()
979
            ->set_context($context)
980
            ->export_related_data($subcontext, $name, $data);
981
 
982
        $nameclean = clean_param($name, PARAM_FILE);
983
 
984
        $contextpath = $this->get_context_path($context, $subcontext, "{$nameclean}.json");
985
        $expectedpath = "System _.{$context->id}/Badchars.json";
986
        $this->assertEquals($expectedpath, $contextpath);
987
 
988
        $fileroot = $this->fetch_exported_content($writer);
989
        $json = $fileroot->getChild($contextpath)->getContent();
990
 
991
        $this->assertEquals($data, json_decode($json));
992
    }
993
 
994
    /**
995
     * Test that exported user preference is human readable.
996
     *
997
     * @dataProvider unescaped_unicode_export_provider
998
     * @param string $text
999
     * @covers ::export_user_preference
1000
     */
11 efrain 1001
    public function test_export_user_preference_unescaped_unicode($text): void {
1 efrain 1002
        $context = \context_system::instance();
1003
        $component = 'core_privacy';
1004
 
1005
        $writer = $this->get_writer_instance()
1006
                ->set_context($context)
1007
                ->export_user_preference($component, $text, $text, $text);
1008
 
1009
        $fileroot = $this->fetch_exported_content($writer);
1010
 
1011
        $contextpath = $this->get_context_path($context, [get_string('userpreferences')], "{$component}.json");
1012
 
1013
        $json = $fileroot->getChild($contextpath)->getContent();
1014
        $this->assertMatchesRegularExpression("/$text.*$text.*$text/is", $json);
1015
 
1016
        $expanded = json_decode($json);
1017
        $this->assertTrue(isset($expanded->$text));
1018
        $this->assertEquals($text, $expanded->$text->value);
1019
        $this->assertEquals($text, $expanded->$text->description);
1020
    }
1021
 
1022
    /**
1023
     * Provider for various user preferences.
1024
     *
1025
     * @return array
1026
     */
1027
    public function unescaped_unicode_export_provider() {
1028
        return [
1029
            'Unicode' => ['ةكءيٓ‌پچژکگیٹڈڑہھےâîûğŞAaÇÖáǽ你好!'],
1030
        ];
1031
    }
1032
 
1033
    /**
1034
     * Test that exported data subcontext is properly cleaned
1035
     *
1036
     * @covers ::export_data
1037
     */
11 efrain 1038
    public function test_export_data_clean_subcontext(): void {
1 efrain 1039
        $context = \context_system::instance();
1040
        $subcontext = ['Something/weird', 'More/bad:>', 'Bad&chars:>'];
1041
        $data = (object) ['foo' => 'bar'];
1042
 
1043
        $writer = $this->get_writer_instance()
1044
            ->set_context($context)
1045
            ->export_data($subcontext, $data);
1046
 
1047
        $contextpath = $this->get_context_path($context, $subcontext, 'data.json');
1048
        $expectedpath = "System _.{$context->id}/Something/weird/More/bad/Badchars/data.json";
1049
        $this->assertEquals($expectedpath, $contextpath);
1050
 
1051
        $fileroot = $this->fetch_exported_content($writer);
1052
        $json = $fileroot->getChild($contextpath)->getContent();
1053
 
1054
        $this->assertEquals($data, json_decode($json));
1055
    }
1056
 
1057
    /**
1058
     * Test that exported data is shortened when exceeds the limit.
1059
     *
1060
     * @dataProvider long_filename_provider
1061
     * @param string $longtext
1062
     * @param string $expected
1063
     * @param string $text
1064
     *
1065
     * @covers ::export_data
1066
     */
11 efrain 1067
    public function test_export_data_long_filename($longtext, $expected, $text): void {
1 efrain 1068
        $context = \context_system::instance();
1069
        $subcontext = [$longtext];
1070
        $data = (object) ['key' => $text];
1071
 
1072
        $writer = $this->get_writer_instance()
1073
                ->set_context($context)
1074
                ->export_data($subcontext, $data);
1075
 
1076
        $fileroot = $this->fetch_exported_content($writer);
1077
 
1078
        $contextpath = $this->get_context_path($context, $subcontext, 'data.json');
1079
        $expectedpath = "System _.{$context->id}/{$expected}/data.json";
1080
        $this->assertEquals($expectedpath, $contextpath);
1081
 
1082
        $json = $fileroot->getChild($contextpath)->getContent();
1083
        $this->assertMatchesRegularExpression("/$text/", $json);
1084
 
1085
        $expanded = json_decode($json);
1086
        $this->assertEquals($data, $expanded);
1087
    }
1088
 
1089
    /**
1090
     * Test that exported related data is shortened when exceeds the limit.
1091
     *
1092
     * @dataProvider long_filename_provider
1093
     * @param string $longtext
1094
     * @param string $expected
1095
     * @param string $text
1096
     *
1097
     * @covers ::export_related_data
1098
     */
11 efrain 1099
    public function test_export_related_data_long_filename($longtext, $expected, $text): void {
1 efrain 1100
        $context = \context_system::instance();
1101
        $subcontext = [$longtext];
1102
        $data = (object) ['key' => $text];
1103
 
1104
        $writer = $this->get_writer_instance()
1105
                ->set_context($context)
1106
                ->export_related_data($subcontext, 'name', $data);
1107
 
1108
        $fileroot = $this->fetch_exported_content($writer);
1109
 
1110
        $contextpath = $this->get_context_path($context, $subcontext, 'name.json');
1111
        $expectedpath = "System _.{$context->id}/{$expected}/name.json";
1112
        $this->assertEquals($expectedpath, $contextpath);
1113
 
1114
        $json = $fileroot->getChild($contextpath)->getContent();
1115
        $this->assertMatchesRegularExpression("/$text/", $json);
1116
 
1117
        $expanded = json_decode($json);
1118
        $this->assertEquals($data, $expanded);
1119
    }
1120
 
1121
    /**
1122
     * Test that exported metadata is shortened when exceeds the limit.
1123
     *
1124
     * @dataProvider long_filename_provider
1125
     * @param string $longtext
1126
     * @param string $expected
1127
     * @param string $text
1128
     */
11 efrain 1129
    public function test_export_metadata_long_filename($longtext, $expected, $text): void {
1 efrain 1130
        $context = \context_system::instance();
1131
        $subcontext = [$longtext];
1132
        $data = (object) ['key' => $text];
1133
 
1134
        $writer = $this->get_writer_instance()
1135
                ->set_context($context)
1136
                ->export_metadata($subcontext, $text, $text, $text);
1137
 
1138
        $fileroot = $this->fetch_exported_content($writer);
1139
 
1140
        $contextpath = $this->get_context_path($context, $subcontext, 'metadata.json');
1141
        $expectedpath = "System _.{$context->id}/{$expected}/metadata.json";
1142
        $this->assertEquals($expectedpath, $contextpath);
1143
 
1144
        $json = $fileroot->getChild($contextpath)->getContent();
1145
        $this->assertMatchesRegularExpression("/$text.*$text.*$text/is", $json);
1146
 
1147
        $expanded = json_decode($json);
1148
        $this->assertTrue(isset($expanded->$text));
1149
        $this->assertEquals($text, $expanded->$text->value);
1150
        $this->assertEquals($text, $expanded->$text->description);
1151
    }
1152
 
1153
    /**
1154
     * Test that exported user preference is shortened when exceeds the limit.
1155
     *
1156
     * @dataProvider long_filename_provider
1157
     * @param string $longtext
1158
     * @param string $expected
1159
     * @param string $text
1160
     */
11 efrain 1161
    public function test_export_user_preference_long_filename($longtext, $expected, $text): void {
1 efrain 1162
        $this->resetAfterTest();
1163
 
1164
        if (!array_key_exists('json', core_filetypes::get_types())) {
1165
            // Add json as mime type to avoid lose the extension when shortening filenames.
1166
            core_filetypes::add_type('json', 'application/json', 'archive', [], '', 'JSON file archive');
1167
        }
1168
        $context = \context_system::instance();
1169
        $expectedpath = "System _.{$context->id}/User preferences/{$expected}.json";
1170
 
1171
        $component = $longtext;
1172
 
1173
        $writer = $this->get_writer_instance()
1174
                ->set_context($context)
1175
                ->export_user_preference($component, $text, $text, $text);
1176
 
1177
        $fileroot = $this->fetch_exported_content($writer);
1178
 
1179
        $contextpath = $this->get_context_path($context, [get_string('userpreferences')], "{$component}.json");
1180
        $this->assertEquals($expectedpath, $contextpath);
1181
 
1182
        $json = $fileroot->getChild($contextpath)->getContent();
1183
        $this->assertMatchesRegularExpression("/$text.*$text.*$text/is", $json);
1184
 
1185
        $expanded = json_decode($json);
1186
        $this->assertTrue(isset($expanded->$text));
1187
        $this->assertEquals($text, $expanded->$text->value);
1188
        $this->assertEquals($text, $expanded->$text->description);
1189
    }
1190
 
1191
    /**
1192
     * Provider for long filenames.
1193
     *
1194
     * @return array
1195
     */
1196
    public function long_filename_provider() {
1197
        return [
1198
            'More than 100 characters' => [
1199
                'Etiam sit amet dui vel leo blandit viverra. Proin viverra suscipit velit. Aenean efficitur suscipit nibh nec suscipit',
1200
                'Etiam sit amet dui vel leo blandit viverra. Proin viverra suscipit velit. Aenean effici - 22f7a5030d',
1201
                'value',
1202
            ],
1203
        ];
1204
    }
1205
 
1206
    /**
1207
     * Get a fresh content writer.
1208
     *
1209
     * @return  moodle_content_writer
1210
     */
1211
    public function get_writer_instance() {
1212
        $factory = $this->createMock(writer::class);
1213
        return new moodle_content_writer($factory);
1214
    }
1215
 
1216
    /**
1217
     * Fetch the exported content for inspection.
1218
     *
1219
     * @param   moodle_content_writer   $writer
1220
     * @return  \org\bovigo\vfs\vfsStreamDirectory
1221
     */
1222
    protected function fetch_exported_content(moodle_content_writer $writer) {
1223
        $export = $writer
1224
            ->set_context(\context_system::instance())
1225
            ->finalise_content();
1226
 
1227
        $fileroot = \org\bovigo\vfs\vfsStream::setup('root');
1228
 
1229
        $target = \org\bovigo\vfs\vfsStream::url('root');
1230
        $fp = get_file_packer();
1231
        $fp->extract_to_pathname($export, $target);
1232
 
1233
        return $fileroot;
1234
    }
1235
 
1236
    /**
1237
     * Determine the path for the current context.
1238
     *
1239
     * Note: This is a wrapper around the real function.
1240
     *
1241
     * @param   \context        $context    The context being written
1242
     * @param   array           $subcontext The subcontext path
1243
     * @param   string          $name       THe name of the file target
1244
     * @return  array                       The context path.
1245
     */
1246
    protected function get_context_path($context, $subcontext = null, $name = '') {
1247
        $rc = new ReflectionClass(moodle_content_writer::class);
1248
        $writer = $this->get_writer_instance();
1249
        $writer->set_context($context);
1250
 
1251
        if (null === $subcontext) {
1252
            $rcm = $rc->getMethod('get_context_path');
1253
            $path = $rcm->invoke($writer);
1254
        } else {
1255
            $rcm = $rc->getMethod('get_path');
1256
            $path = $rcm->invoke($writer, $subcontext, $name);
1257
        }
1258
 
1259
        // PHPUnit uses mikey179/vfsStream which is a stream wrapper for a virtual file system that uses '/'
1260
        // as the directory separator.
1261
        $path = str_replace(DIRECTORY_SEPARATOR, '/', $path);
1262
 
1263
        return $path;
1264
    }
1265
 
1266
    /**
1267
     * Test correct rewriting of @@PLUGINFILE@@ in the exported contents.
1268
     *
1269
     * @dataProvider rewrite_pluginfile_urls_provider
1270
     * @param string $filearea The filearea within that component.
1271
     * @param int $itemid Which item those files belong to.
1272
     * @param string $input Raw text as stored in the database.
1273
     * @param string $expectedoutput Expected output of URL rewriting.
1274
     * @covers ::rewrite_pluginfile_urls
1275
     */
11 efrain 1276
    public function test_rewrite_pluginfile_urls($filearea, $itemid, $input, $expectedoutput): void {
1 efrain 1277
 
1278
        $writer = $this->get_writer_instance();
1279
        $writer->set_context(\context_system::instance());
1280
 
1281
        $realoutput = $writer->rewrite_pluginfile_urls([], 'core_test', $filearea, $itemid, $input);
1282
 
1283
        $this->assertEquals($expectedoutput, $realoutput);
1284
    }
1285
 
1286
    /**
1287
     * Provides testable sample data for {@link self::test_rewrite_pluginfile_urls()}.
1288
     *
1289
     * @return array
1290
     */
1291
    public function rewrite_pluginfile_urls_provider() {
1292
        return [
1293
            'nullcontent' => [
1294
                'intro',
1295
                0,
1296
                null,
1297
                '',
1298
            ],
1299
            'emptycontent' => [
1300
                'intro',
1301
                0,
1302
                '',
1303
                '',
1304
            ],
1305
            'zeroitemid' => [
1306
                'intro',
1307
                0,
1308
                '<p><img src="@@PLUGINFILE@@/hello.gif" /></p>',
1309
                '<p><img src="System _.1/_files/intro/hello.gif" /></p>',
1310
            ],
1311
            'nonzeroitemid' => [
1312
                'submission_content',
1313
                34,
1314
                '<p><img src="@@PLUGINFILE@@/first.png" alt="First" /></p>',
1315
                '<p><img src="System _.1/_files/submission_content/_34/first.png" alt="First" /></p>',
1316
            ],
1317
            'withfilepath' => [
1318
                'post_content',
1319
                9889,
1320
                '<a href="@@PLUGINFILE@@/embedded/docs/muhehe.exe">Click here!</a>',
1321
                '<a href="System _.1/_files/post_content/_9889/embedded/docs/muhehe.exe">Click here!</a>',
1322
            ],
1323
        ];
1324
    }
1325
 
11 efrain 1326
    public function test_export_html_functions(): void {
1 efrain 1327
        $this->resetAfterTest();
1328
 
1329
        $data = (object) ['key' => 'value'];
1330
 
1331
        $context = \context_system::instance();
1332
        $subcontext = [];
1333
 
1334
        $writer = $this->get_writer_instance()
1335
            ->set_context($context)
1336
            ->export_data($subcontext, (object) $data);
1337
 
1338
        $writer->set_context($context)->export_data(['paper'], $data);
1339
 
1340
        $coursecategory = $this->getDataGenerator()->create_category();
1341
        $categorycontext = \context_coursecat::instance($coursecategory->id);
1342
        $course = $this->getDataGenerator()->create_course();
1343
        $misccoursecxt = \context_coursecat::instance($course->category);
1344
        $coursecontext = \context_course::instance($course->id);
1345
        $cm = $this->getDataGenerator()->create_module('chat', ['course' => $course->id]);
1346
        $modulecontext = \context_module::instance($cm->cmid);
1347
 
1348
        $writer->set_context($modulecontext)->export_data([], $data);
1349
        $writer->set_context($coursecontext)->export_data(['grades'], $data);
1350
        $writer->set_context($categorycontext)->export_data([], $data);
1351
        $writer->set_context($context)->export_data([get_string('privacy:path:logs', 'tool_log'), 'Standard log'], $data);
1352
 
1353
        // Add a file.
1354
        $fs = get_file_storage();
1355
        $file = (object) [
1356
            'component' => 'core_privacy',
1357
            'filearea' => 'tests',
1358
            'itemid' => 0,
1359
            'path' => '/',
1360
            'name' => 'a.txt',
1361
            'content' => 'Test file 0',
1362
        ];
1363
        $record = [
1364
            'contextid' => $context->id,
1365
            'component' => $file->component,
1366
            'filearea'  => $file->filearea,
1367
            'itemid'    => $file->itemid,
1368
            'filepath'  => $file->path,
1369
            'filename'  => $file->name,
1370
        ];
1371
 
1372
        $file->namepath = '/' . $file->filearea . '/' . ($file->itemid ?: '') . $file->path . $file->name;
1373
        $file->storedfile = $fs->create_file_from_string($record, $file->content);
1374
        $writer->set_context($context)->export_area_files([], 'core_privacy', 'tests', 0);
1375
 
1376
        list($tree, $treelist, $indexdata) = phpunit_util::call_internal_method($writer, 'prepare_for_export', [],
1377
                '\core_privacy\local\request\moodle_content_writer');
1378
 
1379
        $expectedtreeoutput = [
1380
            'System _.1' => [
1381
                'data.json',
1382
                'paper' => 'data.json',
1383
                'Category Category 1 _.' . $misccoursecxt->id => [
1384
                    'Course Test course 1 _.' . $coursecontext->id => [
1385
                        'Chat Chat 1 _.' . $modulecontext->id => 'data.json',
1386
                        'grades' => 'data.json'
1387
                    ]
1388
                ],
1389
                'Category Course category 1 _.' . $categorycontext->id => 'data.json',
1390
                '_files' => [
1391
                    'tests' => 'a.txt'
1392
                ],
1393
                'Logs' => [
1394
                    'Standard log' => 'data.json'
1395
                ]
1396
            ]
1397
        ];
1398
        $this->assertEquals($expectedtreeoutput, $tree);
1399
 
1400
        $expectedlistoutput = [
1401
            'System _.1/data.json' => 'data_file_1',
1402
            'System _.1/paper/data.json' => 'data_file_2',
1403
            'System _.1/Category Category 1 _.' . $misccoursecxt->id . '/Course Test course 1 _.' .
1404
                    $coursecontext->id . '/Chat Chat 1 _.' . $modulecontext->id . '/data.json'   => 'data_file_3',
1405
            'System _.1/Category Category 1 _.' . $misccoursecxt->id . '/Course Test course 1 _.' .
1406
                    $coursecontext->id . '/grades/data.json'   => 'data_file_4',
1407
            'System _.1/Category Course category 1 _.' . $categorycontext->id . '/data.json' => 'data_file_5',
1408
            'System _.1/_files/tests/a.txt' => 'No var',
1409
            'System _.1/Logs/Standard log/data.json' => 'data_file_6'
1410
        ];
1411
        $this->assertEquals($expectedlistoutput, $treelist);
1412
 
1413
        $expectedindex = [
1414
            'data_file_1' => 'System _.1/data.js',
1415
            'data_file_2' => 'System _.1/paper/data.js',
1416
            'data_file_3' => 'System _.1/Category Category 1 _.' . $misccoursecxt->id . '/Course Test course 1 _.' .
1417
                    $coursecontext->id . '/Chat Chat 1 _.' . $modulecontext->id . '/data.js',
1418
            'data_file_4' => 'System _.1/Category Category 1 _.' . $misccoursecxt->id . '/Course Test course 1 _.' .
1419
                    $coursecontext->id . '/grades/data.js',
1420
            'data_file_5' => 'System _.1/Category Course category 1 _.' . $categorycontext->id . '/data.js',
1421
            'data_file_6' => 'System _.1/Logs/Standard log/data.js'
1422
        ];
1423
        $this->assertEquals($expectedindex, $indexdata);
1424
 
1425
        $richtree = phpunit_util::call_internal_method($writer, 'make_tree_object', [$tree, $treelist],
1426
                '\core_privacy\local\request\moodle_content_writer');
1427
 
1428
        // This is a big one.
1429
        $expectedrichtree = [
1430
            'System _.1' => (object) [
1431
                'itemtype' => 'treeitem',
1432
                'name' => 'System ',
1433
                'context' => \context_system::instance(),
1434
                'children' => [
1435
                    (object) [
1436
                        'name' => 'data.json',
1437
                        'itemtype' => 'item',
1438
                        'datavar' => 'data_file_1'
1439
                    ],
1440
                    'paper' => (object) [
1441
                        'itemtype' => 'treeitem',
1442
                        'name' => 'paper',
1443
                        'children' => [
1444
                            'data.json' => (object) [
1445
                                'name' => 'data.json',
1446
                                'itemtype' => 'item',
1447
                                'datavar' => 'data_file_2'
1448
                            ]
1449
                        ]
1450
                    ],
1451
                    'Category Category 1 _.' . $misccoursecxt->id => (object) [
1452
                        'itemtype' => 'treeitem',
1453
                        'name' => 'Category Category 1 ',
1454
                        'context' => $misccoursecxt,
1455
                        'children' => [
1456
                            'Course Test course 1 _.' . $coursecontext->id => (object) [
1457
                                'itemtype' => 'treeitem',
1458
                                'name' => 'Course Test course 1 ',
1459
                                'context' => $coursecontext,
1460
                                'children' => [
1461
                                    'Chat Chat 1 _.' . $modulecontext->id => (object) [
1462
                                        'itemtype' => 'treeitem',
1463
                                        'name' => 'Chat Chat 1 ',
1464
                                        'context' => $modulecontext,
1465
                                        'children' => [
1466
                                            'data.json' => (object) [
1467
                                                'name' => 'data.json',
1468
                                                'itemtype' => 'item',
1469
                                                'datavar' => 'data_file_3'
1470
                                            ]
1471
                                        ]
1472
                                    ],
1473
                                    'grades' => (object) [
1474
                                        'itemtype' => 'treeitem',
1475
                                        'name' => 'grades',
1476
                                        'children' => [
1477
                                            'data.json' => (object) [
1478
                                                'name' => 'data.json',
1479
                                                'itemtype' => 'item',
1480
                                                'datavar' => 'data_file_4'
1481
                                            ]
1482
                                        ]
1483
                                    ]
1484
                                ]
1485
                            ]
1486
                        ]
1487
                    ],
1488
                    'Category Course category 1 _.' . $categorycontext->id => (object) [
1489
                        'itemtype' => 'treeitem',
1490
                        'name' => 'Category Course category 1 ',
1491
                        'context' => $categorycontext,
1492
                        'children' => [
1493
                            'data.json' => (object) [
1494
                                'name' => 'data.json',
1495
                                'itemtype' => 'item',
1496
                                'datavar' => 'data_file_5'
1497
                            ]
1498
                        ]
1499
                    ],
1500
                    '_files' => (object) [
1501
                        'itemtype' => 'treeitem',
1502
                        'name' => '_files',
1503
                        'children' => [
1504
                            'tests' => (object) [
1505
                                'itemtype' => 'treeitem',
1506
                                'name' => 'tests',
1507
                                'children' => [
1508
                                    'a.txt' => (object) [
1509
                                        'name' => 'a.txt',
1510
                                        'itemtype' => 'item',
1511
                                        'url' => new \moodle_url('System _.1/_files/tests/a.txt')
1512
                                    ]
1513
                                ]
1514
                            ]
1515
                        ]
1516
                    ],
1517
                    'Logs' => (object) [
1518
                        'itemtype' => 'treeitem',
1519
                        'name' => 'Logs',
1520
                        'children' => [
1521
                            'Standard log' => (object) [
1522
                                'itemtype' => 'treeitem',
1523
                                'name' => 'Standard log',
1524
                                'children' => [
1525
                                    'data.json' => (object) [
1526
                                        'name' => 'data.json',
1527
                                        'itemtype' => 'item',
1528
                                        'datavar' => 'data_file_6'
1529
                                    ]
1530
                                ]
1531
                            ]
1532
                        ]
1533
                    ]
1534
                ]
1535
            ]
1536
        ];
1537
        $this->assertEquals($expectedrichtree, $richtree);
1538
 
1539
        // The phpunit_util::call_internal_method() method doesn't allow for referenced parameters so we have this joyful code
1540
        // instead to do the same thing, but with references working obviously.
1541
        $funfunction = function($object, $data) {
1542
            return $object->sort_my_list($data);
1543
        };
1544
 
1545
        $funfunction = Closure::bind($funfunction, null, $writer);
1546
        $funfunction($writer, $richtree);
1547
 
1548
        // This is a big one.
1549
        $expectedsortedtree = [
1550
            'System _.1' => (object) [
1551
                'itemtype' => 'treeitem',
1552
                'name' => 'System ',
1553
                'context' => \context_system::instance(),
1554
                'children' => [
1555
                    'Category Category 1 _.' . $misccoursecxt->id => (object) [
1556
                        'itemtype' => 'treeitem',
1557
                        'name' => 'Category Category 1 ',
1558
                        'context' => $misccoursecxt,
1559
                        'children' => [
1560
                            'Course Test course 1 _.' . $coursecontext->id => (object) [
1561
                                'itemtype' => 'treeitem',
1562
                                'name' => 'Course Test course 1 ',
1563
                                'context' => $coursecontext,
1564
                                'children' => [
1565
                                    'Chat Chat 1 _.' . $modulecontext->id => (object) [
1566
                                        'itemtype' => 'treeitem',
1567
                                        'name' => 'Chat Chat 1 ',
1568
                                        'context' => $modulecontext,
1569
                                        'children' => [
1570
                                            'data.json' => (object) [
1571
                                                'name' => 'data.json',
1572
                                                'itemtype' => 'item',
1573
                                                'datavar' => 'data_file_3'
1574
                                            ]
1575
                                        ]
1576
                                    ],
1577
                                    'grades' => (object) [
1578
                                        'itemtype' => 'treeitem',
1579
                                        'name' => 'grades',
1580
                                        'children' => [
1581
                                            'data.json' => (object) [
1582
                                                'name' => 'data.json',
1583
                                                'itemtype' => 'item',
1584
                                                'datavar' => 'data_file_4'
1585
                                            ]
1586
                                        ]
1587
                                    ]
1588
                                ]
1589
                            ]
1590
                        ]
1591
                    ],
1592
                    'Category Course category 1 _.' . $categorycontext->id => (object) [
1593
                        'itemtype' => 'treeitem',
1594
                        'name' => 'Category Course category 1 ',
1595
                        'context' => $categorycontext,
1596
                        'children' => [
1597
                            'data.json' => (object) [
1598
                                'name' => 'data.json',
1599
                                'itemtype' => 'item',
1600
                                'datavar' => 'data_file_5'
1601
                            ]
1602
                        ]
1603
                    ],
1604
                    '_files' => (object) [
1605
                        'itemtype' => 'treeitem',
1606
                        'name' => '_files',
1607
                        'children' => [
1608
                            'tests' => (object) [
1609
                                'itemtype' => 'treeitem',
1610
                                'name' => 'tests',
1611
                                'children' => [
1612
                                    'a.txt' => (object) [
1613
                                        'name' => 'a.txt',
1614
                                        'itemtype' => 'item',
1615
                                        'url' => new \moodle_url('System _.1/_files/tests/a.txt')
1616
                                    ]
1617
                                ]
1618
                            ]
1619
                        ]
1620
                    ],
1621
                    'Logs' => (object) [
1622
                        'itemtype' => 'treeitem',
1623
                        'name' => 'Logs',
1624
                        'children' => [
1625
                            'Standard log' => (object) [
1626
                                'itemtype' => 'treeitem',
1627
                                'name' => 'Standard log',
1628
                                'children' => [
1629
                                    'data.json' => (object) [
1630
                                        'name' => 'data.json',
1631
                                        'itemtype' => 'item',
1632
                                        'datavar' => 'data_file_6'
1633
                                    ]
1634
                                ]
1635
                            ]
1636
                        ]
1637
                    ],
1638
                    'paper' => (object) [
1639
                        'itemtype' => 'treeitem',
1640
                        'name' => 'paper',
1641
                        'children' => [
1642
                            'data.json' => (object) [
1643
                                'name' => 'data.json',
1644
                                'itemtype' => 'item',
1645
                                'datavar' => 'data_file_2'
1646
                            ]
1647
                        ]
1648
                    ],
1649
                    (object) [
1650
                        'name' => 'data.json',
1651
                        'itemtype' => 'item',
1652
                        'datavar' => 'data_file_1'
1653
                    ]
1654
                ]
1655
            ]
1656
        ];
1657
        $this->assertEquals($expectedsortedtree, $richtree);
1658
    }
1659
}