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