See Release Notes
Long Term Support Release
Differences Between: [Versions 310 and 401] [Versions 311 and 401] [Versions 39 and 401] [Versions 400 and 401]
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 */ 49 public function test_export_data($data) { 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 */ 74 public function test_export_data_different_context($data) { 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 */ 97 public function test_export_data_writes_to_multiple_context() { 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 */ 141 public function test_export_data_multiple_writes_same_context() { 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 */ 197 public function test_export_metadata($key, $value, $description) { 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 */ 222 public function test_export_metadata_additive() { 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 */ 258 public function test_export_metadata_to_multiple_contexts() { 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 */ 336 public function test_export_area_files() { 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 */ 442 public function test_export_file($filearea, $itemid, $filepath, $filename, $content) { 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 */ 539 public function test_export_user_preference_context_user($component, $key, $value, $desc) { 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 */ 572 public function test_export_user_preference_context_coursecat($component, $key, $value, $desc) { 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 */ 606 public function test_export_user_preference_context_course($component, $key, $value, $desc) { 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 */ 641 public function test_export_user_preference_context_module($component, $key, $value, $desc) { 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 */ 677 public function test_export_user_preference_context_block($component, $key, $value, $desc) { 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 */ 708 public function test_export_user_preference_context_block_multiple_instances() { 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 */ 770 public function test_export_user_preference_context_system($component, $key, $value, $desc) { 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 */ 794 public function test_export_multiple_user_preference_context_system() { 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 */ 828 public function test_export_user_preference_replace() { 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 */ 891 public function test_export_data_unescaped_unicode($text) { 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 */ 918 public function test_export_metadata_unescaped_unicode($text) { 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 */ 946 public function test_export_related_data_unescaped_unicode($text) { 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 */ 971 public function test_export_related_data_clean_name() { 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 */ 1001 public function test_export_user_preference_unescaped_unicode($text) { 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 */ 1038 public function test_export_data_clean_subcontext() { 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 */ 1067 public function test_export_data_long_filename($longtext, $expected, $text) { 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 */ 1099 public function test_export_related_data_long_filename($longtext, $expected, $text) { 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 */ 1129 public function test_export_metadata_long_filename($longtext, $expected, $text) { 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 */ 1161 public function test_export_user_preference_long_filename($longtext, $expected, $text) { 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 $rcm->setAccessible(true); 1254 $path = $rcm->invoke($writer); 1255 } else { 1256 $rcm = $rc->getMethod('get_path'); 1257 $rcm->setAccessible(true); 1258 $path = $rcm->invoke($writer, $subcontext, $name); 1259 } 1260 1261 // PHPUnit uses mikey179/vfsStream which is a stream wrapper for a virtual file system that uses '/' 1262 // as the directory separator. 1263 $path = str_replace(DIRECTORY_SEPARATOR, '/', $path); 1264 1265 return $path; 1266 } 1267 1268 /** 1269 * Test correct rewriting of @@PLUGINFILE@@ in the exported contents. 1270 * 1271 * @dataProvider rewrite_pluginfile_urls_provider 1272 * @param string $filearea The filearea within that component. 1273 * @param int $itemid Which item those files belong to. 1274 * @param string $input Raw text as stored in the database. 1275 * @param string $expectedoutput Expected output of URL rewriting. 1276 * @covers ::rewrite_pluginfile_urls 1277 */ 1278 public function test_rewrite_pluginfile_urls($filearea, $itemid, $input, $expectedoutput) { 1279 1280 $writer = $this->get_writer_instance(); 1281 $writer->set_context(\context_system::instance()); 1282 1283 $realoutput = $writer->rewrite_pluginfile_urls([], 'core_test', $filearea, $itemid, $input); 1284 1285 $this->assertEquals($expectedoutput, $realoutput); 1286 } 1287 1288 /** 1289 * Provides testable sample data for {@link self::test_rewrite_pluginfile_urls()}. 1290 * 1291 * @return array 1292 */ 1293 public function rewrite_pluginfile_urls_provider() { 1294 return [ 1295 'nullcontent' => [ 1296 'intro', 1297 0, 1298 null, 1299 '', 1300 ], 1301 'emptycontent' => [ 1302 'intro', 1303 0, 1304 '', 1305 '', 1306 ], 1307 'zeroitemid' => [ 1308 'intro', 1309 0, 1310 '<p><img src="@@PLUGINFILE@@/hello.gif" /></p>', 1311 '<p><img src="System _.1/_files/intro/hello.gif" /></p>', 1312 ], 1313 'nonzeroitemid' => [ 1314 'submission_content', 1315 34, 1316 '<p><img src="@@PLUGINFILE@@/first.png" alt="First" /></p>', 1317 '<p><img src="System _.1/_files/submission_content/_34/first.png" alt="First" /></p>', 1318 ], 1319 'withfilepath' => [ 1320 'post_content', 1321 9889, 1322 '<a href="@@PLUGINFILE@@/embedded/docs/muhehe.exe">Click here!</a>', 1323 '<a href="System _.1/_files/post_content/_9889/embedded/docs/muhehe.exe">Click here!</a>', 1324 ], 1325 ]; 1326 } 1327 1328 public function test_export_html_functions() { 1329 $this->resetAfterTest(); 1330 1331 $data = (object) ['key' => 'value']; 1332 1333 $context = \context_system::instance(); 1334 $subcontext = []; 1335 1336 $writer = $this->get_writer_instance() 1337 ->set_context($context) 1338 ->export_data($subcontext, (object) $data); 1339 1340 $writer->set_context($context)->export_data(['paper'], $data); 1341 1342 $coursecategory = $this->getDataGenerator()->create_category(); 1343 $categorycontext = \context_coursecat::instance($coursecategory->id); 1344 $course = $this->getDataGenerator()->create_course(); 1345 $misccoursecxt = \context_coursecat::instance($course->category); 1346 $coursecontext = \context_course::instance($course->id); 1347 $cm = $this->getDataGenerator()->create_module('chat', ['course' => $course->id]); 1348 $modulecontext = \context_module::instance($cm->cmid); 1349 1350 $writer->set_context($modulecontext)->export_data([], $data); 1351 $writer->set_context($coursecontext)->export_data(['grades'], $data); 1352 $writer->set_context($categorycontext)->export_data([], $data); 1353 $writer->set_context($context)->export_data([get_string('privacy:path:logs', 'tool_log'), 'Standard log'], $data); 1354 1355 // Add a file. 1356 $fs = get_file_storage(); 1357 $file = (object) [ 1358 'component' => 'core_privacy', 1359 'filearea' => 'tests', 1360 'itemid' => 0, 1361 'path' => '/', 1362 'name' => 'a.txt', 1363 'content' => 'Test file 0', 1364 ]; 1365 $record = [ 1366 'contextid' => $context->id, 1367 'component' => $file->component, 1368 'filearea' => $file->filearea, 1369 'itemid' => $file->itemid, 1370 'filepath' => $file->path, 1371 'filename' => $file->name, 1372 ]; 1373 1374 $file->namepath = '/' . $file->filearea . '/' . ($file->itemid ?: '') . $file->path . $file->name; 1375 $file->storedfile = $fs->create_file_from_string($record, $file->content); 1376 $writer->set_context($context)->export_area_files([], 'core_privacy', 'tests', 0); 1377 1378 list($tree, $treelist, $indexdata) = phpunit_util::call_internal_method($writer, 'prepare_for_export', [], 1379 '\core_privacy\local\request\moodle_content_writer'); 1380 1381 $expectedtreeoutput = [ 1382 'System _.1' => [ 1383 'data.json', 1384 'paper' => 'data.json', 1385 'Category Category 1 _.' . $misccoursecxt->id => [ 1386 'Course Test course 1 _.' . $coursecontext->id => [ 1387 'Chat Chat 1 _.' . $modulecontext->id => 'data.json', 1388 'grades' => 'data.json' 1389 ] 1390 ], 1391 'Category Course category 1 _.' . $categorycontext->id => 'data.json', 1392 '_files' => [ 1393 'tests' => 'a.txt' 1394 ], 1395 'Logs' => [ 1396 'Standard log' => 'data.json' 1397 ] 1398 ] 1399 ]; 1400 $this->assertEquals($expectedtreeoutput, $tree); 1401 1402 $expectedlistoutput = [ 1403 'System _.1/data.json' => 'data_file_1', 1404 'System _.1/paper/data.json' => 'data_file_2', 1405 'System _.1/Category Category 1 _.' . $misccoursecxt->id . '/Course Test course 1 _.' . 1406 $coursecontext->id . '/Chat Chat 1 _.' . $modulecontext->id . '/data.json' => 'data_file_3', 1407 'System _.1/Category Category 1 _.' . $misccoursecxt->id . '/Course Test course 1 _.' . 1408 $coursecontext->id . '/grades/data.json' => 'data_file_4', 1409 'System _.1/Category Course category 1 _.' . $categorycontext->id . '/data.json' => 'data_file_5', 1410 'System _.1/_files/tests/a.txt' => 'No var', 1411 'System _.1/Logs/Standard log/data.json' => 'data_file_6' 1412 ]; 1413 $this->assertEquals($expectedlistoutput, $treelist); 1414 1415 $expectedindex = [ 1416 'data_file_1' => 'System _.1/data.js', 1417 'data_file_2' => 'System _.1/paper/data.js', 1418 'data_file_3' => 'System _.1/Category Category 1 _.' . $misccoursecxt->id . '/Course Test course 1 _.' . 1419 $coursecontext->id . '/Chat Chat 1 _.' . $modulecontext->id . '/data.js', 1420 'data_file_4' => 'System _.1/Category Category 1 _.' . $misccoursecxt->id . '/Course Test course 1 _.' . 1421 $coursecontext->id . '/grades/data.js', 1422 'data_file_5' => 'System _.1/Category Course category 1 _.' . $categorycontext->id . '/data.js', 1423 'data_file_6' => 'System _.1/Logs/Standard log/data.js' 1424 ]; 1425 $this->assertEquals($expectedindex, $indexdata); 1426 1427 $richtree = phpunit_util::call_internal_method($writer, 'make_tree_object', [$tree, $treelist], 1428 '\core_privacy\local\request\moodle_content_writer'); 1429 1430 // This is a big one. 1431 $expectedrichtree = [ 1432 'System _.1' => (object) [ 1433 'itemtype' => 'treeitem', 1434 'name' => 'System ', 1435 'context' => \context_system::instance(), 1436 'children' => [ 1437 (object) [ 1438 'name' => 'data.json', 1439 'itemtype' => 'item', 1440 'datavar' => 'data_file_1' 1441 ], 1442 'paper' => (object) [ 1443 'itemtype' => 'treeitem', 1444 'name' => 'paper', 1445 'children' => [ 1446 'data.json' => (object) [ 1447 'name' => 'data.json', 1448 'itemtype' => 'item', 1449 'datavar' => 'data_file_2' 1450 ] 1451 ] 1452 ], 1453 'Category Category 1 _.' . $misccoursecxt->id => (object) [ 1454 'itemtype' => 'treeitem', 1455 'name' => 'Category Category 1 ', 1456 'context' => $misccoursecxt, 1457 'children' => [ 1458 'Course Test course 1 _.' . $coursecontext->id => (object) [ 1459 'itemtype' => 'treeitem', 1460 'name' => 'Course Test course 1 ', 1461 'context' => $coursecontext, 1462 'children' => [ 1463 'Chat Chat 1 _.' . $modulecontext->id => (object) [ 1464 'itemtype' => 'treeitem', 1465 'name' => 'Chat Chat 1 ', 1466 'context' => $modulecontext, 1467 'children' => [ 1468 'data.json' => (object) [ 1469 'name' => 'data.json', 1470 'itemtype' => 'item', 1471 'datavar' => 'data_file_3' 1472 ] 1473 ] 1474 ], 1475 'grades' => (object) [ 1476 'itemtype' => 'treeitem', 1477 'name' => 'grades', 1478 'children' => [ 1479 'data.json' => (object) [ 1480 'name' => 'data.json', 1481 'itemtype' => 'item', 1482 'datavar' => 'data_file_4' 1483 ] 1484 ] 1485 ] 1486 ] 1487 ] 1488 ] 1489 ], 1490 'Category Course category 1 _.' . $categorycontext->id => (object) [ 1491 'itemtype' => 'treeitem', 1492 'name' => 'Category Course category 1 ', 1493 'context' => $categorycontext, 1494 'children' => [ 1495 'data.json' => (object) [ 1496 'name' => 'data.json', 1497 'itemtype' => 'item', 1498 'datavar' => 'data_file_5' 1499 ] 1500 ] 1501 ], 1502 '_files' => (object) [ 1503 'itemtype' => 'treeitem', 1504 'name' => '_files', 1505 'children' => [ 1506 'tests' => (object) [ 1507 'itemtype' => 'treeitem', 1508 'name' => 'tests', 1509 'children' => [ 1510 'a.txt' => (object) [ 1511 'name' => 'a.txt', 1512 'itemtype' => 'item', 1513 'url' => new \moodle_url('System _.1/_files/tests/a.txt') 1514 ] 1515 ] 1516 ] 1517 ] 1518 ], 1519 'Logs' => (object) [ 1520 'itemtype' => 'treeitem', 1521 'name' => 'Logs', 1522 'children' => [ 1523 'Standard log' => (object) [ 1524 'itemtype' => 'treeitem', 1525 'name' => 'Standard log', 1526 'children' => [ 1527 'data.json' => (object) [ 1528 'name' => 'data.json', 1529 'itemtype' => 'item', 1530 'datavar' => 'data_file_6' 1531 ] 1532 ] 1533 ] 1534 ] 1535 ] 1536 ] 1537 ] 1538 ]; 1539 $this->assertEquals($expectedrichtree, $richtree); 1540 1541 // The phpunit_util::call_internal_method() method doesn't allow for referenced parameters so we have this joyful code 1542 // instead to do the same thing, but with references working obviously. 1543 $funfunction = function($object, $data) { 1544 return $object->sort_my_list($data); 1545 }; 1546 1547 $funfunction = Closure::bind($funfunction, null, $writer); 1548 $funfunction($writer, $richtree); 1549 1550 // This is a big one. 1551 $expectedsortedtree = [ 1552 'System _.1' => (object) [ 1553 'itemtype' => 'treeitem', 1554 'name' => 'System ', 1555 'context' => \context_system::instance(), 1556 'children' => [ 1557 'Category Category 1 _.' . $misccoursecxt->id => (object) [ 1558 'itemtype' => 'treeitem', 1559 'name' => 'Category Category 1 ', 1560 'context' => $misccoursecxt, 1561 'children' => [ 1562 'Course Test course 1 _.' . $coursecontext->id => (object) [ 1563 'itemtype' => 'treeitem', 1564 'name' => 'Course Test course 1 ', 1565 'context' => $coursecontext, 1566 'children' => [ 1567 'Chat Chat 1 _.' . $modulecontext->id => (object) [ 1568 'itemtype' => 'treeitem', 1569 'name' => 'Chat Chat 1 ', 1570 'context' => $modulecontext, 1571 'children' => [ 1572 'data.json' => (object) [ 1573 'name' => 'data.json', 1574 'itemtype' => 'item', 1575 'datavar' => 'data_file_3' 1576 ] 1577 ] 1578 ], 1579 'grades' => (object) [ 1580 'itemtype' => 'treeitem', 1581 'name' => 'grades', 1582 'children' => [ 1583 'data.json' => (object) [ 1584 'name' => 'data.json', 1585 'itemtype' => 'item', 1586 'datavar' => 'data_file_4' 1587 ] 1588 ] 1589 ] 1590 ] 1591 ] 1592 ] 1593 ], 1594 'Category Course category 1 _.' . $categorycontext->id => (object) [ 1595 'itemtype' => 'treeitem', 1596 'name' => 'Category Course category 1 ', 1597 'context' => $categorycontext, 1598 'children' => [ 1599 'data.json' => (object) [ 1600 'name' => 'data.json', 1601 'itemtype' => 'item', 1602 'datavar' => 'data_file_5' 1603 ] 1604 ] 1605 ], 1606 '_files' => (object) [ 1607 'itemtype' => 'treeitem', 1608 'name' => '_files', 1609 'children' => [ 1610 'tests' => (object) [ 1611 'itemtype' => 'treeitem', 1612 'name' => 'tests', 1613 'children' => [ 1614 'a.txt' => (object) [ 1615 'name' => 'a.txt', 1616 'itemtype' => 'item', 1617 'url' => new \moodle_url('System _.1/_files/tests/a.txt') 1618 ] 1619 ] 1620 ] 1621 ] 1622 ], 1623 'Logs' => (object) [ 1624 'itemtype' => 'treeitem', 1625 'name' => 'Logs', 1626 'children' => [ 1627 'Standard log' => (object) [ 1628 'itemtype' => 'treeitem', 1629 'name' => 'Standard log', 1630 'children' => [ 1631 'data.json' => (object) [ 1632 'name' => 'data.json', 1633 'itemtype' => 'item', 1634 'datavar' => 'data_file_6' 1635 ] 1636 ] 1637 ] 1638 ] 1639 ], 1640 'paper' => (object) [ 1641 'itemtype' => 'treeitem', 1642 'name' => 'paper', 1643 'children' => [ 1644 'data.json' => (object) [ 1645 'name' => 'data.json', 1646 'itemtype' => 'item', 1647 'datavar' => 'data_file_2' 1648 ] 1649 ] 1650 ], 1651 (object) [ 1652 'name' => 'data.json', 1653 'itemtype' => 'item', 1654 'datavar' => 'data_file_1' 1655 ] 1656 ] 1657 ] 1658 ]; 1659 $this->assertEquals($expectedsortedtree, $richtree); 1660 } 1661 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body