MDL-67192 core_h5p: get site UUID from H5P using the API
[moodle.git] / h5p / classes / core.php
CommitLineData
9e67f5e3
AN
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/**
ab02687e 18 * H5P core class.
9e67f5e3
AN
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 */
24
25namespace core_h5p;
26
5315acb4
VDF
27defined('MOODLE_INTERNAL') || die();
28
29require_once("$CFG->libdir/filelib.php");
d8fe1edd 30
9e67f5e3 31use H5PCore;
abf6d1c3 32use H5PFrameworkInterface;
9e67f5e3
AN
33use stdClass;
34use moodle_url;
35
9e67f5e3 36/**
ab02687e 37 * H5P core class, containing functions and storage shared by the other H5P classes.
9e67f5e3
AN
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 */
43class core extends \H5PCore {
44
ab02687e 45 /** @var array The array containing all the present libraries */
9e67f5e3
AN
46 protected $libraries;
47
abf6d1c3
MG
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) {
59
60 parent::__construct($framework, $path, $url, $language, $export);
61
62 // Aggregate the assets by default.
63 $this->aggregateAssets = true;
64 }
65
ab02687e
MG
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 */
9e67f5e3
AN
72 protected function getDependencyPath(array $dependency): string {
73 $library = $this->find_library($dependency);
74
75 return "libraries/{$library->id}/{$library->machinename}-{$library->majorversion}.{$library->minorversion}";
76 }
77
ab02687e
MG
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 */
9e67f5e3
AN
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 }
99
100 return $roots;
101 }
102
ab02687e
MG
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 {
9e67f5e3
AN
110 global $DB;
111 if (null === $this->libraries) {
112 $this->libraries = $DB->get_records('h5p_libraries');
113 }
114
115 $major = $dependency['majorVersion'];
116 $minor = $dependency['minorVersion'];
117 $patch = $dependency['patchVersion'];
118
119 foreach ($this->libraries as $library) {
120 if ($library->machinename !== $dependency['machineName']) {
121 continue;
122 }
123
124 if ($library->majorversion != $major) {
125 continue;
126 }
127 if ($library->minorversion != $minor) {
128 continue;
129 }
130 if ($library->patchversion != $patch) {
131 continue;
132 }
133
134 return $library;
135 }
136
137 return null;
138 }
139
ab02687e
MG
140 /**
141 * Get core JavaScript files.
142 *
143 * @return array The array containg urls of the core JavaScript files
144 */
9e67f5e3
AN
145 public static function get_scripts(): array {
146 global $CFG;
147 $cachebuster = '?ver='.$CFG->jsrev;
148 $liburl = $CFG->wwwroot . '/lib/h5p/';
149 $urls = [];
150
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");
155
156 return $urls;
157 }
5315acb4
VDF
158
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 {
166
a23969a5 167 $contenttypes = $this->get_latest_content_types();
5315acb4
VDF
168 if (!empty($contenttypes->error)) {
169 return $contenttypes;
170 }
171
172 $typesinstalled = [];
173
d8fe1edd
VDF
174 $factory = new factory();
175 $framework = $factory->get_framework();
176
5315acb4 177 foreach ($contenttypes->contentTypes as $type) {
b7c307f3
VDF
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 }
182
5315acb4
VDF
183 $library = [
184 'machineName' => $type->id,
185 'majorVersion' => $type->version->major,
186 'minorVersion' => $type->version->minor,
a23969a5 187 'patchVersion' => $type->version->patch,
5315acb4
VDF
188 ];
189
5315acb4
VDF
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 }
196
197 if ($shoulddownload) {
198 $installed['id'] = $this->fetch_content_type($library);
199 if ($installed['id']) {
200 $installed['name'] = $librarykey = \H5PCore::libraryToString($library);
201 $typesinstalled[] = $installed;
202 }
203 }
204 }
205
206 $result = new stdClass();
207 $result->error = '';
208 $result->typesinstalled = $typesinstalled;
209
210 return $result;
211 }
212
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 {
d8fe1edd 220 $factory = new factory();
5315acb4
VDF
221
222 // Get a temp path to download the content type.
223 $temppath = make_request_directory();
224 $tempfile = "{$temppath}/" . $library['machineName'] . ".h5p";
225
226 // Download the latest content type from the H5P official repository.
fd9333ca
AN
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']),
5315acb4 238 null,
fd9333ca 239 true
5315acb4
VDF
240 );
241
fd9333ca 242 if (!$file) {
5315acb4
VDF
243 return null;
244 }
245
fd9333ca 246 helper::save_h5p($factory, $file, (object) [], false, true);
5315acb4 247
fd9333ca 248 $file->delete();
5315acb4 249
fd9333ca 250 $librarykey = static::libraryToString($library);
d8fe1edd
VDF
251 $libraryid = $factory->get_storage()->h5pC->librariesJsonData[$librarykey]["libraryId"];
252
253 return $libraryid;
5315acb4
VDF
254 }
255
256 /**
257 * Get H5P endpoints.
258 *
a23969a5
VDF
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.
5315acb4
VDF
262 *
263 * @param string|null $library The machineName of the library whose endpoint is requested.
a23969a5 264 * @param string $endpoint The endpoint required. Valid values: "site", "content".
5315acb4
VDF
265 * @return moodle_url The endpoint moodle_url object.
266 */
a23969a5
VDF
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 }
273
5315acb4
VDF
274 return new moodle_url($h5purl);
275 }
276
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 {
a23969a5
VDF
285 $siteuuid = $this->get_site_uuid() ?? md5($CFG->wwwroot);
286 $postdata = ['uuid' => $siteuuid];
287
5315acb4 288 // Get the latest content-types json.
a23969a5 289 $endpoint = $this->get_api_endpoint();
5315acb4
VDF
290 $request = download_file_content($endpoint, null, $postdata, true);
291
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 }
298
299 $contenttypes = json_decode($request->results);
300 $contenttypes->error = '';
301
302 return $contenttypes;
303 }
b7c307f3 304
a23969a5
VDF
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');
313
314 if (!$siteuuid) {
315 $siteuuid = $this->register_site();
316 }
317
318 return $siteuuid;
319 }
320
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, '');
329
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 }
338
339 return null;
340 }
341
b7c307f3
VDF
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 }
9e67f5e3 357}