See Release Notes
Long Term Support Release
Differences Between: [Versions 310 and 401] [Versions 311 and 401] [Versions 39 and 401] [Versions 400 and 401]
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 Moodle\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 * Retrieve library language file from file storage. Note that parent languages will also be checked until a matching 44 * record is found (e.g. "de_kids" -> "de_du" -> "de") 45 * 46 * @param string $name 47 * @param int $major 48 * @param int $minor 49 * @param string $lang 50 * @return stdClass|bool Translation record if available, false otherwise 51 */ 52 private function get_language_record(string $name, int $major, int $minor, string $lang) { 53 global $DB; 54 55 $params = [ 56 file_storage::COMPONENT, 57 file_storage::LIBRARY_FILEAREA, 58 ]; 59 $sqllike = $DB->sql_like('f.filepath', '?'); 60 $params[] = '%language%'; 61 62 $sql = "SELECT hl.id, f.pathnamehash 63 FROM {h5p_libraries} hl 64 LEFT JOIN {files} f 65 ON hl.id = f.itemid AND f.component = ? AND f.filearea = ? AND $sqllike 66 WHERE ((hl.machinename = ? AND hl.majorversion = ? AND hl.minorversion = ?) 67 AND f.filename = ?) 68 ORDER BY hl.patchversion DESC"; 69 70 $params[] = $name; 71 $params[] = $major; 72 $params[] = $minor; 73 $params[] = $lang.'.json'; 74 75 // Add translations, based initially on the given H5P language code. If missing then recurse language dependencies 76 // until we find a matching H5P language file. 77 $result = $DB->get_record_sql($sql, $params); 78 if ($result === false) { 79 80 // Normalise Moodle language using underscore, as opposed to H5P which uses dash. 81 $moodlelanguage = str_replace('-', '_', $lang); 82 83 $dependencies = get_string_manager()->get_language_dependencies($moodlelanguage); 84 85 // If current language has a dependency, then request it. 86 if (count($dependencies) > 1) { 87 $parentlanguage = get_html_lang_attribute_value($dependencies[count($dependencies) - 2]); 88 $result = $this->get_language_record($name, $major, $minor, $parentlanguage); 89 } 90 } 91 92 return $result; 93 } 94 95 /** 96 * Load language file(JSON). 97 * Used to translate the editor fields(title, description etc.) 98 * 99 * @param string $name The machine readable name of the library(content type) 100 * @param int $major Major part of version number 101 * @param int $minor Minor part of version number 102 * @param string $lang Language code 103 * 104 * @return string|boolean Translation in JSON format if available, false otherwise 105 */ 106 public function getLanguage($name, $major, $minor, $lang) { 107 108 // Check if this information has been saved previously into the cache. 109 $langcache = \cache::make('core', 'h5p_content_type_translations'); 110 $library = new stdClass(); 111 $library->machinename = $name; 112 $library->majorversion = $major; 113 $library->minorversion = $minor; 114 $librarykey = helper::get_cache_librarykey(core::record_to_string($library)); 115 $cachekey = "{$librarykey}/{$lang}"; 116 $translation = $langcache->get($cachekey); 117 118 if ($translation !== false) { 119 // When there is no translation we store it in the cache as `null`. 120 // This API requires it be returned as `false`. 121 if ($translation === null) { 122 return false; 123 } 124 125 return $translation; 126 } 127 128 // Get the language file for this library. 129 $result = $this->get_language_record($name, $major, $minor, $lang); 130 if (empty($result)) { 131 // Save the fact that there is no translation into the cache. 132 // The cache API cannot handle setting a literal `false` value so conver to `null` instead. 133 $langcache->set($cachekey, null); 134 135 return false; 136 } 137 138 // Save translation into the cache, and return its content. 139 $fs = get_file_storage(); 140 $file = $fs->get_file_by_hash($result->pathnamehash); 141 $translation = $file->get_content(); 142 143 $langcache->set($cachekey, $translation); 144 145 return $translation; 146 } 147 148 /** 149 * Load a list of available language codes. 150 * 151 * Until translations is implemented, only returns the "en" language. 152 * 153 * @param string $machinename The machine readable name of the library(content type) 154 * @param int $major Major part of version number 155 * @param int $minor Minor part of version number 156 * 157 * @return array List of possible language codes 158 */ 159 public function getAvailableLanguages($machinename, $major, $minor): array { 160 global $DB; 161 162 // Check if this information has been saved previously into the cache. 163 $langcache = \cache::make('core', 'h5p_content_type_translations'); 164 $library = new stdClass(); 165 $library->machinename = $machinename; 166 $library->majorversion = $major; 167 $library->minorversion = $minor; 168 $librarykey = helper::get_cache_librarykey(core::record_to_string($library)); 169 $languages = $langcache->get($librarykey); 170 if ($languages) { 171 // This contains a list of all of the available languages for the library. 172 return $languages; 173 } 174 175 // Get the language files for this library. 176 $params = [ 177 file_storage::COMPONENT, 178 file_storage::LIBRARY_FILEAREA, 179 ]; 180 $filepathsqllike = $DB->sql_like('f.filepath', '?'); 181 $params[] = '%language%'; 182 $filenamesqllike = $DB->sql_like('f.filename', '?'); 183 $params[] = '%.json'; 184 185 $sql = "SELECT DISTINCT f.filename 186 FROM {h5p_libraries} hl 187 LEFT JOIN {files} f 188 ON hl.id = f.itemid AND f.component = ? AND f.filearea = ? 189 AND $filepathsqllike AND $filenamesqllike 190 WHERE hl.machinename = ? AND hl.majorversion = ? AND hl.minorversion = ?"; 191 $params[] = $machinename; 192 $params[] = $major; 193 $params[] = $minor; 194 195 $defaultcode = 'en'; 196 $languages = []; 197 198 $results = $DB->get_recordset_sql($sql, $params); 199 if ($results->valid()) { 200 // Extract the code language from the JS language files. 201 foreach ($results as $result) { 202 if (!empty($result->filename)) { 203 $lang = substr($result->filename, 0, -5); 204 $languages[$lang] = $languages; 205 } 206 } 207 $results->close(); 208 209 // Semantics is 'en' by default. It has to be added always. 210 if (!array_key_exists($defaultcode, $languages)) { 211 $languages = array_keys($languages); 212 array_unshift($languages, $defaultcode); 213 } 214 } else { 215 $results->close(); 216 $params = [ 217 'machinename' => $machinename, 218 'majorversion' => $major, 219 'minorversion' => $minor, 220 ]; 221 if ($DB->record_exists('h5p_libraries', $params)) { 222 // If the library exists (but it doesn't contain any language file), at least defaultcode should be returned. 223 $languages[] = $defaultcode; 224 } 225 } 226 227 // Save available languages into the cache. 228 $langcache->set($librarykey, $languages); 229 230 return $languages; 231 } 232 233 /** 234 * "Callback" for mark the given file as a permanent file. 235 * 236 * Used when saving content that has new uploaded files. 237 * 238 * @param int $fileid 239 */ 240 public function keepFile($fileid): void { 241 // Temporal files will be removed on a task when they are in the "editor" file area and and are at least one day older. 242 } 243 244 /** 245 * Return libraries details. 246 * 247 * Two use cases: 248 * 1. No input, will list all the available content types. 249 * 2. Libraries supported are specified, load additional data and verify 250 * that the content types are available. Used by e.g. the Presentation Tool 251 * Editor that already knows which content types are supported in its 252 * slides. 253 * 254 * @param array $libraries List of library names + version to load info for. 255 * 256 * @return array List of all libraries loaded. 257 */ 258 public function getLibraries($libraries = null): ?array { 259 260 if ($libraries !== null) { 261 // Get details for the specified libraries. 262 $librariesin = []; 263 $fields = 'title, runnable, metadatasettings, example, tutorial'; 264 265 foreach ($libraries as $library) { 266 $params = [ 267 'machinename' => $library->name, 268 'majorversion' => $library->majorVersion, 269 'minorversion' => $library->minorVersion 270 ]; 271 272 $details = api::get_library_details($params, true, $fields); 273 274 if ($details) { 275 $library->title = $details->title; 276 $library->runnable = $details->runnable; 277 $library->metadataSettings = json_decode($details->metadatasettings ?? ''); 278 $library->example = $details->example; 279 $library->tutorial = $details->tutorial; 280 $librariesin[] = $library; 281 } 282 } 283 } else { 284 $fields = 'id, machinename as name, title, majorversion, minorversion, metadatasettings, example, tutorial'; 285 $librariesin = api::get_contenttype_libraries($fields); 286 } 287 288 return $librariesin; 289 } 290 291 /** 292 * Allow for other plugins to decide which styles and scripts are attached. 293 * 294 * This is useful for adding and/or modifying the functionality and look of 295 * the content types. 296 * 297 * @param array $files List of files as objects with path and version as properties. 298 * @param array $libraries List of libraries indexed by machineName with objects as values. The objects have majorVersion and 299 * minorVersion as properties. 300 */ 301 public function alterLibraryFiles(&$files, $libraries): void { 302 global $PAGE; 303 304 // Refactor dependency list. 305 $librarylist = []; 306 foreach ($libraries as $dependency) { 307 $librarylist[$dependency['machineName']] = [ 308 'majorVersion' => $dependency['majorVersion'], 309 'minorVersion' => $dependency['minorVersion'] 310 ]; 311 } 312 313 $renderer = $PAGE->get_renderer('core_h5p'); 314 315 $embedtype = 'editor'; 316 $renderer->h5p_alter_scripts($files['scripts'], $librarylist, $embedtype); 317 $renderer->h5p_alter_styles($files['styles'], $librarylist, $embedtype); 318 } 319 320 /** 321 * Saves a file or moves it temporarily. 322 * 323 * This is often necessary in order to validate and store uploaded or fetched H5Ps. 324 * 325 * @param string $data Uri of data that should be saved as a temporary file. 326 * @param bool $movefile Can be set to TRUE to move the data instead of saving it. 327 * 328 * @return bool|object Returns false if saving failed or an object with path 329 * of the directory and file that is temporarily saved. 330 */ 331 public static function saveFileTemporarily($data, $movefile = false) { 332 // This is to be implemented when the Hub client is used to upload libraries. 333 return false; 334 } 335 336 /** 337 * Marks a file for later cleanup. 338 * 339 * Useful when files are not instantly cleaned up. E.g. for files that are uploaded through the editor. 340 * 341 * @param int $file Id of file that should be cleaned up 342 * @param int|null $contentid Content id of file 343 */ 344 public static function markFileForCleanup($file, $contentid = null): ?int { 345 // Temporal files will be removed on a task when they are in the "editor" file area and and are at least one day older. 346 return null; 347 } 348 349 /** 350 * Clean up temporary files 351 * 352 * @param string $filepath Path to file or directory 353 */ 354 public static function removeTemporarilySavedFiles($filepath): void { 355 // This is to be implemented when the Hub client is used to upload libraries. 356 } 357 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body