MDL-67192 core_h5p: remove redundant assignment
[moodle.git] / h5p / classes / core.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  * H5P core class.
19  *
20  * @package    core_h5p
21  * @copyright  2019 Sara Arjona <sara@moodle.com>
22  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23  */
25 namespace core_h5p;
27 defined('MOODLE_INTERNAL') || die();
29 require_once("$CFG->libdir/filelib.php");
31 use H5PCore;
32 use H5PFrameworkInterface;
33 use stdClass;
34 use moodle_url;
36 /**
37  * H5P core class, containing functions and storage shared by the other H5P classes.
38  *
39  * @package    core_h5p
40  * @copyright  2019 Sara Arjona <sara@moodle.com>
41  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
42  */
43 class core extends \H5PCore {
45     /** @var array The array containing all the present libraries */
46     protected $libraries;
48     /**
49      * Constructor for core_h5p/core.
50      *
51      * @param H5PFrameworkInterface $framework The frameworks implementation of the H5PFrameworkInterface
52      * @param string|\H5PFileStorage $path The H5P file storage directory or class
53      * @param string $url The URL to the file storage directory
54      * @param string $language The language code. Defaults to english
55      * @param boolean $export Whether export is enabled
56      */
57     public function __construct(H5PFrameworkInterface $framework, $path, string $url, string $language = 'en',
58             bool $export = false) {
60         parent::__construct($framework, $path, $url, $language, $export);
62         // Aggregate the assets by default.
63         $this->aggregateAssets = true;
64     }
66     /**
67      * Get the path to the dependency.
68      *
69      * @param array $dependency An array containing the information of the requested dependency library
70      * @return string The path to the dependency library
71      */
72     protected function getDependencyPath(array $dependency): string {
73         $library = $this->find_library($dependency);
75         return "libraries/{$library->id}/{$library->machinename}-{$library->majorversion}.{$library->minorversion}";
76     }
78     /**
79      * Get the paths to the content dependencies.
80      *
81      * @param int $id The H5P content ID
82      * @return array An array containing the path of each content dependency
83      */
84     public function get_dependency_roots(int $id): array {
85         $roots = [];
86         $dependencies = $this->h5pF->loadContentDependencies($id);
87         $context = \context_system::instance();
88         foreach ($dependencies as $dependency) {
89             $library = $this->find_library($dependency);
90             $roots[self::libraryToString($dependency, true)] = (moodle_url::make_pluginfile_url(
91                 $context->id,
92                 'core_h5p',
93                 'libraries',
94                 $library->id,
95                 "/" . self::libraryToString($dependency, true),
96                 ''
97             ))->out(false);
98         }
100         return $roots;
101     }
103     /**
104      * Get a particular dependency library.
105      *
106      * @param array $dependency An array containing information of the dependency library
107      * @return stdClass|null The library object if the library dependency exists, null otherwise
108      */
109     protected function find_library(array $dependency): ?\stdClass {
110         global $DB;
111         if (null === $this->libraries) {
112             $this->libraries = $DB->get_records('h5p_libraries');
113         }
115         $major = $dependency['majorVersion'];
116         $minor = $dependency['minorVersion'];
117         $patch = $dependency['patchVersion'];
119         foreach ($this->libraries as $library) {
120             if ($library->machinename !== $dependency['machineName']) {
121                 continue;
122             }
124             if ($library->majorversion != $major) {
125                 continue;
126             }
127             if ($library->minorversion != $minor) {
128                 continue;
129             }
130             if ($library->patchversion != $patch) {
131                 continue;
132             }
134             return $library;
135         }
137         return null;
138     }
140     /**
141      * Get core JavaScript files.
142      *
143      * @return array The array containg urls of the core JavaScript files
144      */
145     public static function get_scripts(): array {
146         global $CFG;
147         $cachebuster = '?ver='.$CFG->jsrev;
148         $liburl = $CFG->wwwroot . '/lib/h5p/';
149         $urls = [];
151         foreach (self::$scripts as $script) {
152             $urls[] = new moodle_url($liburl . $script . $cachebuster);
153         }
154         $urls[] = new moodle_url("/h5p/js/h5p_overrides.js");
156         return $urls;
157     }
159     /**
160      * Fetch and install the latest H5P content types libraries from the official H5P repository.
161      * If the latest version of a content type library is present in the system, nothing is done for that content type.
162      *
163      * @return stdClass
164      */
165     public function fetch_latest_content_types(): ?\stdClass {
167         $contenttypes = $this->get_latest_content_types();
168         if (!empty($contenttypes->error)) {
169             return $contenttypes;
170         }
172         $typesinstalled = [];
174         $factory = new factory();
175         $framework = $factory->get_framework();
177         foreach ($contenttypes->contentTypes as $type) {
178             // Don't fetch content types that require a higher H5P core API version.
179             if (!$this->is_required_core_api($type->coreApiVersionNeeded)) {
180                 continue;
181             }
183             $library = [
184                 'machineName' => $type->id,
185                 'majorVersion' => $type->version->major,
186                 'minorVersion' => $type->version->minor,
187                 'patchVersion' => $type->version->patch,
188             ];
190             $shoulddownload = true;
191             if ($framework->getLibraryId($type->id, $type->version->major, $type->version->minor)) {
192                 if (!$framework->isPatchedLibrary($library)) {
193                     $shoulddownload = false;
194                 }
195             }
197             if ($shoulddownload) {
198                 $installed['id'] = $this->fetch_content_type($library);
199                 if ($installed['id']) {
200                     $installed['name'] = \H5PCore::libraryToString($library);
201                     $typesinstalled[] = $installed;
202                 }
203             }
204         }
206         $result = new stdClass();
207         $result->error = '';
208         $result->typesinstalled = $typesinstalled;
210         return $result;
211     }
213     /**
214      * Given an H5P content type machine name, fetch and install the required library from the official H5P repository.
215      *
216      * @param array $library Library machineName, majorversion and minorversion.
217      * @return int|null Returns the id of the content type library installed, null otherwise.
218      */
219     public function fetch_content_type(array $library): ?int {
220         $factory = new factory();
222         // Get a temp path to download the content type.
223         $temppath = make_request_directory();
224         $tempfile = "{$temppath}/" . $library['machineName'] . ".h5p";
226         // Download the latest content type from the H5P official repository.
227         $fs = get_file_storage();
228         $file = $fs->create_file_from_url(
229             (object) [
230                 'component' => 'core_h5p',
231                 'filearea' => 'library_sources',
232                 'itemid' => 0,
233                 'contextid' => (\context_system::instance())->id,
234                 'filepath' => '/',
235                 'filename' => $library['machineName'],
236             ],
237             $this->get_api_endpoint($library['machineName']),
238             null,
239             true
240         );
242         if (!$file) {
243             return null;
244         }
246         helper::save_h5p($factory, $file, (object) [], false, true);
248         $file->delete();
250         $librarykey = static::libraryToString($library);
251         $libraryid = $factory->get_storage()->h5pC->librariesJsonData[$librarykey]["libraryId"];
253         return $libraryid;
254     }
256     /**
257      * Get H5P endpoints.
258      *
259      * If $endpoint = 'content' and $library is null, moodle_url is the endpoint of the latest version of the H5P content
260      * types; however, if $library is the machine name of a content type, moodle_url is the endpoint to download the content type.
261      * The SITES endpoint ($endpoint = 'site') may be use to get a site UUID or send site data.
262      *
263      * @param string|null $library The machineName of the library whose endpoint is requested.
264      * @param string $endpoint The endpoint required. Valid values: "site", "content".
265      * @return moodle_url The endpoint moodle_url object.
266      */
267     public function get_api_endpoint(?string $library = null, string $endpoint = 'content'): moodle_url {
268         if ($endpoint == 'site') {
269             $h5purl = \H5PHubEndpoints::createURL(\H5PHubEndpoints::SITES );
270         } else if ($endpoint == 'content') {
271             $h5purl = \H5PHubEndpoints::createURL(\H5PHubEndpoints::CONTENT_TYPES ) . $library;
272         }
274         return new moodle_url($h5purl);
275     }
277     /**
278      * Get the latest version of the H5P content types available in the official repository.
279      *
280      * @return stdClass An object with 2 properties:
281      *     - string error: error message when there is any problem, empty otherwise
282      *     - array contentTypes: an object for each H5P content type with its information
283      */
284     public function get_latest_content_types(): \stdClass {
285         $siteuuid = $this->get_site_uuid() ?? md5($CFG->wwwroot);
286         $postdata = ['uuid' => $siteuuid];
288         // Get the latest content-types json.
289         $endpoint = $this->get_api_endpoint();
290         $request = download_file_content($endpoint, null, $postdata, true);
292         if (!empty($request->error) || $request->status != '200' || empty($request->results)) {
293             if (empty($request->error)) {
294                 $request->error = get_string('fetchtypesfailure', 'core_h5p');
295             }
296             return $request;
297         }
299         $contenttypes = json_decode($request->results);
300         $contenttypes->error = '';
302         return $contenttypes;
303     }
305     /**
306      * Get the site UUID. If site UUID is not defined, try to register the site.
307      *
308      * return $string The site UUID, null if it is not set.
309      */
310     public function get_site_uuid(): ?string {
311         // Check if the site_uuid is already set.
312         $siteuuid = get_config('core_h5p', 'site_uuid');
314         if (!$siteuuid) {
315             $siteuuid = $this->register_site();
316         }
318         return $siteuuid;
319     }
321     /**
322      * Get H5P generated site UUID.
323      *
324      * return ?string Returns H5P generated site UUID, null if can't get it.
325      */
326     private function register_site(): ?string {
327         $endpoint = $this->get_api_endpoint(null, 'site');
328         $siteuuid = download_file_content($endpoint, null, '');
330         // Successful UUID retrieval from H5P.
331         if ($siteuuid) {
332             $json = json_decode($siteuuid);
333             if (isset($json->uuid)) {
334                 set_config('site_uuid', $json->uuid, 'core_h5p');
335                 return $json->uuid;
336             }
337         }
339         return null;
340     }
342     /**
343      * Checks that the required H5P core API version or higher is installed.
344      *
345      * @param stdClass $coreapi Object with properties major and minor for the core API version required.
346      * @return bool True if the required H5P core API version is installed. False if not.
347      */
348     public function is_required_core_api($coreapi): bool {
349         if (isset($coreapi) && !empty($coreapi)) {
350             if (($coreapi->major > H5PCore::$coreApi['majorVersion']) ||
351                 (($coreapi->major == H5PCore::$coreApi['majorVersion']) && ($coreapi->minor > H5PCore::$coreApi['minorVersion']))) {
352                 return false;
353             }
354         }
355         return true;
356     }