Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 3.9.x will end* 10 May 2021 (12 months).
  • Bug fixes for security issues in 3.9.x will end* 8 May 2023 (36 months).
  • PHP version: minimum PHP 7.2.0 Note: minimum PHP version has increased since Moodle 3.8. PHP 7.3.x and 7.4.x are supported too.

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

   1  <?php
   2  // This file is part of Moodle - http://moodle.org/
   3  //
   4  // Moodle is free software: you can redistribute it and/or modify
   5  // it under the terms of the GNU General Public License as published by
   6  // the Free Software Foundation, either version 3 of the License, or
   7  // (at your option) any later version.
   8  //
   9  // Moodle is distributed in the hope that it will be useful,
  10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12  // GNU General Public License for more details.
  13  //
  14  // You should have received a copy of the GNU General Public License
  15  // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
  16  
  17  /**
  18   * Class \core_h5p\editor_framework
  19   *
  20   * @package    core_h5p
  21   * @copyright  2020 Victor Deniz <victor@moodle.com>, base on code by Joubel AS
  22   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  23   */
  24  
  25  namespace core_h5p;
  26  
  27  use H5peditorStorage;
  28  use stdClass;
  29  
  30  /**
  31   * Moodle's implementation of the H5P Editor storage interface.
  32   *
  33   * Makes it possible for the editor's core library to communicate with the
  34   * database used by Moodle.
  35   *
  36   * @package    core_h5p
  37   * @copyright  2020 Victor Deniz <victor@moodle.com>, base on code by Joubel AS
  38   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  39   */
  40  class editor_framework implements H5peditorStorage {
  41  
  42      /**
  43       * Load language file(JSON).
  44       * Used to translate the editor fields(title, description etc.)
  45       *
  46       * @param string $name The machine readable name of the library(content type)
  47       * @param int $major Major part of version number
  48       * @param int $minor Minor part of version number
  49       * @param string $lang Language code
  50       *
  51       * @return string|boolean Translation in JSON format if available, false otherwise
  52       */
  53      public function getLanguage($name, $major, $minor, $lang) {
  54          global $DB;
  55  
  56          // Check if this information has been saved previously into the cache.
  57          $langcache = \cache::make('core', 'h5p_content_type_translations');
  58          $library = new stdClass();
  59          $library->machinename = $name;
  60          $library->majorversion = $major;
  61          $library->minorversion = $minor;
  62          $librarykey = helper::get_cache_librarykey(core::record_to_string($library));
  63          $cachekey = "{$librarykey}/{$lang}";
  64          $translation = $langcache->get($cachekey);
  65  
  66          if ($translation !== false) {
  67              // When there is no translation we store it in the cache as `null`.
  68              // This API requires it be returned as `false`.
  69              if ($translation === null) {
  70                  return false;
  71              }
  72  
  73              return $translation;
  74          }
  75  
  76          // Get the language file for this library.
  77          $params = [
  78              file_storage::COMPONENT,
  79              file_storage::LIBRARY_FILEAREA,
  80          ];
  81          $sqllike = $DB->sql_like('f.filepath', '?');
  82          $params[] = '%language%';
  83  
  84          $sql = "SELECT hl.id, f.pathnamehash
  85                    FROM {h5p_libraries} hl
  86               LEFT JOIN {files} f
  87                      ON hl.id = f.itemid AND f.component = ? AND f.filearea = ? AND $sqllike
  88                   WHERE ((hl.machinename = ? AND hl.majorversion = ? AND hl.minorversion = ?)
  89                     AND f.filename = ?)
  90                ORDER BY hl.patchversion DESC";
  91          $params[] = $name;
  92          $params[] = $major;
  93          $params[] = $minor;
  94          $params[] = $lang.'.json';
  95  
  96          $result = $DB->get_record_sql($sql, $params);
  97  
  98          if (empty($result)) {
  99              // Save the fact that there is no translation into the cache.
 100              // The cache API cannot handle setting a literal `false` value so conver to `null` instead.
 101              $langcache->set($cachekey, null);
 102  
 103              return false;
 104          }
 105  
 106          // Save translation into the cache, and return its content.
 107          $fs = get_file_storage();
 108          $file = $fs->get_file_by_hash($result->pathnamehash);
 109          $translation = $file->get_content();
 110  
 111          $langcache->set($cachekey, $translation);
 112  
 113          return $translation;
 114      }
 115  
 116      /**
 117       * Load a list of available language codes.
 118       *
 119       * Until translations is implemented, only returns the "en" language.
 120       *
 121       * @param string $machinename The machine readable name of the library(content type)
 122       * @param int $major Major part of version number
 123       * @param int $minor Minor part of version number
 124       *
 125       * @return array List of possible language codes
 126       */
 127      public function getAvailableLanguages($machinename, $major, $minor): array {
 128          global $DB;
 129  
 130          // Check if this information has been saved previously into the cache.
 131          $langcache = \cache::make('core', 'h5p_content_type_translations');
 132          $library = new stdClass();
 133          $library->machinename = $machinename;
 134          $library->majorversion = $major;
 135          $library->minorversion = $minor;
 136          $librarykey = helper::get_cache_librarykey(core::record_to_string($library));
 137          $languages = $langcache->get($librarykey);
 138          if ($languages) {
 139              // This contains a list of all of the available languages for the library.
 140              return $languages;
 141          }
 142  
 143          // Get the language files for this library.
 144          $params = [
 145              file_storage::COMPONENT,
 146              file_storage::LIBRARY_FILEAREA,
 147          ];
 148          $filepathsqllike = $DB->sql_like('f.filepath', '?');
 149          $params[] = '%language%';
 150          $filenamesqllike = $DB->sql_like('f.filename', '?');
 151          $params[] = '%.json';
 152  
 153          $sql = "SELECT DISTINCT f.filename
 154                             FROM {h5p_libraries} hl
 155                        LEFT JOIN {files} f
 156                               ON hl.id = f.itemid AND f.component = ? AND f.filearea = ?
 157                              AND $filepathsqllike AND $filenamesqllike
 158                            WHERE hl.machinename = ? AND hl.majorversion = ? AND hl.minorversion = ?";
 159          $params[] = $machinename;
 160          $params[] = $major;
 161          $params[] = $minor;
 162  
 163          $defaultcode = 'en';
 164          $languages = [];
 165  
 166          $results = $DB->get_recordset_sql($sql, $params);
 167          if ($results->valid()) {
 168              // Extract the code language from the JS language files.
 169              foreach ($results as $result) {
 170                  if (!empty($result->filename)) {
 171                      $lang = substr($result->filename, 0, -5);
 172                      $languages[$lang] = $languages;
 173                  }
 174              }
 175              $results->close();
 176  
 177              // Semantics is 'en' by default. It has to be added always.
 178              if (!array_key_exists($defaultcode, $languages)) {
 179                  $languages = array_keys($languages);
 180                  array_unshift($languages, $defaultcode);
 181              }
 182          } else {
 183              $results->close();
 184              $params = [
 185                  'machinename' => $machinename,
 186                  'majorversion' => $major,
 187                  'minorversion' => $minor,
 188              ];
 189              if ($DB->record_exists('h5p_libraries', $params)) {
 190                  // If the library exists (but it doesn't contain any language file), at least defaultcode should be returned.
 191                  $languages[] = $defaultcode;
 192              }
 193          }
 194  
 195          // Save available languages into the cache.
 196          $langcache->set($librarykey, $languages);
 197  
 198          return $languages;
 199      }
 200  
 201      /**
 202       * "Callback" for mark the given file as a permanent file.
 203       *
 204       * Used when saving content that has new uploaded files.
 205       *
 206       * @param int $fileid
 207       */
 208      public function keepFile($fileid): void {
 209          // Temporal files will be removed on a task when they are in the "editor" file area and and are at least one day older.
 210      }
 211  
 212      /**
 213       * Return libraries details.
 214       *
 215       * Two use cases:
 216       * 1. No input, will list all the available content types.
 217       * 2. Libraries supported are specified, load additional data and verify
 218       * that the content types are available. Used by e.g. the Presentation Tool
 219       * Editor that already knows which content types are supported in its
 220       * slides.
 221       *
 222       * @param array $libraries List of library names + version to load info for.
 223       *
 224       * @return array List of all libraries loaded.
 225       */
 226      public function getLibraries($libraries = null): ?array {
 227  
 228          if ($libraries !== null) {
 229              // Get details for the specified libraries.
 230              $librariesin = [];
 231              $fields = 'title, runnable, metadatasettings';
 232  
 233              foreach ($libraries as $library) {
 234                  $params = [
 235                      'machinename' => $library->name,
 236                      'majorversion' => $library->majorVersion,
 237                      'minorversion' => $library->minorVersion
 238                  ];
 239  
 240                  $details = api::get_library_details($params, true, $fields);
 241  
 242                  if ($details) {
 243                      $library->title = $details->title;
 244                      $library->runnable = $details->runnable;
 245                      $library->metadataSettings = json_decode($details->metadatasettings);
 246                      $librariesin[] = $library;
 247                  }
 248              }
 249          } else {
 250              $fields = 'id, machinename as name, title, majorversion, minorversion, metadatasettings';
 251              $librariesin = api::get_contenttype_libraries($fields);
 252          }
 253  
 254          return $librariesin;
 255      }
 256  
 257      /**
 258       * Allow for other plugins to decide which styles and scripts are attached.
 259       *
 260       * This is useful for adding and/or modifying the functionality and look of
 261       * the content types.
 262       *
 263       * @param array $files List of files as objects with path and version as properties.
 264       * @param array $libraries List of libraries indexed by machineName with objects as values. The objects have majorVersion and
 265       *     minorVersion as properties.
 266       */
 267      public function alterLibraryFiles(&$files, $libraries): void {
 268          global $PAGE;
 269  
 270          // Refactor dependency list.
 271          $librarylist = [];
 272          foreach ($libraries as $dependency) {
 273              $librarylist[$dependency['machineName']] = [
 274                  'majorVersion' => $dependency['majorVersion'],
 275                  'minorVersion' => $dependency['minorVersion']
 276              ];
 277          }
 278  
 279          $renderer = $PAGE->get_renderer('core_h5p');
 280  
 281          $embedtype = 'editor';
 282          $renderer->h5p_alter_scripts($files['scripts'], $librarylist, $embedtype);
 283          $renderer->h5p_alter_styles($files['styles'], $librarylist, $embedtype);
 284      }
 285  
 286      /**
 287       * Saves a file or moves it temporarily.
 288       *
 289       * This is often necessary in order to validate and store uploaded or fetched H5Ps.
 290       *
 291       * @param string $data Uri of data that should be saved as a temporary file.
 292       * @param bool $movefile Can be set to TRUE to move the data instead of saving it.
 293       *
 294       * @return bool|object Returns false if saving failed or an object with path
 295       * of the directory and file that is temporarily saved.
 296       */
 297      public static function saveFileTemporarily($data, $movefile = false) {
 298          // This is to be implemented when the Hub client is used to upload libraries.
 299          return false;
 300      }
 301  
 302      /**
 303       * Marks a file for later cleanup.
 304       *
 305       * Useful when files are not instantly cleaned up. E.g. for files that are uploaded through the editor.
 306       *
 307       * @param int $file Id of file that should be cleaned up
 308       * @param int|null $contentid Content id of file
 309       */
 310      public static function markFileForCleanup($file, $contentid = null): ?int {
 311          // Temporal files will be removed on a task when they are in the "editor" file area and and are at least one day older.
 312          return null;
 313      }
 314  
 315      /**
 316       * Clean up temporary files
 317       *
 318       * @param string $filepath Path to file or directory
 319       */
 320      public static function removeTemporarilySavedFiles($filepath): void {
 321          // This is to be implemented when the Hub client is used to upload libraries.
 322      }
 323  }