MDL-67814 core_h5p: implemented H5P Core/Editor library interfaces
[moodle.git] / h5p / classes / core.php
blobe76d6b21e6a03abea5914004180bd5ebedd0f751
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.
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.
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
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;
35 use core_h5p\local\library\autoloader;
37 /**
38 * H5P core class, containing functions and storage shared by the other H5P classes.
40 * @package core_h5p
41 * @copyright 2019 Sara Arjona <sara@moodle.com>
42 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
44 class core extends \H5PCore {
46 /** @var array The array containing all the present libraries */
47 protected $libraries;
49 /**
50 * Constructor for core_h5p/core.
52 * @param H5PFrameworkInterface $framework The frameworks implementation of the H5PFrameworkInterface
53 * @param string|\H5PFileStorage $path The H5P file storage directory or class
54 * @param string $url The URL to the file storage directory
55 * @param string $language The language code. Defaults to english
56 * @param boolean $export Whether export is enabled
58 public function __construct(H5PFrameworkInterface $framework, $path, string $url, string $language = 'en',
59 bool $export = false) {
61 parent::__construct($framework, $path, $url, $language, $export);
63 // Aggregate the assets by default.
64 $this->aggregateAssets = true;
67 /**
68 * Get the path to the dependency.
70 * @param array $dependency An array containing the information of the requested dependency library
71 * @return string The path to the dependency library
73 protected function getDependencyPath(array $dependency): string {
74 $library = $this->find_library($dependency);
76 return "libraries/{$library->id}/{$library->machinename}-{$library->majorversion}.{$library->minorversion}";
79 /**
80 * Get the paths to the content dependencies.
82 * @param int $id The H5P content ID
83 * @return array An array containing the path of each content dependency
85 public function get_dependency_roots(int $id): array {
86 $roots = [];
87 $dependencies = $this->h5pF->loadContentDependencies($id);
88 $context = \context_system::instance();
89 foreach ($dependencies as $dependency) {
90 $library = $this->find_library($dependency);
91 $roots[self::libraryToString($dependency, true)] = (moodle_url::make_pluginfile_url(
92 $context->id,
93 'core_h5p',
94 'libraries',
95 $library->id,
96 "/" . self::libraryToString($dependency, true),
98 ))->out(false);
101 return $roots;
105 * Get a particular dependency library.
107 * @param array $dependency An array containing information of the dependency library
108 * @return stdClass|null The library object if the library dependency exists, null otherwise
110 protected function find_library(array $dependency): ?\stdClass {
111 global $DB;
112 if (null === $this->libraries) {
113 $this->libraries = $DB->get_records('h5p_libraries');
116 $major = $dependency['majorVersion'];
117 $minor = $dependency['minorVersion'];
118 $patch = $dependency['patchVersion'];
120 foreach ($this->libraries as $library) {
121 if ($library->machinename !== $dependency['machineName']) {
122 continue;
125 if ($library->majorversion != $major) {
126 continue;
128 if ($library->minorversion != $minor) {
129 continue;
131 if ($library->patchversion != $patch) {
132 continue;
135 return $library;
138 return null;
142 * Get the list of JS scripts to include on the page.
144 * @return array The array containg urls of the core JavaScript files
146 public static function get_scripts(): array {
147 global $PAGE;
149 $jsrev = $PAGE->requires->get_jsrev();
150 $urls = [];
151 foreach (self::$scripts as $script) {
152 $urls[] = autoloader::get_h5p_core_library_url($script, [
153 'ver' => $jsrev,
156 $urls[] = new moodle_url("/h5p/js/h5p_overrides.js", [
157 'ver' => $jsrev,
160 return $urls;
164 * Fetch and install the latest H5P content types libraries from the official H5P repository.
165 * If the latest version of a content type library is present in the system, nothing is done for that content type.
167 * @return stdClass
169 public function fetch_latest_content_types(): ?\stdClass {
171 $contenttypes = $this->get_latest_content_types();
172 if (!empty($contenttypes->error)) {
173 return $contenttypes;
176 $typesinstalled = [];
178 $factory = new factory();
179 $framework = $factory->get_framework();
181 foreach ($contenttypes->contentTypes as $type) {
182 // Don't fetch content types that require a higher H5P core API version.
183 if (!$this->is_required_core_api($type->coreApiVersionNeeded)) {
184 continue;
187 $library = [
188 'machineName' => $type->id,
189 'majorVersion' => $type->version->major,
190 'minorVersion' => $type->version->minor,
191 'patchVersion' => $type->version->patch,
194 $shoulddownload = true;
195 if ($framework->getLibraryId($type->id, $type->version->major, $type->version->minor)) {
196 if (!$framework->isPatchedLibrary($library)) {
197 $shoulddownload = false;
201 if ($shoulddownload) {
202 $installed['id'] = $this->fetch_content_type($library);
203 if ($installed['id']) {
204 $installed['name'] = \H5PCore::libraryToString($library);
205 $typesinstalled[] = $installed;
210 $result = new stdClass();
211 $result->error = '';
212 $result->typesinstalled = $typesinstalled;
214 return $result;
218 * Given an H5P content type machine name, fetch and install the required library from the official H5P repository.
220 * @param array $library Library machineName, majorversion and minorversion.
221 * @return int|null Returns the id of the content type library installed, null otherwise.
223 public function fetch_content_type(array $library): ?int {
224 $factory = new factory();
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'],
237 $this->get_api_endpoint($library['machineName']),
238 null,
239 true
242 if (!$file) {
243 return null;
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;
257 * Get H5P endpoints.
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.
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.
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;
274 return new moodle_url($h5purl);
278 * Get the latest version of the H5P content types available in the official repository.
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
284 public function get_latest_content_types(): \stdClass {
285 global $CFG;
287 $siteuuid = $this->get_site_uuid() ?? md5($CFG->wwwroot);
288 $postdata = ['uuid' => $siteuuid];
290 // Get the latest content-types json.
291 $endpoint = $this->get_api_endpoint();
292 $request = download_file_content($endpoint, null, $postdata, true);
294 if (!empty($request->error) || $request->status != '200' || empty($request->results)) {
295 if (empty($request->error)) {
296 $request->error = get_string('fetchtypesfailure', 'core_h5p');
298 return $request;
301 $contenttypes = json_decode($request->results);
302 $contenttypes->error = '';
304 return $contenttypes;
308 * Get the site UUID. If site UUID is not defined, try to register the site.
310 * return $string The site UUID, null if it is not set.
312 public function get_site_uuid(): ?string {
313 // Check if the site_uuid is already set.
314 $siteuuid = get_config('core_h5p', 'site_uuid');
316 if (!$siteuuid) {
317 $siteuuid = $this->register_site();
320 return $siteuuid;
324 * Get H5P generated site UUID.
326 * return ?string Returns H5P generated site UUID, null if can't get it.
328 private function register_site(): ?string {
329 $endpoint = $this->get_api_endpoint(null, 'site');
330 $siteuuid = download_file_content($endpoint, null, '');
332 // Successful UUID retrieval from H5P.
333 if ($siteuuid) {
334 $json = json_decode($siteuuid);
335 if (isset($json->uuid)) {
336 set_config('site_uuid', $json->uuid, 'core_h5p');
337 return $json->uuid;
341 return null;
345 * Checks that the required H5P core API version or higher is installed.
347 * @param stdClass $coreapi Object with properties major and minor for the core API version required.
348 * @return bool True if the required H5P core API version is installed. False if not.
350 public function is_required_core_api($coreapi): bool {
351 if (isset($coreapi) && !empty($coreapi)) {
352 if (($coreapi->major > H5PCore::$coreApi['majorVersion']) ||
353 (($coreapi->major == H5PCore::$coreApi['majorVersion']) && ($coreapi->minor > H5PCore::$coreApi['minorVersion']))) {
354 return false;
357 return true;
361 * Use sesskey instead of the H5P security token.
363 * @param string $action Not used.
364 * @return string sesskey
366 public static function createToken($action) {
367 return sesskey();
371 * Check if the token matches the sesskey.
373 * @param string $action Not used.
374 * @param string $token Token submitted.
375 * @return boolean valid token
377 public static function validToken($action, $token) {
378 return confirm_sesskey($token);