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