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