Proyectos de Subversion Moodle

Rev

Rev 1 | | Comparar con el anterior | 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
namespace core_h5p;
18
 
19
use core_xapi\handler;
20
use core_xapi\xapi_exception;
21
use Moodle\H5PFrameworkInterface;
22
use Moodle\H5PCore;
23
 
24
// phpcs:disable moodle.NamingConventions.ValidFunctionName.LowercaseMethod
25
 
26
/**
27
 * Moodle's implementation of the H5P framework interface.
28
 *
29
 * @package    core_h5p
30
 * @copyright  2019 Mihail Geshoski <mihail@moodle.com>
31
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
32
 */
33
class framework implements H5PFrameworkInterface {
34
 
35
    /** @var string The path to the last uploaded h5p */
36
    private $lastuploadedfolder;
37
 
38
    /** @var string The path to the last uploaded h5p file */
39
    private $lastuploadedfile;
40
 
41
    /** @var \stored_file The .h5p file */
42
    private $file;
43
 
44
    /**
45
     * Returns info for the current platform.
46
     * Implements getPlatformInfo.
47
     *
48
     * @return array An associative array containing:
49
     *               - name: The name of the platform, for instance "Moodle"
50
     *               - version: The version of the platform, for instance "3.8"
51
     *               - h5pVersion: The version of the H5P component
52
     */
53
    public function getPlatformInfo() {
54
        global $CFG;
55
 
56
        return array(
57
            'name' => 'Moodle',
58
            'version' => $CFG->version,
59
            'h5pVersion' => $CFG->version,
60
        );
61
    }
62
 
63
    /**
64
     * Fetches a file from a remote server using HTTP GET
65
     * Implements fetchExternalData.
66
     *
67
     * @param  string  $url  Where you want to get or send data.
68
     * @param  array  $data  Data to post to the URL.
69
     * @param  bool  $blocking  Set to 'FALSE' to instantly time out (fire and forget).
70
     * @param  string  $stream  Path to where the file should be saved.
71
     * @param  bool  $fulldata  Return additional response data such as headers and potentially other data
72
     * @param  array  $headers  Headers to send
73
     * @param  array  $files Files to send
74
     * @param  string  $method
75
     *
76
     * @return string|array The content (response body), or an array with data. NULL if something went wrong
77
     */
78
    public function fetchExternalData($url, $data = null, $blocking = true, $stream = null, $fulldata = false, $headers = [],
79
            $files = [], $method = 'POST') {
80
 
81
        if ($stream === null) {
82
            // Download file.
83
            set_time_limit(0);
84
 
85
            // Get the extension of the remote file.
86
            $parsedurl = parse_url($url);
87
            $ext = pathinfo($parsedurl['path'], PATHINFO_EXTENSION);
88
 
89
            // Generate local tmp file path.
90
            $fs = new \core_h5p\file_storage();
91
            $localfolder = $fs->getTmpPath();
92
            $stream = $localfolder;
93
 
94
            // Add the remote file's extension to the temp file.
95
            if ($ext) {
96
                $stream .= '.' . $ext;
97
            }
98
 
99
            $this->getUploadedH5pFolderPath($localfolder);
100
            $this->getUploadedH5pPath($stream);
101
        }
102
 
103
        $response = download_file_content($url, null, $data, true, 300, 20,
104
                false, $stream);
105
 
106
        if (empty($response->error) && ($response->status != '404')) {
107
            return $response->results;
108
        } else {
109
            $this->setErrorMessage($response->error, 'failed-fetching-external-data');
110
        }
111
    }
112
 
113
    /**
114
     * Set the tutorial URL for a library. All versions of the library is set.
115
     * Implements setLibraryTutorialUrl.
116
     *
117
     * @param string $libraryname
118
     * @param string $url
119
     */
120
    public function setLibraryTutorialUrl($libraryname, $url) {
121
        global $DB;
122
 
123
        $sql = 'UPDATE {h5p_libraries}
124
                   SET tutorial = :tutorial
125
                 WHERE machinename = :machinename';
126
        $params = [
127
            'tutorial' => $url,
128
            'machinename' => $libraryname,
129
        ];
130
        $DB->execute($sql, $params);
131
    }
132
 
133
    /**
134
     * Set an error message.
135
     * Implements setErrorMessage.
136
     *
137
     * @param string $message The error message
138
     * @param string $code An optional code
139
     */
140
    public function setErrorMessage($message, $code = null) {
141
        if ($message !== null) {
142
            $this->set_message('error', $message, $code);
143
        }
144
    }
145
 
146
    /**
147
     * Set an info message.
148
     * Implements setInfoMessage.
149
     *
150
     * @param string $message The info message
151
     */
152
    public function setInfoMessage($message) {
153
        if ($message !== null) {
154
            $this->set_message('info', $message);
155
        }
156
    }
157
 
158
    /**
159
     * Return messages.
160
     * Implements getMessages.
161
     *
162
     * @param string $type The message type, e.g. 'info' or 'error'
163
     * @return string[] Array of messages
164
     */
165
    public function getMessages($type) {
166
        global $SESSION;
167
 
168
        // Return and reset messages.
169
        $messages = array();
170
        if (isset($SESSION->core_h5p_messages[$type])) {
171
            $messages = $SESSION->core_h5p_messages[$type];
172
            unset($SESSION->core_h5p_messages[$type]);
173
            if (empty($SESSION->core_h5p_messages)) {
174
                unset($SESSION->core_h5p_messages);
175
            }
176
        }
177
 
178
        return $messages;
179
    }
180
 
181
    /**
182
     * Translation function.
183
     * The purpose of this function is to map the strings used in the core h5p methods
184
     * and replace them with the translated ones. If a translation for a particular string
185
     * is not available, the default message (key) will be returned.
186
     * Implements t.
187
     *
188
     * @param string $message The english string to be translated
189
     * @param array $replacements An associative array of replacements to make after translation
190
     * @return string Translated string or the english string if a translation is not available
191
     */
192
    public function t($message, $replacements = array()) {
193
 
194
        // Create mapping.
195
        $translationsmap = [
196
            'The file you uploaded is not a valid HTML5 Package (It does not have the .h5p file extension)' => 'noextension',
197
            'The file you uploaded is not a valid HTML5 Package (We are unable to unzip it)' => 'nounzip',
198
            'The main h5p.json file is not valid' => 'nojson',
199
            'Library directory name must match machineName or machineName-majorVersion.minorVersion (from library.json).' .
200
                ' (Directory: %directoryName , machineName: %machineName, majorVersion: %majorVersion, minorVersion:' .
201
                ' %minorVersion)'
202
                => 'librarydirectoryerror',
203
            'A valid content folder is missing' => 'missingcontentfolder',
204
            'A valid main h5p.json file is missing' => 'invalidmainjson',
205
            'Missing required library @library' => 'missinglibrary',
206
            "Note that the libraries may exist in the file you uploaded, but you're not allowed to upload new libraries." .
207
                ' Contact the site administrator about this.' => 'missinguploadpermissions',
208
            'Invalid library name: %name' => 'invalidlibraryname',
209
            'Could not find library.json file with valid json format for library %name' => 'missinglibraryjson',
210
            'Invalid semantics.json file has been included in the library %name' => 'invalidsemanticsjson',
211
            'Invalid language file %file in library %library' => 'invalidlanguagefile',
212
            'Invalid language file %languageFile has been included in the library %name' => 'invalidlanguagefile2',
213
            'The file "%file" is missing from library: "%name"' => 'missinglibraryfile',
214
            'The system was unable to install the <em>%component</em> component from the package, it requires a newer' .
215
                ' version of the H5P plugin. This site is currently running version %current, whereas the required version' .
216
                ' is %required or higher. You should consider upgrading and then try again.' => 'missingcoreversion',
217
            "Invalid data provided for %property in %library. Boolean expected." => 'invalidlibrarydataboolean',
218
            "Invalid data provided for %property in %library" => 'invalidlibrarydata',
219
            "Can't read the property %property in %library" => 'invalidlibraryproperty',
220
            'The required property %property is missing from %library' => 'missinglibraryproperty',
221
            'Illegal option %option in %library' => 'invalidlibraryoption',
222
            'Added %new new H5P library and updated %old old one.' => 'addedandupdatedss',
223
            'Added %new new H5P library and updated %old old ones.' => 'addedandupdatedsp',
224
            'Added %new new H5P libraries and updated %old old one.' => 'addedandupdatedps',
225
            'Added %new new H5P libraries and updated %old old ones.' => 'addedandupdatedpp',
226
            'Added %new new H5P library.' => 'addednewlibrary',
227
            'Added %new new H5P libraries.' => 'addednewlibraries',
228
            'Updated %old H5P library.' => 'updatedlibrary',
229
            'Updated %old H5P libraries.' => 'updatedlibraries',
230
            'Missing dependency @dep required by @lib.' => 'missingdependency',
231
            'Provided string is not valid according to regexp in semantics. (value: "%value", regexp: "%regexp")'
232
                => 'invalidstring',
233
            'File "%filename" not allowed. Only files with the following extensions are allowed: %files-allowed.'
234
                => 'invalidfile',
235
            'Invalid selected option in multi-select.' => 'invalidmultiselectoption',
236
            'Invalid selected option in select.' => 'invalidselectoption',
237
            'H5P internal error: unknown content type "@type" in semantics. Removing content!' => 'invalidsemanticstype',
238
            'Copyright information' => 'copyrightinfo',
239
            'Title' => 'title',
240
            'Author' => 'author',
241
            'Year(s)' => 'years',
242
            'Year' => 'year',
243
            'Source' => 'source',
244
            'License' => 'license',
245
            'Undisclosed' => 'undisclosed',
246
            'General Public License v3' => 'gpl',
247
            'Public Domain' => 'pd',
248
            'Public Domain Dedication and Licence' => 'pddl',
249
            'Public Domain Mark' => 'pdm',
250
            'Public Domain Mark (PDM)' => 'pdm',
251
            'Copyright' => 'copyrightstring',
252
            'The mbstring PHP extension is not loaded. H5P need this to function properly' => 'missingmbstring',
253
            'The version of the H5P library %machineName used in this content is not valid. Content contains %contentLibrary, ' .
254
                'but it should be %semanticsLibrary.' => 'wrongversion',
255
            'The H5P library %library used in the content is not valid' => 'invalidlibrarynamed',
256
            'Fullscreen' => 'fullscreen',
257
            'Disable fullscreen' => 'disablefullscreen',
258
            'Download' => 'download',
259
            'Rights of use' => 'copyright',
260
            'Embed' => 'embed',
261
            'Size' => 'size',
262
            'Show advanced' => 'showadvanced',
263
            'Hide advanced' => 'hideadvanced',
264
            'Include this script on your website if you want dynamic sizing of the embedded content:' => 'resizescript',
265
            'Close' => 'close',
266
            'Thumbnail' => 'thumbnail',
267
            'No copyright information available for this content.' => 'nocopyright',
268
            'Download this content as a H5P file.' => 'downloadtitle',
269
            'View copyright information for this content.' => 'copyrighttitle',
270
            'View the embed code for this content.' => 'embedtitle',
271
            'Visit H5P.org to check out more cool content.' => 'h5ptitle',
272
            'This content has changed since you last used it.' => 'contentchanged',
273
            "You'll be starting over." => 'startingover',
274
            'by' => 'by',
275
            'Show more' => 'showmore',
276
            'Show less' => 'showless',
277
            'Sublevel' => 'sublevel',
278
            'Confirm action' => 'confirmdialogheader',
279
            'Please confirm that you wish to proceed. This action is not reversible.' => 'confirmdialogbody',
280
            'Cancel' => 'cancellabel',
281
            'Confirm' => 'confirmlabel',
282
            '4.0 International' => 'licenseCC40',
283
            '3.0 Unported' => 'licenseCC30',
284
            '2.5 Generic' => 'licenseCC25',
285
            '2.0 Generic' => 'licenseCC20',
286
            '1.0 Generic' => 'licenseCC10',
287
            'General Public License' => 'licenseGPL',
288
            'Version 3' => 'licenseV3',
289
            'Version 2' => 'licenseV2',
290
            'Version 1' => 'licenseV1',
291
            'CC0 1.0 Universal (CC0 1.0) Public Domain Dedication' => 'licenseCC010',
292
            'CC0 1.0 Universal' => 'licenseCC010U',
293
            'License Version' => 'licenseversion',
294
            'Creative Commons' => 'creativecommons',
295
            'Attribution' => 'ccattribution',
296
            'Attribution (CC BY)' => 'ccattribution',
297
            'Attribution-ShareAlike' => 'ccattributionsa',
298
            'Attribution-ShareAlike (CC BY-SA)' => 'ccattributionsa',
299
            'Attribution-NoDerivs' => 'ccattributionnd',
300
            'Attribution-NoDerivs (CC BY-ND)' => 'ccattributionnd',
301
            'Attribution-NonCommercial' => 'ccattributionnc',
302
            'Attribution-NonCommercial (CC BY-NC)' => 'ccattributionnc',
303
            'Attribution-NonCommercial-ShareAlike' => 'ccattributionncsa',
304
            'Attribution-NonCommercial-ShareAlike (CC BY-NC-SA)' => 'ccattributionncsa',
305
            'Attribution-NonCommercial-NoDerivs' => 'ccattributionncnd',
306
            'Attribution-NonCommercial-NoDerivs (CC BY-NC-ND)' => 'ccattributionncnd',
307
            'Public Domain Dedication (CC0)' => 'ccpdd',
308
            'Years (from)' => 'yearsfrom',
309
            'Years (to)' => 'yearsto',
310
            "Author's name" => 'authorname',
311
            "Author's role" => 'authorrole',
312
            'Editor' => 'editor',
313
            'Licensee' => 'licensee',
314
            'Originator' => 'originator',
315
            'Any additional information about the license' => 'additionallicenseinfo',
316
            'License Extras' => 'licenseextras',
317
            'Changelog' => 'changelog',
318
            'Content Type' => 'contenttype',
319
            'Date' => 'date',
320
            'Changed by' => 'changedby',
321
            'Description of change' => 'changedescription',
322
            'Photo cropped, text changed, etc.' => 'changeplaceholder',
323
            'Author comments' => 'authorcomments',
324
            'Comments for the editor of the content (This text will not be published as a part of copyright info)'
325
                => 'authorcommentsdescription',
326
            'Reuse' => 'reuse',
327
            'Reuse Content' => 'reuseContent',
328
            'Reuse this content.' => 'reuseDescription',
329
            'Content is copied to the clipboard' => 'contentCopied',
330
            'Connection lost. Results will be stored and sent when you regain connection.' => 'connectionLost',
331
            'Connection reestablished.' => 'connectionReestablished',
332
            'Attempting to submit stored results.' => 'resubmitScores',
333
            'Your connection to the server was lost' => 'offlineDialogHeader',
334
            'We were unable to send information about your completion of this task. Please check your internet connection.'
335
                => 'offlineDialogBody',
336
            'Retrying in :num....' => 'offlineDialogRetryMessage',
337
            'Retry now' => 'offlineDialogRetryButtonLabel',
338
            'Successfully submitted results.' => 'offlineSuccessfulSubmit',
339
            'One of the files inside the package exceeds the maximum file size allowed. (%file %used > %max)'
340
                => 'fileExceedsMaxSize',
341
            'The total size of the unpacked files exceeds the maximum size allowed. (%used > %max)'
342
                => 'unpackedFilesExceedsMaxSize',
343
            'Unable to read file from the package: %fileName' => 'couldNotReadFileFromZip',
344
            'Unable to parse JSON from the package: %fileName' => 'couldNotParseJSONFromZip',
345
            'A problem with the server write access was detected. Please make sure that your server can write to your data folder.' => 'nowriteaccess',
346
            'H5P hub communication has been disabled because one or more H5P requirements failed.' => 'hubcommunicationdisabled',
347
            'Site could not be registered with the hub. Please contact your site administrator.' => 'sitecouldnotberegistered',
348
            'The H5P Hub has been disabled until this problem can be resolved. You may still upload libraries through the "H5P Libraries" page.' => 'hubisdisableduploadlibraries',
349
            'When you have revised your server setup you may re-enable H5P hub communication in H5P Settings.' => 'reviseserversetupandretry',
350
            'You have been provided a unique key that identifies you with the Hub when receiving new updates. The key is available for viewing in the "H5P Settings" page.' => 'sitekeyregistered',
351
            'Your PHP max post size is quite small. With your current setup, you may not upload files larger than {$a->%number} MB. This might be a problem when trying to upload H5Ps, images and videos. Please consider to increase it to more than 5MB' => 'maxpostsizetoosmall',
352
            'Your PHP max upload size is bigger than your max post size. This is known to cause issues in some installations.' => 'uploadsizelargerthanpostsize',
353
            'Your PHP max upload size is quite small. With your current setup, you may not upload files larger than {$a->%number} MB. This might be a problem when trying to upload H5Ps, images and videos. Please consider to increase it to more than 5MB.' => 'maxuploadsizetoosmall',
354
            'Your PHP version does not support ZipArchive.' => 'noziparchive',
355
            'Your PHP version is outdated. H5P requires version 5.2 to function properly. Version 5.6 or later is recommended.' => 'oldphpversion',
356
            'Your server does not have SSL enabled. SSL should be enabled to ensure a secure connection with the H5P hub.' => 'sslnotenabled',
357
            'Your site was successfully registered with the H5P Hub.' => 'successfullyregisteredwithhub',
358
            'Sharing <strong>:title</strong>' => 'mainTitle',
359
            'Edit info for <strong>:title</strong>' => 'editInfoTitle',
360
            'Back' => 'back',
361
            'Next' => 'next',
362
            'Review info' => 'reviewInfo',
363
            'Share' => 'share',
364
            'Save changes' => 'saveChanges',
365
            'Register on the H5P Hub' => 'registerOnHub',
366
            'Save account settings' => 'updateRegistrationOnHub',
367
            'Required Info' => 'requiredInfo',
368
            'Optional Info' => 'optionalInfo',
369
            'Review & Share' => 'reviewAndShare',
370
            'Review & Save' => 'reviewAndSave',
371
            'Shared' => 'shared',
372
            'Step :step of :total' => 'currentStep',
373
            'All content details can be edited after sharing' => 'sharingNote',
374
            'Select a license for your content' => 'licenseDescription',
375
            'Select a license version' => 'licenseVersionDescription',
376
            'Disciplines' => 'disciplineLabel',
377
            'You can select multiple disciplines' => 'disciplineDescription',
378
            'You can select up to :numDisciplines disciplines' => 'disciplineLimitReachedMessage',
379
            'Type to search for disciplines' => 'discipline:searchPlaceholder',
380
            'in' => 'discipline:in',
381
            'Dropdown button' => 'discipline:dropdownButton',
382
            'Remove :chip from the list' => 'removeChip',
383
            'Add keywords' => 'keywordsPlaceholder',
384
            'Keywords' => 'keywords',
385
            'You can add multiple keywords separated by commas. Press "Enter" or "Add" to confirm keywords' => 'keywordsDescription',
386
            'Alt text' => 'altText',
387
            'Please review the info below before you share' => 'reviewMessage',
388
            'Sub-content (images, questions etc.) will be shared under :license unless otherwise specified in the authoring tool' => 'subContentWarning',
389
            'Disciplines' => 'disciplines',
390
            'Short description' => 'shortDescription',
391
            'Long description' => 'longDescription',
392
            'Icon' => 'icon',
393
            'Screenshots' => 'screenshots',
394
            'Help me choose a license' => 'helpChoosingLicense',
395
            'Share failed.' => 'shareFailed',
396
            'Editing failed.' => 'editingFailed',
397
            'Something went wrong, please try to share again.' => 'shareTryAgain',
398
            'Please wait...' => 'pleaseWait',
399
            'Language' => 'language',
400
            'Level' => 'level',
401
            'Short description of your content' => 'shortDescriptionPlaceholder',
402
            'Long description of your content' => 'longDescriptionPlaceholder',
403
            'Description' => 'description',
404
            '640x480px. If not selected content will use category icon' => 'iconDescription',
405
            'Add up to five screenshots of your content' => 'screenshotsDescription',
406
            'Submitted!' => 'submitted',
407
            'Is now submitted to H5P Hub' => 'isNowSubmitted',
408
            'A change has been submited for' => 'changeHasBeenSubmitted',
409
            'Your content will normally be available in the Hub within one business day.' => 'contentAvailable',
410
            'Your content will update soon' => 'contentUpdateSoon',
411
            'Content License Info' => 'contentLicenseTitle',
412
            'Click on a specific license to get info about proper usage' => 'licenseDialogDescription',
413
            'Publisher' => 'publisherFieldTitle',
414
            'This will display as the "Publisher name" on shared content' => 'publisherFieldDescription',
415
            'Email Address' => 'emailAddress',
416
            'Publisher description' => 'publisherDescription',
417
            'This will be displayed under "Publisher info" on shared content' => 'publisherDescriptionText',
418
            'Contact Person' => 'contactPerson',
419
            'Phone' => 'phone',
420
            'Address' => 'address',
421
            'City' => 'city',
422
            'Zip' => 'zip',
423
            'Country' => 'country',
424
            'Organization logo or avatar' => 'logoUploadText',
425
            'I accept the <a href=":url" target="_blank">terms of use</a>' => 'acceptTerms',
426
            'You have successfully registered an account on the H5P Hub' => 'successfullyRegistred',
427
            'You account details can be changed' => 'successfullyRegistredDescription',
428
            'Your H5P Hub account settings have successfully been changed' => 'successfullyUpdated',
429
            'here' => 'accountDetailsLinkText',
430
            'H5P Hub Registration' => 'registrationTitle',
431
            'An error occurred' => 'registrationFailed',
432
            'We were not able to create an account at this point. Something went wrong. Try again later.' => 'registrationFailedDescription',
433
            ':length is the maximum number of characters' => 'maxLength',
434
            'Keyword already exists!' => 'keywordExists',
435
            'License details' => 'licenseDetails',
436
            'Remove' => 'remove',
437
            'Remove image' => 'removeImage',
438
            'Cancel sharing' => 'cancelPublishConfirmationDialogTitle',
439
            'Are you sure you want to cancel the sharing process?' => 'cancelPublishConfirmationDialogDescription',
440
            'No' => 'cancelPublishConfirmationDialogCancelButtonText',
441
            'Yes' => 'cancelPublishConfirmationDialogConfirmButtonText',
442
            'Add' => 'add',
443
            'Typical age' => 'age',
444
            'The target audience of this content. Possible input formats separated by commas: "1,34-45,-50,59-".' => 'ageDescription',
445
            'Invalid input format for Typical age. Possible input formats separated by commas: "1, 34-45, -50, -59-".' => 'invalidAge',
446
            'H5P will reach out to the contact person in case there are any issues with the content shared by the publisher. The contact person\'s name or other information will not be published or shared with third parties' => 'contactPersonDescription',
447
            'The email address will be used by H5P to reach out to the publisher in case of any issues with the content or in case the publisher needs to recover their account. It will not be published or shared with any third parties' => 'emailAddressDescription',
448
            'Copyrighted material cannot be shared in the H5P Content Hub. If the content is licensed with a OER friendly license like Creative Commons, please choose the appropriate license. If not this content cannot be shared.' => 'copyrightWarning',
449
            'Keywords already exists!' => 'keywordsExits',
450
            'Some of these keywords already exist' => 'someKeywordsExits',
451
            'Assistive Technologies label' => 'a11yTitle:label',
452
            'width' => 'width',
453
            'height' => 'height',
454
            'Missing main library @library' => 'missingmainlibrary',
1441 ariadna 455
            'Rotate Left' => 'rotateLeft',
456
            'Rotate Right' => 'rotateRight',
457
            'Crop Image' => 'cropImage',
458
            'Confirm Crop' => 'confirmCrop',
459
            'Cancel Crop' => 'cancelCrop',
1 efrain 460
        ];
461
 
462
        if (isset($translationsmap[$message])) {
463
            return get_string($translationsmap[$message], 'core_h5p', $replacements);
464
        }
465
 
466
        debugging("String translation cannot be found. Please add a string definition for '" .
467
            $message . "' in the core_h5p component.", DEBUG_DEVELOPER);
468
 
469
        return $message;
470
    }
471
 
472
    /**
473
     * Get URL to file in the specifimake_pluginfile_urlc library.
474
     * Implements getLibraryFileUrl.
475
     *
476
     * @param string $libraryfoldername The name or path of the library's folder
477
     * @param string $filename The file name
478
     * @return string URL to file
479
     */
480
    public function getLibraryFileUrl($libraryfoldername, $filename) {
481
        global $DB;
482
 
483
        // Remove unnecessary slashes (first and last, if present) from the path to the folder
484
        // of the library file.
485
        $libraryfilepath = trim($libraryfoldername, '/');
486
 
487
        // Get the folder name of the library from the path.
488
        // The first element should represent the folder name of the library.
489
        $libfoldername = explode('/', $libraryfilepath)[0];
490
 
491
        $factory = new \core_h5p\factory();
492
        $core = $factory->get_core();
493
 
494
        // The provided folder name of the library must have a valid format (can be parsed).
495
        // The folder name is parsed with a purpose of getting the library related information
496
        // such as 'machineName', 'majorVersion' and 'minorVersion'.
497
        // This information is later used to retrieve the library ID.
498
        if (!$libdata = $core->libraryFromString($libfoldername, true)) {
499
            debugging('The provided string value "' . $libfoldername .
500
                '" is not a valid name for a library folder.', DEBUG_DEVELOPER);
501
 
502
            return;
503
        }
504
 
505
        $params = array(
506
            'machinename' => $libdata['machineName'],
507
            'majorversion' => $libdata['majorVersion'],
508
            'minorversion' => $libdata['minorVersion']
509
        );
510
 
511
        $libraries = $DB->get_records('h5p_libraries', $params, 'patchversion DESC', 'id',
512
            0, 1);
513
 
514
        if (!$library = reset($libraries)) {
515
            debugging('The library "' . $libfoldername . '" does not exist.', DEBUG_DEVELOPER);
516
 
517
            return;
518
        }
519
 
520
        $context = \context_system::instance();
521
 
522
        return \moodle_url::make_pluginfile_url($context->id, 'core_h5p', 'libraries',
523
            $library->id, '/' . $libraryfilepath . '/', $filename)->out();
524
    }
525
 
526
    /**
527
     * Get the Path to the last uploaded h5p.
528
     * Implements getUploadedH5PFolderPath.
529
     *
530
     * @param string $setpath The path to the folder of the last uploaded h5p
531
     * @return string Path to the folder where the last uploaded h5p for this session is located
532
     */
533
    public function getUploadedH5pFolderPath($setpath = null) {
534
        if ($setpath !== null) {
535
            $this->lastuploadedfolder = $setpath;
536
        }
537
 
538
        if (!isset($this->lastuploadedfolder)) {
539
            throw new \coding_exception('Using getUploadedH5pFolderPath() before path is set');
540
        }
541
 
542
        return $this->lastuploadedfolder;
543
    }
544
 
545
    /**
546
     * Get the path to the last uploaded h5p file.
547
     * Implements getUploadedH5PPath.
548
     *
549
     * @param string $setpath The path to the last uploaded h5p
550
     * @return string Path to the last uploaded h5p
551
     */
552
    public function getUploadedH5pPath($setpath = null) {
553
        if ($setpath !== null) {
554
            $this->lastuploadedfile = $setpath;
555
        }
556
 
557
        if (!isset($this->lastuploadedfile)) {
558
            throw new \coding_exception('Using getUploadedH5pPath() before path is set');
559
        }
560
 
561
        return $this->lastuploadedfile;
562
    }
563
 
564
    /**
565
     * Load addon libraries.
566
     * Implements loadAddons.
567
     *
568
     * @return array The array containing the addon libraries
569
     */
570
    public function loadAddons() {
571
        global $DB;
572
 
573
        $addons = array();
574
 
575
        $records = $DB->get_records_sql(
576
                "SELECT l1.id AS library_id,
577
                            l1.machinename AS machine_name,
578
                            l1.majorversion AS major_version,
579
                            l1.minorversion AS minor_version,
580
                            l1.patchversion AS patch_version,
581
                            l1.addto AS add_to,
582
                            l1.preloadedjs AS preloaded_js,
583
                            l1.preloadedcss AS preloaded_css
584
                       FROM {h5p_libraries} l1
585
                  LEFT JOIN {h5p_libraries} l2
586
                         ON l1.machinename = l2.machinename
587
                        AND (l1.majorversion < l2.majorversion
588
                             OR (l1.majorversion = l2.majorversion
589
                                 AND l1.minorversion < l2.minorversion))
590
                      WHERE l1.addto IS NOT NULL
591
                        AND l2.machinename IS NULL");
592
 
593
        // NOTE: These are treated as library objects but are missing the following properties:
594
        // title, droplibrarycss, fullscreen, runnable, semantics.
595
 
596
        // Extract num from records.
597
        foreach ($records as $addon) {
598
            $addons[] = H5PCore::snakeToCamel($addon);
599
        }
600
 
601
        return $addons;
602
    }
603
 
604
    /**
605
     * Load config for libraries.
606
     * Implements getLibraryConfig.
607
     *
608
     * @param array|null $libraries List of libraries
609
     * @return array|null The library config if it exists, null otherwise
610
     */
611
    public function getLibraryConfig($libraries = null) {
612
        global $CFG;
613
        return isset($CFG->core_h5p_library_config) ? $CFG->core_h5p_library_config : null;
614
    }
615
 
616
    /**
617
     * Get a list of the current installed libraries.
618
     * Implements loadLibraries.
619
     *
620
     * @return array Associative array containing one entry per machine name.
621
     *               For each machineName there is a list of libraries(with different versions).
622
     */
623
    public function loadLibraries() {
624
        global $DB;
625
 
626
        $results = $DB->get_records('h5p_libraries', [], 'title ASC, majorversion ASC, minorversion ASC',
627
            'id, machinename AS machine_name, majorversion AS major_version, minorversion AS minor_version,
628
            patchversion AS patch_version, runnable, title, enabled');
629
 
630
        $libraries = array();
631
        foreach ($results as $library) {
632
            $libraries[$library->machine_name][] = $library;
633
        }
634
 
635
        return $libraries;
636
    }
637
 
638
    /**
639
     * Returns the URL to the library admin page.
640
     * Implements getAdminUrl.
641
     *
642
     * @return string URL to admin page
643
     */
644
    public function getAdminUrl() {
645
        // Not supported.
646
    }
647
 
648
    /**
649
     * Return the library's ID.
650
     * Implements getLibraryId.
651
     *
652
     * @param string $machinename The librarys machine name
653
     * @param string $majorversion Major version number for library (optional)
654
     * @param string $minorversion Minor version number for library (optional)
655
     * @return int|bool Identifier, or false if non-existent
656
     */
657
    public function getLibraryId($machinename, $majorversion = null, $minorversion = null) {
658
        global $DB;
659
 
660
        $params = array(
661
            'machinename' => $machinename
662
        );
663
 
664
        if ($majorversion !== null) {
665
            $params['majorversion'] = $majorversion;
666
        }
667
 
668
        if ($minorversion !== null) {
669
            $params['minorversion'] = $minorversion;
670
        }
671
 
672
        $libraries = $DB->get_records('h5p_libraries', $params,
673
            'majorversion DESC, minorversion DESC, patchversion DESC', 'id', 0, 1);
674
 
675
        // Get the latest version which matches the input parameters.
676
        if ($libraries) {
677
            $library = reset($libraries);
678
            return $library->id ?? false;
679
        }
680
 
681
        return false;
682
    }
683
 
684
    /**
685
     * Get allowed file extension list.
686
     * Implements getWhitelist.
687
     *
688
     * The default extension list is part of h5p, but admins should be allowed to modify it.
689
     *
690
     * @param boolean $islibrary TRUE if this is the whitelist for a library. FALSE if it is the whitelist
691
     *                           for the content folder we are getting.
692
     * @param string $defaultcontentwhitelist A string of file extensions separated by whitespace.
693
     * @param string $defaultlibrarywhitelist A string of file extensions separated by whitespace.
694
     * @return string A string containing the allowed file extensions separated by whitespace.
695
     */
696
    public function getWhitelist($islibrary, $defaultcontentwhitelist, $defaultlibrarywhitelist) {
697
        return $defaultcontentwhitelist . ($islibrary ? ' ' . $defaultlibrarywhitelist : '');
698
    }
699
 
700
    /**
701
     * Is the library a patched version of an existing library?
702
     * Implements isPatchedLibrary.
703
     *
704
     * @param array $library An associative array containing:
705
     *                       - machineName: The library machine name
706
     *                       - majorVersion: The librarys major version
707
     *                       - minorVersion: The librarys minor version
708
     *                       - patchVersion: The librarys patch version
709
     * @return boolean TRUE if the library is a patched version of an existing library FALSE otherwise
710
     */
711
    public function isPatchedLibrary($library) {
712
        global $DB;
713
 
714
        $sql = "SELECT id
715
                  FROM {h5p_libraries}
716
                 WHERE machinename = :machinename
717
                   AND majorversion = :majorversion
718
                   AND minorversion = :minorversion
719
                   AND patchversion < :patchversion";
720
 
721
        $library = $DB->get_records_sql(
722
            $sql,
723
            array(
724
                'machinename' => $library['machineName'],
725
                'majorversion' => $library['majorVersion'],
726
                'minorversion' => $library['minorVersion'],
727
                'patchversion' => $library['patchVersion']
728
            ),
729
            0,
730
            1
731
        );
732
 
733
        return !empty($library);
734
    }
735
 
736
    /**
737
     * Is H5P in development mode?
738
     * Implements isInDevMode.
739
     *
740
     * @return boolean TRUE if H5P development mode is active FALSE otherwise
741
     */
742
    public function isInDevMode() {
743
        return false; // Not supported (Files in moodle not editable).
744
    }
745
 
746
    /**
747
     * Is the current user allowed to update libraries?
748
     * Implements mayUpdateLibraries.
749
     *
750
     * @return boolean TRUE if the user is allowed to update libraries,
751
     *                 FALSE if the user is not allowed to update libraries.
752
     */
753
    public function mayUpdateLibraries() {
754
        return helper::can_update_library($this->get_file());
755
    }
756
 
757
    /**
758
     * Get the .h5p file.
759
     *
760
     * @return \stored_file The .h5p file.
761
     */
762
    public function get_file(): \stored_file {
763
        if (!isset($this->file)) {
764
            throw new \coding_exception('Using get_file() before file is set');
765
        }
766
 
767
        return $this->file;
768
    }
769
 
770
    /**
771
     * Set the .h5p file.
772
     *
773
     * @param  stored_file $file The .h5p file.
774
     */
775
    public function set_file(\stored_file $file): void {
776
        $this->file = $file;
777
    }
778
 
779
    /**
780
     * Store data about a library.
781
     * Implements saveLibraryData.
782
     *
783
     * Also fills in the libraryId in the libraryData object if the object is new.
784
     *
785
     * @param array $librarydata Associative array containing:
786
     *                           - libraryId: The id of the library if it is an existing library
787
     *                           - title: The library's name
788
     *                           - machineName: The library machineName
789
     *                           - majorVersion: The library's majorVersion
790
     *                           - minorVersion: The library's minorVersion
791
     *                           - patchVersion: The library's patchVersion
792
     *                           - runnable: 1 if the library is a content type, 0 otherwise
793
     *                           - fullscreen(optional): 1 if the library supports fullscreen, 0 otherwise
794
     *                           - embedtypes: list of supported embed types
795
     *                           - preloadedJs(optional): list of associative arrays containing:
796
     *                             - path: path to a js file relative to the library root folder
797
     *                           - preloadedCss(optional): list of associative arrays containing:
798
     *                             - path: path to css file relative to the library root folder
799
     *                           - dropLibraryCss(optional): list of associative arrays containing:
800
     *                             - machineName: machine name for the librarys that are to drop their css
801
     *                           - semantics(optional): Json describing the content structure for the library
802
     *                           - metadataSettings(optional): object containing:
803
     *                             - disable: 1 if metadata is disabled completely
804
     *                             - disableExtraTitleField: 1 if the title field is hidden in the form
805
     * @param bool $new Whether it is a new or existing library.
806
     */
807
    public function saveLibraryData(&$librarydata, $new = true) {
808
        global $DB;
809
 
810
        // Some special properties needs some checking and converting before they can be saved.
811
        $preloadedjs = $this->library_parameter_values_to_csv($librarydata, 'preloadedJs', 'path');
812
        $preloadedcss = $this->library_parameter_values_to_csv($librarydata, 'preloadedCss', 'path');
813
        $droplibrarycss = $this->library_parameter_values_to_csv($librarydata, 'dropLibraryCss', 'machineName');
814
 
815
        if (!isset($librarydata['semantics'])) {
816
            $librarydata['semantics'] = '';
817
        }
818
        if (!isset($librarydata['fullscreen'])) {
819
            $librarydata['fullscreen'] = 0;
820
        }
821
        $embedtypes = '';
822
        if (isset($librarydata['embedTypes'])) {
823
            $embedtypes = implode(', ', $librarydata['embedTypes']);
824
        }
825
 
826
        $library = (object) array(
827
            'title' => $librarydata['title'],
828
            'machinename' => $librarydata['machineName'],
829
            'majorversion' => $librarydata['majorVersion'],
830
            'minorversion' => $librarydata['minorVersion'],
831
            'patchversion' => $librarydata['patchVersion'],
832
            'runnable' => $librarydata['runnable'],
833
            'fullscreen' => $librarydata['fullscreen'],
834
            'embedtypes' => $embedtypes,
835
            'preloadedjs' => $preloadedjs,
836
            'preloadedcss' => $preloadedcss,
837
            'droplibrarycss' => $droplibrarycss,
838
            'semantics' => $librarydata['semantics'],
839
            'addto' => isset($librarydata['addTo']) ? json_encode($librarydata['addTo']) : null,
840
            'coremajor' => isset($librarydata['coreApi']['majorVersion']) ? $librarydata['coreApi']['majorVersion'] : null,
841
            'coreminor' => isset($librarydata['coreApi']['majorVersion']) ? $librarydata['coreApi']['minorVersion'] : null,
842
            'metadatasettings' => isset($librarydata['metadataSettings']) ? $librarydata['metadataSettings'] : null,
843
        );
844
 
845
        if ($new) {
846
            // Create new library and keep track of id.
847
            $library->id = $DB->insert_record('h5p_libraries', $library);
848
            $librarydata['libraryId'] = $library->id;
849
        } else {
850
            // Update library data.
851
            $library->id = $librarydata['libraryId'];
852
            // Save library data.
853
            $DB->update_record('h5p_libraries', $library);
854
            // Remove old dependencies.
855
            $this->deleteLibraryDependencies($librarydata['libraryId']);
856
        }
857
    }
858
 
859
    /**
860
     * Insert new content.
861
     * Implements insertContent.
862
     *
863
     * @param array $content An associative array containing:
864
     *                       - id: The content id
865
     *                       - params: The content in json format
866
     *                       - library: An associative array containing:
867
     *                         - libraryId: The id of the main library for this content
868
     *                       - disable: H5P Button display options
869
     *                       - pathnamehash: The pathnamehash linking the record with the entry in the mdl_files table
870
     *                       - contenthash: The contenthash linking the record with the entry in the mdl_files table
871
     * @param int $contentmainid Main id for the content if this is a system that supports versions
872
     * @return int The ID of the newly inserted content
873
     */
874
    public function insertContent($content, $contentmainid = null) {
875
        return $this->updateContent($content);
876
    }
877
 
878
    /**
879
     * Update old content or insert new content.
880
     * Implements updateContent.
881
     *
882
     * @param array $content An associative array containing:
883
     *                       - id: The content id
884
     *                       - params: The content in json format
885
     *                       - library: An associative array containing:
886
     *                         - libraryId: The id of the main library for this content
887
     *                       - disable: H5P Button display options
888
     *                       - pathnamehash: The pathnamehash linking the record with the entry in the mdl_files table
889
     *                       - contenthash: The contenthash linking the record with the entry in the mdl_files table
890
     * @param int $contentmainid Main id for the content if this is a system that supports versions
891
     * @return int The ID of the newly inserted or updated content
892
     */
893
    public function updateContent($content, $contentmainid = null) {
894
        global $DB;
895
 
896
        // If the libraryid declared in the package is empty, get the latest version.
897
        if (empty($content['library']['libraryId'])) {
898
            $mainlibrary = $this->get_latest_library_version($content['library']['machineName']);
899
            if (empty($mainlibrary)) {
900
                // Raise an error if the main library is not defined and the latest version doesn't exist.
901
                $message = $this->t('Missing required library @library', ['@library' => $content['library']['machineName']]);
902
                $this->setErrorMessage($message, 'missing-required-library');
903
                return false;
904
            }
905
            $content['library']['libraryId'] = $mainlibrary->id;
906
        }
907
 
908
        $content['disable'] = $content['disable'] ?? null;
909
        // Add title to 'params' to use in the editor.
910
        if (!empty($content['title'])) {
911
            $params = json_decode($content['params']);
912
            $params->title = $content['title'];
913
            $content['params'] = json_encode($params);
914
        }
915
        // Add metadata to 'params'.
916
        if (!empty($content['metadata'])) {
917
            $params = json_decode($content['params']);
918
            $params->metadata = $content['metadata'];
919
            $content['params'] = json_encode($params);
920
        }
921
 
922
        $data = [
923
            'jsoncontent' => $content['params'],
924
            'displayoptions' => $content['disable'],
925
            'mainlibraryid' => $content['library']['libraryId'],
926
            'timemodified' => time(),
927
            'filtered' => null,
928
        ];
929
 
930
        if (isset($content['pathnamehash'])) {
931
            $data['pathnamehash'] = $content['pathnamehash'];
932
        }
933
 
934
        if (isset($content['contenthash'])) {
935
            $data['contenthash'] = $content['contenthash'];
936
        }
937
 
938
        if (!isset($content['id'])) {
939
            $data['pathnamehash'] = $data['pathnamehash'] ?? '';
940
            $data['contenthash'] = $data['contenthash'] ?? '';
941
            $data['timecreated'] = $data['timemodified'];
942
            $id = $DB->insert_record('h5p', $data);
943
        } else {
944
            $id = $data['id'] = $content['id'];
945
            $DB->update_record('h5p', $data);
946
        }
947
 
948
        return $id;
949
    }
950
 
951
    /**
952
     * Resets marked user data for the given content.
953
     * Implements resetContentUserData.
954
     *
955
     * @param int $contentid The h5p content id
956
     */
957
    public function resetContentUserData($contentid) {
958
        global $DB;
959
 
960
        // Get the component associated to the H5P content to reset.
961
        $h5p = $DB->get_record('h5p', ['id' => $contentid]);
962
        if (!$h5p) {
963
            return;
964
        }
965
 
966
        $fs = get_file_storage();
967
        $file = $fs->get_file_by_hash($h5p->pathnamehash);
968
        if (!$file) {
969
            return;
970
        }
971
 
972
        // Reset user data.
973
        try {
974
            $xapihandler = handler::create($file->get_component());
975
            // Reset only entries with 'state' as stateid (the ones restored shouldn't be restored, because the H5P
976
            // content hasn't been created yet).
977
            $xapihandler->reset_states($file->get_contextid(), null, 'state');
978
        } catch (xapi_exception $exception) {
979
            // This component doesn't support xAPI State, so no content needs to be reset.
980
            return;
981
        }
982
    }
983
 
984
    /**
985
     * Save what libraries a library is depending on.
986
     * Implements saveLibraryDependencies.
987
     *
988
     * @param int $libraryid Library Id for the library we're saving dependencies for
989
     * @param array $dependencies List of dependencies as associative arrays containing:
990
     *                            - machineName: The library machineName
991
     *                            - majorVersion: The library's majorVersion
992
     *                            - minorVersion: The library's minorVersion
993
     * @param string $dependencytype The type of dependency
994
     */
995
    public function saveLibraryDependencies($libraryid, $dependencies, $dependencytype) {
996
        global $DB;
997
 
998
        foreach ($dependencies as $dependency) {
999
            // Find dependency library.
1000
            $dependencylibrary = $DB->get_record('h5p_libraries',
1001
                array(
1002
                    'machinename' => $dependency['machineName'],
1003
                    'majorversion' => $dependency['majorVersion'],
1004
                    'minorversion' => $dependency['minorVersion']
1005
                )
1006
            );
1007
 
1008
            // Create relation.
1009
            $DB->insert_record('h5p_library_dependencies', array(
1010
                'libraryid' => $libraryid,
1011
                'requiredlibraryid' => $dependencylibrary->id,
1012
                'dependencytype' => $dependencytype
1013
            ));
1014
        }
1015
    }
1016
 
1017
    /**
1018
     * Give an H5P the same library dependencies as a given H5P.
1019
     * Implements copyLibraryUsage.
1020
     *
1021
     * @param int $contentid Id identifying the content
1022
     * @param int $copyfromid Id identifying the content to be copied
1023
     * @param int $contentmainid Main id for the content, typically used in frameworks
1024
     */
1025
    public function copyLibraryUsage($contentid, $copyfromid, $contentmainid = null) {
1026
        // Currently not being called.
1027
    }
1028
 
1029
    /**
1030
     * Deletes content data.
1031
     * Implements deleteContentData.
1032
     *
1033
     * @param int $contentid Id identifying the content
1034
     */
1035
    public function deleteContentData($contentid) {
1036
        global $DB;
1037
 
1038
        // The user content should be reset (instead of removed), because this method is called when H5P content needs
1039
        // to be updated too (and the previous states must be kept, but reset).
1040
        $this->resetContentUserData($contentid);
1041
 
1042
        // Remove content.
1043
        $DB->delete_records('h5p', ['id' => $contentid]);
1044
 
1045
        // Remove content library dependencies.
1046
        $this->deleteLibraryUsage($contentid);
1047
    }
1048
 
1049
    /**
1050
     * Delete what libraries a content item is using.
1051
     * Implements deleteLibraryUsage.
1052
     *
1053
     * @param int $contentid Content Id of the content we'll be deleting library usage for
1054
     */
1055
    public function deleteLibraryUsage($contentid) {
1056
        global $DB;
1057
 
1058
        $DB->delete_records('h5p_contents_libraries', array('h5pid' => $contentid));
1059
    }
1060
 
1061
    /**
1062
     * Saves what libraries the content uses.
1063
     * Implements saveLibraryUsage.
1064
     *
1065
     * @param int $contentid Id identifying the content
1066
     * @param array $librariesinuse List of libraries the content uses
1067
     */
1068
    public function saveLibraryUsage($contentid, $librariesinuse) {
1069
        global $DB;
1070
 
1071
        $droplibrarycsslist = array();
1072
        foreach ($librariesinuse as $dependency) {
1073
            if (!empty($dependency['library']['dropLibraryCss'])) {
1074
                $droplibrarycsslist = array_merge($droplibrarycsslist,
1075
                        explode(', ', $dependency['library']['dropLibraryCss']));
1076
            }
1077
        }
1078
 
1079
        foreach ($librariesinuse as $dependency) {
1080
            $dropcss = in_array($dependency['library']['machineName'], $droplibrarycsslist) ? 1 : 0;
1081
            $DB->insert_record('h5p_contents_libraries', array(
1082
                'h5pid' => $contentid,
1083
                'libraryid' => $dependency['library']['libraryId'],
1084
                'dependencytype' => $dependency['type'],
1085
                'dropcss' => $dropcss,
1086
                'weight' => $dependency['weight']
1087
            ));
1088
        }
1089
    }
1090
 
1091
    /**
1092
     * Get number of content/nodes using a library, and the number of dependencies to other libraries.
1093
     * Implements getLibraryUsage.
1094
     *
1095
     * @param int $id Library identifier
1096
     * @param boolean $skipcontent Optional. Set as true to get number of content instances for library
1097
     * @return array The array contains two elements, keyed by 'content' and 'libraries'.
1098
     *               Each element contains a number
1099
     */
1100
    public function getLibraryUsage($id, $skipcontent = false) {
1101
        global $DB;
1102
 
1103
        if ($skipcontent) {
1104
            $content = -1;
1105
        } else {
1106
            $sql = "SELECT COUNT(distinct c.id)
1107
                      FROM {h5p_libraries} l
1108
                      JOIN {h5p_contents_libraries} cl ON l.id = cl.libraryid
1109
                      JOIN {h5p} c ON cl.h5pid = c.id
1110
                     WHERE l.id = :libraryid";
1111
 
1112
            $sqlargs = array(
1113
                'libraryid' => $id
1114
            );
1115
 
1116
            $content = $DB->count_records_sql($sql, $sqlargs);
1117
        }
1118
 
1119
        $libraries = $DB->count_records('h5p_library_dependencies', ['requiredlibraryid' => $id]);
1120
 
1121
        return array(
1122
            'content' => $content,
1123
            'libraries' => $libraries,
1124
        );
1125
    }
1126
 
1127
    /**
1128
     * Loads a library.
1129
     * Implements loadLibrary.
1130
     *
1131
     * @param string $machinename The library's machine name
1132
     * @param int $majorversion The library's major version
1133
     * @param int $minorversion The library's minor version
1134
     * @return array|bool Returns FALSE if the library does not exist
1135
     *                     Otherwise an associative array containing:
1136
     *                     - libraryId: The id of the library if it is an existing library,
1137
     *                     - title: The library's name,
1138
     *                     - machineName: The library machineName
1139
     *                     - majorVersion: The library's majorVersion
1140
     *                     - minorVersion: The library's minorVersion
1141
     *                     - patchVersion: The library's patchVersion
1142
     *                     - runnable: 1 if the library is a content type, 0 otherwise
1143
     *                     - fullscreen: 1 if the library supports fullscreen, 0 otherwise
1144
     *                     - embedTypes: list of supported embed types
1145
     *                     - preloadedJs: comma separated string with js file paths
1146
     *                     - preloadedCss: comma separated sting with css file paths
1147
     *                     - dropLibraryCss: list of associative arrays containing:
1148
     *                       - machineName: machine name for the librarys that are to drop their css
1149
     *                     - semantics: Json describing the content structure for the library
1150
     *                     - preloadedDependencies(optional): list of associative arrays containing:
1151
     *                       - machineName: Machine name for a library this library is depending on
1152
     *                       - majorVersion: Major version for a library this library is depending on
1153
     *                       - minorVersion: Minor for a library this library is depending on
1154
     *                     - dynamicDependencies(optional): list of associative arrays containing:
1155
     *                       - machineName: Machine name for a library this library is depending on
1156
     *                       - majorVersion: Major version for a library this library is depending on
1157
     *                       - minorVersion: Minor for a library this library is depending on
1158
     */
1159
    public function loadLibrary($machinename, $majorversion, $minorversion) {
1160
        global $DB;
1161
 
1162
        $library = $DB->get_record('h5p_libraries', array(
1163
            'machinename' => $machinename,
1164
            'majorversion' => $majorversion,
1165
            'minorversion' => $minorversion
1166
        ));
1167
 
1168
        if (!$library) {
1169
            return false;
1170
        }
1171
 
1172
        $librarydata = array(
1173
            'libraryId' => $library->id,
1174
            'title' => $library->title,
1175
            'machineName' => $library->machinename,
1176
            'majorVersion' => $library->majorversion,
1177
            'minorVersion' => $library->minorversion,
1178
            'patchVersion' => $library->patchversion,
1179
            'runnable' => $library->runnable,
1180
            'fullscreen' => $library->fullscreen,
1181
            'embedTypes' => $library->embedtypes,
1182
            'preloadedJs' => $library->preloadedjs,
1183
            'preloadedCss' => $library->preloadedcss,
1184
            'dropLibraryCss' => $library->droplibrarycss,
1185
            'semantics'     => $library->semantics
1186
        );
1187
 
1188
        $sql = 'SELECT hl.id, hl.machinename, hl.majorversion, hl.minorversion, hll.dependencytype
1189
                  FROM {h5p_library_dependencies} hll
1190
                  JOIN {h5p_libraries} hl ON hll.requiredlibraryid = hl.id
1191
                 WHERE hll.libraryid = :libraryid
1192
              ORDER BY hl.id ASC';
1193
 
1194
        $sqlargs = array(
1195
            'libraryid' => $library->id
1196
        );
1197
 
1198
        $dependencies = $DB->get_records_sql($sql, $sqlargs);
1199
 
1200
        foreach ($dependencies as $dependency) {
1201
            $librarydata[$dependency->dependencytype . 'Dependencies'][] = array(
1202
                'machineName' => $dependency->machinename,
1203
                'majorVersion' => $dependency->majorversion,
1204
                'minorVersion' => $dependency->minorversion
1205
            );
1206
        }
1207
 
1208
        return $librarydata;
1209
    }
1210
 
1211
    /**
1212
     * Loads library semantics.
1213
     * Implements loadLibrarySemantics.
1214
     *
1215
     * @param string $name Machine name for the library
1216
     * @param int $majorversion The library's major version
1217
     * @param int $minorversion The library's minor version
1218
     * @return string The library's semantics as json
1219
     */
1220
    public function loadLibrarySemantics($name, $majorversion, $minorversion) {
1221
        global $DB;
1222
 
1223
        $semantics = $DB->get_field('h5p_libraries', 'semantics',
1224
            array(
1225
                'machinename' => $name,
1226
                'majorversion' => $majorversion,
1227
                'minorversion' => $minorversion
1228
            )
1229
        );
1230
 
1231
        return ($semantics === false ? null : $semantics);
1232
    }
1233
 
1234
    /**
1235
     * Makes it possible to alter the semantics, adding custom fields, etc.
1236
     * Implements alterLibrarySemantics.
1237
     *
1238
     * @param array $semantics Associative array representing the semantics
1239
     * @param string $name The library's machine name
1240
     * @param int $majorversion The library's major version
1241
     * @param int $minorversion The library's minor version
1242
     */
1243
    public function alterLibrarySemantics(&$semantics, $name, $majorversion, $minorversion) {
1244
        global $PAGE;
1245
 
1246
        $renderer = $PAGE->get_renderer('core_h5p');
1247
        $renderer->h5p_alter_semantics($semantics, $name, $majorversion, $minorversion);
1248
    }
1249
 
1250
    /**
1251
     * Delete all dependencies belonging to given library.
1252
     * Implements deleteLibraryDependencies.
1253
     *
1254
     * @param int $libraryid Library identifier
1255
     */
1256
    public function deleteLibraryDependencies($libraryid) {
1257
        global $DB;
1258
 
1259
        $DB->delete_records('h5p_library_dependencies', array('libraryid' => $libraryid));
1260
    }
1261
 
1262
    /**
1263
     * Start an atomic operation against the dependency storage.
1264
     * Implements lockDependencyStorage.
1265
     */
1266
    public function lockDependencyStorage() {
1267
        // Library development mode not supported.
1268
    }
1269
 
1270
    /**
1271
     * Start an atomic operation against the dependency storage.
1272
     * Implements unlockDependencyStorage.
1273
     */
1274
    public function unlockDependencyStorage() {
1275
        // Library development mode not supported.
1276
    }
1277
 
1278
    /**
1279
     * Delete a library from database and file system.
1280
     * Implements deleteLibrary.
1281
     *
1282
     * @param \stdClass $library Library object with id, name, major version and minor version
1283
     */
1284
    public function deleteLibrary($library) {
1285
        $factory = new \core_h5p\factory();
1286
        \core_h5p\api::delete_library($factory, $library);
1287
    }
1288
 
1289
    /**
1290
     * Load content.
1291
     * Implements loadContent.
1292
     *
1293
     * @param int $id Content identifier
1294
     * @return array Associative array containing:
1295
     *               - id: Identifier for the content
1296
     *               - params: json content as string
1297
     *               - embedType: list of supported embed types
1298
     *               - disable: H5P Button display options
1299
     *               - title: H5P content title
1300
     *               - slug: Human readable content identifier that is unique
1301
     *               - libraryId: Id for the main library
1302
     *               - libraryName: The library machine name
1303
     *               - libraryMajorVersion: The library's majorVersion
1304
     *               - libraryMinorVersion: The library's minorVersion
1305
     *               - libraryEmbedTypes: CSV of the main library's embed types
1306
     *               - libraryFullscreen: 1 if fullscreen is supported. 0 otherwise
1307
     *               - metadata: The content's metadata
1308
     */
1309
    public function loadContent($id) {
1310
        global $DB;
1311
 
1312
        $sql = "SELECT hc.id, hc.jsoncontent, hc.displayoptions, hl.id AS libraryid,
1313
                       hl.machinename, hl.title, hl.majorversion, hl.minorversion, hl.fullscreen,
1314
                       hl.embedtypes, hl.semantics, hc.filtered, hc.pathnamehash
1315
                  FROM {h5p} hc
1316
                  JOIN {h5p_libraries} hl ON hl.id = hc.mainlibraryid
1317
                 WHERE hc.id = :h5pid";
1318
 
1319
        $sqlargs = array(
1320
            'h5pid' => $id
1321
        );
1322
 
1323
        $data = $DB->get_record_sql($sql, $sqlargs);
1324
 
1325
        // Return null if not found.
1326
        if ($data === false) {
1327
            return null;
1328
        }
1329
 
1330
        // Some databases do not support camelCase, so we need to manually
1331
        // map the values to the camelCase names used by the H5P core.
1332
        $content = array(
1333
            'id' => $data->id,
1334
            'params' => $data->jsoncontent,
1335
            // It has been decided that the embedtype will be always set to 'iframe' (at least for now) because the 'div'
1336
            // may cause conflicts with CSS and JS in some cases.
1337
            'embedType' => 'iframe',
1338
            'disable' => $data->displayoptions,
1339
            'title' => $data->title,
1340
            'slug' => H5PCore::slugify($data->title) . '-' . $data->id,
1341
            'filtered' => $data->filtered,
1342
            'libraryId' => $data->libraryid,
1343
            'libraryName' => $data->machinename,
1344
            'libraryMajorVersion' => $data->majorversion,
1345
            'libraryMinorVersion' => $data->minorversion,
1346
            'libraryEmbedTypes' => $data->embedtypes,
1347
            'libraryFullscreen' => $data->fullscreen,
1348
            'metadata' => '',
1349
            'pathnamehash' => $data->pathnamehash
1350
        );
1351
 
1352
        $params = json_decode($data->jsoncontent);
1353
        if (empty($params->metadata)) {
1354
            $params->metadata = new \stdClass();
1355
        }
1356
        // Add title to metadata.
1357
        if (!empty($params->title) && empty($params->metadata->title)) {
1358
            $params->metadata->title = $params->title;
1359
        }
1360
        $content['metadata'] = $params->metadata;
1361
        $content['params'] = json_encode($params->params ?? $params);
1362
 
1363
        return $content;
1364
    }
1365
 
1366
    /**
1367
     * Load dependencies for the given content of the given type.
1368
     * Implements loadContentDependencies.
1369
     *
1370
     * @param int $id Content identifier
1371
     * @param int $type The dependency type
1372
     * @return array List of associative arrays containing:
1373
     *               - libraryId: The id of the library if it is an existing library
1374
     *               - machineName: The library machineName
1375
     *               - majorVersion: The library's majorVersion
1376
     *               - minorVersion: The library's minorVersion
1377
     *               - patchVersion: The library's patchVersion
1378
     *               - preloadedJs(optional): comma separated string with js file paths
1379
     *               - preloadedCss(optional): comma separated sting with css file paths
1380
     *               - dropCss(optional): csv of machine names
1381
     *               - dependencyType: The dependency type
1382
     */
1383
    public function loadContentDependencies($id, $type = null) {
1384
        global $DB;
1385
 
1386
        $query = "SELECT hcl.id AS unidepid, hl.id AS library_id, hl.machinename AS machine_name,
1387
                         hl.majorversion AS major_version, hl.minorversion AS minor_version,
1388
                         hl.patchversion AS patch_version, hl.preloadedcss AS preloaded_css,
1389
                         hl.preloadedjs AS preloaded_js, hcl.dropcss AS drop_css,
1390
                         hcl.dependencytype as dependency_type
1391
                    FROM {h5p_contents_libraries} hcl
1392
                    JOIN {h5p_libraries} hl ON hcl.libraryid = hl.id
1393
                   WHERE hcl.h5pid = :h5pid";
1394
        $queryargs = array(
1395
            'h5pid' => $id
1396
        );
1397
 
1398
        if ($type !== null) {
1399
            $query .= " AND hcl.dependencytype = :dependencytype";
1400
            $queryargs['dependencytype'] = $type;
1401
        }
1402
 
1403
        $query .= " ORDER BY hcl.weight";
1404
        $data = $DB->get_records_sql($query, $queryargs);
1405
 
1406
        $dependencies = array();
1407
        foreach ($data as $dependency) {
1408
            unset($dependency->unidepid);
1409
            $dependencies[$dependency->machine_name] = H5PCore::snakeToCamel($dependency);
1410
        }
1411
 
1412
        return $dependencies;
1413
    }
1414
 
1415
    /**
1416
     * Get stored setting.
1417
     * Implements getOption.
1418
     *
1419
     * To avoid updating the cache libraries when using the Hub selector,
1420
     * {@see \Moodle\H5PEditorAjax::isContentTypeCacheUpdated}, the setting content_type_cache_updated_at
1421
     * always return the current time.
1422
     *
1423
     * @param string $name Identifier for the setting
1424
     * @param string $default Optional default value if settings is not set
1425
     * @return mixed Return  Whatever has been stored as the setting
1426
     */
1427
    public function getOption($name, $default = false) {
1428
        if ($name == core::DISPLAY_OPTION_DOWNLOAD || $name == core::DISPLAY_OPTION_EMBED) {
1429
            // For now, the download and the embed displayoptions are disabled by default, so only will be rendered when
1430
            // defined in the displayoptions DB field.
1431
            // This check should be removed if they are added as new H5P settings, to let admins to define the default value.
1432
            return \Moodle\H5PDisplayOptionBehaviour::CONTROLLED_BY_AUTHOR_DEFAULT_OFF;
1433
        }
1434
 
1435
        // To avoid update the libraries cache using the Hub selector.
1436
        if ($name == 'content_type_cache_updated_at') {
1437
            return time();
1438
        }
1439
 
1440
        $value = get_config('core_h5p', $name);
1441
        if ($value === false) {
1442
            return $default;
1443
        }
1444
        return $value;
1445
    }
1446
 
1447
    /**
1448
     * Stores the given setting.
1449
     * For example when did we last check h5p.org for updates to our libraries.
1450
     * Implements setOption.
1451
     *
1452
     * @param string $name Identifier for the setting
1453
     * @param mixed $value Data Whatever we want to store as the setting
1454
     */
1455
    public function setOption($name, $value) {
1456
        set_config($name, $value, 'core_h5p');
1457
    }
1458
 
1459
    /**
1460
     * This will update selected fields on the given content.
1461
     * Implements updateContentFields().
1462
     *
1463
     * @param int $id Content identifier
1464
     * @param array $fields Content fields, e.g. filtered
1465
     */
1466
    public function updateContentFields($id, $fields) {
1467
        global $DB;
1468
 
1469
        $content = new \stdClass();
1470
        $content->id = $id;
1471
 
1472
        foreach ($fields as $name => $value) {
1473
            // Skip 'slug' as it currently does not exist in the h5p content table.
1474
            if ($name == 'slug') {
1475
                continue;
1476
            }
1477
 
1478
            $content->$name = $value;
1479
        }
1480
 
1481
        $DB->update_record('h5p', $content);
1482
    }
1483
 
1484
    /**
1485
     * Will clear filtered params for all the content that uses the specified.
1486
     * libraries. This means that the content dependencies will have to be rebuilt and the parameters re-filtered.
1487
     * Implements clearFilteredParameters().
1488
     *
1489
     * @param array $libraryids Array of library ids
1490
     */
1491
    public function clearFilteredParameters($libraryids) {
1492
        global $DB;
1493
 
1494
        if (empty($libraryids)) {
1495
            return;
1496
        }
1497
 
1498
        list($insql, $inparams) = $DB->get_in_or_equal($libraryids);
1499
 
1500
        $DB->set_field_select('h5p', 'filtered', null,
1501
            "mainlibraryid $insql", $inparams);
1502
    }
1503
 
1504
    /**
1505
     * Get number of contents that has to get their content dependencies rebuilt.
1506
     * and parameters re-filtered.
1507
     * Implements getNumNotFiltered().
1508
     *
1509
     * @return int The number of contents that has to get their content dependencies rebuilt
1510
     *             and parameters re-filtered
1511
     */
1512
    public function getNumNotFiltered() {
1513
        global $DB;
1514
 
1515
        $sql = "SELECT COUNT(id)
1516
                  FROM {h5p}
1517
                 WHERE " . $DB->sql_compare_text('filtered') . " IS NULL";
1518
 
1519
        return $DB->count_records_sql($sql);
1520
    }
1521
 
1522
    /**
1523
     * Get number of contents using library as main library.
1524
     * Implements getNumContent().
1525
     *
1526
     * @param int $libraryid The library ID
1527
     * @param array $skip The array of h5p content ID's that should be ignored
1528
     * @return int The number of contents using library as main library
1529
     */
1530
    public function getNumContent($libraryid, $skip = null) {
1531
        global $DB;
1532
 
1533
        $notinsql = '';
1534
        $params = array();
1535
 
1536
        if (!empty($skip)) {
1537
            list($sql, $params) = $DB->get_in_or_equal($skip, SQL_PARAMS_NAMED, 'param', false);
1538
            $notinsql = " AND id {$sql}";
1539
        }
1540
 
1541
        $sql = "SELECT COUNT(id)
1542
                  FROM {h5p}
1543
                 WHERE mainlibraryid = :libraryid {$notinsql}";
1544
 
1545
        $params['libraryid'] = $libraryid;
1546
 
1547
        return $DB->count_records_sql($sql, $params);
1548
    }
1549
 
1550
    /**
1551
     * Determines if content slug is used.
1552
     * Implements isContentSlugAvailable.
1553
     *
1554
     * @param string $slug The content slug
1555
     * @return boolean Whether the content slug is used
1556
     */
1557
    public function isContentSlugAvailable($slug) {
1558
        // By default the slug should be available as it's currently generated as a unique
1559
        // value for each h5p content (not stored in the h5p table).
1560
        return true;
1561
    }
1562
 
1563
    /**
1564
     * Generates statistics from the event log per library.
1565
     * Implements getLibraryStats.
1566
     *
1567
     * @param string $type Type of event to generate stats for
1568
     * @return array Number values indexed by library name and version
1569
     */
1570
    public function getLibraryStats($type) {
1571
        // Event logs are not being stored.
1572
    }
1573
 
1574
    /**
1575
     * Aggregate the current number of H5P authors.
1576
     * Implements getNumAuthors.
1577
     *
1578
     * @return int The current number of H5P authors
1579
     */
1580
    public function getNumAuthors() {
1581
        // Currently, H5P authors are not being stored.
1582
    }
1583
 
1584
    /**
1585
     * Stores hash keys for cached assets, aggregated JavaScripts and
1586
     * stylesheets, and connects it to libraries so that we know which cache file
1587
     * to delete when a library is updated.
1588
     * Implements saveCachedAssets.
1589
     *
1590
     * @param string $key Hash key for the given libraries
1591
     * @param array $libraries List of dependencies(libraries) used to create the key
1592
     */
1593
    public function saveCachedAssets($key, $libraries) {
1594
        global $DB;
1595
 
1596
        foreach ($libraries as $library) {
1597
            $cachedasset = new \stdClass();
1598
            $cachedasset->libraryid = $library['libraryId'];
1599
            $cachedasset->hash = $key;
1600
 
1601
            $DB->insert_record('h5p_libraries_cachedassets', $cachedasset);
1602
        }
1603
    }
1604
 
1605
    /**
1606
     * Locate hash keys for given library and delete them.
1607
     * Used when cache file are deleted.
1608
     * Implements deleteCachedAssets.
1609
     *
1610
     * @param int $libraryid Library identifier
1611
     * @return array List of hash keys removed
1612
     */
1613
    public function deleteCachedAssets($libraryid) {
1614
        global $DB;
1615
 
1616
        // Get all the keys so we can remove the files.
1617
        $results = $DB->get_records('h5p_libraries_cachedassets', ['libraryid' => $libraryid]);
1618
 
1619
        $hashes = array_map(function($result) {
1620
            return $result->hash;
1621
        }, $results);
1622
 
1623
        if (!empty($hashes)) {
1624
            list($sql, $params) = $DB->get_in_or_equal($hashes, SQL_PARAMS_NAMED);
1625
            // Remove all invalid keys.
1626
            $DB->delete_records_select('h5p_libraries_cachedassets', 'hash ' . $sql, $params);
1627
 
1628
            // Remove also the cachedassets files.
1629
            $fs = new file_storage();
1630
            $fs->deleteCachedAssets($hashes);
1631
        }
1632
 
1633
        return $hashes;
1634
    }
1635
 
1636
    /**
1637
     * Get the amount of content items associated to a library.
1638
     * Implements getLibraryContentCount.
1639
     *
1640
     * return array The number of content items associated to a library
1641
     */
1642
    public function getLibraryContentCount() {
1643
        global $DB;
1644
 
1645
        $contentcount = array();
1646
 
1647
        $sql = "SELECT h.mainlibraryid,
1648
                       l.machinename,
1649
                       l.majorversion,
1650
                       l.minorversion,
1651
                       COUNT(h.id) AS count
1652
                  FROM {h5p} h
1653
             LEFT JOIN {h5p_libraries} l
1654
                    ON h.mainlibraryid = l.id
1655
              GROUP BY h.mainlibraryid, l.machinename, l.majorversion, l.minorversion";
1656
 
1657
        // Count content using the same content type.
1658
        $res = $DB->get_records_sql($sql);
1659
 
1660
        // Extract results.
1661
        foreach ($res as $lib) {
1662
            $contentcount["{$lib->machinename} {$lib->majorversion}.{$lib->minorversion}"] = $lib->count;
1663
        }
1664
 
1665
        return $contentcount;
1666
    }
1667
 
1668
    /**
1669
     * Will trigger after the export file is created.
1670
     * Implements afterExportCreated.
1671
     *
1672
     * @param array $content The content
1673
     * @param string $filename The file name
1674
     */
1675
    public function afterExportCreated($content, $filename) {
1676
        // Not being used.
1677
    }
1678
 
1679
    /**
1680
     * Check whether a user has permissions to execute an action, such as embed H5P content.
1681
     * Implements hasPermission.
1682
     *
1683
     * @param  H5PPermission $permission Permission type
1684
     * @param  int $id Id need by platform to determine permission
1685
     * @return boolean true if the user can execute the action defined in $permission; false otherwise
1686
     */
1687
    public function hasPermission($permission, $id = null) {
1688
        // H5P capabilities have not been introduced.
1689
    }
1690
 
1691
    /**
1692
     * Replaces existing content type cache with the one passed in.
1693
     * Implements replaceContentTypeCache.
1694
     *
1695
     * @param object $contenttypecache Json with an array called 'libraries' containing the new content type cache
1696
     *                                 that should replace the old one
1697
     */
1698
    public function replaceContentTypeCache($contenttypecache) {
1699
        // Currently, content type caches are not being stored.
1700
    }
1701
 
1702
    /**
1703
     * Checks if the given library has a higher version.
1704
     * Implements libraryHasUpgrade.
1705
     *
1706
     * @param array $library An associative array containing:
1707
     *                       - machineName: The library machineName
1708
     *                       - majorVersion: The library's majorVersion
1709
     *                       - minorVersion: The library's minorVersion
1710
     * @return boolean Whether the library has a higher version
1711
     */
1712
    public function libraryHasUpgrade($library) {
1713
        global $DB;
1714
 
1715
        $sql = "SELECT id
1716
                  FROM {h5p_libraries}
1717
                 WHERE machinename = :machinename
1718
                   AND (majorversion > :majorversion1
1719
                    OR (majorversion = :majorversion2 AND minorversion > :minorversion))";
1720
 
1721
        $results = $DB->get_records_sql(
1722
            $sql,
1723
            array(
1724
                'machinename' => $library['machineName'],
1725
                'majorversion1' => $library['majorVersion'],
1726
                'majorversion2' => $library['majorVersion'],
1727
                'minorversion' => $library['minorVersion']
1728
            ),
1729
            0,
1730
            1
1731
        );
1732
 
1733
        return !empty($results);
1734
    }
1735
 
1736
    /**
1737
     * Get current H5P language code.
1738
     *
1739
     * @return string Language Code
1740
     */
1741
    public static function get_language() {
1742
        static $map;
1743
 
1744
        if (empty($map)) {
1745
            // Create mapping for "converting" language codes.
1746
            $map = array(
1747
                'no' => 'nb'
1748
            );
1749
        }
1750
 
1751
        // Get current language in Moodle.
1752
        $language = get_html_lang_attribute_value(strtolower(\current_language()));
1753
 
1754
        // Try to map.
1755
        return $map[$language] ?? $language;
1756
    }
1757
 
1758
    /**
1759
     * Store messages until they can be printed to the current user.
1760
     *
1761
     * @param string $type Type of messages, e.g. 'info', 'error', etc
1762
     * @param string $newmessage The message
1763
     * @param string $code The message code
1764
     */
1441 ariadna 1765
    private function set_message(string $type, ?string $newmessage = null, ?string $code = null) {
1 efrain 1766
        global $SESSION;
1767
 
1768
        // We expect to get out an array of strings when getting info
1769
        // and an array of objects when getting errors for consistency across platforms.
1770
        // This implementation should be improved for consistency across the data type returned here.
1771
        if ($type === 'error') {
1772
            $SESSION->core_h5p_messages[$type][] = (object) array(
1773
                'code' => $code,
1774
                'message' => $newmessage
1775
            );
1776
        } else {
1777
            $SESSION->core_h5p_messages[$type][] = $newmessage;
1778
        }
1779
    }
1780
 
1781
    /**
1782
     * Convert list of library parameter values to csv.
1783
     *
1784
     * @param array $librarydata Library data as found in library.json files
1785
     * @param string $key Key that should be found in $librarydata
1786
     * @param string $searchparam The library parameter (Default: 'path')
1787
     * @return string Library parameter values separated by ', '
1788
     */
1789
    private function library_parameter_values_to_csv(array $librarydata, string $key, string $searchparam = 'path'): string {
1790
        if (isset($librarydata[$key])) {
1791
            $parametervalues = array();
1792
            foreach ($librarydata[$key] as $file) {
1793
                foreach ($file as $index => $value) {
1794
                    if ($index === $searchparam) {
1795
                        $parametervalues[] = $value;
1796
                    }
1797
                }
1798
            }
1799
            return implode(', ', $parametervalues);
1800
        }
1801
        return '';
1802
    }
1803
 
1804
    /**
1805
     * Get the latest library version.
1806
     *
1807
     * @param  string $machinename The library's machine name
1808
     * @return stdClass|null An object with the latest library version
1809
     */
1810
    public function get_latest_library_version(string $machinename): ?\stdClass {
1811
        global $DB;
1812
 
1813
        $libraries = $DB->get_records('h5p_libraries', ['machinename' => $machinename],
1814
            'majorversion DESC, minorversion DESC, patchversion DESC', '*', 0, 1);
1815
        if ($libraries) {
1816
            return reset($libraries);
1817
        }
1818
 
1819
        return null;
1820
    }
1821
 
1822
    /**
1823
     * Replace content hub metadata cache
1824
     *
1825
     * @param JsonSerializable $metadata Metadata as received from content hub
1826
     * @param string $lang Language in ISO 639-1
1827
     *
1828
     * @return mixed
1829
     */
1830
    public function replaceContentHubMetadataCache($metadata, $lang) {
1831
        debugging('The replaceContentHubMetadataCache() method is not implemented.', DEBUG_DEVELOPER);
1832
        return null;
1833
    }
1834
 
1835
    /**
1836
     * Get content hub metadata cache from db
1837
     *
1838
     * @param  string  $lang Language code in ISO 639-1
1839
     *
1840
     * @return JsonSerializable Json string
1841
     */
1842
    public function getContentHubMetadataCache($lang = 'en') {
1843
        debugging('The getContentHubMetadataCache() method is not implemented.', DEBUG_DEVELOPER);
1844
        return null;
1845
    }
1846
 
1847
    /**
1848
     * Get time of last content hub metadata check
1849
     *
1850
     * @param  string  $lang Language code iin ISO 639-1 format
1851
     *
1852
     * @return string|null Time in RFC7231 format
1853
     */
1854
    public function getContentHubMetadataChecked($lang = 'en') {
1855
        debugging('The getContentHubMetadataChecked() method is not implemented.', DEBUG_DEVELOPER);
1856
        return null;
1857
    }
1858
 
1859
    /**
1860
     * Set time of last content hub metadata check
1861
     *
1862
     * @param  int|null  $time Time in RFC7231 format
1863
     * @param  string  $lang Language code iin ISO 639-1 format
1864
     *
1865
     * @return bool True if successful
1866
     */
1867
    public function setContentHubMetadataChecked($time, $lang = 'en') {
1868
        debugging('The setContentHubMetadataChecked() method is not implemented.', DEBUG_DEVELOPER);
1869
        return false;
1870
    }
1871
}