MDL-68909 h5p: move temporary editor files to draft area
[moodle.git] / h5p / tests / generator / lib.php
CommitLineData
9ea303e7
AG
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
08fda3e0 26use core_h5p\local\library\autoloader;
e942844d 27use core_h5p\core;
14b463c9 28use core_h5p\player;
29use core_h5p\factory;
5315acb4 30
9ea303e7
AG
31defined('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 */
41class core_h5p_generator extends \component_generator_base {
42
14b463c9 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
9ea303e7
AG
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 {
27fa4ba3 74 $files[$type][] = (object)[
9ea303e7
AG
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).
f6359151 88 * @param array $langs Languages to be included into the library.
9ea303e7
AG
89 * @return array A list of library data and files that the core API will understand.
90 */
27fa4ba3 91 public function create_library(string $uploaddirectory, int $libraryid, string $machinename, int $majorversion,
f6359151
SA
92 int $minorversion, ?array $langs = []): array {
93 // Array $files used in the cache tests.
94 $files = ['scripts' => [], 'styles' => [], 'language' => []];
9ea303e7
AG
95
96 check_dir_exists($uploaddirectory . '/' . 'scripts');
97 check_dir_exists($uploaddirectory . '/' . 'styles');
f6359151
SA
98 if (!empty($langs)) {
99 check_dir_exists($uploaddirectory . '/' . 'language');
100 }
9ea303e7
AG
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);
f6359151
SA
108 foreach ($langs as $lang => $value) {
109 $jsonfile = $uploaddirectory . '/' . 'language/' . $lang . '.json';
110 $this->create_file($jsonfile, $value);
111 }
9ea303e7
AG
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);
f6359151
SA
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 }
9ea303e7
AG
144
145 return [$lib, $files];
146 }
147
27fa4ba3
MG
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);
177 $lib1 = $libraries[] = $this->create_library_record('Library1', 'Lib1', 2, 0);
178 $lib2 = $libraries[] = $this->create_library_record('Library2', 'Lib2', 2, 1);
179 $lib3 = $libraries[] = $this->create_library_record('Library3', 'Lib3', 3, 2);
180 $lib4 = $libraries[] = $this->create_library_record('Library4', 'Lib4', 1, 1);
181 $lib5 = $libraries[] = $this->create_library_record('Library5', 'Lib5', 1, 3);
182
183 if ($createlibraryfiles) {
184 foreach ($libraries as $lib) {
185 // Create and save the library files on the filesystem.
186 $this->save_library($lib);
187 }
188 }
189
190 // Create h5p content.
191 $h5p = $this->create_h5p_record($mainlib->id);
192 // Create h5p content library dependencies.
193 $this->create_contents_libraries_record($h5p, $mainlib->id);
194 $this->create_contents_libraries_record($h5p, $lib1->id);
195 $this->create_contents_libraries_record($h5p, $lib2->id);
196 $this->create_contents_libraries_record($h5p, $lib3->id);
197 $this->create_contents_libraries_record($h5p, $lib4->id);
198 // Create library dependencies for $mainlib.
199 $this->create_library_dependency_record($mainlib->id, $lib1->id);
200 $this->create_library_dependency_record($mainlib->id, $lib2->id);
201 $this->create_library_dependency_record($mainlib->id, $lib3->id);
202 // Create library dependencies for $lib1.
203 $this->create_library_dependency_record($lib1->id, $lib2->id);
204 $this->create_library_dependency_record($lib1->id, $lib3->id);
205 $this->create_library_dependency_record($lib1->id, $lib4->id);
206 // Create library dependencies for $lib3.
207 $this->create_library_dependency_record($lib3->id, $lib5->id);
208
209 return (object) [
210 'h5pcontent' => (object) array(
211 'h5pid' => $h5p,
212 'contentdependencies' => array($mainlib, $lib1, $lib2, $lib3, $lib4)
213 ),
214 'mainlib' => (object) array(
215 'data' => $mainlib,
216 'dependencies' => array($lib1, $lib2, $lib3)
217 ),
218 'lib1' => (object) array(
219 'data' => $lib1,
220 'dependencies' => array($lib2, $lib3, $lib4)
221 ),
222 'lib2' => (object) array(
223 'data' => $lib2,
224 'dependencies' => array()
225 ),
226 'lib3' => (object) array(
227 'data' => $lib3,
228 'dependencies' => array($lib5)
229 ),
230 'lib4' => (object) array(
231 'data' => $lib4,
232 'dependencies' => array()
233 ),
234 'lib5' => (object) array(
235 'data' => $lib5,
236 'dependencies' => array()
237 ),
238 ];
239 }
240
241 /**
242 * Create a record in the h5p_libraries database table.
243 *
244 * @param string $machinename The library machine name
245 * @param string $title The library's name
246 * @param int $majorversion The library's major version
247 * @param int $minorversion The library's minor version
248 * @param int $patchversion The library's patch version
249 * @param string $semantics Json describing the content structure for the library
250 * @param string $addto The plugin configuration data
251 * @return stdClass An object representing the added library record
252 */
253 public function create_library_record(string $machinename, string $title, int $majorversion = 1,
254 int $minorversion = 0, int $patchversion = 1, string $semantics = '', string $addto = null): stdClass {
255 global $DB;
256
257 $content = array(
258 'machinename' => $machinename,
259 'title' => $title,
260 'majorversion' => $majorversion,
261 'minorversion' => $minorversion,
262 'patchversion' => $patchversion,
263 'runnable' => 1,
264 'fullscreen' => 1,
265 'preloadedjs' => 'js/example.js',
266 'preloadedcss' => 'css/example.css',
267 'droplibrarycss' => '',
268 'semantics' => $semantics,
269 'addto' => $addto
270 );
271
272 $libraryid = $DB->insert_record('h5p_libraries', $content);
273
274 return $DB->get_record('h5p_libraries', ['id' => $libraryid]);
275 }
276
277 /**
278 * Create a record in the h5p database table.
279 *
280 * @param int $mainlibid The ID of the content's main library
281 * @param string $jsoncontent The content in json format
282 * @param string $filtered The filtered content parameters
283 * @return int The ID of the added record
284 */
285 public function create_h5p_record(int $mainlibid, string $jsoncontent = null, string $filtered = null): int {
286 global $DB;
287
288 if (!$jsoncontent) {
289 $jsoncontent = json_encode(
290 array(
291 'text' => '<p>Dummy text<\/p>\n',
292 'questions' => '<p>Test question<\/p>\n'
293 )
294 );
295 }
296
297 if (!$filtered) {
298 $filtered = json_encode(
299 array(
300 'text' => 'Dummy text',
301 'questions' => 'Test question'
302 )
303 );
304 }
305
306 return $DB->insert_record(
307 'h5p',
308 array(
309 'jsoncontent' => $jsoncontent,
310 'displayoptions' => 8,
311 'mainlibraryid' => $mainlibid,
312 'timecreated' => time(),
313 'timemodified' => time(),
314 'filtered' => $filtered,
315 'pathnamehash' => sha1('pathname'),
316 'contenthash' => sha1('content')
317 )
318 );
319 }
320
321 /**
322 * Create a record in the h5p_contents_libraries database table.
323 *
324 * @param string $h5pid The ID of the H5P content
325 * @param int $libid The ID of the library
326 * @param string $dependencytype The dependency type
327 * @return int The ID of the added record
328 */
329 public function create_contents_libraries_record(string $h5pid, int $libid,
330 string $dependencytype = 'preloaded'): int {
331 global $DB;
332
333 return $DB->insert_record(
334 'h5p_contents_libraries',
335 array(
336 'h5pid' => $h5pid,
337 'libraryid' => $libid,
338 'dependencytype' => $dependencytype,
339 'dropcss' => 0,
340 'weight' => 1
341 )
342 );
343 }
344
345 /**
346 * Create a record in the h5p_library_dependencies database table.
347 *
348 * @param int $libid The ID of the library
349 * @param int $requiredlibid The ID of the required library
350 * @param string $dependencytype The dependency type
351 * @return int The ID of the added record
352 */
353 public function create_library_dependency_record(int $libid, int $requiredlibid,
354 string $dependencytype = 'preloaded'): int {
355 global $DB;
356
357 return $DB->insert_record(
358 'h5p_library_dependencies',
359 array(
360 'libraryid' => $libid,
361 'requiredlibraryid' => $requiredlibid,
362 'dependencytype' => $dependencytype
363 )
364 );
365 }
5315acb4
VDF
366
367 /**
e942844d 368 * Create H5P content type records in the h5p_libraries database table.
5315acb4 369 *
e942844d
VDF
370 * @param array $typestonotinstall H5P content types that should not be installed
371 * @param core $core h5p_test_core instance required to use the exttests URL
5315acb4
VDF
372 * @return array Data of the content types not installed.
373 */
e942844d 374 public function create_content_types(array $typestonotinstall, core $core): array {
5315acb4
VDF
375 global $DB;
376
e942844d 377 autoloader::register();
5315acb4
VDF
378
379 // Get info of latest content types versions.
380 $contenttypes = $core->get_latest_content_types()->contentTypes;
381
e942844d 382 $installedtypes = 0;
5315acb4
VDF
383
384 // Fake installation of all other H5P content types.
385 foreach ($contenttypes as $contenttype) {
e942844d
VDF
386 // Don't install pending content types.
387 if (in_array($contenttype->id, $typestonotinstall)) {
388 continue;
389 }
5315acb4
VDF
390 $library = [
391 'machinename' => $contenttype->id,
392 'majorversion' => $contenttype->version->major,
393 'minorversion' => $contenttype->version->minor,
394 'patchversion' => $contenttype->version->patch,
b7c307f3
VDF
395 'runnable' => 1,
396 'coremajor' => $contenttype->coreApiVersionNeeded->major,
397 'coreminor' => $contenttype->coreApiVersionNeeded->minor
5315acb4
VDF
398 ];
399 $DB->insert_record('h5p_libraries', (object) $library);
e942844d 400 $installedtypes++;
5315acb4
VDF
401 }
402
e942844d 403 return [$installedtypes, count($typestonotinstall)];
5315acb4 404 }
6da050d7
VDF
405
406 /**
407 * Add a record on files table for a file that belongs to
408 *
409 * @param string $file File name and path inside the filearea.
410 * @param string $filearea The filearea in which the file is ("editor" or "content").
411 * @param int $contentid Id of the H5P content to which the file belongs. null if the file is in the editor.
412 *
413 * @return stored_file;
414 * @throws coding_exception
415 */
416 public function create_content_file(string $file, string $filearea, int $contentid = 0): stored_file {
67a11150
SA
417 global $USER;
418
6da050d7
VDF
419 $filepath = '/'.dirname($file).'/';
420 $filename = basename($file);
421
422 if (($filearea === 'content') && ($contentid == 0)) {
423 throw new coding_exception('Files belonging to an H5P content must specify the H5P content id');
424 }
425
67a11150
SA
426 if ($filearea === 'draft') {
427 $usercontext = \context_user::instance($USER->id);
428 $context = $usercontext->id;
429 $component = 'user';
430 $itemid = 0;
431 } else {
432 $systemcontext = context_system::instance();
433 $context = $systemcontext->id;
434 $component = \core_h5p\file_storage::COMPONENT;
435 $itemid = $contentid;
436 }
6da050d7 437
67a11150 438 $content = 'fake content';
6da050d7
VDF
439
440 $filerecord = array(
67a11150
SA
441 'contextid' => $context,
442 'component' => $component,
6da050d7 443 'filearea' => $filearea,
67a11150 444 'itemid' => $itemid,
6da050d7
VDF
445 'filepath' => $filepath,
446 'filename' => $filename,
447 );
448
449 $fs = new file_storage();
450 return $fs->create_file_from_string($filerecord, $content);
451 }
14b463c9 452
453 /**
454 * Create a fake export H5P deployed file.
455 *
456 * @param string $filename Name of the H5P file to deploy.
457 * @param int $contextid Context id of the H5P activity.
458 * @param string $component component.
459 * @param string $filearea file area.
460 * @param int $typeurl Type of url to create the export url plugin file.
461 * @return array return deployed file information.
462 */
463 public function create_export_file(string $filename, int $contextid,
464 string $component,
465 string $filearea,
466 int $typeurl = self::WSPLUGINFILE): array {
467 global $CFG;
468
469 // We need the autoloader for H5P player.
470 autoloader::register();
471
472 $path = $CFG->dirroot.'/h5p/tests/fixtures/'.$filename;
473 $filerecord = [
474 'contextid' => $contextid,
475 'component' => $component,
476 'filearea' => $filearea,
477 'itemid' => 0,
478 'filepath' => '/',
479 'filename' => $filename,
480 ];
481 // Load the h5p file into DB.
482 $fs = get_file_storage();
483 if (!$fs->get_file($contextid, $component, $filearea, $filerecord['itemid'], $filerecord['filepath'], $filename)) {
484 $fs->create_file_from_pathname($filerecord, $path);
485 }
486
487 // Make the URL to pass to the player.
488 if ($typeurl == self::WSPLUGINFILE) {
489 $url = \moodle_url::make_webservice_pluginfile_url(
490 $filerecord['contextid'],
491 $filerecord['component'],
492 $filerecord['filearea'],
493 $filerecord['itemid'],
494 $filerecord['filepath'],
495 $filerecord['filename']
496 );
497 } else {
498 $includetoken = false;
499 if ($typeurl == self::TOKENPLUGINFILE) {
500 $includetoken = true;
501 }
502 $url = \moodle_url::make_pluginfile_url(
503 $filerecord['contextid'],
504 $filerecord['component'],
505 $filerecord['filearea'],
506 $filerecord['itemid'],
507 $filerecord['filepath'],
508 $filerecord['filename'],
509 false,
510 $includetoken
511 );
512 }
513
514 $config = new stdClass();
515 $h5pplayer = new player($url->out(false), $config);
516 // We need to add assets to page to create the export file.
517 $h5pplayer->add_assets_to_page();
518
519 // Call the method. We need the id of the new H5P content.
520 $rc = new \ReflectionClass(player::class);
521 $rcp = $rc->getProperty('h5pid');
522 $rcp->setAccessible(true);
523 $h5pid = $rcp->getValue($h5pplayer);
524
525 // Get the info export file.
526 $factory = new factory();
527 $core = $factory->get_core();
528 $content = $core->loadContent($h5pid);
529 $slug = $content['slug'] ? $content['slug'] . '-' : '';
530 $exportfilename = "{$slug}{$h5pid}.h5p";
531 $fileh5p = $core->fs->get_export_file($exportfilename);
532 $deployedfile = [];
533 $deployedfile['filename'] = $fileh5p->get_filename();
534 $deployedfile['filepath'] = $fileh5p->get_filepath();
535 $deployedfile['mimetype'] = $fileh5p->get_mimetype();
536 $deployedfile['filesize'] = $fileh5p->get_filesize();
537 $deployedfile['timemodified'] = $fileh5p->get_timemodified();
538
539 // Create the url depending the request was made through typeurl.
540 if ($typeurl == self::WSPLUGINFILE) {
541 $url = \moodle_url::make_webservice_pluginfile_url(
542 $fileh5p->get_contextid(),
543 $fileh5p->get_component(),
544 $fileh5p->get_filearea(),
545 '',
546 '',
547 $fileh5p->get_filename()
548 );
549 } else {
550 $includetoken = false;
551 if ($typeurl == self::TOKENPLUGINFILE) {
552 $includetoken = true;
553 }
554 $url = \moodle_url::make_pluginfile_url(
555 $fileh5p->get_contextid(),
556 $fileh5p->get_component(),
557 $fileh5p->get_filearea(),
558 '',
559 '',
560 $fileh5p->get_filename(),
561 false,
562 $includetoken
563 );
564 }
565 $deployedfile['fileurl'] = $url->out(false);
566
567 return $deployedfile;
568 }
27fa4ba3 569}