MDL-68921 core_h5p: Add H5P libraries metadata settings
[moodle.git] / h5p / classes / editor_framework.php
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/>.
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  */
25 namespace core_h5p;
27 use H5peditorStorage;
28 use stdClass;
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 {
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;
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);
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             }
73             return $translation;
74         }
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%';
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';
96         $result = $DB->get_record_sql($sql, $params);
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);
103             return false;
104         }
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();
111         $langcache->set($cachekey, $translation);
113         return $translation;
114     }
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;
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         }
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';
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;
163         $defaultcode = 'en';
164         $languages = [];
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();
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         }
195         // Save available languages into the cache.
196         $langcache->set($librarykey, $languages);
198         return $languages;
199     }
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     }
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 {
228         if ($libraries !== null) {
229             // Get details for the specified libraries.
230             $librariesin = [];
231             $fields = 'title, runnable, metadatasettings';
233             foreach ($libraries as $library) {
234                 $params = [
235                     'machinename' => $library->name,
236                     'majorversion' => $library->majorVersion,
237                     'minorversion' => $library->minorVersion
238                 ];
240                 $details = api::get_library_details($params, true, $fields);
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         }
254         return $librariesin;
255     }
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         // This is to be implemented when the renderer is used.
269     }
271     /**
272      * Saves a file or moves it temporarily.
273      *
274      * This is often necessary in order to validate and store uploaded or fetched H5Ps.
275      *
276      * @param string $data Uri of data that should be saved as a temporary file.
277      * @param bool $movefile Can be set to TRUE to move the data instead of saving it.
278      *
279      * @return bool|object Returns false if saving failed or an object with path
280      * of the directory and file that is temporarily saved.
281      */
282     public static function saveFileTemporarily($data, $movefile = false) {
283         // This is to be implemented when the Hub client is used to upload libraries.
284         return false;
285     }
287     /**
288      * Marks a file for later cleanup.
289      *
290      * Useful when files are not instantly cleaned up. E.g. for files that are uploaded through the editor.
291      *
292      * @param int $file Id of file that should be cleaned up
293      * @param int|null $contentid Content id of file
294      */
295     public static function markFileForCleanup($file, $contentid = null): ?int {
296         // Temporal files will be removed on a task when they are in the "editor" file area and and are at least one day older.
297         return null;
298     }
300     /**
301      * Clean up temporary files
302      *
303      * @param string $filepath Path to file or directory
304      */
305     public static function removeTemporarilySavedFiles($filepath): void {
306         // This is to be implemented when the Hub client is used to upload libraries.
307     }