See Release Notes
Long Term Support Release
<?php // This file is part of Moodle - http://moodle.org/ // // Moodle is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // Moodle is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Moodle. If not, see <http://www.gnu.org/licenses/>. /** * Class \core_h5p\editor_framework * * @package core_h5p * @copyright 2020 Victor Deniz <victor@moodle.com>, base on code by Joubel AS * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ namespace core_h5p;< use H5peditorStorage;> use Moodle\H5peditorStorage;use stdClass; /** * Moodle's implementation of the H5P Editor storage interface. * * Makes it possible for the editor's core library to communicate with the * database used by Moodle. * * @package core_h5p * @copyright 2020 Victor Deniz <victor@moodle.com>, base on code by Joubel AS * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class editor_framework implements H5peditorStorage { /**> * Retrieve library language file from file storage. Note that parent languages will also be checked until a matching * Load language file(JSON). > * record is found (e.g. "de_kids" -> "de_du" -> "de") * Used to translate the editor fields(title, description etc.) > * * > * @param string $name * @param string $name The machine readable name of the library(content type) > * @param int $major * @param int $major Major part of version number > * @param int $minor * @param int $minor Minor part of version number > * @param string $lang * @param string $lang Language code > * @return stdClass|bool Translation record if available, false otherwise * > */ * @return string|boolean Translation in JSON format if available, false otherwise > private function get_language_record(string $name, int $major, int $minor, string $lang) { */ > global $DB; public function getLanguage($name, $major, $minor, $lang) { > global $DB; > $params = [ > file_storage::COMPONENT, // Check if this information has been saved previously into the cache. > file_storage::LIBRARY_FILEAREA, $langcache = \cache::make('core', 'h5p_content_type_translations'); > ]; $library = new stdClass(); > $sqllike = $DB->sql_like('f.filepath', '?'); $library->machinename = $name; > $params[] = '%language%'; $library->majorversion = $major; > $library->minorversion = $minor; > $sql = "SELECT hl.id, f.pathnamehash $librarykey = helper::get_cache_librarykey(core::record_to_string($library)); > FROM {h5p_libraries} hl $cachekey = "{$librarykey}/{$lang}"; > LEFT JOIN {files} f $translation = $langcache->get($cachekey); > ON hl.id = f.itemid AND f.component = ? AND f.filearea = ? AND $sqllike > WHERE ((hl.machinename = ? AND hl.majorversion = ? AND hl.minorversion = ?) if ($translation !== false) { > AND f.filename = ?) // When there is no translation we store it in the cache as `null`. > ORDER BY hl.patchversion DESC"; // This API requires it be returned as `false`. > if ($translation === null) { > $params[] = $name; return false; > $params[] = $major; } > $params[] = $minor; > $params[] = $lang.'.json'; return $translation; > } > // Add translations, based initially on the given H5P language code. If missing then recurse language dependencies > // until we find a matching H5P language file. // Get the language file for this library. > $result = $DB->get_record_sql($sql, $params); $params = [ > if ($result === false) { file_storage::COMPONENT, > file_storage::LIBRARY_FILEAREA, > // Normalise Moodle language using underscore, as opposed to H5P which uses dash. ]; > $moodlelanguage = str_replace('-', '_', $lang); $sqllike = $DB->sql_like('f.filepath', '?'); > $params[] = '%language%'; > $dependencies = get_string_manager()->get_language_dependencies($moodlelanguage); > $sql = "SELECT hl.id, f.pathnamehash > // If current language has a dependency, then request it. FROM {h5p_libraries} hl > if (count($dependencies) > 1) { LEFT JOIN {files} f > $parentlanguage = get_html_lang_attribute_value($dependencies[count($dependencies) - 2]); ON hl.id = f.itemid AND f.component = ? AND f.filearea = ? AND $sqllike > $result = $this->get_language_record($name, $major, $minor, $parentlanguage); WHERE ((hl.machinename = ? AND hl.majorversion = ? AND hl.minorversion = ?) > } AND f.filename = ?) > } ORDER BY hl.patchversion DESC"; > $params[] = $name; > return $result; $params[] = $major; > } $params[] = $minor; > $params[] = $lang.'.json'; > /**< global $DB;< $params = [ < file_storage::COMPONENT, < file_storage::LIBRARY_FILEAREA, < ]; < $sqllike = $DB->sql_like('f.filepath', '?'); < $params[] = '%language%'; < < $sql = "SELECT hl.id, f.pathnamehash < FROM {h5p_libraries} hl < LEFT JOIN {files} f < ON hl.id = f.itemid AND f.component = ? AND f.filearea = ? AND $sqllike < WHERE ((hl.machinename = ? AND hl.majorversion = ? AND hl.minorversion = ?) < AND f.filename = ?) < ORDER BY hl.patchversion DESC"; < $params[] = $name; < $params[] = $major; < $params[] = $minor; < $params[] = $lang.'.json'; < < $result = $DB->get_record_sql($sql, $params); <> $result = $this->get_language_record($name, $major, $minor, $lang);* Load a list of available language codes. * * Until translations is implemented, only returns the "en" language. * * @param string $machinename The machine readable name of the library(content type) * @param int $major Major part of version number * @param int $minor Minor part of version number * * @return array List of possible language codes */ public function getAvailableLanguages($machinename, $major, $minor): array { global $DB; // Check if this information has been saved previously into the cache. $langcache = \cache::make('core', 'h5p_content_type_translations'); $library = new stdClass(); $library->machinename = $machinename; $library->majorversion = $major; $library->minorversion = $minor; $librarykey = helper::get_cache_librarykey(core::record_to_string($library)); $languages = $langcache->get($librarykey); if ($languages) { // This contains a list of all of the available languages for the library. return $languages; } // Get the language files for this library. $params = [ file_storage::COMPONENT, file_storage::LIBRARY_FILEAREA, ]; $filepathsqllike = $DB->sql_like('f.filepath', '?'); $params[] = '%language%'; $filenamesqllike = $DB->sql_like('f.filename', '?'); $params[] = '%.json'; $sql = "SELECT DISTINCT f.filename FROM {h5p_libraries} hl LEFT JOIN {files} f ON hl.id = f.itemid AND f.component = ? AND f.filearea = ? AND $filepathsqllike AND $filenamesqllike WHERE hl.machinename = ? AND hl.majorversion = ? AND hl.minorversion = ?"; $params[] = $machinename; $params[] = $major; $params[] = $minor; $defaultcode = 'en'; $languages = []; $results = $DB->get_recordset_sql($sql, $params); if ($results->valid()) { // Extract the code language from the JS language files. foreach ($results as $result) { if (!empty($result->filename)) { $lang = substr($result->filename, 0, -5); $languages[$lang] = $languages; } } $results->close(); // Semantics is 'en' by default. It has to be added always. if (!array_key_exists($defaultcode, $languages)) { $languages = array_keys($languages); array_unshift($languages, $defaultcode); } } else { $results->close(); $params = [ 'machinename' => $machinename, 'majorversion' => $major, 'minorversion' => $minor, ]; if ($DB->record_exists('h5p_libraries', $params)) { // If the library exists (but it doesn't contain any language file), at least defaultcode should be returned. $languages[] = $defaultcode; } } // Save available languages into the cache. $langcache->set($librarykey, $languages); return $languages; } /** * "Callback" for mark the given file as a permanent file. * * Used when saving content that has new uploaded files. * * @param int $fileid */ public function keepFile($fileid): void { // Temporal files will be removed on a task when they are in the "editor" file area and and are at least one day older. } /** * Return libraries details. * * Two use cases: * 1. No input, will list all the available content types. * 2. Libraries supported are specified, load additional data and verify * that the content types are available. Used by e.g. the Presentation Tool * Editor that already knows which content types are supported in its * slides. * * @param array $libraries List of library names + version to load info for. * * @return array List of all libraries loaded. */ public function getLibraries($libraries = null): ?array { if ($libraries !== null) { // Get details for the specified libraries. $librariesin = [];< $fields = 'title, runnable, metadatasettings';> $fields = 'title, runnable, metadatasettings, example, tutorial';foreach ($libraries as $library) { $params = [ 'machinename' => $library->name, 'majorversion' => $library->majorVersion, 'minorversion' => $library->minorVersion ]; $details = api::get_library_details($params, true, $fields); if ($details) { $library->title = $details->title; $library->runnable = $details->runnable;< $library->metadataSettings = json_decode($details->metadatasettings);> $library->metadataSettings = json_decode($details->metadatasettings ?? ''); > $library->example = $details->example; > $library->tutorial = $details->tutorial;$librariesin[] = $library; } } } else {< $fields = 'id, machinename as name, title, majorversion, minorversion, metadatasettings';> $fields = 'id, machinename as name, title, majorversion, minorversion, metadatasettings, example, tutorial';$librariesin = api::get_contenttype_libraries($fields); } return $librariesin; } /** * Allow for other plugins to decide which styles and scripts are attached. * * This is useful for adding and/or modifying the functionality and look of * the content types. * * @param array $files List of files as objects with path and version as properties. * @param array $libraries List of libraries indexed by machineName with objects as values. The objects have majorVersion and * minorVersion as properties. */ public function alterLibraryFiles(&$files, $libraries): void { global $PAGE; // Refactor dependency list. $librarylist = []; foreach ($libraries as $dependency) { $librarylist[$dependency['machineName']] = [ 'majorVersion' => $dependency['majorVersion'], 'minorVersion' => $dependency['minorVersion'] ]; } $renderer = $PAGE->get_renderer('core_h5p'); $embedtype = 'editor'; $renderer->h5p_alter_scripts($files['scripts'], $librarylist, $embedtype); $renderer->h5p_alter_styles($files['styles'], $librarylist, $embedtype); } /** * Saves a file or moves it temporarily. * * This is often necessary in order to validate and store uploaded or fetched H5Ps. * * @param string $data Uri of data that should be saved as a temporary file. * @param bool $movefile Can be set to TRUE to move the data instead of saving it. * * @return bool|object Returns false if saving failed or an object with path * of the directory and file that is temporarily saved. */ public static function saveFileTemporarily($data, $movefile = false) { // This is to be implemented when the Hub client is used to upload libraries. return false; } /** * Marks a file for later cleanup. * * Useful when files are not instantly cleaned up. E.g. for files that are uploaded through the editor. * * @param int $file Id of file that should be cleaned up * @param int|null $contentid Content id of file */ public static function markFileForCleanup($file, $contentid = null): ?int { // Temporal files will be removed on a task when they are in the "editor" file area and and are at least one day older. return null; } /** * Clean up temporary files * * @param string $filepath Path to file or directory */ public static function removeTemporarilySavedFiles($filepath): void { // This is to be implemented when the Hub client is used to upload libraries. } }