See Release Notes
Long Term Support Release
Differences Between: [Versions 310 and 401] [Versions 311 and 401] [Versions 39 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 * Testing the H5P API. 19 * 20 * @package core_h5p 21 * @category test 22 * @copyright 2020 Sara Arjona <sara@moodle.com> 23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 */ 25 26 declare(strict_types = 1); 27 28 namespace core_h5p; 29 30 use stdClass; 31 32 defined('MOODLE_INTERNAL') || die(); 33 34 /** 35 * Test class covering the H5P API. 36 * 37 * @package core_h5p 38 * @copyright 2020 Sara Arjona <sara@moodle.com> 39 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 40 * @coversDefaultClass \core_h5p\api 41 */ 42 class api_test extends \advanced_testcase { 43 44 /** 45 * Test the behaviour of delete_library(). 46 * 47 * @dataProvider delete_library_provider 48 * @param string $libraryname Machine name of the library to delete. 49 * @param int $expectedh5p Total of H5P contents expected after deleting the library. 50 * @param int $expectedlibraries Total of H5P libraries expected after deleting the library. 51 * @param int $expectedcontents Total of H5P content_libraries expected after deleting the library. 52 * @param int $expecteddependencies Total of H5P library dependencies expected after deleting the library. 53 */ 54 public function test_delete_library(string $libraryname, int $expectedh5p, int $expectedlibraries, 55 int $expectedcontents, int $expecteddependencies): void { 56 global $DB; 57 58 $this->setRunTestInSeparateProcess(true); 59 $this->resetAfterTest(); 60 61 // Generate h5p related data. 62 $generator = $this->getDataGenerator()->get_plugin_generator('core_h5p'); 63 $generator->generate_h5p_data(); 64 $generator->create_library_record('H5P.TestingLibrary', 'TestingLibrary', 1, 0); 65 66 // Check the current content in H5P tables is the expected. 67 $counth5p = $DB->count_records('h5p'); 68 $counth5plibraries = $DB->count_records('h5p_libraries'); 69 $counth5pcontents = $DB->count_records('h5p_contents_libraries'); 70 $counth5pdependencies = $DB->count_records('h5p_library_dependencies'); 71 72 $this->assertSame(1, $counth5p); 73 $this->assertSame(7, $counth5plibraries); 74 $this->assertSame(5, $counth5pcontents); 75 $this->assertSame(7, $counth5pdependencies); 76 77 // Delete this library. 78 $factory = new factory(); 79 $library = $DB->get_record('h5p_libraries', ['machinename' => $libraryname]); 80 if ($library) { 81 api::delete_library($factory, $library); 82 } 83 84 // Check the expected libraries and content have been removed. 85 $counth5p = $DB->count_records('h5p'); 86 $counth5plibraries = $DB->count_records('h5p_libraries'); 87 $counth5pcontents = $DB->count_records('h5p_contents_libraries'); 88 $counth5pdependencies = $DB->count_records('h5p_library_dependencies'); 89 90 $this->assertSame($expectedh5p, $counth5p); 91 $this->assertSame($expectedlibraries, $counth5plibraries); 92 $this->assertSame($expectedcontents, $counth5pcontents); 93 $this->assertSame($expecteddependencies, $counth5pdependencies); 94 } 95 96 /** 97 * Data provider for test_delete_library(). 98 * 99 * @return array 100 */ 101 public function delete_library_provider(): array { 102 return [ 103 'Delete MainLibrary' => [ 104 'MainLibrary', 105 0, 106 6, 107 0, 108 4, 109 ], 110 'Delete Library1' => [ 111 'Library1', 112 0, 113 5, 114 0, 115 1, 116 ], 117 'Delete Library2' => [ 118 'Library2', 119 0, 120 4, 121 0, 122 1, 123 ], 124 'Delete Library3' => [ 125 'Library3', 126 0, 127 4, 128 0, 129 0, 130 ], 131 'Delete Library4' => [ 132 'Library4', 133 0, 134 4, 135 0, 136 1, 137 ], 138 'Delete Library5' => [ 139 'Library5', 140 0, 141 3, 142 0, 143 0, 144 ], 145 'Delete a library without dependencies' => [ 146 'H5P.TestingLibrary', 147 1, 148 6, 149 5, 150 7, 151 ], 152 'Delete unexisting library' => [ 153 'LibraryX', 154 1, 155 7, 156 5, 157 7, 158 ], 159 ]; 160 } 161 162 /** 163 * Test the behaviour of get_dependent_libraries(). 164 * 165 * @dataProvider get_dependent_libraries_provider 166 * @param string $libraryname Machine name of the library to delete. 167 * @param int $expectedvalue Total of H5P required libraries expected. 168 */ 169 public function test_get_dependent_libraries(string $libraryname, int $expectedvalue): void { 170 global $DB; 171 172 $this->resetAfterTest(); 173 174 // Generate h5p related data. 175 $generator = $this->getDataGenerator()->get_plugin_generator('core_h5p'); 176 $generator->generate_h5p_data(); 177 $generator->create_library_record('H5P.TestingLibrary', 'TestingLibrary', 1, 0); 178 179 // Get required libraries. 180 $library = $DB->get_record('h5p_libraries', ['machinename' => $libraryname], 'id'); 181 if ($library) { 182 $libraries = api::get_dependent_libraries((int)$library->id); 183 } else { 184 $libraries = []; 185 } 186 187 $this->assertCount($expectedvalue, $libraries); 188 } 189 190 /** 191 * Data provider for test_get_dependent_libraries(). 192 * 193 * @return array 194 */ 195 public function get_dependent_libraries_provider(): array { 196 return [ 197 'Main library of a content' => [ 198 'MainLibrary', 199 0, 200 ], 201 'Library1' => [ 202 'Library1', 203 1, 204 ], 205 'Library2' => [ 206 'Library2', 207 2, 208 ], 209 'Library without dependencies' => [ 210 'H5P.TestingLibrary', 211 0, 212 ], 213 'Unexisting library' => [ 214 'LibraryX', 215 0, 216 ], 217 ]; 218 } 219 220 /** 221 * Test the behaviour of get_library(). 222 * 223 * @dataProvider get_library_provider 224 * @param string $libraryname Machine name of the library to delete. 225 * @param bool $emptyexpected Wether the expected result is empty or not. 226 */ 227 public function test_get_library(string $libraryname, bool $emptyexpected): void { 228 global $DB; 229 230 $this->resetAfterTest(); 231 232 // Generate h5p related data. 233 $generator = $this->getDataGenerator()->get_plugin_generator('core_h5p'); 234 $generator->generate_h5p_data(); 235 $generator->create_library_record('H5P.TestingLibrary', 'TestingLibrary', 1, 0); 236 237 // Get the library identifier. 238 $library = $DB->get_record('h5p_libraries', ['machinename' => $libraryname], 'id'); 239 if ($library) { 240 $result = api::get_library((int)$library->id); 241 } else { 242 $result = null; 243 } 244 245 if ($emptyexpected) { 246 $this->assertEmpty($result); 247 } else { 248 $this->assertEquals($library->id, $result->id); 249 $this->assertEquals($libraryname, $result->machinename); 250 } 251 252 } 253 254 /** 255 * Data provider for test_get_library(). 256 * 257 * @return array 258 */ 259 public function get_library_provider(): array { 260 return [ 261 'Main library of a content' => [ 262 'MainLibrary', 263 false, 264 ], 265 'Library1' => [ 266 'Library1', 267 false, 268 ], 269 'Library without dependencies' => [ 270 'H5P.TestingLibrary', 271 false, 272 ], 273 'Unexisting library' => [ 274 'LibraryX', 275 true, 276 ], 277 ]; 278 } 279 280 /** 281 * Test the behaviour of get_content_from_pluginfile_url(). 282 */ 283 public function test_get_content_from_pluginfile_url(): void { 284 $this->setRunTestInSeparateProcess(true); 285 $this->resetAfterTest(); 286 $factory = new factory(); 287 288 // Create the H5P data. 289 $filename = 'find-the-words.h5p'; 290 $path = __DIR__ . '/fixtures/' . $filename; 291 $fakefile = helper::create_fake_stored_file_from_path($path); 292 $config = (object)[ 293 'frame' => 1, 294 'export' => 1, 295 'embed' => 0, 296 'copyright' => 0, 297 ]; 298 299 // Get URL for this H5P content file. 300 $syscontext = \context_system::instance(); 301 $url = \moodle_url::make_pluginfile_url( 302 $syscontext->id, 303 \core_h5p\file_storage::COMPONENT, 304 'unittest', 305 $fakefile->get_itemid(), 306 '/', 307 $filename 308 ); 309 310 // Scenario 1: Get the H5P for this URL and check there isn't any existing H5P (because it hasn't been saved). 311 list($newfile, $h5p) = api::get_content_from_pluginfile_url($url->out()); 312 $this->assertEquals($fakefile->get_pathnamehash(), $newfile->get_pathnamehash()); 313 $this->assertEquals($fakefile->get_contenthash(), $newfile->get_contenthash()); 314 $this->assertFalse($h5p); 315 316 // Scenario 2: Save the H5P and check now the H5P is exactly the same as the original one. 317 $h5pid = helper::save_h5p($factory, $fakefile, $config); 318 list($newfile, $h5p) = api::get_content_from_pluginfile_url($url->out()); 319 320 $this->assertEquals($h5pid, $h5p->id); 321 $this->assertEquals($fakefile->get_pathnamehash(), $h5p->pathnamehash); 322 $this->assertEquals($fakefile->get_contenthash(), $h5p->contenthash); 323 324 // Scenario 3: Get the H5P for an unexisting H5P file. 325 $url = \moodle_url::make_pluginfile_url( 326 $syscontext->id, 327 \core_h5p\file_storage::COMPONENT, 328 'unittest', 329 $fakefile->get_itemid(), 330 '/', 331 'unexisting.h5p' 332 ); 333 list($newfile, $h5p) = api::get_content_from_pluginfile_url($url->out()); 334 $this->assertFalse($newfile); 335 $this->assertFalse($h5p); 336 } 337 338 /** 339 * Test the behaviour of get_original_content_from_pluginfile_url(). 340 * 341 * @covers ::get_original_content_from_pluginfile_url 342 */ 343 public function test_get_original_content_from_pluginfile_url(): void { 344 $this->setRunTestInSeparateProcess(true); 345 $this->resetAfterTest(); 346 $this->setAdminUser(); 347 348 $factory = new factory(); 349 $syscontext = \context_system::instance(); 350 351 // Create the original file. 352 $filename = 'greeting-card.h5p'; 353 $path = __DIR__ . '/fixtures/' . $filename; 354 $originalfile = helper::create_fake_stored_file_from_path($path); 355 $originalfilerecord = [ 356 'contextid' => $originalfile->get_contextid(), 357 'component' => $originalfile->get_component(), 358 'filearea' => $originalfile->get_filearea(), 359 'itemid' => $originalfile->get_itemid(), 360 'filepath' => $originalfile->get_filepath(), 361 'filename' => $originalfile->get_filename(), 362 ]; 363 364 $config = (object)[ 365 'frame' => 1, 366 'export' => 1, 367 'embed' => 0, 368 'copyright' => 0, 369 ]; 370 371 $originalurl = \moodle_url::make_pluginfile_url( 372 $originalfile->get_contextid(), 373 $originalfile->get_component(), 374 $originalfile->get_filearea(), 375 $originalfile->get_itemid(), 376 $originalfile->get_filepath(), 377 $originalfile->get_filename() 378 ); 379 380 // Create a reference to the original file. 381 $reffilerecord = [ 382 'contextid' => $syscontext->id, 383 'component' => 'core', 384 'filearea' => 'phpunit', 385 'itemid' => 0, 386 'filepath' => '/', 387 'filename' => $filename 388 ]; 389 390 $fs = get_file_storage(); 391 $ref = $fs->pack_reference($originalfilerecord); 392 $repos = \repository::get_instances(['type' => 'user']); 393 $userrepository = reset($repos); 394 $referencedfile = $fs->create_file_from_reference($reffilerecord, $userrepository->id, $ref); 395 $this->assertEquals($referencedfile->get_contenthash(), $originalfile->get_contenthash()); 396 397 $referencedurl = \moodle_url::make_pluginfile_url( 398 $syscontext->id, 399 'core', 400 'phpunit', 401 0, 402 '/', 403 $filename 404 ); 405 406 // Scenario 1: Original file (without any reference). 407 $originalh5pid = helper::save_h5p($factory, $originalfile, $config); 408 list($source, $h5p, $file) = api::get_original_content_from_pluginfile_url($originalurl->out()); 409 $this->assertEquals($originalfile->get_pathnamehash(), $source->get_pathnamehash()); 410 $this->assertEquals($originalfile->get_contenthash(), $source->get_contenthash()); 411 $this->assertEquals($originalh5pid, $h5p->id); 412 $this->assertFalse($file); 413 414 // Scenario 2: Referenced file (alias to originalfile). 415 list($source, $h5p, $file) = api::get_original_content_from_pluginfile_url($referencedurl->out()); 416 $this->assertEquals($originalfile->get_pathnamehash(), $source->get_pathnamehash()); 417 $this->assertEquals($originalfile->get_contenthash(), $source->get_contenthash()); 418 $this->assertEquals($originalfile->get_contenthash(), $source->get_contenthash()); 419 $this->assertEquals($originalh5pid, $h5p->id); 420 $this->assertEquals($referencedfile->get_pathnamehash(), $file->get_pathnamehash()); 421 $this->assertEquals($referencedfile->get_contenthash(), $file->get_contenthash()); 422 $this->assertEquals($referencedfile->get_contenthash(), $file->get_contenthash()); 423 424 // Scenario 3: Unexisting file. 425 $unexistingurl = \moodle_url::make_pluginfile_url( 426 $syscontext->id, 427 'core', 428 'phpunit', 429 0, 430 '/', 431 'unexisting.h5p' 432 ); 433 list($source, $h5p, $file) = api::get_original_content_from_pluginfile_url($unexistingurl->out()); 434 $this->assertFalse($source); 435 $this->assertFalse($h5p); 436 $this->assertFalse($file); 437 } 438 439 /** 440 * Test the behaviour of can_edit_content(). 441 * 442 * @covers ::can_edit_content 443 * @dataProvider can_edit_content_provider 444 * 445 * @param string $currentuser User who will call the method. 446 * @param string $fileauthor Author of the file to check. 447 * @param string $filecomponent Component of the file to check. 448 * @param bool $expected Expected result after calling the can_edit_content method. 449 * @param string $filearea Area of the file to check. 450 * 451 * @return void 452 */ 453 public function test_can_edit_content(string $currentuser, string $fileauthor, string $filecomponent, bool $expected, 454 $filearea = 'unittest'): void { 455 global $USER, $DB; 456 457 $this->setRunTestInSeparateProcess(true); 458 $this->resetAfterTest(); 459 460 // Create course. 461 $course = $this->getDataGenerator()->create_course(); 462 $context = \context_course::instance($course->id); 463 464 // Create some users. 465 $this->setAdminUser(); 466 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'editingteacher'); 467 $student = $this->getDataGenerator()->create_and_enrol($course, 'student'); 468 $users = [ 469 'admin' => $USER, 470 'teacher' => $teacher, 471 'student' => $student, 472 ]; 473 474 // Set current user. 475 if ($currentuser !== 'admin') { 476 $this->setUser($users[$currentuser]); 477 } 478 479 $itemid = rand(); 480 if ($filearea === 'post') { 481 // Create a forum and add a discussion. 482 $forum = $this->getDataGenerator()->create_module('forum', ['course' => $course->id]); 483 484 $record = new stdClass(); 485 $record->course = $course->id; 486 $record->userid = $users[$fileauthor]->id; 487 $record->forum = $forum->id; 488 $discussion = $this->getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record); 489 $post = $DB->get_record('forum_posts', ['discussion' => $discussion->id]); 490 $itemid = $post->id; 491 } 492 493 // Create the file. 494 $filename = 'greeting-card.h5p'; 495 $path = __DIR__ . '/fixtures/' . $filename; 496 if ($filecomponent === 'contentbank') { 497 $generator = $this->getDataGenerator()->get_plugin_generator('core_contentbank'); 498 $contents = $generator->generate_contentbank_data( 499 'contenttype_h5p', 500 1, 501 (int)$users[$fileauthor]->id, 502 $context, 503 true, 504 $path 505 ); 506 $content = array_shift($contents); 507 $file = $content->get_file(); 508 } else { 509 $filerecord = [ 510 'contextid' => $context->id, 511 'component' => $filecomponent, 512 'filearea' => $filearea, 513 'itemid' => $itemid, 514 'filepath' => '/', 515 'filename' => basename($path), 516 'userid' => $users[$fileauthor]->id, 517 ]; 518 $fs = get_file_storage(); 519 $file = $fs->create_file_from_pathname($filerecord, $path); 520 } 521 522 // Check if the currentuser can edit the file. 523 $result = api::can_edit_content($file); 524 $this->assertEquals($expected, $result); 525 } 526 527 /** 528 * Data provider for test_can_edit_content(). 529 * 530 * @return array 531 */ 532 public function can_edit_content_provider(): array { 533 return [ 534 // Component = user. 535 'user: Admin user is author' => [ 536 'currentuser' => 'admin', 537 'fileauthor' => 'admin', 538 'filecomponent' => 'user', 539 'expected' => true, 540 ], 541 'user: Admin user, teacher is author' => [ 542 'currentuser' => 'admin', 543 'fileauthor' => 'teacher', 544 'filecomponent' => 'user', 545 'expected' => false, 546 ], 547 'user: Teacher user, teacher is author' => [ 548 'currentuser' => 'teacher', 549 'fileauthor' => 'teacher', 550 'filecomponent' => 'user', 551 'expected' => true, 552 ], 553 'user: Teacher user, admin is author' => [ 554 'currentuser' => 'teacher', 555 'fileauthor' => 'admin', 556 'filecomponent' => 'user', 557 'expected' => false, 558 ], 559 'user: Student user, student is author' => [ 560 'currentuser' => 'student', 561 'fileauthor' => 'student', 562 'filecomponent' => 'user', 563 'expected' => true, 564 ], 565 'user: Student user, teacher is author' => [ 566 'currentuser' => 'student', 567 'fileauthor' => 'teacher', 568 'filecomponent' => 'user', 569 'expected' => false, 570 ], 571 572 // Component = mod_h5pactivity. 573 'mod_h5pactivity: Admin user is author' => [ 574 'currentuser' => 'admin', 575 'fileauthor' => 'admin', 576 'filecomponent' => 'mod_h5pactivity', 577 'expected' => true, 578 ], 579 'mod_h5pactivity: Admin user, teacher is author' => [ 580 'currentuser' => 'admin', 581 'fileauthor' => 'teacher', 582 'filecomponent' => 'mod_h5pactivity', 583 'expected' => true, 584 ], 585 'mod_h5pactivity: Teacher user, teacher is author' => [ 586 'currentuser' => 'teacher', 587 'fileauthor' => 'teacher', 588 'filecomponent' => 'mod_h5pactivity', 589 'expected' => true, 590 ], 591 'mod_h5pactivity: Teacher user, admin is author' => [ 592 'currentuser' => 'teacher', 593 'fileauthor' => 'admin', 594 'filecomponent' => 'mod_h5pactivity', 595 'expected' => true, 596 ], 597 'mod_h5pactivity: Student user, student is author' => [ 598 'currentuser' => 'student', 599 'fileauthor' => 'student', 600 'filecomponent' => 'mod_h5pactivity', 601 'expected' => false, 602 ], 603 'mod_h5pactivity: Student user, teacher is author' => [ 604 'currentuser' => 'student', 605 'fileauthor' => 'teacher', 606 'filecomponent' => 'mod_h5pactivity', 607 'expected' => false, 608 ], 609 610 // Component = mod_book. 611 'mod_book: Admin user is author' => [ 612 'currentuser' => 'admin', 613 'fileauthor' => 'admin', 614 'filecomponent' => 'mod_book', 615 'expected' => true, 616 ], 617 'mod_book: Admin user, teacher is author' => [ 618 'currentuser' => 'admin', 619 'fileauthor' => 'teacher', 620 'filecomponent' => 'mod_book', 621 'expected' => true, 622 ], 623 624 // Component = mod_forum. 625 'mod_forum: Admin user is author' => [ 626 'currentuser' => 'admin', 627 'fileauthor' => 'admin', 628 'filecomponent' => 'mod_forum', 629 'expected' => true, 630 ], 631 'mod_forum: Admin user, teacher is author' => [ 632 'currentuser' => 'admin', 633 'fileauthor' => 'teacher', 634 'filecomponent' => 'mod_forum', 635 'expected' => true, 636 ], 637 'mod_forum: Teacher user, admin is author' => [ 638 'currentuser' => 'teacher', 639 'fileauthor' => 'admin', 640 'filecomponent' => 'mod_forum', 641 'expected' => true, 642 ], 643 'mod_forum: Student user, teacher is author' => [ 644 'currentuser' => 'student', 645 'fileauthor' => 'teacher', 646 'filecomponent' => 'mod_forum', 647 'expected' => false, 648 ], 649 'mod_forum/post: Admin user is author' => [ 650 'currentuser' => 'admin', 651 'fileauthor' => 'admin', 652 'filecomponent' => 'mod_forum', 653 'expected' => true, 654 'filearea' => 'post', 655 ], 656 'mod_forum/post: Teacher user, admin is author' => [ 657 'currentuser' => 'teacher', 658 'fileauthor' => 'admin', 659 'filecomponent' => 'mod_forum', 660 'expected' => true, 661 'filearea' => 'post', 662 ], 663 'mod_forum/post: Student user, teacher is author' => [ 664 'currentuser' => 'student', 665 'fileauthor' => 'teacher', 666 'filecomponent' => 'mod_forum', 667 'expected' => false, 668 'filearea' => 'post', 669 ], 670 671 // Component = block_html. 672 'block_html: Admin user is author' => [ 673 'currentuser' => 'admin', 674 'fileauthor' => 'admin', 675 'filecomponent' => 'block_html', 676 'expected' => true, 677 ], 678 'block_html: Admin user, teacher is author' => [ 679 'currentuser' => 'admin', 680 'fileauthor' => 'teacher', 681 'filecomponent' => 'block_html', 682 'expected' => true, 683 ], 684 685 // Component = contentbank. 686 'contentbank: Admin user is author' => [ 687 'currentuser' => 'admin', 688 'fileauthor' => 'admin', 689 'filecomponent' => 'contentbank', 690 'expected' => true, 691 ], 692 'contentbank: Admin user, teacher is author' => [ 693 'currentuser' => 'admin', 694 'fileauthor' => 'teacher', 695 'filecomponent' => 'contentbank', 696 'expected' => true, 697 ], 698 'contentbank: Teacher user, teacher is author' => [ 699 'currentuser' => 'teacher', 700 'fileauthor' => 'teacher', 701 'filecomponent' => 'contentbank', 702 'expected' => true, 703 ], 704 'contentbank: Teacher user, admin is author' => [ 705 'currentuser' => 'teacher', 706 'fileauthor' => 'admin', 707 'filecomponent' => 'contentbank', 708 'expected' => false, 709 ], 710 'contentbank: Student user, student is author' => [ 711 'currentuser' => 'student', 712 'fileauthor' => 'student', 713 'filecomponent' => 'contentbank', 714 'expected' => false, 715 ], 716 'contentbank: Student user, teacher is author' => [ 717 'currentuser' => 'student', 718 'fileauthor' => 'teacher', 719 'filecomponent' => 'contentbank', 720 'expected' => false, 721 ], 722 723 // Unexisting components. 724 'Unexisting component' => [ 725 'currentuser' => 'admin', 726 'fileauthor' => 'admin', 727 'filecomponent' => 'unexisting_component', 728 'expected' => false, 729 ], 730 'Unexisting module activity' => [ 731 'currentuser' => 'admin', 732 'fileauthor' => 'admin', 733 'filecomponent' => 'mod_unexisting', 734 'expected' => false, 735 ], 736 'Unexisting block' => [ 737 'currentuser' => 'admin', 738 'fileauthor' => 'admin', 739 'filecomponent' => 'block_unexisting', 740 'expected' => false, 741 ], 742 ]; 743 } 744 745 /** 746 * Test the behaviour of create_content_from_pluginfile_url(). 747 */ 748 public function test_create_content_from_pluginfile_url(): void { 749 global $DB; 750 751 $this->setRunTestInSeparateProcess(true); 752 $this->resetAfterTest(); 753 $factory = new factory(); 754 755 // Create the H5P data. 756 $filename = 'find-the-words.h5p'; 757 $path = __DIR__ . '/fixtures/' . $filename; 758 $fakefile = helper::create_fake_stored_file_from_path($path); 759 $config = (object)[ 760 'frame' => 1, 761 'export' => 1, 762 'embed' => 0, 763 'copyright' => 0, 764 ]; 765 766 // Get URL for this H5P content file. 767 $syscontext = \context_system::instance(); 768 $url = \moodle_url::make_pluginfile_url( 769 $syscontext->id, 770 \core_h5p\file_storage::COMPONENT, 771 'unittest', 772 $fakefile->get_itemid(), 773 '/', 774 $filename 775 ); 776 777 // Scenario 1: Create the H5P from this URL and check the content is exactly the same as the fake file. 778 $messages = new \stdClass(); 779 list($newfile, $h5pid) = api::create_content_from_pluginfile_url($url->out(), $config, $factory, $messages); 780 $this->assertNotFalse($h5pid); 781 $h5p = $DB->get_record('h5p', ['id' => $h5pid]); 782 $this->assertEquals($fakefile->get_pathnamehash(), $h5p->pathnamehash); 783 $this->assertEquals($fakefile->get_contenthash(), $h5p->contenthash); 784 $this->assertTrue(empty($messages->error)); 785 $this->assertTrue(empty($messages->info)); 786 787 // Scenario 2: Create the H5P for an unexisting H5P file. 788 $url = \moodle_url::make_pluginfile_url( 789 $syscontext->id, 790 \core_h5p\file_storage::COMPONENT, 791 'unittest', 792 $fakefile->get_itemid(), 793 '/', 794 'unexisting.h5p' 795 ); 796 list($newfile, $h5p) = api::create_content_from_pluginfile_url($url->out(), $config, $factory, $messages); 797 $this->assertFalse($newfile); 798 $this->assertFalse($h5p); 799 $this->assertTrue(empty($messages->error)); 800 $this->assertTrue(empty($messages->info)); 801 } 802 803 /** 804 * Test the behaviour of delete_content_from_pluginfile_url(). 805 */ 806 public function test_delete_content_from_pluginfile_url(): void { 807 global $DB; 808 809 $this->setRunTestInSeparateProcess(true); 810 $this->resetAfterTest(); 811 $factory = new factory(); 812 813 // Create the H5P data. 814 $filename = 'find-the-words.h5p'; 815 $path = __DIR__ . '/fixtures/' . $filename; 816 $fakefile = helper::create_fake_stored_file_from_path($path); 817 $config = (object)[ 818 'frame' => 1, 819 'export' => 1, 820 'embed' => 0, 821 'copyright' => 0, 822 ]; 823 824 // Get URL for this H5P content file. 825 $syscontext = \context_system::instance(); 826 $url = \moodle_url::make_pluginfile_url( 827 $syscontext->id, 828 \core_h5p\file_storage::COMPONENT, 829 'unittest', 830 $fakefile->get_itemid(), 831 '/', 832 $filename 833 ); 834 835 // Scenario 1: Try to remove the H5P content for an undeployed file. 836 list($newfile, $h5p) = api::get_content_from_pluginfile_url($url->out()); 837 $this->assertEquals(0, $DB->count_records('h5p')); 838 api::delete_content_from_pluginfile_url($url->out(), $factory); 839 $this->assertEquals(0, $DB->count_records('h5p')); 840 841 // Scenario 2: Deploy an H5P from this URL, check it's created, remove it and check it has been removed as expected. 842 $this->assertEquals(0, $DB->count_records('h5p')); 843 844 $messages = new \stdClass(); 845 list($newfile, $h5pid) = api::create_content_from_pluginfile_url($url->out(), $config, $factory, $messages); 846 $this->assertEquals(1, $DB->count_records('h5p')); 847 848 api::delete_content_from_pluginfile_url($url->out(), $factory); 849 $this->assertEquals(0, $DB->count_records('h5p')); 850 851 // Scenario 3: Try to remove the H5P for an unexisting H5P URL. 852 $url = \moodle_url::make_pluginfile_url( 853 $syscontext->id, 854 \core_h5p\file_storage::COMPONENT, 855 'unittest', 856 $fakefile->get_itemid(), 857 '/', 858 'unexisting.h5p' 859 ); 860 $this->assertEquals(0, $DB->count_records('h5p')); 861 api::delete_content_from_pluginfile_url($url->out(), $factory); 862 $this->assertEquals(0, $DB->count_records('h5p')); 863 } 864 865 /** 866 * Test the behaviour of get_export_info_from_context_id(). 867 */ 868 public function test_get_export_info_from_context_id(): void { 869 global $DB; 870 871 $this->setRunTestInSeparateProcess(true); 872 $this->resetAfterTest(); 873 $factory = new factory(); 874 875 // Create the H5P data. 876 $filename = 'find-the-words.h5p'; 877 $syscontext = \context_system::instance(); 878 879 // Test scenario 1: H5P exists and deployed. 880 $generator = $this->getDataGenerator()->get_plugin_generator('core_h5p'); 881 $fakeexportfile = $generator->create_export_file($filename, 882 $syscontext->id, 883 \core_h5p\file_storage::COMPONENT, 884 \core_h5p\file_storage::EXPORT_FILEAREA); 885 886 $exportfile = api::get_export_info_from_context_id($syscontext->id, 887 $factory, 888 \core_h5p\file_storage::COMPONENT, 889 \core_h5p\file_storage::EXPORT_FILEAREA); 890 $this->assertEquals($fakeexportfile['filename'], $exportfile['filename']); 891 $this->assertEquals($fakeexportfile['filepath'], $exportfile['filepath']); 892 $this->assertEquals($fakeexportfile['filesize'], $exportfile['filesize']); 893 $this->assertEquals($fakeexportfile['timemodified'], $exportfile['timemodified']); 894 $this->assertEquals($fakeexportfile['fileurl'], $exportfile['fileurl']); 895 896 // Test scenario 2: H5P exist, deployed but the content has changed. 897 // We need to change the contenthash to simulate the H5P file was changed. 898 $h5pfile = $DB->get_record('h5p', []); 899 $h5pfile->contenthash = sha1('testedit'); 900 $DB->update_record('h5p', $h5pfile); 901 $exportfile = api::get_export_info_from_context_id($syscontext->id, 902 $factory, 903 \core_h5p\file_storage::COMPONENT, 904 \core_h5p\file_storage::EXPORT_FILEAREA); 905 $this->assertNull($exportfile); 906 907 // Tests scenario 3: H5P is not deployed. 908 // We need to delete the H5P record to simulate the H5P was not deployed. 909 $DB->delete_records('h5p', ['id' => $h5pfile->id]); 910 $exportfile = api::get_export_info_from_context_id($syscontext->id, 911 $factory, 912 \core_h5p\file_storage::COMPONENT, 913 \core_h5p\file_storage::EXPORT_FILEAREA); 914 $this->assertNull($exportfile); 915 } 916 917 /** 918 * Test the behaviour of set_library_enabled(). 919 * 920 * @covers ::set_library_enabled 921 * @dataProvider set_library_enabled_provider 922 * 923 * @param string $libraryname Library name to enable/disable. 924 * @param string $action Action to be done with the library. Supported values: enable, disable. 925 * @param int $expected Expected value for the enabled library field. -1 will be passed if the library doesn't exist. 926 */ 927 public function test_set_library_enabled(string $libraryname, string $action, int $expected): void { 928 global $DB; 929 930 $this->resetAfterTest(); 931 932 // Create libraries. 933 $generator = $this->getDataGenerator()->get_plugin_generator('core_h5p'); 934 $generator->generate_h5p_data(); 935 936 // Check by default the library is enabled. 937 $library = $DB->get_record('h5p_libraries', ['machinename' => $libraryname]); 938 if ($expected >= 0) { 939 $this->assertEquals(1, $library->enabled); 940 $libraryid = (int) $library->id; 941 } else { 942 // Unexisting library. Set libraryid to some unexisting id. 943 $libraryid = -1; 944 $this->expectException('dml_missing_record_exception'); 945 } 946 947 \core_h5p\api::set_library_enabled($libraryid, ($action == 'enable')); 948 949 // Check the value of the "enabled" field after calling enable/disable method. 950 $libraries = $DB->get_records('h5p_libraries'); 951 foreach ($libraries as $libraryid => $library) { 952 if ($library->machinename == $libraryname) { 953 $this->assertEquals($expected, $library->enabled); 954 } else { 955 // Check that only $libraryname has been enabled/disabled. 956 $this->assertEquals(1, $library->enabled); 957 } 958 } 959 } 960 961 /** 962 * Data provider for test_set_library_enabled(). 963 * 964 * @return array 965 */ 966 public function set_library_enabled_provider(): array { 967 return [ 968 'Disable existing library' => [ 969 'libraryname' => 'MainLibrary', 970 'action' => 'disable', 971 'expected' => 0, 972 ], 973 'Enable existing library' => [ 974 'libraryname' => 'MainLibrary', 975 'action' => 'enable', 976 'expected' => 1, 977 ], 978 'Disable existing library (not main)' => [ 979 'libraryname' => 'Library1', 980 'action' => 'disable', 981 'expected' => 0, 982 ], 983 'Enable existing library (not main)' => [ 984 'libraryname' => 'Library1', 985 'action' => 'enable', 986 'expected' => 1, 987 ], 988 'Disable existing library (not runnable)' => [ 989 'libraryname' => 'Library3', 990 'action' => 'disable', 991 'expected' => 1, // Not runnable libraries can't be disabled. 992 ], 993 'Enable existing library (not runnable)' => [ 994 'libraryname' => 'Library3', 995 'action' => 'enable', 996 'expected' => 1, 997 ], 998 'Enable unexisting library' => [ 999 'libraryname' => 'Unexisting library', 1000 'action' => 'enable', 1001 'expected' => -1, 1002 ], 1003 'Disable unexisting library' => [ 1004 'libraryname' => 'Unexisting library', 1005 'action' => 'disable', 1006 'expected' => -1, 1007 ], 1008 ]; 1009 } 1010 1011 /** 1012 * Test the behaviour of is_library_enabled(). 1013 * 1014 * @covers ::is_library_enabled 1015 * @dataProvider is_library_enabled_provider 1016 * 1017 * @param string $libraryname Library name to check. 1018 * @param bool $expected Expected result after calling the method. 1019 * @param bool $exception Exception expected or not. 1020 * @param bool $useid Whether to use id for calling is_library_enabled method. 1021 * @param bool $uselibraryname Whether to use libraryname for calling is_library_enabled method. 1022 */ 1023 public function test_is_library_enabled(string $libraryname, bool $expected, bool $exception = false, 1024 bool $useid = false, bool $uselibraryname = true): void { 1025 global $DB; 1026 1027 $this->resetAfterTest(); 1028 1029 // Create the following libraries: 1030 // - H5P.Lib1: 1 version enabled, 1 version disabled. 1031 // - H5P.Lib2: 2 versions enabled. 1032 // - H5P.Lib3: 2 versions disabled. 1033 // - H5P.Lib4: 1 version disabled. 1034 // - H5P.Lib5: 1 version enabled. 1035 $generator = $this->getDataGenerator()->get_plugin_generator('core_h5p'); 1036 $libraries = [ 1037 'H5P.Lib1.1' => $generator->create_library_record('H5P.Lib1', 'Lib1', 1, 1, 0, '', null, null, null, false), 1038 'H5P.Lib1.2' => $generator->create_library_record('H5P.Lib1', 'Lib1', 1, 2), 1039 'H5P.Lib2.1' => $generator->create_library_record('H5P.Lib2', 'Lib2', 2, 1), 1040 'H5P.Lib2.2' => $generator->create_library_record('H5P.Lib2', 'Lib2', 2, 2), 1041 'H5P.Lib3.1' => $generator->create_library_record('H5P.Lib3', 'Lib3', 3, 1, 0, '', null, null, null, false), 1042 'H5P.Lib3.2' => $generator->create_library_record('H5P.Lib3', 'Lib3', 3, 2, 0, '', null, null, null, false), 1043 'H5P.Lib4.1' => $generator->create_library_record('H5P.Lib4', 'Lib4', 4, 1, 0, '', null, null, null, false), 1044 'H5P.Lib5.1' => $generator->create_library_record('H5P.Lib5', 'Lib5', 5, 1), 1045 ]; 1046 1047 $countenabledlibraries = $DB->count_records('h5p_libraries', ['enabled' => 1]); 1048 $this->assertEquals(4, $countenabledlibraries); 1049 1050 if ($useid) { 1051 $librarydata = ['id' => $libraries[$libraryname]->id]; 1052 } else if ($uselibraryname) { 1053 $librarydata = ['machinename' => $libraryname]; 1054 } else { 1055 $librarydata = ['invalid' => true]; 1056 } 1057 1058 if ($exception) { 1059 $this->expectException(\moodle_exception::class); 1060 } 1061 1062 $result = api::is_library_enabled((object) $librarydata); 1063 $this->assertEquals($expected, $result); 1064 } 1065 1066 /** 1067 * Data provider for test_is_library_enabled(). 1068 * 1069 * @return array 1070 */ 1071 public function is_library_enabled_provider(): array { 1072 return [ 1073 'Library with 2 versions, one of them disabled' => [ 1074 'libraryname' => 'H5P.Lib1', 1075 'expected' => false, 1076 ], 1077 'Library with 2 versions, all enabled' => [ 1078 'libraryname' => 'H5P.Lib2', 1079 'expected' => true, 1080 ], 1081 'Library with 2 versions, all disabled' => [ 1082 'libraryname' => 'H5P.Lib3', 1083 'expected' => false, 1084 ], 1085 'Library with only one version, disabled' => [ 1086 'libraryname' => 'H5P.Lib4', 1087 'expected' => false, 1088 ], 1089 'Library with only one version, enabled' => [ 1090 'libraryname' => 'H5P.Lib5', 1091 'expected' => true, 1092 ], 1093 'Library with 2 versions, one of them disabled (using id) - 1.1 (disabled)' => [ 1094 'libraryname' => 'H5P.Lib1.1', 1095 'expected' => false, 1096 'exception' => false, 1097 'useid' => true, 1098 ], 1099 'Library with 2 versions, one of them disabled (using id) - 1.2 (enabled)' => [ 1100 'libraryname' => 'H5P.Lib1.2', 1101 'expected' => true, 1102 'exception' => false, 1103 'useid' => true, 1104 ], 1105 'Library with 2 versions, all enabled (using id) - 2.1' => [ 1106 'libraryname' => 'H5P.Lib2.1', 1107 'expected' => true, 1108 'exception' => false, 1109 'useid' => true, 1110 ], 1111 'Library with 2 versions, all enabled (using id) - 2.2' => [ 1112 'libraryname' => 'H5P.Lib2.2', 1113 'expected' => true, 1114 'exception' => false, 1115 'useid' => true, 1116 ], 1117 'Library with 2 versions, all disabled (using id) - 3.1' => [ 1118 'libraryname' => 'H5P.Lib3.1', 1119 'expected' => false, 1120 'exception' => false, 1121 'useid' => true, 1122 ], 1123 'Library with 2 versions, all disabled (using id) - 3.2' => [ 1124 'libraryname' => 'H5P.Lib3.2', 1125 'expected' => false, 1126 'exception' => false, 1127 'useid' => true, 1128 ], 1129 'Library with only one version, disabled (using id)' => [ 1130 'libraryname' => 'H5P.Lib4.1', 1131 'expected' => false, 1132 'exception' => false, 1133 'useid' => true, 1134 ], 1135 'Library with only one version, enabled (using id)' => [ 1136 'libraryname' => 'H5P.Lib5.1', 1137 'expected' => true, 1138 'exception' => false, 1139 'useid' => true, 1140 ], 1141 'Unexisting library' => [ 1142 'libraryname' => 'H5P.Unexisting', 1143 'expected' => true, 1144 ], 1145 'Missing required parameters' => [ 1146 'libraryname' => 'H5P.Unexisting', 1147 'expected' => false, 1148 'exception' => true, 1149 'useid' => false, 1150 'uselibraryname' => false, 1151 ], 1152 ]; 1153 } 1154 1155 /** 1156 * Test the behaviour of is_valid_package(). 1157 * @runInSeparateProcess 1158 * 1159 * @covers ::is_valid_package 1160 * @dataProvider is_valid_package_provider 1161 * 1162 * @param string $filename The H5P content to validate. 1163 * @param bool $expected Expected result after calling the method. 1164 * @param bool $isadmin Whether the user calling the method will be admin or not. 1165 * @param bool $onlyupdatelibs Whether new libraries can be installed or only the existing ones can be updated. 1166 * @param bool $skipcontent Should the content be skipped (so only the libraries will be saved)? 1167 */ 1168 public function test_is_valid_package(string $filename, bool $expected, bool $isadmin = false, bool $onlyupdatelibs = false, 1169 bool $skipcontent = false): void { 1170 global $USER; 1171 1172 $this->resetAfterTest(); 1173 1174 if ($isadmin) { 1175 $this->setAdminUser(); 1176 $user = $USER; 1177 } else { 1178 // Create a user. 1179 $user = $this->getDataGenerator()->create_user(); 1180 $this->setUser($user); 1181 } 1182 1183 // Prepare the file. 1184 $path = __DIR__ . $filename; 1185 $file = helper::create_fake_stored_file_from_path($path, (int)$user->id); 1186 1187 // Check if the H5P content is valid or not. 1188 $result = api::is_valid_package($file, $onlyupdatelibs, $skipcontent); 1189 $this->assertEquals($expected, $result); 1190 } 1191 1192 /** 1193 * Data provider for test_is_valid_package(). 1194 * 1195 * @return array 1196 */ 1197 public function is_valid_package_provider(): array { 1198 return [ 1199 'Valid H5P file (as admin)' => [ 1200 'filename' => '/fixtures/greeting-card.h5p', 1201 'expected' => true, 1202 'isadmin' => true, 1203 ], 1204 'Valid H5P file (as user) without library update and checking content' => [ 1205 'filename' => '/fixtures/greeting-card.h5p', 1206 'expected' => false, // Libraries are missing and user hasn't the right permissions to upload them. 1207 'isadmin' => false, 1208 'onlyupdatelibs' => false, 1209 'skipcontent' => false, 1210 ], 1211 'Valid H5P file (as user) with library update and checking content' => [ 1212 'filename' => '/fixtures/greeting-card.h5p', 1213 'expected' => false, // Libraries are missing and user hasn't the right permissions to upload them. 1214 'isadmin' => false, 1215 'onlyupdatelibs' => true, 1216 'skipcontent' => false, 1217 ], 1218 'Valid H5P file (as user) without library update and skipping content' => [ 1219 'filename' => '/fixtures/greeting-card.h5p', 1220 'expected' => true, // Content check is skipped so the package will be considered valid. 1221 'isadmin' => false, 1222 'onlyupdatelibs' => false, 1223 'skipcontent' => true, 1224 ], 1225 'Valid H5P file (as user) with library update and skipping content' => [ 1226 'filename' => '/fixtures/greeting-card.h5p', 1227 'expected' => true, // Content check is skipped so the package will be considered valid. 1228 'isadmin' => false, 1229 'onlyupdatelibs' => true, 1230 'skipcontent' => true, 1231 ], 1232 'Invalid H5P file (as admin)' => [ 1233 'filename' => '/fixtures/h5ptest.zip', 1234 'expected' => false, 1235 'isadmin' => true, 1236 ], 1237 'Invalid H5P file (as user)' => [ 1238 'filename' => '/fixtures/h5ptest.zip', 1239 'expected' => false, 1240 'isadmin' => false, 1241 ], 1242 'Invalid H5P file (as user) skipping content' => [ 1243 'filename' => '/fixtures/h5ptest.zip', 1244 'expected' => true, // Content check is skipped so the package will be considered valid. 1245 'isadmin' => false, 1246 'onlyupdatelibs' => false, 1247 'skipcontent' => true, 1248 ], 1249 ]; 1250 } 1251 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body