Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.2.x will end 22 April 2024 (12 months).
  • Bug fixes for security issues in 4.2.x will end 7 October 2024 (18 months).
  • PHP version: minimum PHP 8.0.0 Note: minimum PHP version has increased since Moodle 4.1. PHP 8.1.x is supported too.

Differences Between: [Versions 311 and 402]

   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  namespace core_h5p;
  18  
  19  use core_h5p\file_storage;
  20  use core_h5p\local\library\autoloader;
  21  use core_h5p\helper;
  22  use file_archive;
  23  use moodle_exception;
  24  use ReflectionMethod;
  25  use stored_file;
  26  use zip_archive;
  27  
  28  /**
  29   * Test class covering the H5PFileStorage interface implementation.
  30   *
  31   * @package    core_h5p
  32   * @category   test
  33   * @copyright  2019 Victor Deniz <victor@moodle.com>
  34   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  35   * @runTestsInSeparateProcesses
  36   */
  37  class file_storage_test extends \advanced_testcase {
  38  
  39      /** @var \core_h5p\file_storage H5P file storage instance */
  40      protected $h5p_file_storage;
  41      /** @var \file_storage Core Moodle file_storage associated to the H5P file_storage */
  42      protected $h5p_fs_fs;
  43      /** @var \context Moodle context of the H5P file_storage */
  44      protected $h5p_fs_context;
  45      /** @var string Path to temp directory */
  46      protected $h5p_tempath;
  47      /** @var \core_h5p_generator  H5P generator instance */
  48      protected $h5p_generator;
  49      /** @var array $files an array used in the cache tests. */
  50      protected $files = ['scripts' => [], 'styles' => []];
  51      /** @var int $libraryid an id for the library. */
  52      protected $libraryid = 1;
  53  
  54      protected function setUp(): void {
  55          parent::setUp();
  56          $this->resetAfterTest(true);
  57  
  58          autoloader::register();
  59  
  60          // Fetch generator.
  61          $generator = \testing_util::get_data_generator();
  62          $this->h5p_generator = $generator->get_plugin_generator('core_h5p');
  63  
  64          // Create file_storage_instance and create H5P temp directory.
  65          $this->h5p_file_storage = new file_storage();
  66          $this->h5p_tempath = $this->h5p_file_storage->getTmpPath();
  67          check_dir_exists($this->h5p_tempath);
  68  
  69          // Get value of protected properties.
  70          $h5p_fs_rc = new \ReflectionClass(file_storage::class);
  71          $h5p_file_storage_context = $h5p_fs_rc->getProperty('context');
  72          $h5p_file_storage_context->setAccessible(true);
  73          $this->h5p_fs_context = $h5p_file_storage_context->getValue($this->h5p_file_storage);
  74  
  75          $h5p_file_storage_fs = $h5p_fs_rc->getProperty('fs');
  76          $h5p_file_storage_fs->setAccessible(true);
  77          $this->h5p_fs_fs = $h5p_file_storage_fs->getValue($this->h5p_file_storage);
  78      }
  79  
  80      /**
  81       * Test that given the main directory of a library that all files are saved
  82       * into the file system.
  83       */
  84      public function test_saveLibrary(): void {
  85  
  86          $machinename = 'TestLib';
  87          $majorversion = 1;
  88          $minorversion = 0;
  89          [$lib, $files] = $this->h5p_generator->create_library($this->h5p_tempath, $this->libraryid, $machinename, $majorversion,
  90              $minorversion);
  91  
  92          // Now run the API call.
  93          $this->h5p_file_storage->saveLibrary($lib);
  94  
  95          // Check that files are in the Moodle file system.
  96          $file =  $this->h5p_fs_fs->get_file ($this->h5p_fs_context->id, file_storage::COMPONENT,
  97              file_storage::LIBRARY_FILEAREA, '1', "/{$machinename}-{$majorversion}.{$minorversion}/", 'library.json');
  98          $filepath = "/{$machinename}-{$majorversion}.{$minorversion}/";
  99          $this->assertEquals($filepath, $file->get_filepath());
 100  
 101          $file =  $this->h5p_fs_fs->get_file ($this->h5p_fs_context->id, file_storage::COMPONENT,
 102              file_storage::LIBRARY_FILEAREA, '1', "/{$machinename}-{$majorversion}.{$minorversion}/scripts/", 'testlib.min.js');
 103          $jsfilepath = "{$filepath}scripts/";
 104          $this->assertEquals($jsfilepath, $file->get_filepath());
 105  
 106          $file =  $this->h5p_fs_fs->get_file ($this->h5p_fs_context->id, file_storage::COMPONENT,
 107              file_storage::LIBRARY_FILEAREA, '1', "/{$machinename}-{$majorversion}.{$minorversion}/styles/", 'testlib.min.css');
 108          $cssfilepath = "{$filepath}styles/";
 109          $this->assertEquals($cssfilepath, $file->get_filepath());
 110      }
 111  
 112      /**
 113       * Test that a content file can be saved.
 114       */
 115      public function test_saveContent(): void {
 116  
 117          $source = $this->h5p_tempath . '/' . 'content.json';
 118          $this->h5p_generator->create_file($source);
 119  
 120          $this->h5p_file_storage->saveContent($this->h5p_tempath, ['id' => 5]);
 121  
 122          $file =  $this->h5p_fs_fs->get_file ($this->h5p_fs_context->id, file_storage::COMPONENT,
 123              file_storage::CONTENT_FILEAREA, '5', '/', 'content.json');
 124          $this->assertEquals(file_storage::CONTENT_FILEAREA, $file->get_filearea());
 125          $this->assertEquals('content.json', $file->get_filename());
 126          $this->assertEquals(5, $file->get_itemid());
 127      }
 128  
 129      /**
 130       * Test that content files located on the file system can be deleted.
 131       */
 132      public function test_deleteContent(): void {
 133  
 134          $source = $this->h5p_tempath . '/' . 'content.json';
 135          $this->h5p_generator->create_file($source);
 136  
 137          $this->h5p_file_storage->saveContent($this->h5p_tempath, ['id' => 5]);
 138  
 139          $file =  $this->h5p_fs_fs->get_file ($this->h5p_fs_context->id, file_storage::COMPONENT,
 140              file_storage::CONTENT_FILEAREA, '5', '/', 'content.json');
 141          $this->assertEquals('content.json', $file->get_filename());
 142  
 143          // Now to delete the record.
 144          $this->h5p_file_storage->deleteContent(['id' => 5]);
 145          $file =  $this->h5p_fs_fs->get_file ($this->h5p_fs_context->id, file_storage::COMPONENT,
 146              file_storage::CONTENT_FILEAREA, '5', '/', 'content.json');
 147          $this->assertFalse($file);
 148      }
 149  
 150      /**
 151       * Test that returning a temp path returns what is expected by the h5p library.
 152       */
 153      public function test_getTmpPath(): void {
 154  
 155          $temparray = explode('/', $this->h5p_tempath);
 156          $h5pdirectory = array_pop($temparray);
 157          $this->assertTrue(stripos($h5pdirectory, 'h5p-') === 0);
 158      }
 159  
 160      /**
 161       * Test that the content files can be exported to a specified location.
 162       */
 163      public function test_exportContent(): void {
 164  
 165          // Create a file to store.
 166          $source = $this->h5p_tempath . '/' . 'content.json';
 167          $this->h5p_generator->create_file($source);
 168  
 169          $this->h5p_file_storage->saveContent($this->h5p_tempath, ['id' => 5]);
 170  
 171          $file =  $this->h5p_fs_fs->get_file ($this->h5p_fs_context->id, file_storage::COMPONENT,
 172              file_storage::CONTENT_FILEAREA, '5', '/', 'content.json');
 173          $this->assertEquals('content.json', $file->get_filename());
 174  
 175          // Now export it.
 176          $destinationdirectory = $this->h5p_tempath . '/' . 'testdir';
 177          check_dir_exists($destinationdirectory);
 178  
 179          $this->h5p_file_storage->exportContent(5, $destinationdirectory);
 180          // Check that there is a file now in that directory.
 181          $contents = scandir($destinationdirectory);
 182          $value = array_search('content.json', $contents);
 183          $this->assertEquals('content.json', $contents[$value]);
 184      }
 185  
 186      /**
 187       * Test that libraries on the file system can be exported to a specified location.
 188       */
 189      public function test_exportLibrary(): void {
 190  
 191          $machinename = 'TestLib';
 192          $majorversion = 1;
 193          $minorversion = 0;
 194          [$lib, $files] = $this->h5p_generator->create_library($this->h5p_tempath, $this->libraryid, $machinename, $majorversion,
 195          $minorversion);
 196  
 197          // Now run the API call.
 198          $this->h5p_file_storage->saveLibrary($lib);
 199  
 200          $destinationdirectory = $this->h5p_tempath . '/' . 'testdir';
 201          check_dir_exists($destinationdirectory);
 202  
 203          $this->h5p_file_storage->exportLibrary($lib, $destinationdirectory);
 204  
 205          $filepath = "/{$machinename}-{$majorversion}.{$minorversion}/";
 206          // There should be at least three items here (but could be more with . and ..).
 207          $this->assertFileExists($destinationdirectory . $filepath . 'library.json');
 208          $this->assertFileExists($destinationdirectory . $filepath . 'scripts/' . 'testlib.min.js');
 209          $this->assertFileExists($destinationdirectory . $filepath . 'styles/' . 'testlib.min.css');
 210      }
 211  
 212      /**
 213       * Test that an export file can be saved into the file system.
 214       */
 215      public function test_saveExport(): void {
 216  
 217          $filename = 'someexportedfile.h5p';
 218          $source = $this->h5p_tempath . '/' . $filename;
 219          $this->h5p_generator->create_file($source);
 220  
 221          $this->h5p_file_storage->saveExport($source, $filename);
 222  
 223          // Check out if the file is there.
 224          $file =  $this->h5p_fs_fs->get_file ($this->h5p_fs_context->id, file_storage::COMPONENT,
 225              file_storage::EXPORT_FILEAREA, '0', '/', $filename);
 226          $this->assertEquals(file_storage::EXPORT_FILEAREA, $file->get_filearea());
 227      }
 228  
 229      /**
 230       * Test that an exort file can be deleted from the file system.
 231       * @return [type] [description]
 232       */
 233      public function test_deleteExport(): void {
 234  
 235          $filename = 'someexportedfile.h5p';
 236          $source = $this->h5p_tempath . '/' . $filename;
 237          $this->h5p_generator->create_file($source);
 238  
 239          $this->h5p_file_storage->saveExport($source, $filename);
 240  
 241          // Check out if the file is there.
 242          $file =  $this->h5p_fs_fs->get_file ($this->h5p_fs_context->id, file_storage::COMPONENT,
 243              file_storage::EXPORT_FILEAREA, '0', '/', $filename);
 244          $this->assertEquals(file_storage::EXPORT_FILEAREA, $file->get_filearea());
 245  
 246          // Time to delete.
 247          $this->h5p_file_storage->deleteExport($filename);
 248  
 249          // Check out if the file is there.
 250          $file =  $this->h5p_fs_fs->get_file ($this->h5p_fs_context->id, file_storage::COMPONENT,
 251              file_storage::EXPORT_FILEAREA, '0', '/', $filename);
 252          $this->assertFalse($file);
 253      }
 254  
 255      /**
 256       * Test to check if an export file already exists on the file system.
 257       */
 258      public function test_hasExport(): void {
 259  
 260          $filename = 'someexportedfile.h5p';
 261          $source = $this->h5p_tempath . '/' . $filename;
 262          $this->h5p_generator->create_file($source);
 263  
 264          // Check that it doesn't exist in the file system.
 265          $this->assertFalse($this->h5p_file_storage->hasExport($filename));
 266  
 267          $this->h5p_file_storage->saveExport($source, $filename);
 268          // Now it should be present.
 269          $this->assertTrue($this->h5p_file_storage->hasExport($filename));
 270      }
 271  
 272      /**
 273       * Test that all the library files for an H5P activity can be concatenated into "cache" files. One for js and another for css.
 274       */
 275      public function test_cacheAssets(): void {
 276  
 277          $basedirectory = $this->h5p_tempath . '/' . 'test-1.0';
 278  
 279          $machinename = 'TestLib';
 280          $majorversion = 1;
 281          $minorversion = 0;
 282          [$lib, $libfiles] = $this->h5p_generator->create_library($basedirectory, $this->libraryid, $machinename, $majorversion,
 283              $minorversion);
 284          array_push($this->files['scripts'], ...$libfiles['scripts']);
 285          array_push($this->files['styles'], ...$libfiles['styles']);
 286  
 287          // Now run the API call.
 288          $this->h5p_file_storage->saveLibrary($lib);
 289  
 290          // Second library.
 291          $basedirectory = $this->h5p_tempath . '/' . 'supertest-2.4';
 292  
 293          $this->libraryid++;
 294          $machinename = 'SuperTest';
 295          $majorversion = 2;
 296          $minorversion = 4;
 297          [$lib, $libfiles] = $this->h5p_generator->create_library($basedirectory, $this->libraryid, $machinename, $majorversion,
 298              $minorversion);
 299          array_push($this->files['scripts'], ...$libfiles['scripts']);
 300          array_push($this->files['styles'], ...$libfiles['styles']);
 301  
 302          $this->h5p_file_storage->saveLibrary($lib);
 303  
 304          $this->assertCount(2, $this->files['scripts']);
 305          $this->assertCount(2, $this->files['styles']);
 306  
 307          $key = 'testhashkey';
 308  
 309          $this->h5p_file_storage->cacheAssets($this->files, $key);
 310          $this->assertCount(1, $this->files['scripts']);
 311          $this->assertCount(1, $this->files['styles']);
 312  
 313  
 314          $expectedfile = '/' . file_storage::CACHED_ASSETS_FILEAREA . '/' . $key . '.js';
 315          $this->assertEquals($expectedfile, $this->files['scripts'][0]->path);
 316          $expectedfile = '/' . file_storage::CACHED_ASSETS_FILEAREA . '/' . $key . '.css';
 317          $this->assertEquals($expectedfile, $this->files['styles'][0]->path);
 318      }
 319  
 320      /**
 321       * Test that cached files can be retrieved via a key.
 322       */
 323      public function test_getCachedAssets() {
 324  
 325          $basedirectory = $this->h5p_tempath . '/' . 'test-1.0';
 326  
 327          $machinename = 'TestLib';
 328          $majorversion = 1;
 329          $minorversion = 0;
 330          [$lib, $libfiles] = $this->h5p_generator->create_library($basedirectory, $this->libraryid, $machinename, $majorversion,
 331              $minorversion);
 332          array_push($this->files['scripts'], ...$libfiles['scripts']);
 333          array_push($this->files['styles'], ...$libfiles['styles']);
 334  
 335          // Now run the API call.
 336          $this->h5p_file_storage->saveLibrary($lib);
 337  
 338          // Second library.
 339          $basedirectory = $this->h5p_tempath . '/' . 'supertest-2.4';
 340  
 341          $this->libraryid++;
 342          $machinename = 'SuperTest';
 343          $majorversion = 2;
 344          $minorversion = 4;
 345          [$lib, $libfiles] = $this->h5p_generator->create_library($basedirectory, $this->libraryid, $machinename, $majorversion,
 346              $minorversion);
 347          array_push($this->files['scripts'], ...$libfiles['scripts']);
 348          array_push($this->files['styles'], ...$libfiles['styles']);
 349  
 350          $this->h5p_file_storage->saveLibrary($lib);
 351  
 352          $this->assertCount(2, $this->files['scripts']);
 353          $this->assertCount(2, $this->files['styles']);
 354  
 355          $key = 'testhashkey';
 356  
 357          $this->h5p_file_storage->cacheAssets($this->files, $key);
 358  
 359          $testarray = $this->h5p_file_storage->getCachedAssets($key);
 360          $this->assertCount(1, $testarray['scripts']);
 361          $this->assertCount(1, $testarray['styles']);
 362          $expectedfile = '/' . file_storage::CACHED_ASSETS_FILEAREA . '/' . $key . '.js';
 363          $this->assertEquals($expectedfile, $testarray['scripts'][0]->path);
 364          $expectedfile = '/' . file_storage::CACHED_ASSETS_FILEAREA . '/' . $key . '.css';
 365          $this->assertEquals($expectedfile, $testarray['styles'][0]->path);
 366      }
 367  
 368      /**
 369       * Test that cache files in the files system can be removed.
 370       */
 371      public function test_deleteCachedAssets(): void {
 372          $basedirectory = $this->h5p_tempath . '/' . 'test-1.0';
 373  
 374          $machinename = 'TestLib';
 375          $majorversion = 1;
 376          $minorversion = 0;
 377          [$lib, $libfiles] = $this->h5p_generator->create_library($basedirectory, $this->libraryid, $machinename, $majorversion,
 378              $minorversion);
 379          array_push($this->files['scripts'], ...$libfiles['scripts']);
 380          array_push($this->files['styles'], ...$libfiles['styles']);
 381  
 382          // Now run the API call.
 383          $this->h5p_file_storage->saveLibrary($lib);
 384  
 385          // Second library.
 386          $basedirectory = $this->h5p_tempath . '/' . 'supertest-2.4';
 387  
 388          $this->libraryid++;
 389          $machinename = 'SuperTest';
 390          $majorversion = 2;
 391          $minorversion = 4;
 392          [$lib, $libfiles] = $this->h5p_generator->create_library($basedirectory, $this->libraryid, $machinename, $majorversion,
 393              $minorversion);
 394          array_push($this->files['scripts'], ...$libfiles['scripts']);
 395          array_push($this->files['styles'], ...$libfiles['styles']);
 396  
 397          $this->h5p_file_storage->saveLibrary($lib);
 398  
 399          $this->assertCount(2, $this->files['scripts']);
 400          $this->assertCount(2, $this->files['styles']);
 401  
 402          $key = 'testhashkey';
 403  
 404          $this->h5p_file_storage->cacheAssets($this->files, $key);
 405  
 406          $testarray = $this->h5p_file_storage->getCachedAssets($key);
 407          $this->assertCount(1, $testarray['scripts']);
 408          $this->assertCount(1, $testarray['styles']);
 409  
 410          // Time to delete.
 411          $this->h5p_file_storage->deleteCachedAssets([$key]);
 412          $testarray = $this->h5p_file_storage->getCachedAssets($key);
 413          $this->assertNull($testarray);
 414      }
 415  
 416      /**
 417       * Retrieve content from a file given a specific path.
 418       */
 419      public function test_getContent() {
 420          $basedirectory = $this->h5p_tempath . '/' . 'test-1.0';
 421  
 422          $machinename = 'TestLib';
 423          $majorversion = 1;
 424          $minorversion = 0;
 425          [$lib, $libfiles] = $this->h5p_generator->create_library($basedirectory, $this->libraryid, $machinename, $majorversion,
 426              $minorversion);
 427          array_push($this->files['scripts'], ...$libfiles['scripts']);
 428          array_push($this->files['styles'], ...$libfiles['styles']);
 429  
 430          // Now run the API call.
 431          $this->h5p_file_storage->saveLibrary($lib);
 432  
 433          $content = $this->h5p_file_storage->getContent($this->files['scripts'][0]->path);
 434          // The file content is created based on the file system path (\core_h5p_generator::create_file).
 435          $expectedcontent = hash("md5", $basedirectory. '/' . 'scripts' . '/' . 'testlib.min.js');
 436  
 437          $this->assertEquals($expectedcontent, $content);
 438      }
 439  
 440      /**
 441       * Test that an upgrade script can be found on the file system.
 442       */
 443      public function test_getUpgradeScript() {
 444          // Upload an upgrade file.
 445          $machinename = 'TestLib';
 446          $majorversion = 3;
 447          $minorversion = 1;
 448          $filepath = '/' . "{$machinename}-{$majorversion}.{$minorversion}" . '/';
 449          $fs = get_file_storage();
 450          $filerecord = [
 451              'contextid' => \context_system::instance()->id,
 452              'component' => file_storage::COMPONENT,
 453              'filearea' => file_storage::LIBRARY_FILEAREA,
 454              'itemid' => 15,
 455              'filepath' => $filepath,
 456              'filename' => 'upgrade.js'
 457          ];
 458          $filestorage = new file_storage();
 459          $fs->create_file_from_string($filerecord, 'test string info');
 460          $expectedfilepath = '/' . file_storage::LIBRARY_FILEAREA . $filepath . 'upgrade.js';
 461          $this->assertEquals($expectedfilepath, $filestorage->getUpgradeScript($machinename, $majorversion, $minorversion));
 462          $this->assertNull($filestorage->getUpgradeScript($machinename, $majorversion, 7));
 463      }
 464  
 465      /**
 466       * Test that information from a source can be saved to the specified path.
 467       * The zip file has the following contents
 468       * - h5ptest
 469       * |- content
 470       * |     |- content.json
 471       * |- testFont
 472       * |     |- testfont.min.css
 473       * |- testJavaScript
 474       * |     |- testscript.min.js
 475       * |- h5p.json
 476       */
 477      public function test_saveFileFromZip() {
 478  
 479          $ziparchive = new zip_archive();
 480          $path = __DIR__ . '/fixtures/h5ptest.zip';
 481          $result = $ziparchive->open($path, file_archive::OPEN);
 482  
 483          $files = $ziparchive->list_files();
 484          foreach ($files as $file) {
 485              if (!$file->is_directory) {
 486                  $stream = $ziparchive->get_stream($file->index);
 487                  $items = explode('/', $file->pathname);
 488                  array_shift($items);
 489                  $path = implode('/', $items);
 490                  $this->h5p_file_storage->saveFileFromZip($this->h5p_tempath, $path, $stream);
 491                  $filestocheck[] = $path;
 492              }
 493          }
 494          $ziparchive->close();
 495  
 496          foreach ($filestocheck as $filetocheck) {
 497              $pathtocheck = $this->h5p_tempath .'/'. $filetocheck;
 498              $this->assertFileExists($pathtocheck);
 499          }
 500      }
 501  
 502      /**
 503       * Test that a library is fully deleted from the file system
 504       */
 505      public function test_delete_library() {
 506  
 507          $basedirectory = $this->h5p_tempath . '/' . 'test-1.0';
 508  
 509          $machinename = 'TestLib';
 510          $majorversion = 1;
 511          $minorversion = 0;
 512          [$lib, $files] = $this->h5p_generator->create_library($basedirectory, $this->libraryid, $machinename, $majorversion,
 513              $minorversion);
 514  
 515          // Now run the API call.
 516          $this->h5p_file_storage->saveLibrary($lib);
 517  
 518          // Save a second library to ensure we aren't deleting all libraries, but just the one specified.
 519          $basedirectory = $this->h5p_tempath . '/' . 'awesomelib-2.1';
 520  
 521          $this->libraryid++;
 522          $machinename = 'AwesomeLib';
 523          $majorversion = 2;
 524          $minorversion = 1;
 525          [$lib2, $files2] = $this->h5p_generator->create_library($basedirectory, $this->libraryid, $machinename, $majorversion,
 526              $minorversion);
 527  
 528          // Now run the API call.
 529          $this->h5p_file_storage->saveLibrary($lib2);
 530  
 531          $files =  $this->h5p_fs_fs->get_area_files($this->h5p_fs_context->id, file_storage::COMPONENT,
 532                  file_storage::LIBRARY_FILEAREA);
 533          $this->assertCount(14, $files);
 534  
 535          $this->h5p_file_storage->delete_library($lib);
 536  
 537          // Let's look at the records.
 538          $files =  $this->h5p_fs_fs->get_area_files($this->h5p_fs_context->id, file_storage::COMPONENT,
 539                  file_storage::LIBRARY_FILEAREA);
 540          $this->assertCount(7, $files);
 541  
 542          // Check that the db count is still the same after setting the libraryId to false.
 543          $lib['libraryId'] = false;
 544          $this->h5p_file_storage->delete_library($lib);
 545  
 546          $files =  $this->h5p_fs_fs->get_area_files($this->h5p_fs_context->id, file_storage::COMPONENT,
 547                  file_storage::LIBRARY_FILEAREA);
 548          $this->assertCount(7, $files);
 549      }
 550  
 551      /**
 552       * Test get_icon_url() function behaviour.
 553       *
 554       * @dataProvider get_icon_url_provider
 555       * @param  string  $filename  The name of the H5P file to load.
 556       * @param  bool    $expected  Whether the icon should exist or not.
 557       */
 558      public function test_get_icon_url(string $filename, bool $expected): void {
 559          global $DB;
 560  
 561          $this->resetAfterTest();
 562          $factory = new \core_h5p\factory();
 563  
 564          $admin = get_admin();
 565  
 566          // Prepare a valid .H5P file.
 567          $path = __DIR__ . '/fixtures/'.$filename;
 568  
 569          // Libraries can be updated when the file has been created by admin, even when the current user is not the admin.
 570          $this->setUser($admin);
 571          $file = helper::create_fake_stored_file_from_path($path, (int)$admin->id);
 572          $factory->get_framework()->set_file($file);
 573          $config = (object)[
 574              'frame' => 1,
 575              'export' => 1,
 576              'embed' => 0,
 577              'copyright' => 0,
 578          ];
 579  
 580          $h5pid = helper::save_h5p($factory, $file, $config);
 581          $h5p = $DB->get_record('h5p', ['id' => $h5pid]);
 582          $h5plib = $DB->get_record('h5p_libraries', ['id' => $h5p->mainlibraryid]);
 583          $iconurl = $this->h5p_file_storage->get_icon_url(
 584              $h5plib->id,
 585              $h5plib->machinename,
 586              $h5plib->majorversion,
 587              $h5plib->minorversion
 588          );
 589          if ($expected) {
 590              $this->assertStringContainsString(file_storage::ICON_FILENAME, $iconurl);
 591          } else {
 592              $this->assertFalse($iconurl);
 593          }
 594      }
 595  
 596      /**
 597       * Data provider for test_get_icon_url().
 598       *
 599       * @return array
 600       */
 601      public function get_icon_url_provider(): array {
 602          return [
 603              'Icon included' => [
 604                  'filltheblanks.h5p',
 605                  true,
 606              ],
 607              'Icon not included' => [
 608                  'greeting-card.h5p',
 609                  false,
 610              ],
 611          ];
 612      }
 613  
 614      /**
 615       * Test the private method get_file, a wrapper for getting an H5P content file.
 616       */
 617      public function test_get_file(): void {
 618  
 619          $this->setAdminUser();
 620          $file = 'img/fake.png';
 621          $h5pcontentid = 3;
 622  
 623          // Add a file to a H5P content.
 624          $this->h5p_generator->create_content_file($file, file_storage::CONTENT_FILEAREA, $h5pcontentid);
 625  
 626          // Set get_file method accessibility.
 627          $method = new ReflectionMethod(file_storage::class, 'get_file');
 628          $method->setAccessible(true);
 629  
 630          $contentfile = $method->invoke(new file_storage(), file_storage::CONTENT_FILEAREA, $h5pcontentid, $file);
 631  
 632          // Check that it returns an instance of store_file.
 633          $this->assertInstanceOf('stored_file', $contentfile);
 634  
 635          // Add a file to editor.
 636          $this->h5p_generator->create_content_file($file, 'draft', $h5pcontentid);
 637  
 638          $editorfile = $method->invoke(new file_storage(), 'draft', $h5pcontentid, $file);
 639  
 640          // Check that it returns an instance of store_file.
 641          $this->assertInstanceOf('stored_file', $editorfile);
 642      }
 643  
 644      /**
 645       * Test that a single file is added to Moodle files.
 646       */
 647      public function test_move_file(): void {
 648  
 649          // Create temp folder.
 650          $tempfolder = make_request_directory(false);
 651  
 652          // Create H5P content folder.
 653          $filepath = '/img/';
 654          $filename = 'fake.png';
 655          $h5pcontentfolder = $tempfolder . '/fakeH5Pcontent/content' . $filepath;
 656          if (!check_dir_exists($h5pcontentfolder, true, true)) {
 657              throw new moodle_exception('error_creating_temp_dir', 'error', $h5pcontentfolder);
 658          }
 659  
 660          $file = $h5pcontentfolder . $filename;
 661          touch($file);
 662  
 663          $h5pcontentid = 3;
 664  
 665          // Check the file doesn't exist in Moodle files.
 666          $this->assertFalse($this->h5p_fs_fs->file_exists($this->h5p_fs_context->id, file_storage::COMPONENT,
 667              file_storage::CONTENT_FILEAREA, $h5pcontentid, $filepath, $filename));
 668  
 669          // Set get_file method accessibility.
 670          $method = new ReflectionMethod(file_storage::class, 'move_file');
 671          $method->setAccessible(true);
 672  
 673          $method->invoke(new file_storage(), $file, $h5pcontentid);
 674  
 675          // Check the file exist in Moodle files.
 676          $this->assertTrue($this->h5p_fs_fs->file_exists($this->h5p_fs_context->id, file_storage::COMPONENT,
 677              file_storage::CONTENT_FILEAREA, $h5pcontentid, $filepath, $filename));
 678      }
 679  
 680      /**
 681       * Test that a file is copied from another H5P content or the H5P editor.
 682       *
 683       * @return void
 684       */
 685      public function test_cloneContentFile(): void {
 686  
 687          $admin = get_admin();
 688          $usercontext = \context_user::instance($admin->id);
 689          $this->setUser($admin);
 690          // Upload a file to the editor.
 691          $file = 'images/fake.jpg';
 692          $filepath = '/'.dirname($file).'/';
 693          $filename = basename($file);
 694  
 695          $content = 'abcd';
 696  
 697          $filerecord = array(
 698              'contextid' => $usercontext->id,
 699              'component' => 'user',
 700              'filearea'  => 'draft',
 701              'itemid'    => 0,
 702              'filepath'  => $filepath,
 703              'filename'  => $filename,
 704          );
 705  
 706          $this->h5p_fs_fs->create_file_from_string($filerecord, $content);
 707  
 708          // Target H5P content, where the file will be cloned.
 709          $targetcontent = new \stdClass();
 710          $targetcontent->id = 999;
 711  
 712          // Check the file doesn't exists before cloning.
 713          $this->assertFalse($this->h5p_fs_fs->get_file($this->h5p_fs_context->id, file_storage::COMPONENT,
 714              file_storage::CONTENT_FILEAREA, $targetcontent->id, $filepath, $filename));
 715  
 716          // Copy file from the editor.
 717          $this->h5p_file_storage->cloneContentFile($file, 'editor', $targetcontent);
 718  
 719          // Check the file exists after cloning.
 720          $this->assertInstanceOf(\stored_file::class, $this->h5p_fs_fs->get_file($this->h5p_fs_context->id, file_storage::COMPONENT,
 721              file_storage::CONTENT_FILEAREA, $targetcontent->id, $filepath, $filename));
 722  
 723          // Simulate that an H5P content, with id $sourcecontentid, has a file.
 724          $file = 'images/fake2.jpg';
 725          $filepath = '/'.dirname($file).'/';
 726          $filename = basename($file);
 727  
 728          $sourcecontentid = 111;
 729          $filerecord['contextid'] = $this->h5p_fs_context->id;
 730          $filerecord['component'] = file_storage::COMPONENT;
 731          $filerecord['filearea'] = file_storage::CONTENT_FILEAREA;
 732          $filerecord['itemid'] = $sourcecontentid;
 733          $filerecord['filepath'] = $filepath;
 734          $filerecord['filename'] = $filename;
 735  
 736          $this->h5p_fs_fs->create_file_from_string($filerecord, $content);
 737  
 738          // Check the file doesn't exists before cloning.
 739          $this->assertFalse($this->h5p_fs_fs->get_file($this->h5p_fs_context->id, file_storage::COMPONENT,
 740              file_storage::CONTENT_FILEAREA, $targetcontent->id, $filepath, $filename));
 741  
 742          // Copy file from another H5P content.
 743          $this->h5p_file_storage->cloneContentFile($file, $sourcecontentid, $targetcontent);
 744  
 745          // Check the file exists after cloning.
 746          $this->assertInstanceOf(\stored_file::class, $this->h5p_fs_fs->get_file($this->h5p_fs_context->id, file_storage::COMPONENT,
 747              file_storage::CONTENT_FILEAREA, $targetcontent->id, $filepath, $filename));
 748      }
 749  
 750      /**
 751       * Test that a given file exists in an H5P content.
 752       *
 753       * @return void
 754       */
 755      public function test_getContentFile(): void {
 756  
 757          $file = 'img/fake.png';
 758          $contentid = 3;
 759  
 760          // Add a file to a H5P content.
 761          $this->h5p_generator->create_content_file($file, file_storage::CONTENT_FILEAREA, $contentid);
 762  
 763          // Get an existing file id.
 764          $fileid = $this->h5p_file_storage->getContentFile($file, $contentid);
 765          $this->assertNotNull($fileid);
 766  
 767          // Try to get a nonexistent file.
 768          $fileid = $this->h5p_file_storage->getContentFile($file, 5);
 769          $this->assertNull($fileid);
 770      }
 771  
 772      /**
 773       * Tests that the content folder of an H5P content is imported in the Moodle filesystem.
 774       */
 775      public function test_moveContentDiretory(): void {
 776  
 777          // Create temp folder.
 778          $tempfolder = make_request_directory(false);
 779  
 780          // Create H5P content folder.
 781          $h5pcontentfolder = $tempfolder . '/fakeH5Pcontent';
 782          $contentfolder = $h5pcontentfolder . '/content';
 783          if (!check_dir_exists($contentfolder, true, true)) {
 784              throw new moodle_exception('error_creating_temp_dir', 'error', $contentfolder);
 785          }
 786  
 787          // Add content.json file.
 788          touch($contentfolder . 'content.json');
 789  
 790          // Create several folders and files inside content folder.
 791          $filesexpected = array();
 792          $numfolders = random_int(2, 5);
 793          for ($numfolder = 1; $numfolder < $numfolders; $numfolder++) {
 794              $foldername = '/folder' . $numfolder;
 795              $newfolder = $contentfolder . $foldername;
 796              if (!check_dir_exists($newfolder, true, true)) {
 797                  throw new moodle_exception('error_creating_temp_dir', 'error', $newfolder);
 798              }
 799              $numfiles = random_int(2, 5);
 800              for ($numfile = 1; $numfile < $numfiles; $numfile++) {
 801                  $filename = '/file' . $numfile . '.ext';
 802                  touch($newfolder . $filename);
 803                  $filesexpected[] = $foldername . $filename;
 804              }
 805          }
 806  
 807          $targeth5pcontentid = 111;
 808          $this->h5p_file_storage->moveContentDirectory($h5pcontentfolder, $targeth5pcontentid);
 809  
 810          // Get database records.
 811          $files = $this->h5p_fs_fs->get_area_files(
 812              $this->h5p_fs_context->id,
 813              file_storage::COMPONENT,
 814              file_storage::CONTENT_FILEAREA,
 815              $targeth5pcontentid,
 816              'filepath, filename',
 817              false
 818          );
 819  
 820          $filepaths = array_map(static function(stored_file $file): string {
 821              return $file->get_filepath() . $file->get_filename();
 822          }, $files);
 823  
 824          // Check that created files match with database records.
 825          $this->assertEquals($filesexpected, array_values($filepaths));
 826      }
 827  
 828      /**
 829       * Test that an H5P content file is removed.
 830       */
 831      public function test_removeContentFile(): void {
 832  
 833          $file = 'img/fake.png';
 834          $filepath = '/' . dirname($file) . '/';
 835          $filename = basename($file);
 836          $h5pcontentid = 3;
 837  
 838          // Add a file to a H5P content.
 839          $this->h5p_generator->create_content_file($file, file_storage::CONTENT_FILEAREA, $h5pcontentid);
 840  
 841          // Check the file exists.
 842          $this->assertTrue($this->h5p_fs_fs->file_exists($this->h5p_fs_context->id, file_storage::COMPONENT,
 843              file_storage::CONTENT_FILEAREA, $h5pcontentid, $filepath, $filename));
 844  
 845          $this->h5p_file_storage->removeContentFile($file, $h5pcontentid);
 846  
 847          // Check the file doesn't exists.
 848          $this->assertFalse($this->h5p_fs_fs->file_exists($this->h5p_fs_context->id, file_storage::COMPONENT,
 849              file_storage::CONTENT_FILEAREA, $h5pcontentid, $filepath, $filename));
 850      }
 851  }