Differences Between: [Versions 310 and 311] [Versions 310 and 400] [Versions 310 and 401] [Versions 310 and 402] [Versions 310 and 403] [Versions 39 and 310]
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 * Generator for the core_h5p subsystem. 19 * 20 * @package core_h5p 21 * @category test 22 * @copyright 2019 Victor Deniz <victor@moodle.com> 23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 */ 25 26 use core_h5p\local\library\autoloader; 27 use core_h5p\core; 28 use core_h5p\player; 29 use core_h5p\factory; 30 31 defined('MOODLE_INTERNAL') || die(); 32 33 /** 34 * Generator for the core_h5p subsystem. 35 * 36 * @package core_h5p 37 * @category test 38 * @copyright 2019 Victor Deniz <victor@moodle.com> 39 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 40 */ 41 class core_h5p_generator extends \component_generator_base { 42 43 /** Url pointing to webservice plugin file. */ 44 public const WSPLUGINFILE = 0; 45 /** Url pointing to token plugin file. */ 46 public const TOKENPLUGINFILE = 1; 47 /** Url pointing to plugin file. */ 48 public const PLUGINFILE = 2; 49 50 /** 51 * Convenience function to create a file. 52 * 53 * @param string $file path to a file. 54 * @param string $content file content. 55 */ 56 public function create_file(string $file, string $content=''): void { 57 $handle = fopen($file, 'w+'); 58 // File content is not relevant. 59 if (empty($content)) { 60 $content = hash("md5", $file); 61 } 62 fwrite($handle, $content); 63 fclose($handle); 64 } 65 66 /** 67 * Creates the file record. Currently used for the cache tests. 68 * 69 * @param string $type Either 'scripts' or 'styles'. 70 * @param string $path Path to the file in the file system. 71 * @param string $version Not really needed at the moment. 72 */ 73 protected function add_libfile_to_array(string $type, string $path, string $version, &$files): void { 74 $files[$type][] = (object)[ 75 'path' => $path, 76 'version' => "?ver=$version" 77 ]; 78 } 79 80 /** 81 * Create the necessary files and return an array structure for a library. 82 * 83 * @param string $uploaddirectory Base directory for the library. 84 * @param int $libraryid Library id. 85 * @param string $machinename Name for this library. 86 * @param int $majorversion Major version (any number will do). 87 * @param int $minorversion Minor version (any number will do). 88 * @param array $langs Languages to be included into the library. 89 * @return array A list of library data and files that the core API will understand. 90 */ 91 public function create_library(string $uploaddirectory, int $libraryid, string $machinename, int $majorversion, 92 int $minorversion, ?array $langs = []): array { 93 // Array $files used in the cache tests. 94 $files = ['scripts' => [], 'styles' => [], 'language' => []]; 95 96 check_dir_exists($uploaddirectory . '/' . 'scripts'); 97 check_dir_exists($uploaddirectory . '/' . 'styles'); 98 if (!empty($langs)) { 99 check_dir_exists($uploaddirectory . '/' . 'language'); 100 } 101 102 $jsonfile = $uploaddirectory . '/' . 'library.json'; 103 $jsfile = $uploaddirectory . '/' . 'scripts/testlib.min.js'; 104 $cssfile = $uploaddirectory . '/' . 'styles/testlib.min.css'; 105 $this->create_file($jsonfile); 106 $this->create_file($jsfile); 107 $this->create_file($cssfile); 108 foreach ($langs as $lang => $value) { 109 $jsonfile = $uploaddirectory . '/' . 'language/' . $lang . '.json'; 110 $this->create_file($jsonfile, $value); 111 } 112 113 $lib = [ 114 'title' => 'Test lib', 115 'description' => 'Test library description', 116 'majorVersion' => $majorversion, 117 'minorVersion' => $minorversion, 118 'patchVersion' => 2, 119 'machineName' => $machinename, 120 'preloadedJs' => [ 121 [ 122 'path' => 'scripts' . '/' . 'testlib.min.js' 123 ] 124 ], 125 'preloadedCss' => [ 126 [ 127 'path' => 'styles' . '/' . 'testlib.min.css' 128 ] 129 ], 130 'uploadDirectory' => $uploaddirectory, 131 'libraryId' => $libraryid 132 ]; 133 134 $version = "{$majorversion}.{$minorversion}.2"; 135 $libname = "{$machinename}-{$majorversion}.{$minorversion}"; 136 $path = '/' . 'libraries' . '/' . $libraryid . '/' . $libname . '/' . 'scripts' . '/' . 'testlib.min.js'; 137 $this->add_libfile_to_array('scripts', $path, $version, $files); 138 $path = '/' . 'libraries' . '/' . $libraryid .'/' . $libname . '/' . 'styles' . '/' . 'testlib.min.css'; 139 $this->add_libfile_to_array('styles', $path, $version, $files); 140 foreach ($langs as $lang => $notused) { 141 $path = '/' . 'libraries' . '/' . $libraryid . '/' . $libname . '/' . 'language' . '/' . $lang . '.json'; 142 $this->add_libfile_to_array('language', $path, $version, $files); 143 } 144 145 return [$lib, $files]; 146 } 147 148 /** 149 * Save the library files on the filesystem. 150 * 151 * @param stdClss $lib The library data 152 */ 153 private function save_library(stdClass $lib) { 154 // Get a temp path. 155 $filestorage = new \core_h5p\file_storage(); 156 $temppath = $filestorage->getTmpPath(); 157 158 // Create and save the library files on the filesystem. 159 $basedirectorymain = $temppath . '/' . $lib->machinename . '-' . 160 $lib->majorversion . '.' . $lib->minorversion; 161 162 list($library, $libraryfiles) = $this->create_library($basedirectorymain, $lib->id, $lib->machinename, 163 $lib->majorversion, $lib->minorversion); 164 165 $filestorage->saveLibrary($library); 166 } 167 168 /** 169 * Populate H5P database tables with relevant data to simulate the process of adding H5P content. 170 * 171 * @param bool $createlibraryfiles Whether to create and store library files on the filesystem 172 * @return stdClass An object representing the added H5P records 173 */ 174 public function generate_h5p_data(bool $createlibraryfiles = false): stdClass { 175 // Create libraries. 176 $mainlib = $libraries[] = $this->create_library_record('MainLibrary', 'Main Lib', 1, 0, 1, '', null, 177 'http://tutorial.org', 'http://example.org'); 178 $lib1 = $libraries[] = $this->create_library_record('Library1', 'Lib1', 2, 0, 1, '', null, null, 'http://example.org'); 179 $lib2 = $libraries[] = $this->create_library_record('Library2', 'Lib2', 2, 1, 1, '', null, 'http://tutorial.org'); 180 $lib3 = $libraries[] = $this->create_library_record('Library3', 'Lib3', 3, 2); 181 $lib4 = $libraries[] = $this->create_library_record('Library4', 'Lib4', 1, 1); 182 $lib5 = $libraries[] = $this->create_library_record('Library5', 'Lib5', 1, 3); 183 184 if ($createlibraryfiles) { 185 foreach ($libraries as $lib) { 186 // Create and save the library files on the filesystem. 187 $this->save_library($lib); 188 } 189 } 190 191 // Create h5p content. 192 $h5p = $this->create_h5p_record($mainlib->id); 193 // Create h5p content library dependencies. 194 $this->create_contents_libraries_record($h5p, $mainlib->id); 195 $this->create_contents_libraries_record($h5p, $lib1->id); 196 $this->create_contents_libraries_record($h5p, $lib2->id); 197 $this->create_contents_libraries_record($h5p, $lib3->id); 198 $this->create_contents_libraries_record($h5p, $lib4->id); 199 // Create library dependencies for $mainlib. 200 $this->create_library_dependency_record($mainlib->id, $lib1->id); 201 $this->create_library_dependency_record($mainlib->id, $lib2->id); 202 $this->create_library_dependency_record($mainlib->id, $lib3->id); 203 // Create library dependencies for $lib1. 204 $this->create_library_dependency_record($lib1->id, $lib2->id); 205 $this->create_library_dependency_record($lib1->id, $lib3->id); 206 $this->create_library_dependency_record($lib1->id, $lib4->id); 207 // Create library dependencies for $lib3. 208 $this->create_library_dependency_record($lib3->id, $lib5->id); 209 210 return (object) [ 211 'h5pcontent' => (object) array( 212 'h5pid' => $h5p, 213 'contentdependencies' => array($mainlib, $lib1, $lib2, $lib3, $lib4) 214 ), 215 'mainlib' => (object) array( 216 'data' => $mainlib, 217 'dependencies' => array($lib1, $lib2, $lib3) 218 ), 219 'lib1' => (object) array( 220 'data' => $lib1, 221 'dependencies' => array($lib2, $lib3, $lib4) 222 ), 223 'lib2' => (object) array( 224 'data' => $lib2, 225 'dependencies' => array() 226 ), 227 'lib3' => (object) array( 228 'data' => $lib3, 229 'dependencies' => array($lib5) 230 ), 231 'lib4' => (object) array( 232 'data' => $lib4, 233 'dependencies' => array() 234 ), 235 'lib5' => (object) array( 236 'data' => $lib5, 237 'dependencies' => array() 238 ), 239 ]; 240 } 241 242 /** 243 * Create a record in the h5p_libraries database table. 244 * 245 * @param string $machinename The library machine name 246 * @param string $title The library's name 247 * @param int $majorversion The library's major version 248 * @param int $minorversion The library's minor version 249 * @param int $patchversion The library's patch version 250 * @param string $semantics Json describing the content structure for the library 251 * @param string $addto The plugin configuration data 252 * @param string $tutorial The tutorial URL 253 * @param string $examlpe The example URL 254 * @return stdClass An object representing the added library record 255 */ 256 public function create_library_record(string $machinename, string $title, int $majorversion = 1, 257 int $minorversion = 0, int $patchversion = 1, string $semantics = '', string $addto = null, 258 string $tutorial = null, string $example = null): stdClass { 259 global $DB; 260 261 $content = array( 262 'machinename' => $machinename, 263 'title' => $title, 264 'majorversion' => $majorversion, 265 'minorversion' => $minorversion, 266 'patchversion' => $patchversion, 267 'runnable' => 1, 268 'fullscreen' => 1, 269 'preloadedjs' => 'js/example.js', 270 'preloadedcss' => 'css/example.css', 271 'droplibrarycss' => '', 272 'semantics' => $semantics, 273 'addto' => $addto, 274 'tutorial' => $tutorial, 275 'example' => $example 276 ); 277 278 $libraryid = $DB->insert_record('h5p_libraries', $content); 279 280 return $DB->get_record('h5p_libraries', ['id' => $libraryid]); 281 } 282 283 /** 284 * Create a record in the h5p database table. 285 * 286 * @param int $mainlibid The ID of the content's main library 287 * @param string $jsoncontent The content in json format 288 * @param string $filtered The filtered content parameters 289 * @return int The ID of the added record 290 */ 291 public function create_h5p_record(int $mainlibid, string $jsoncontent = null, string $filtered = null): int { 292 global $DB; 293 294 if (!$jsoncontent) { 295 $jsoncontent = json_encode( 296 array( 297 'text' => '<p>Dummy text<\/p>\n', 298 'questions' => '<p>Test question<\/p>\n' 299 ) 300 ); 301 } 302 303 if (!$filtered) { 304 $filtered = json_encode( 305 array( 306 'text' => 'Dummy text', 307 'questions' => 'Test question' 308 ) 309 ); 310 } 311 312 return $DB->insert_record( 313 'h5p', 314 array( 315 'jsoncontent' => $jsoncontent, 316 'displayoptions' => 8, 317 'mainlibraryid' => $mainlibid, 318 'timecreated' => time(), 319 'timemodified' => time(), 320 'filtered' => $filtered, 321 'pathnamehash' => sha1('pathname'), 322 'contenthash' => sha1('content') 323 ) 324 ); 325 } 326 327 /** 328 * Create a record in the h5p_contents_libraries database table. 329 * 330 * @param string $h5pid The ID of the H5P content 331 * @param int $libid The ID of the library 332 * @param string $dependencytype The dependency type 333 * @return int The ID of the added record 334 */ 335 public function create_contents_libraries_record(string $h5pid, int $libid, 336 string $dependencytype = 'preloaded'): int { 337 global $DB; 338 339 return $DB->insert_record( 340 'h5p_contents_libraries', 341 array( 342 'h5pid' => $h5pid, 343 'libraryid' => $libid, 344 'dependencytype' => $dependencytype, 345 'dropcss' => 0, 346 'weight' => 1 347 ) 348 ); 349 } 350 351 /** 352 * Create a record in the h5p_library_dependencies database table. 353 * 354 * @param int $libid The ID of the library 355 * @param int $requiredlibid The ID of the required library 356 * @param string $dependencytype The dependency type 357 * @return int The ID of the added record 358 */ 359 public function create_library_dependency_record(int $libid, int $requiredlibid, 360 string $dependencytype = 'preloaded'): int { 361 global $DB; 362 363 return $DB->insert_record( 364 'h5p_library_dependencies', 365 array( 366 'libraryid' => $libid, 367 'requiredlibraryid' => $requiredlibid, 368 'dependencytype' => $dependencytype 369 ) 370 ); 371 } 372 373 /** 374 * Create H5P content type records in the h5p_libraries database table. 375 * 376 * @param array $typestonotinstall H5P content types that should not be installed 377 * @param core $core h5p_test_core instance required to use the exttests URL 378 * @return array Data of the content types not installed. 379 */ 380 public function create_content_types(array $typestonotinstall, core $core): array { 381 global $DB; 382 383 autoloader::register(); 384 385 // Get info of latest content types versions. 386 $contenttypes = $core->get_latest_content_types()->contentTypes; 387 388 $installedtypes = 0; 389 390 // Fake installation of all other H5P content types. 391 foreach ($contenttypes as $contenttype) { 392 // Don't install pending content types. 393 if (in_array($contenttype->id, $typestonotinstall)) { 394 continue; 395 } 396 $library = [ 397 'machinename' => $contenttype->id, 398 'majorversion' => $contenttype->version->major, 399 'minorversion' => $contenttype->version->minor, 400 'patchversion' => $contenttype->version->patch, 401 'runnable' => 1, 402 'coremajor' => $contenttype->coreApiVersionNeeded->major, 403 'coreminor' => $contenttype->coreApiVersionNeeded->minor 404 ]; 405 $DB->insert_record('h5p_libraries', (object) $library); 406 $installedtypes++; 407 } 408 409 return [$installedtypes, count($typestonotinstall)]; 410 } 411 412 /** 413 * Add a record on files table for a file that belongs to 414 * 415 * @param string $file File name and path inside the filearea. 416 * @param string $filearea The filearea in which the file is ("editor" or "content"). 417 * @param int $contentid Id of the H5P content to which the file belongs. null if the file is in the editor. 418 * 419 * @return stored_file; 420 * @throws coding_exception 421 */ 422 public function create_content_file(string $file, string $filearea, int $contentid = 0): stored_file { 423 global $USER; 424 425 $filepath = '/'.dirname($file).'/'; 426 $filename = basename($file); 427 428 if (($filearea === 'content') && ($contentid == 0)) { 429 throw new coding_exception('Files belonging to an H5P content must specify the H5P content id'); 430 } 431 432 if ($filearea === 'draft') { 433 $usercontext = \context_user::instance($USER->id); 434 $context = $usercontext->id; 435 $component = 'user'; 436 $itemid = 0; 437 } else { 438 $systemcontext = context_system::instance(); 439 $context = $systemcontext->id; 440 $component = \core_h5p\file_storage::COMPONENT; 441 $itemid = $contentid; 442 } 443 444 $content = 'fake content'; 445 446 $filerecord = array( 447 'contextid' => $context, 448 'component' => $component, 449 'filearea' => $filearea, 450 'itemid' => $itemid, 451 'filepath' => $filepath, 452 'filename' => $filename, 453 ); 454 455 $fs = new file_storage(); 456 return $fs->create_file_from_string($filerecord, $content); 457 } 458 459 /** 460 * Create a fake export H5P deployed file. 461 * 462 * @param string $filename Name of the H5P file to deploy. 463 * @param int $contextid Context id of the H5P activity. 464 * @param string $component component. 465 * @param string $filearea file area. 466 * @param int $typeurl Type of url to create the export url plugin file. 467 * @return array return deployed file information. 468 */ 469 public function create_export_file(string $filename, int $contextid, 470 string $component, 471 string $filearea, 472 int $typeurl = self::WSPLUGINFILE): array { 473 global $CFG; 474 475 // We need the autoloader for H5P player. 476 autoloader::register(); 477 478 $path = $CFG->dirroot.'/h5p/tests/fixtures/'.$filename; 479 $filerecord = [ 480 'contextid' => $contextid, 481 'component' => $component, 482 'filearea' => $filearea, 483 'itemid' => 0, 484 'filepath' => '/', 485 'filename' => $filename, 486 ]; 487 // Load the h5p file into DB. 488 $fs = get_file_storage(); 489 if (!$fs->get_file($contextid, $component, $filearea, $filerecord['itemid'], $filerecord['filepath'], $filename)) { 490 $fs->create_file_from_pathname($filerecord, $path); 491 } 492 493 // Make the URL to pass to the player. 494 if ($typeurl == self::WSPLUGINFILE) { 495 $url = \moodle_url::make_webservice_pluginfile_url( 496 $filerecord['contextid'], 497 $filerecord['component'], 498 $filerecord['filearea'], 499 $filerecord['itemid'], 500 $filerecord['filepath'], 501 $filerecord['filename'] 502 ); 503 } else { 504 $includetoken = false; 505 if ($typeurl == self::TOKENPLUGINFILE) { 506 $includetoken = true; 507 } 508 $url = \moodle_url::make_pluginfile_url( 509 $filerecord['contextid'], 510 $filerecord['component'], 511 $filerecord['filearea'], 512 $filerecord['itemid'], 513 $filerecord['filepath'], 514 $filerecord['filename'], 515 false, 516 $includetoken 517 ); 518 } 519 520 $config = new stdClass(); 521 $h5pplayer = new player($url->out(false), $config); 522 // We need to add assets to page to create the export file. 523 $h5pplayer->add_assets_to_page(); 524 525 // Call the method. We need the id of the new H5P content. 526 $rc = new \ReflectionClass(player::class); 527 $rcp = $rc->getProperty('h5pid'); 528 $rcp->setAccessible(true); 529 $h5pid = $rcp->getValue($h5pplayer); 530 531 // Get the info export file. 532 $factory = new factory(); 533 $core = $factory->get_core(); 534 $content = $core->loadContent($h5pid); 535 $slug = $content['slug'] ? $content['slug'] . '-' : ''; 536 $exportfilename = "{$slug}{$h5pid}.h5p"; 537 $fileh5p = $core->fs->get_export_file($exportfilename); 538 $deployedfile = []; 539 $deployedfile['filename'] = $fileh5p->get_filename(); 540 $deployedfile['filepath'] = $fileh5p->get_filepath(); 541 $deployedfile['mimetype'] = $fileh5p->get_mimetype(); 542 $deployedfile['filesize'] = $fileh5p->get_filesize(); 543 $deployedfile['timemodified'] = $fileh5p->get_timemodified(); 544 545 // Create the url depending the request was made through typeurl. 546 if ($typeurl == self::WSPLUGINFILE) { 547 $url = \moodle_url::make_webservice_pluginfile_url( 548 $fileh5p->get_contextid(), 549 $fileh5p->get_component(), 550 $fileh5p->get_filearea(), 551 '', 552 '', 553 $fileh5p->get_filename() 554 ); 555 } else { 556 $includetoken = false; 557 if ($typeurl == self::TOKENPLUGINFILE) { 558 $includetoken = true; 559 } 560 $url = \moodle_url::make_pluginfile_url( 561 $fileh5p->get_contextid(), 562 $fileh5p->get_component(), 563 $fileh5p->get_filearea(), 564 '', 565 '', 566 $fileh5p->get_filename(), 567 false, 568 $includetoken 569 ); 570 } 571 $deployedfile['fileurl'] = $url->out(false); 572 573 return $deployedfile; 574 } 575 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body