Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.3.x will end 7 October 2024 (12 months).
  • Bug fixes for security issues in 4.3.x will end 21 April 2025 (18 months).
  • PHP version: minimum PHP 8.0.0 Note: minimum PHP version has increased since Moodle 4.1. PHP 8.2.x is supported too.

Differences Between: [Versions 310 and 403] [Versions 311 and 403] [Versions 39 and 403] [Versions 400 and 403] [Versions 401 and 403]

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