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
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',
455
        ];
456
 
457
        if (isset($translationsmap[$message])) {
458
            return get_string($translationsmap[$message], 'core_h5p', $replacements);
459
        }
460
 
461
        debugging("String translation cannot be found. Please add a string definition for '" .
462
            $message . "' in the core_h5p component.", DEBUG_DEVELOPER);
463
 
464
        return $message;
465
    }
466
 
467
    /**
468
     * Get URL to file in the specifimake_pluginfile_urlc library.
469
     * Implements getLibraryFileUrl.
470
     *
471
     * @param string $libraryfoldername The name or path of the library's folder
472
     * @param string $filename The file name
473
     * @return string URL to file
474
     */
475
    public function getLibraryFileUrl($libraryfoldername, $filename) {
476
        global $DB;
477
 
478
        // Remove unnecessary slashes (first and last, if present) from the path to the folder
479
        // of the library file.
480
        $libraryfilepath = trim($libraryfoldername, '/');
481
 
482
        // Get the folder name of the library from the path.
483
        // The first element should represent the folder name of the library.
484
        $libfoldername = explode('/', $libraryfilepath)[0];
485
 
486
        $factory = new \core_h5p\factory();
487
        $core = $factory->get_core();
488
 
489
        // The provided folder name of the library must have a valid format (can be parsed).
490
        // The folder name is parsed with a purpose of getting the library related information
491
        // such as 'machineName', 'majorVersion' and 'minorVersion'.
492
        // This information is later used to retrieve the library ID.
493
        if (!$libdata = $core->libraryFromString($libfoldername, true)) {
494
            debugging('The provided string value "' . $libfoldername .
495
                '" is not a valid name for a library folder.', DEBUG_DEVELOPER);
496
 
497
            return;
498
        }
499
 
500
        $params = array(
501
            'machinename' => $libdata['machineName'],
502
            'majorversion' => $libdata['majorVersion'],
503
            'minorversion' => $libdata['minorVersion']
504
        );
505
 
506
        $libraries = $DB->get_records('h5p_libraries', $params, 'patchversion DESC', 'id',
507
            0, 1);
508
 
509
        if (!$library = reset($libraries)) {
510
            debugging('The library "' . $libfoldername . '" does not exist.', DEBUG_DEVELOPER);
511
 
512
            return;
513
        }
514
 
515
        $context = \context_system::instance();
516
 
517
        return \moodle_url::make_pluginfile_url($context->id, 'core_h5p', 'libraries',
518
            $library->id, '/' . $libraryfilepath . '/', $filename)->out();
519
    }
520
 
521
    /**
522
     * Get the Path to the last uploaded h5p.
523
     * Implements getUploadedH5PFolderPath.
524
     *
525
     * @param string $setpath The path to the folder of the last uploaded h5p
526
     * @return string Path to the folder where the last uploaded h5p for this session is located
527
     */
528
    public function getUploadedH5pFolderPath($setpath = null) {
529
        if ($setpath !== null) {
530
            $this->lastuploadedfolder = $setpath;
531
        }
532
 
533
        if (!isset($this->lastuploadedfolder)) {
534
            throw new \coding_exception('Using getUploadedH5pFolderPath() before path is set');
535
        }
536
 
537
        return $this->lastuploadedfolder;
538
    }
539
 
540
    /**
541
     * Get the path to the last uploaded h5p file.
542
     * Implements getUploadedH5PPath.
543
     *
544
     * @param string $setpath The path to the last uploaded h5p
545
     * @return string Path to the last uploaded h5p
546
     */
547
    public function getUploadedH5pPath($setpath = null) {
548
        if ($setpath !== null) {
549
            $this->lastuploadedfile = $setpath;
550
        }
551
 
552
        if (!isset($this->lastuploadedfile)) {
553
            throw new \coding_exception('Using getUploadedH5pPath() before path is set');
554
        }
555
 
556
        return $this->lastuploadedfile;
557
    }
558
 
559
    /**
560
     * Load addon libraries.
561
     * Implements loadAddons.
562
     *
563
     * @return array The array containing the addon libraries
564
     */
565
    public function loadAddons() {
566
        global $DB;
567
 
568
        $addons = array();
569
 
570
        $records = $DB->get_records_sql(
571
                "SELECT l1.id AS library_id,
572
                            l1.machinename AS machine_name,
573
                            l1.majorversion AS major_version,
574
                            l1.minorversion AS minor_version,
575
                            l1.patchversion AS patch_version,
576
                            l1.addto AS add_to,
577
                            l1.preloadedjs AS preloaded_js,
578
                            l1.preloadedcss AS preloaded_css
579
                       FROM {h5p_libraries} l1
580
                  LEFT JOIN {h5p_libraries} l2
581
                         ON l1.machinename = l2.machinename
582
                        AND (l1.majorversion < l2.majorversion
583
                             OR (l1.majorversion = l2.majorversion
584
                                 AND l1.minorversion < l2.minorversion))
585
                      WHERE l1.addto IS NOT NULL
586
                        AND l2.machinename IS NULL");
587
 
588
        // NOTE: These are treated as library objects but are missing the following properties:
589
        // title, droplibrarycss, fullscreen, runnable, semantics.
590
 
591
        // Extract num from records.
592
        foreach ($records as $addon) {
593
            $addons[] = H5PCore::snakeToCamel($addon);
594
        }
595
 
596
        return $addons;
597
    }
598
 
599
    /**
600
     * Load config for libraries.
601
     * Implements getLibraryConfig.
602
     *
603
     * @param array|null $libraries List of libraries
604
     * @return array|null The library config if it exists, null otherwise
605
     */
606
    public function getLibraryConfig($libraries = null) {
607
        global $CFG;
608
        return isset($CFG->core_h5p_library_config) ? $CFG->core_h5p_library_config : null;
609
    }
610
 
611
    /**
612
     * Get a list of the current installed libraries.
613
     * Implements loadLibraries.
614
     *
615
     * @return array Associative array containing one entry per machine name.
616
     *               For each machineName there is a list of libraries(with different versions).
617
     */
618
    public function loadLibraries() {
619
        global $DB;
620
 
621
        $results = $DB->get_records('h5p_libraries', [], 'title ASC, majorversion ASC, minorversion ASC',
622
            'id, machinename AS machine_name, majorversion AS major_version, minorversion AS minor_version,
623
            patchversion AS patch_version, runnable, title, enabled');
624
 
625
        $libraries = array();
626
        foreach ($results as $library) {
627
            $libraries[$library->machine_name][] = $library;
628
        }
629
 
630
        return $libraries;
631
    }
632
 
633
    /**
634
     * Returns the URL to the library admin page.
635
     * Implements getAdminUrl.
636
     *
637
     * @return string URL to admin page
638
     */
639
    public function getAdminUrl() {
640
        // Not supported.
641
    }
642
 
643
    /**
644
     * Return the library's ID.
645
     * Implements getLibraryId.
646
     *
647
     * @param string $machinename The librarys machine name
648
     * @param string $majorversion Major version number for library (optional)
649
     * @param string $minorversion Minor version number for library (optional)
650
     * @return int|bool Identifier, or false if non-existent
651
     */
652
    public function getLibraryId($machinename, $majorversion = null, $minorversion = null) {
653
        global $DB;
654
 
655
        $params = array(
656
            'machinename' => $machinename
657
        );
658
 
659
        if ($majorversion !== null) {
660
            $params['majorversion'] = $majorversion;
661
        }
662
 
663
        if ($minorversion !== null) {
664
            $params['minorversion'] = $minorversion;
665
        }
666
 
667
        $libraries = $DB->get_records('h5p_libraries', $params,
668
            'majorversion DESC, minorversion DESC, patchversion DESC', 'id', 0, 1);
669
 
670
        // Get the latest version which matches the input parameters.
671
        if ($libraries) {
672
            $library = reset($libraries);
673
            return $library->id ?? false;
674
        }
675
 
676
        return false;
677
    }
678
 
679
    /**
680
     * Get allowed file extension list.
681
     * Implements getWhitelist.
682
     *
683
     * The default extension list is part of h5p, but admins should be allowed to modify it.
684
     *
685
     * @param boolean $islibrary TRUE if this is the whitelist for a library. FALSE if it is the whitelist
686
     *                           for the content folder we are getting.
687
     * @param string $defaultcontentwhitelist A string of file extensions separated by whitespace.
688
     * @param string $defaultlibrarywhitelist A string of file extensions separated by whitespace.
689
     * @return string A string containing the allowed file extensions separated by whitespace.
690
     */
691
    public function getWhitelist($islibrary, $defaultcontentwhitelist, $defaultlibrarywhitelist) {
692
        return $defaultcontentwhitelist . ($islibrary ? ' ' . $defaultlibrarywhitelist : '');
693
    }
694
 
695
    /**
696
     * Is the library a patched version of an existing library?
697
     * Implements isPatchedLibrary.
698
     *
699
     * @param array $library An associative array containing:
700
     *                       - machineName: The library machine name
701
     *                       - majorVersion: The librarys major version
702
     *                       - minorVersion: The librarys minor version
703
     *                       - patchVersion: The librarys patch version
704
     * @return boolean TRUE if the library is a patched version of an existing library FALSE otherwise
705
     */
706
    public function isPatchedLibrary($library) {
707
        global $DB;
708
 
709
        $sql = "SELECT id
710
                  FROM {h5p_libraries}
711
                 WHERE machinename = :machinename
712
                   AND majorversion = :majorversion
713
                   AND minorversion = :minorversion
714
                   AND patchversion < :patchversion";
715
 
716
        $library = $DB->get_records_sql(
717
            $sql,
718
            array(
719
                'machinename' => $library['machineName'],
720
                'majorversion' => $library['majorVersion'],
721
                'minorversion' => $library['minorVersion'],
722
                'patchversion' => $library['patchVersion']
723
            ),
724
            0,
725
            1
726
        );
727
 
728
        return !empty($library);
729
    }
730
 
731
    /**
732
     * Is H5P in development mode?
733
     * Implements isInDevMode.
734
     *
735
     * @return boolean TRUE if H5P development mode is active FALSE otherwise
736
     */
737
    public function isInDevMode() {
738
        return false; // Not supported (Files in moodle not editable).
739
    }
740
 
741
    /**
742
     * Is the current user allowed to update libraries?
743
     * Implements mayUpdateLibraries.
744
     *
745
     * @return boolean TRUE if the user is allowed to update libraries,
746
     *                 FALSE if the user is not allowed to update libraries.
747
     */
748
    public function mayUpdateLibraries() {
749
        return helper::can_update_library($this->get_file());
750
    }
751
 
752
    /**
753
     * Get the .h5p file.
754
     *
755
     * @return \stored_file The .h5p file.
756
     */
757
    public function get_file(): \stored_file {
758
        if (!isset($this->file)) {
759
            throw new \coding_exception('Using get_file() before file is set');
760
        }
761
 
762
        return $this->file;
763
    }
764
 
765
    /**
766
     * Set the .h5p file.
767
     *
768
     * @param  stored_file $file The .h5p file.
769
     */
770
    public function set_file(\stored_file $file): void {
771
        $this->file = $file;
772
    }
773
 
774
    /**
775
     * Store data about a library.
776
     * Implements saveLibraryData.
777
     *
778
     * Also fills in the libraryId in the libraryData object if the object is new.
779
     *
780
     * @param array $librarydata Associative array containing:
781
     *                           - libraryId: The id of the library if it is an existing library
782
     *                           - title: The library's name
783
     *                           - machineName: The library machineName
784
     *                           - majorVersion: The library's majorVersion
785
     *                           - minorVersion: The library's minorVersion
786
     *                           - patchVersion: The library's patchVersion
787
     *                           - runnable: 1 if the library is a content type, 0 otherwise
788
     *                           - fullscreen(optional): 1 if the library supports fullscreen, 0 otherwise
789
     *                           - embedtypes: list of supported embed types
790
     *                           - preloadedJs(optional): list of associative arrays containing:
791
     *                             - path: path to a js file relative to the library root folder
792
     *                           - preloadedCss(optional): list of associative arrays containing:
793
     *                             - path: path to css file relative to the library root folder
794
     *                           - dropLibraryCss(optional): list of associative arrays containing:
795
     *                             - machineName: machine name for the librarys that are to drop their css
796
     *                           - semantics(optional): Json describing the content structure for the library
797
     *                           - metadataSettings(optional): object containing:
798
     *                             - disable: 1 if metadata is disabled completely
799
     *                             - disableExtraTitleField: 1 if the title field is hidden in the form
800
     * @param bool $new Whether it is a new or existing library.
801
     */
802
    public function saveLibraryData(&$librarydata, $new = true) {
803
        global $DB;
804
 
805
        // Some special properties needs some checking and converting before they can be saved.
806
        $preloadedjs = $this->library_parameter_values_to_csv($librarydata, 'preloadedJs', 'path');
807
        $preloadedcss = $this->library_parameter_values_to_csv($librarydata, 'preloadedCss', 'path');
808
        $droplibrarycss = $this->library_parameter_values_to_csv($librarydata, 'dropLibraryCss', 'machineName');
809
 
810
        if (!isset($librarydata['semantics'])) {
811
            $librarydata['semantics'] = '';
812
        }
813
        if (!isset($librarydata['fullscreen'])) {
814
            $librarydata['fullscreen'] = 0;
815
        }
816
        $embedtypes = '';
817
        if (isset($librarydata['embedTypes'])) {
818
            $embedtypes = implode(', ', $librarydata['embedTypes']);
819
        }
820
 
821
        $library = (object) array(
822
            'title' => $librarydata['title'],
823
            'machinename' => $librarydata['machineName'],
824
            'majorversion' => $librarydata['majorVersion'],
825
            'minorversion' => $librarydata['minorVersion'],
826
            'patchversion' => $librarydata['patchVersion'],
827
            'runnable' => $librarydata['runnable'],
828
            'fullscreen' => $librarydata['fullscreen'],
829
            'embedtypes' => $embedtypes,
830
            'preloadedjs' => $preloadedjs,
831
            'preloadedcss' => $preloadedcss,
832
            'droplibrarycss' => $droplibrarycss,
833
            'semantics' => $librarydata['semantics'],
834
            'addto' => isset($librarydata['addTo']) ? json_encode($librarydata['addTo']) : null,
835
            'coremajor' => isset($librarydata['coreApi']['majorVersion']) ? $librarydata['coreApi']['majorVersion'] : null,
836
            'coreminor' => isset($librarydata['coreApi']['majorVersion']) ? $librarydata['coreApi']['minorVersion'] : null,
837
            'metadatasettings' => isset($librarydata['metadataSettings']) ? $librarydata['metadataSettings'] : null,
838
        );
839
 
840
        if ($new) {
841
            // Create new library and keep track of id.
842
            $library->id = $DB->insert_record('h5p_libraries', $library);
843
            $librarydata['libraryId'] = $library->id;
844
        } else {
845
            // Update library data.
846
            $library->id = $librarydata['libraryId'];
847
            // Save library data.
848
            $DB->update_record('h5p_libraries', $library);
849
            // Remove old dependencies.
850
            $this->deleteLibraryDependencies($librarydata['libraryId']);
851
        }
852
    }
853
 
854
    /**
855
     * Insert new content.
856
     * Implements insertContent.
857
     *
858
     * @param array $content An associative array containing:
859
     *                       - id: The content id
860
     *                       - params: The content in json format
861
     *                       - library: An associative array containing:
862
     *                         - libraryId: The id of the main library for this content
863
     *                       - disable: H5P Button display options
864
     *                       - pathnamehash: The pathnamehash linking the record with the entry in the mdl_files table
865
     *                       - contenthash: The contenthash linking the record with the entry in the mdl_files table
866
     * @param int $contentmainid Main id for the content if this is a system that supports versions
867
     * @return int The ID of the newly inserted content
868
     */
869
    public function insertContent($content, $contentmainid = null) {
870
        return $this->updateContent($content);
871
    }
872
 
873
    /**
874
     * Update old content or insert new content.
875
     * Implements updateContent.
876
     *
877
     * @param array $content An associative array containing:
878
     *                       - id: The content id
879
     *                       - params: The content in json format
880
     *                       - library: An associative array containing:
881
     *                         - libraryId: The id of the main library for this content
882
     *                       - disable: H5P Button display options
883
     *                       - pathnamehash: The pathnamehash linking the record with the entry in the mdl_files table
884
     *                       - contenthash: The contenthash linking the record with the entry in the mdl_files table
885
     * @param int $contentmainid Main id for the content if this is a system that supports versions
886
     * @return int The ID of the newly inserted or updated content
887
     */
888
    public function updateContent($content, $contentmainid = null) {
889
        global $DB;
890
 
891
        // If the libraryid declared in the package is empty, get the latest version.
892
        if (empty($content['library']['libraryId'])) {
893
            $mainlibrary = $this->get_latest_library_version($content['library']['machineName']);
894
            if (empty($mainlibrary)) {
895
                // Raise an error if the main library is not defined and the latest version doesn't exist.
896
                $message = $this->t('Missing required library @library', ['@library' => $content['library']['machineName']]);
897
                $this->setErrorMessage($message, 'missing-required-library');
898
                return false;
899
            }
900
            $content['library']['libraryId'] = $mainlibrary->id;
901
        }
902
 
903
        $content['disable'] = $content['disable'] ?? null;
904
        // Add title to 'params' to use in the editor.
905
        if (!empty($content['title'])) {
906
            $params = json_decode($content['params']);
907
            $params->title = $content['title'];
908
            $content['params'] = json_encode($params);
909
        }
910
        // Add metadata to 'params'.
911
        if (!empty($content['metadata'])) {
912
            $params = json_decode($content['params']);
913
            $params->metadata = $content['metadata'];
914
            $content['params'] = json_encode($params);
915
        }
916
 
917
        $data = [
918
            'jsoncontent' => $content['params'],
919
            'displayoptions' => $content['disable'],
920
            'mainlibraryid' => $content['library']['libraryId'],
921
            'timemodified' => time(),
922
            'filtered' => null,
923
        ];
924
 
925
        if (isset($content['pathnamehash'])) {
926
            $data['pathnamehash'] = $content['pathnamehash'];
927
        }
928
 
929
        if (isset($content['contenthash'])) {
930
            $data['contenthash'] = $content['contenthash'];
931
        }
932
 
933
        if (!isset($content['id'])) {
934
            $data['pathnamehash'] = $data['pathnamehash'] ?? '';
935
            $data['contenthash'] = $data['contenthash'] ?? '';
936
            $data['timecreated'] = $data['timemodified'];
937
            $id = $DB->insert_record('h5p', $data);
938
        } else {
939
            $id = $data['id'] = $content['id'];
940
            $DB->update_record('h5p', $data);
941
        }
942
 
943
        return $id;
944
    }
945
 
946
    /**
947
     * Resets marked user data for the given content.
948
     * Implements resetContentUserData.
949
     *
950
     * @param int $contentid The h5p content id
951
     */
952
    public function resetContentUserData($contentid) {
953
        global $DB;
954
 
955
        // Get the component associated to the H5P content to reset.
956
        $h5p = $DB->get_record('h5p', ['id' => $contentid]);
957
        if (!$h5p) {
958
            return;
959
        }
960
 
961
        $fs = get_file_storage();
962
        $file = $fs->get_file_by_hash($h5p->pathnamehash);
963
        if (!$file) {
964
            return;
965
        }
966
 
967
        // Reset user data.
968
        try {
969
            $xapihandler = handler::create($file->get_component());
970
            // Reset only entries with 'state' as stateid (the ones restored shouldn't be restored, because the H5P
971
            // content hasn't been created yet).
972
            $xapihandler->reset_states($file->get_contextid(), null, 'state');
973
        } catch (xapi_exception $exception) {
974
            // This component doesn't support xAPI State, so no content needs to be reset.
975
            return;
976
        }
977
    }
978
 
979
    /**
980
     * Save what libraries a library is depending on.
981
     * Implements saveLibraryDependencies.
982
     *
983
     * @param int $libraryid Library Id for the library we're saving dependencies for
984
     * @param array $dependencies List of dependencies as associative arrays containing:
985
     *                            - machineName: The library machineName
986
     *                            - majorVersion: The library's majorVersion
987
     *                            - minorVersion: The library's minorVersion
988
     * @param string $dependencytype The type of dependency
989
     */
990
    public function saveLibraryDependencies($libraryid, $dependencies, $dependencytype) {
991
        global $DB;
992
 
993
        foreach ($dependencies as $dependency) {
994
            // Find dependency library.
995
            $dependencylibrary = $DB->get_record('h5p_libraries',
996
                array(
997
                    'machinename' => $dependency['machineName'],
998
                    'majorversion' => $dependency['majorVersion'],
999
                    'minorversion' => $dependency['minorVersion']
1000
                )
1001
            );
1002
 
1003
            // Create relation.
1004
            $DB->insert_record('h5p_library_dependencies', array(
1005
                'libraryid' => $libraryid,
1006
                'requiredlibraryid' => $dependencylibrary->id,
1007
                'dependencytype' => $dependencytype
1008
            ));
1009
        }
1010
    }
1011
 
1012
    /**
1013
     * Give an H5P the same library dependencies as a given H5P.
1014
     * Implements copyLibraryUsage.
1015
     *
1016
     * @param int $contentid Id identifying the content
1017
     * @param int $copyfromid Id identifying the content to be copied
1018
     * @param int $contentmainid Main id for the content, typically used in frameworks
1019
     */
1020
    public function copyLibraryUsage($contentid, $copyfromid, $contentmainid = null) {
1021
        // Currently not being called.
1022
    }
1023
 
1024
    /**
1025
     * Deletes content data.
1026
     * Implements deleteContentData.
1027
     *
1028
     * @param int $contentid Id identifying the content
1029
     */
1030
    public function deleteContentData($contentid) {
1031
        global $DB;
1032
 
1033
        // The user content should be reset (instead of removed), because this method is called when H5P content needs
1034
        // to be updated too (and the previous states must be kept, but reset).
1035
        $this->resetContentUserData($contentid);
1036
 
1037
        // Remove content.
1038
        $DB->delete_records('h5p', ['id' => $contentid]);
1039
 
1040
        // Remove content library dependencies.
1041
        $this->deleteLibraryUsage($contentid);
1042
    }
1043
 
1044
    /**
1045
     * Delete what libraries a content item is using.
1046
     * Implements deleteLibraryUsage.
1047
     *
1048
     * @param int $contentid Content Id of the content we'll be deleting library usage for
1049
     */
1050
    public function deleteLibraryUsage($contentid) {
1051
        global $DB;
1052
 
1053
        $DB->delete_records('h5p_contents_libraries', array('h5pid' => $contentid));
1054
    }
1055
 
1056
    /**
1057
     * Saves what libraries the content uses.
1058
     * Implements saveLibraryUsage.
1059
     *
1060
     * @param int $contentid Id identifying the content
1061
     * @param array $librariesinuse List of libraries the content uses
1062
     */
1063
    public function saveLibraryUsage($contentid, $librariesinuse) {
1064
        global $DB;
1065
 
1066
        $droplibrarycsslist = array();
1067
        foreach ($librariesinuse as $dependency) {
1068
            if (!empty($dependency['library']['dropLibraryCss'])) {
1069
                $droplibrarycsslist = array_merge($droplibrarycsslist,
1070
                        explode(', ', $dependency['library']['dropLibraryCss']));
1071
            }
1072
        }
1073
 
1074
        foreach ($librariesinuse as $dependency) {
1075
            $dropcss = in_array($dependency['library']['machineName'], $droplibrarycsslist) ? 1 : 0;
1076
            $DB->insert_record('h5p_contents_libraries', array(
1077
                'h5pid' => $contentid,
1078
                'libraryid' => $dependency['library']['libraryId'],
1079
                'dependencytype' => $dependency['type'],
1080
                'dropcss' => $dropcss,
1081
                'weight' => $dependency['weight']
1082
            ));
1083
        }
1084
    }
1085
 
1086
    /**
1087
     * Get number of content/nodes using a library, and the number of dependencies to other libraries.
1088
     * Implements getLibraryUsage.
1089
     *
1090
     * @param int $id Library identifier
1091
     * @param boolean $skipcontent Optional. Set as true to get number of content instances for library
1092
     * @return array The array contains two elements, keyed by 'content' and 'libraries'.
1093
     *               Each element contains a number
1094
     */
1095
    public function getLibraryUsage($id, $skipcontent = false) {
1096
        global $DB;
1097
 
1098
        if ($skipcontent) {
1099
            $content = -1;
1100
        } else {
1101
            $sql = "SELECT COUNT(distinct c.id)
1102
                      FROM {h5p_libraries} l
1103
                      JOIN {h5p_contents_libraries} cl ON l.id = cl.libraryid
1104
                      JOIN {h5p} c ON cl.h5pid = c.id
1105
                     WHERE l.id = :libraryid";
1106
 
1107
            $sqlargs = array(
1108
                'libraryid' => $id
1109
            );
1110
 
1111
            $content = $DB->count_records_sql($sql, $sqlargs);
1112
        }
1113
 
1114
        $libraries = $DB->count_records('h5p_library_dependencies', ['requiredlibraryid' => $id]);
1115
 
1116
        return array(
1117
            'content' => $content,
1118
            'libraries' => $libraries,
1119
        );
1120
    }
1121
 
1122
    /**
1123
     * Loads a library.
1124
     * Implements loadLibrary.
1125
     *
1126
     * @param string $machinename The library's machine name
1127
     * @param int $majorversion The library's major version
1128
     * @param int $minorversion The library's minor version
1129
     * @return array|bool Returns FALSE if the library does not exist
1130
     *                     Otherwise an associative array containing:
1131
     *                     - libraryId: The id of the library if it is an existing library,
1132
     *                     - title: The library's name,
1133
     *                     - machineName: The library machineName
1134
     *                     - majorVersion: The library's majorVersion
1135
     *                     - minorVersion: The library's minorVersion
1136
     *                     - patchVersion: The library's patchVersion
1137
     *                     - runnable: 1 if the library is a content type, 0 otherwise
1138
     *                     - fullscreen: 1 if the library supports fullscreen, 0 otherwise
1139
     *                     - embedTypes: list of supported embed types
1140
     *                     - preloadedJs: comma separated string with js file paths
1141
     *                     - preloadedCss: comma separated sting with css file paths
1142
     *                     - dropLibraryCss: list of associative arrays containing:
1143
     *                       - machineName: machine name for the librarys that are to drop their css
1144
     *                     - semantics: Json describing the content structure for the library
1145
     *                     - preloadedDependencies(optional): list of associative arrays containing:
1146
     *                       - machineName: Machine name for a library this library is depending on
1147
     *                       - majorVersion: Major version for a library this library is depending on
1148
     *                       - minorVersion: Minor for a library this library is depending on
1149
     *                     - dynamicDependencies(optional): list of associative arrays containing:
1150
     *                       - machineName: Machine name for a library this library is depending on
1151
     *                       - majorVersion: Major version for a library this library is depending on
1152
     *                       - minorVersion: Minor for a library this library is depending on
1153
     */
1154
    public function loadLibrary($machinename, $majorversion, $minorversion) {
1155
        global $DB;
1156
 
1157
        $library = $DB->get_record('h5p_libraries', array(
1158
            'machinename' => $machinename,
1159
            'majorversion' => $majorversion,
1160
            'minorversion' => $minorversion
1161
        ));
1162
 
1163
        if (!$library) {
1164
            return false;
1165
        }
1166
 
1167
        $librarydata = array(
1168
            'libraryId' => $library->id,
1169
            'title' => $library->title,
1170
            'machineName' => $library->machinename,
1171
            'majorVersion' => $library->majorversion,
1172
            'minorVersion' => $library->minorversion,
1173
            'patchVersion' => $library->patchversion,
1174
            'runnable' => $library->runnable,
1175
            'fullscreen' => $library->fullscreen,
1176
            'embedTypes' => $library->embedtypes,
1177
            'preloadedJs' => $library->preloadedjs,
1178
            'preloadedCss' => $library->preloadedcss,
1179
            'dropLibraryCss' => $library->droplibrarycss,
1180
            'semantics'     => $library->semantics
1181
        );
1182
 
1183
        $sql = 'SELECT hl.id, hl.machinename, hl.majorversion, hl.minorversion, hll.dependencytype
1184
                  FROM {h5p_library_dependencies} hll
1185
                  JOIN {h5p_libraries} hl ON hll.requiredlibraryid = hl.id
1186
                 WHERE hll.libraryid = :libraryid
1187
              ORDER BY hl.id ASC';
1188
 
1189
        $sqlargs = array(
1190
            'libraryid' => $library->id
1191
        );
1192
 
1193
        $dependencies = $DB->get_records_sql($sql, $sqlargs);
1194
 
1195
        foreach ($dependencies as $dependency) {
1196
            $librarydata[$dependency->dependencytype . 'Dependencies'][] = array(
1197
                'machineName' => $dependency->machinename,
1198
                'majorVersion' => $dependency->majorversion,
1199
                'minorVersion' => $dependency->minorversion
1200
            );
1201
        }
1202
 
1203
        return $librarydata;
1204
    }
1205
 
1206
    /**
1207
     * Loads library semantics.
1208
     * Implements loadLibrarySemantics.
1209
     *
1210
     * @param string $name Machine name for the library
1211
     * @param int $majorversion The library's major version
1212
     * @param int $minorversion The library's minor version
1213
     * @return string The library's semantics as json
1214
     */
1215
    public function loadLibrarySemantics($name, $majorversion, $minorversion) {
1216
        global $DB;
1217
 
1218
        $semantics = $DB->get_field('h5p_libraries', 'semantics',
1219
            array(
1220
                'machinename' => $name,
1221
                'majorversion' => $majorversion,
1222
                'minorversion' => $minorversion
1223
            )
1224
        );
1225
 
1226
        return ($semantics === false ? null : $semantics);
1227
    }
1228
 
1229
    /**
1230
     * Makes it possible to alter the semantics, adding custom fields, etc.
1231
     * Implements alterLibrarySemantics.
1232
     *
1233
     * @param array $semantics Associative array representing the semantics
1234
     * @param string $name The library's machine name
1235
     * @param int $majorversion The library's major version
1236
     * @param int $minorversion The library's minor version
1237
     */
1238
    public function alterLibrarySemantics(&$semantics, $name, $majorversion, $minorversion) {
1239
        global $PAGE;
1240
 
1241
        $renderer = $PAGE->get_renderer('core_h5p');
1242
        $renderer->h5p_alter_semantics($semantics, $name, $majorversion, $minorversion);
1243
    }
1244
 
1245
    /**
1246
     * Delete all dependencies belonging to given library.
1247
     * Implements deleteLibraryDependencies.
1248
     *
1249
     * @param int $libraryid Library identifier
1250
     */
1251
    public function deleteLibraryDependencies($libraryid) {
1252
        global $DB;
1253
 
1254
        $DB->delete_records('h5p_library_dependencies', array('libraryid' => $libraryid));
1255
    }
1256
 
1257
    /**
1258
     * Start an atomic operation against the dependency storage.
1259
     * Implements lockDependencyStorage.
1260
     */
1261
    public function lockDependencyStorage() {
1262
        // Library development mode not supported.
1263
    }
1264
 
1265
    /**
1266
     * Start an atomic operation against the dependency storage.
1267
     * Implements unlockDependencyStorage.
1268
     */
1269
    public function unlockDependencyStorage() {
1270
        // Library development mode not supported.
1271
    }
1272
 
1273
    /**
1274
     * Delete a library from database and file system.
1275
     * Implements deleteLibrary.
1276
     *
1277
     * @param \stdClass $library Library object with id, name, major version and minor version
1278
     */
1279
    public function deleteLibrary($library) {
1280
        $factory = new \core_h5p\factory();
1281
        \core_h5p\api::delete_library($factory, $library);
1282
    }
1283
 
1284
    /**
1285
     * Load content.
1286
     * Implements loadContent.
1287
     *
1288
     * @param int $id Content identifier
1289
     * @return array Associative array containing:
1290
     *               - id: Identifier for the content
1291
     *               - params: json content as string
1292
     *               - embedType: list of supported embed types
1293
     *               - disable: H5P Button display options
1294
     *               - title: H5P content title
1295
     *               - slug: Human readable content identifier that is unique
1296
     *               - libraryId: Id for the main library
1297
     *               - libraryName: The library machine name
1298
     *               - libraryMajorVersion: The library's majorVersion
1299
     *               - libraryMinorVersion: The library's minorVersion
1300
     *               - libraryEmbedTypes: CSV of the main library's embed types
1301
     *               - libraryFullscreen: 1 if fullscreen is supported. 0 otherwise
1302
     *               - metadata: The content's metadata
1303
     */
1304
    public function loadContent($id) {
1305
        global $DB;
1306
 
1307
        $sql = "SELECT hc.id, hc.jsoncontent, hc.displayoptions, hl.id AS libraryid,
1308
                       hl.machinename, hl.title, hl.majorversion, hl.minorversion, hl.fullscreen,
1309
                       hl.embedtypes, hl.semantics, hc.filtered, hc.pathnamehash
1310
                  FROM {h5p} hc
1311
                  JOIN {h5p_libraries} hl ON hl.id = hc.mainlibraryid
1312
                 WHERE hc.id = :h5pid";
1313
 
1314
        $sqlargs = array(
1315
            'h5pid' => $id
1316
        );
1317
 
1318
        $data = $DB->get_record_sql($sql, $sqlargs);
1319
 
1320
        // Return null if not found.
1321
        if ($data === false) {
1322
            return null;
1323
        }
1324
 
1325
        // Some databases do not support camelCase, so we need to manually
1326
        // map the values to the camelCase names used by the H5P core.
1327
        $content = array(
1328
            'id' => $data->id,
1329
            'params' => $data->jsoncontent,
1330
            // It has been decided that the embedtype will be always set to 'iframe' (at least for now) because the 'div'
1331
            // may cause conflicts with CSS and JS in some cases.
1332
            'embedType' => 'iframe',
1333
            'disable' => $data->displayoptions,
1334
            'title' => $data->title,
1335
            'slug' => H5PCore::slugify($data->title) . '-' . $data->id,
1336
            'filtered' => $data->filtered,
1337
            'libraryId' => $data->libraryid,
1338
            'libraryName' => $data->machinename,
1339
            'libraryMajorVersion' => $data->majorversion,
1340
            'libraryMinorVersion' => $data->minorversion,
1341
            'libraryEmbedTypes' => $data->embedtypes,
1342
            'libraryFullscreen' => $data->fullscreen,
1343
            'metadata' => '',
1344
            'pathnamehash' => $data->pathnamehash
1345
        );
1346
 
1347
        $params = json_decode($data->jsoncontent);
1348
        if (empty($params->metadata)) {
1349
            $params->metadata = new \stdClass();
1350
        }
1351
        // Add title to metadata.
1352
        if (!empty($params->title) && empty($params->metadata->title)) {
1353
            $params->metadata->title = $params->title;
1354
        }
1355
        $content['metadata'] = $params->metadata;
1356
        $content['params'] = json_encode($params->params ?? $params);
1357
 
1358
        return $content;
1359
    }
1360
 
1361
    /**
1362
     * Load dependencies for the given content of the given type.
1363
     * Implements loadContentDependencies.
1364
     *
1365
     * @param int $id Content identifier
1366
     * @param int $type The dependency type
1367
     * @return array List of associative arrays containing:
1368
     *               - libraryId: The id of the library if it is an existing library
1369
     *               - machineName: The library machineName
1370
     *               - majorVersion: The library's majorVersion
1371
     *               - minorVersion: The library's minorVersion
1372
     *               - patchVersion: The library's patchVersion
1373
     *               - preloadedJs(optional): comma separated string with js file paths
1374
     *               - preloadedCss(optional): comma separated sting with css file paths
1375
     *               - dropCss(optional): csv of machine names
1376
     *               - dependencyType: The dependency type
1377
     */
1378
    public function loadContentDependencies($id, $type = null) {
1379
        global $DB;
1380
 
1381
        $query = "SELECT hcl.id AS unidepid, hl.id AS library_id, hl.machinename AS machine_name,
1382
                         hl.majorversion AS major_version, hl.minorversion AS minor_version,
1383
                         hl.patchversion AS patch_version, hl.preloadedcss AS preloaded_css,
1384
                         hl.preloadedjs AS preloaded_js, hcl.dropcss AS drop_css,
1385
                         hcl.dependencytype as dependency_type
1386
                    FROM {h5p_contents_libraries} hcl
1387
                    JOIN {h5p_libraries} hl ON hcl.libraryid = hl.id
1388
                   WHERE hcl.h5pid = :h5pid";
1389
        $queryargs = array(
1390
            'h5pid' => $id
1391
        );
1392
 
1393
        if ($type !== null) {
1394
            $query .= " AND hcl.dependencytype = :dependencytype";
1395
            $queryargs['dependencytype'] = $type;
1396
        }
1397
 
1398
        $query .= " ORDER BY hcl.weight";
1399
        $data = $DB->get_records_sql($query, $queryargs);
1400
 
1401
        $dependencies = array();
1402
        foreach ($data as $dependency) {
1403
            unset($dependency->unidepid);
1404
            $dependencies[$dependency->machine_name] = H5PCore::snakeToCamel($dependency);
1405
        }
1406
 
1407
        return $dependencies;
1408
    }
1409
 
1410
    /**
1411
     * Get stored setting.
1412
     * Implements getOption.
1413
     *
1414
     * To avoid updating the cache libraries when using the Hub selector,
1415
     * {@see \Moodle\H5PEditorAjax::isContentTypeCacheUpdated}, the setting content_type_cache_updated_at
1416
     * always return the current time.
1417
     *
1418
     * @param string $name Identifier for the setting
1419
     * @param string $default Optional default value if settings is not set
1420
     * @return mixed Return  Whatever has been stored as the setting
1421
     */
1422
    public function getOption($name, $default = false) {
1423
        if ($name == core::DISPLAY_OPTION_DOWNLOAD || $name == core::DISPLAY_OPTION_EMBED) {
1424
            // For now, the download and the embed displayoptions are disabled by default, so only will be rendered when
1425
            // defined in the displayoptions DB field.
1426
            // This check should be removed if they are added as new H5P settings, to let admins to define the default value.
1427
            return \Moodle\H5PDisplayOptionBehaviour::CONTROLLED_BY_AUTHOR_DEFAULT_OFF;
1428
        }
1429
 
1430
        // To avoid update the libraries cache using the Hub selector.
1431
        if ($name == 'content_type_cache_updated_at') {
1432
            return time();
1433
        }
1434
 
1435
        $value = get_config('core_h5p', $name);
1436
        if ($value === false) {
1437
            return $default;
1438
        }
1439
        return $value;
1440
    }
1441
 
1442
    /**
1443
     * Stores the given setting.
1444
     * For example when did we last check h5p.org for updates to our libraries.
1445
     * Implements setOption.
1446
     *
1447
     * @param string $name Identifier for the setting
1448
     * @param mixed $value Data Whatever we want to store as the setting
1449
     */
1450
    public function setOption($name, $value) {
1451
        set_config($name, $value, 'core_h5p');
1452
    }
1453
 
1454
    /**
1455
     * This will update selected fields on the given content.
1456
     * Implements updateContentFields().
1457
     *
1458
     * @param int $id Content identifier
1459
     * @param array $fields Content fields, e.g. filtered
1460
     */
1461
    public function updateContentFields($id, $fields) {
1462
        global $DB;
1463
 
1464
        $content = new \stdClass();
1465
        $content->id = $id;
1466
 
1467
        foreach ($fields as $name => $value) {
1468
            // Skip 'slug' as it currently does not exist in the h5p content table.
1469
            if ($name == 'slug') {
1470
                continue;
1471
            }
1472
 
1473
            $content->$name = $value;
1474
        }
1475
 
1476
        $DB->update_record('h5p', $content);
1477
    }
1478
 
1479
    /**
1480
     * Will clear filtered params for all the content that uses the specified.
1481
     * libraries. This means that the content dependencies will have to be rebuilt and the parameters re-filtered.
1482
     * Implements clearFilteredParameters().
1483
     *
1484
     * @param array $libraryids Array of library ids
1485
     */
1486
    public function clearFilteredParameters($libraryids) {
1487
        global $DB;
1488
 
1489
        if (empty($libraryids)) {
1490
            return;
1491
        }
1492
 
1493
        list($insql, $inparams) = $DB->get_in_or_equal($libraryids);
1494
 
1495
        $DB->set_field_select('h5p', 'filtered', null,
1496
            "mainlibraryid $insql", $inparams);
1497
    }
1498
 
1499
    /**
1500
     * Get number of contents that has to get their content dependencies rebuilt.
1501
     * and parameters re-filtered.
1502
     * Implements getNumNotFiltered().
1503
     *
1504
     * @return int The number of contents that has to get their content dependencies rebuilt
1505
     *             and parameters re-filtered
1506
     */
1507
    public function getNumNotFiltered() {
1508
        global $DB;
1509
 
1510
        $sql = "SELECT COUNT(id)
1511
                  FROM {h5p}
1512
                 WHERE " . $DB->sql_compare_text('filtered') . " IS NULL";
1513
 
1514
        return $DB->count_records_sql($sql);
1515
    }
1516
 
1517
    /**
1518
     * Get number of contents using library as main library.
1519
     * Implements getNumContent().
1520
     *
1521
     * @param int $libraryid The library ID
1522
     * @param array $skip The array of h5p content ID's that should be ignored
1523
     * @return int The number of contents using library as main library
1524
     */
1525
    public function getNumContent($libraryid, $skip = null) {
1526
        global $DB;
1527
 
1528
        $notinsql = '';
1529
        $params = array();
1530
 
1531
        if (!empty($skip)) {
1532
            list($sql, $params) = $DB->get_in_or_equal($skip, SQL_PARAMS_NAMED, 'param', false);
1533
            $notinsql = " AND id {$sql}";
1534
        }
1535
 
1536
        $sql = "SELECT COUNT(id)
1537
                  FROM {h5p}
1538
                 WHERE mainlibraryid = :libraryid {$notinsql}";
1539
 
1540
        $params['libraryid'] = $libraryid;
1541
 
1542
        return $DB->count_records_sql($sql, $params);
1543
    }
1544
 
1545
    /**
1546
     * Determines if content slug is used.
1547
     * Implements isContentSlugAvailable.
1548
     *
1549
     * @param string $slug The content slug
1550
     * @return boolean Whether the content slug is used
1551
     */
1552
    public function isContentSlugAvailable($slug) {
1553
        // By default the slug should be available as it's currently generated as a unique
1554
        // value for each h5p content (not stored in the h5p table).
1555
        return true;
1556
    }
1557
 
1558
    /**
1559
     * Generates statistics from the event log per library.
1560
     * Implements getLibraryStats.
1561
     *
1562
     * @param string $type Type of event to generate stats for
1563
     * @return array Number values indexed by library name and version
1564
     */
1565
    public function getLibraryStats($type) {
1566
        // Event logs are not being stored.
1567
    }
1568
 
1569
    /**
1570
     * Aggregate the current number of H5P authors.
1571
     * Implements getNumAuthors.
1572
     *
1573
     * @return int The current number of H5P authors
1574
     */
1575
    public function getNumAuthors() {
1576
        // Currently, H5P authors are not being stored.
1577
    }
1578
 
1579
    /**
1580
     * Stores hash keys for cached assets, aggregated JavaScripts and
1581
     * stylesheets, and connects it to libraries so that we know which cache file
1582
     * to delete when a library is updated.
1583
     * Implements saveCachedAssets.
1584
     *
1585
     * @param string $key Hash key for the given libraries
1586
     * @param array $libraries List of dependencies(libraries) used to create the key
1587
     */
1588
    public function saveCachedAssets($key, $libraries) {
1589
        global $DB;
1590
 
1591
        foreach ($libraries as $library) {
1592
            $cachedasset = new \stdClass();
1593
            $cachedasset->libraryid = $library['libraryId'];
1594
            $cachedasset->hash = $key;
1595
 
1596
            $DB->insert_record('h5p_libraries_cachedassets', $cachedasset);
1597
        }
1598
    }
1599
 
1600
    /**
1601
     * Locate hash keys for given library and delete them.
1602
     * Used when cache file are deleted.
1603
     * Implements deleteCachedAssets.
1604
     *
1605
     * @param int $libraryid Library identifier
1606
     * @return array List of hash keys removed
1607
     */
1608
    public function deleteCachedAssets($libraryid) {
1609
        global $DB;
1610
 
1611
        // Get all the keys so we can remove the files.
1612
        $results = $DB->get_records('h5p_libraries_cachedassets', ['libraryid' => $libraryid]);
1613
 
1614
        $hashes = array_map(function($result) {
1615
            return $result->hash;
1616
        }, $results);
1617
 
1618
        if (!empty($hashes)) {
1619
            list($sql, $params) = $DB->get_in_or_equal($hashes, SQL_PARAMS_NAMED);
1620
            // Remove all invalid keys.
1621
            $DB->delete_records_select('h5p_libraries_cachedassets', 'hash ' . $sql, $params);
1622
 
1623
            // Remove also the cachedassets files.
1624
            $fs = new file_storage();
1625
            $fs->deleteCachedAssets($hashes);
1626
        }
1627
 
1628
        return $hashes;
1629
    }
1630
 
1631
    /**
1632
     * Get the amount of content items associated to a library.
1633
     * Implements getLibraryContentCount.
1634
     *
1635
     * return array The number of content items associated to a library
1636
     */
1637
    public function getLibraryContentCount() {
1638
        global $DB;
1639
 
1640
        $contentcount = array();
1641
 
1642
        $sql = "SELECT h.mainlibraryid,
1643
                       l.machinename,
1644
                       l.majorversion,
1645
                       l.minorversion,
1646
                       COUNT(h.id) AS count
1647
                  FROM {h5p} h
1648
             LEFT JOIN {h5p_libraries} l
1649
                    ON h.mainlibraryid = l.id
1650
              GROUP BY h.mainlibraryid, l.machinename, l.majorversion, l.minorversion";
1651
 
1652
        // Count content using the same content type.
1653
        $res = $DB->get_records_sql($sql);
1654
 
1655
        // Extract results.
1656
        foreach ($res as $lib) {
1657
            $contentcount["{$lib->machinename} {$lib->majorversion}.{$lib->minorversion}"] = $lib->count;
1658
        }
1659
 
1660
        return $contentcount;
1661
    }
1662
 
1663
    /**
1664
     * Will trigger after the export file is created.
1665
     * Implements afterExportCreated.
1666
     *
1667
     * @param array $content The content
1668
     * @param string $filename The file name
1669
     */
1670
    public function afterExportCreated($content, $filename) {
1671
        // Not being used.
1672
    }
1673
 
1674
    /**
1675
     * Check whether a user has permissions to execute an action, such as embed H5P content.
1676
     * Implements hasPermission.
1677
     *
1678
     * @param  H5PPermission $permission Permission type
1679
     * @param  int $id Id need by platform to determine permission
1680
     * @return boolean true if the user can execute the action defined in $permission; false otherwise
1681
     */
1682
    public function hasPermission($permission, $id = null) {
1683
        // H5P capabilities have not been introduced.
1684
    }
1685
 
1686
    /**
1687
     * Replaces existing content type cache with the one passed in.
1688
     * Implements replaceContentTypeCache.
1689
     *
1690
     * @param object $contenttypecache Json with an array called 'libraries' containing the new content type cache
1691
     *                                 that should replace the old one
1692
     */
1693
    public function replaceContentTypeCache($contenttypecache) {
1694
        // Currently, content type caches are not being stored.
1695
    }
1696
 
1697
    /**
1698
     * Checks if the given library has a higher version.
1699
     * Implements libraryHasUpgrade.
1700
     *
1701
     * @param array $library An associative array containing:
1702
     *                       - machineName: The library machineName
1703
     *                       - majorVersion: The library's majorVersion
1704
     *                       - minorVersion: The library's minorVersion
1705
     * @return boolean Whether the library has a higher version
1706
     */
1707
    public function libraryHasUpgrade($library) {
1708
        global $DB;
1709
 
1710
        $sql = "SELECT id
1711
                  FROM {h5p_libraries}
1712
                 WHERE machinename = :machinename
1713
                   AND (majorversion > :majorversion1
1714
                    OR (majorversion = :majorversion2 AND minorversion > :minorversion))";
1715
 
1716
        $results = $DB->get_records_sql(
1717
            $sql,
1718
            array(
1719
                'machinename' => $library['machineName'],
1720
                'majorversion1' => $library['majorVersion'],
1721
                'majorversion2' => $library['majorVersion'],
1722
                'minorversion' => $library['minorVersion']
1723
            ),
1724
            0,
1725
            1
1726
        );
1727
 
1728
        return !empty($results);
1729
    }
1730
 
1731
    /**
1732
     * Get current H5P language code.
1733
     *
1734
     * @return string Language Code
1735
     */
1736
    public static function get_language() {
1737
        static $map;
1738
 
1739
        if (empty($map)) {
1740
            // Create mapping for "converting" language codes.
1741
            $map = array(
1742
                'no' => 'nb'
1743
            );
1744
        }
1745
 
1746
        // Get current language in Moodle.
1747
        $language = get_html_lang_attribute_value(strtolower(\current_language()));
1748
 
1749
        // Try to map.
1750
        return $map[$language] ?? $language;
1751
    }
1752
 
1753
    /**
1754
     * Store messages until they can be printed to the current user.
1755
     *
1756
     * @param string $type Type of messages, e.g. 'info', 'error', etc
1757
     * @param string $newmessage The message
1758
     * @param string $code The message code
1759
     */
1760
    private function set_message(string $type, string $newmessage = null, string $code = null) {
1761
        global $SESSION;
1762
 
1763
        // We expect to get out an array of strings when getting info
1764
        // and an array of objects when getting errors for consistency across platforms.
1765
        // This implementation should be improved for consistency across the data type returned here.
1766
        if ($type === 'error') {
1767
            $SESSION->core_h5p_messages[$type][] = (object) array(
1768
                'code' => $code,
1769
                'message' => $newmessage
1770
            );
1771
        } else {
1772
            $SESSION->core_h5p_messages[$type][] = $newmessage;
1773
        }
1774
    }
1775
 
1776
    /**
1777
     * Convert list of library parameter values to csv.
1778
     *
1779
     * @param array $librarydata Library data as found in library.json files
1780
     * @param string $key Key that should be found in $librarydata
1781
     * @param string $searchparam The library parameter (Default: 'path')
1782
     * @return string Library parameter values separated by ', '
1783
     */
1784
    private function library_parameter_values_to_csv(array $librarydata, string $key, string $searchparam = 'path'): string {
1785
        if (isset($librarydata[$key])) {
1786
            $parametervalues = array();
1787
            foreach ($librarydata[$key] as $file) {
1788
                foreach ($file as $index => $value) {
1789
                    if ($index === $searchparam) {
1790
                        $parametervalues[] = $value;
1791
                    }
1792
                }
1793
            }
1794
            return implode(', ', $parametervalues);
1795
        }
1796
        return '';
1797
    }
1798
 
1799
    /**
1800
     * Get the latest library version.
1801
     *
1802
     * @param  string $machinename The library's machine name
1803
     * @return stdClass|null An object with the latest library version
1804
     */
1805
    public function get_latest_library_version(string $machinename): ?\stdClass {
1806
        global $DB;
1807
 
1808
        $libraries = $DB->get_records('h5p_libraries', ['machinename' => $machinename],
1809
            'majorversion DESC, minorversion DESC, patchversion DESC', '*', 0, 1);
1810
        if ($libraries) {
1811
            return reset($libraries);
1812
        }
1813
 
1814
        return null;
1815
    }
1816
 
1817
    /**
1818
     * Replace content hub metadata cache
1819
     *
1820
     * @param JsonSerializable $metadata Metadata as received from content hub
1821
     * @param string $lang Language in ISO 639-1
1822
     *
1823
     * @return mixed
1824
     */
1825
    public function replaceContentHubMetadataCache($metadata, $lang) {
1826
        debugging('The replaceContentHubMetadataCache() method is not implemented.', DEBUG_DEVELOPER);
1827
        return null;
1828
    }
1829
 
1830
    /**
1831
     * Get content hub metadata cache from db
1832
     *
1833
     * @param  string  $lang Language code in ISO 639-1
1834
     *
1835
     * @return JsonSerializable Json string
1836
     */
1837
    public function getContentHubMetadataCache($lang = 'en') {
1838
        debugging('The getContentHubMetadataCache() method is not implemented.', DEBUG_DEVELOPER);
1839
        return null;
1840
    }
1841
 
1842
    /**
1843
     * Get time of last content hub metadata check
1844
     *
1845
     * @param  string  $lang Language code iin ISO 639-1 format
1846
     *
1847
     * @return string|null Time in RFC7231 format
1848
     */
1849
    public function getContentHubMetadataChecked($lang = 'en') {
1850
        debugging('The getContentHubMetadataChecked() method is not implemented.', DEBUG_DEVELOPER);
1851
        return null;
1852
    }
1853
 
1854
    /**
1855
     * Set time of last content hub metadata check
1856
     *
1857
     * @param  int|null  $time Time in RFC7231 format
1858
     * @param  string  $lang Language code iin ISO 639-1 format
1859
     *
1860
     * @return bool True if successful
1861
     */
1862
    public function setContentHubMetadataChecked($time, $lang = 'en') {
1863
        debugging('The setContentHubMetadataChecked() method is not implemented.', DEBUG_DEVELOPER);
1864
        return false;
1865
    }
1866
}