Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 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
}