Differences Between: [Versions 310 and 311] [Versions 310 and 400] [Versions 310 and 401] [Versions 310 and 402] [Versions 310 and 403]
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 /** 18 * H5P player 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 */ 24 25 namespace core_h5p; 26 27 defined('MOODLE_INTERNAL') || die(); 28 29 use core_h5p\local\library\autoloader; 30 use core_xapi\local\statement\item_activity; 31 32 /** 33 * H5P player class, for displaying any local H5P content. 34 * 35 * @package core_h5p 36 * @copyright 2019 Sara Arjona <sara@moodle.com> 37 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 38 */ 39 class player { 40 41 /** 42 * @var string The local H5P URL containing the .h5p file to display. 43 */ 44 private $url; 45 46 /** 47 * @var core The H5PCore object. 48 */ 49 private $core; 50 51 /** 52 * @var int H5P DB id. 53 */ 54 private $h5pid; 55 56 /** 57 * @var array JavaScript requirements for this H5P. 58 */ 59 private $jsrequires = []; 60 61 /** 62 * @var array CSS requirements for this H5P. 63 */ 64 private $cssrequires = []; 65 66 /** 67 * @var array H5P content to display. 68 */ 69 private $content; 70 71 /** 72 * @var string optional component name to send xAPI statements. 73 */ 74 private $component; 75 76 /** 77 * @var string Type of embed object, div or iframe. 78 */ 79 private $embedtype; 80 81 /** 82 * @var context The context object where the .h5p belongs. 83 */ 84 private $context; 85 86 /** 87 * @var factory The \core_h5p\factory object. 88 */ 89 private $factory; 90 91 /** 92 * @var stdClass The error, exception and info messages, raised while preparing and running the player. 93 */ 94 private $messages; 95 96 /** 97 * @var bool Set to true in scripts that can not redirect (CLI, RSS feeds, etc.), throws exceptions. 98 */ 99 private $preventredirect; 100 101 /** 102 * Inits the H5P player for rendering the content. 103 * 104 * @param string $url Local URL of the H5P file to display. 105 * @param stdClass $config Configuration for H5P buttons. 106 * @param bool $preventredirect Set to true in scripts that can not redirect (CLI, RSS feeds, etc.), throws exceptions 107 * @param string $component optional moodle component to sent xAPI tracking 108 */ 109 public function __construct(string $url, \stdClass $config, bool $preventredirect = true, string $component = '') { 110 if (empty($url)) { 111 throw new \moodle_exception('h5pinvalidurl', 'core_h5p'); 112 } 113 $this->url = new \moodle_url($url); 114 $this->preventredirect = $preventredirect; 115 116 $this->factory = new \core_h5p\factory(); 117 118 $this->messages = new \stdClass(); 119 120 $this->component = $component; 121 122 // Create \core_h5p\core instance. 123 $this->core = $this->factory->get_core(); 124 125 // Get the H5P identifier linked to this URL. 126 list($file, $this->h5pid) = api::create_content_from_pluginfile_url( 127 $url, 128 $config, 129 $this->factory, 130 $this->messages, 131 $this->preventredirect 132 ); 133 if ($file) { 134 $this->context = \context::instance_by_id($file->get_contextid()); 135 if ($this->h5pid) { 136 // Load the content of the H5P content associated to this $url. 137 $this->content = $this->core->loadContent($this->h5pid); 138 139 // Get the embedtype to use for displaying the H5P content. 140 $this->embedtype = core::determineEmbedType($this->content['embedType'], $this->content['library']['embedTypes']); 141 } 142 } 143 } 144 145 /** 146 * Get the encoded URL for embeding this H5P content. 147 * 148 * @param string $url Local URL of the H5P file to display. 149 * @param stdClass $config Configuration for H5P buttons. 150 * @param bool $preventredirect Set to true in scripts that can not redirect (CLI, RSS feeds, etc.), throws exceptions 151 * @param string $component optional moodle component to sent xAPI tracking 152 * 153 * @return string The embedable code to display a H5P file. 154 */ 155 public static function display(string $url, \stdClass $config, bool $preventredirect = true, 156 string $component = ''): string { 157 global $OUTPUT; 158 $params = [ 159 'url' => $url, 160 'preventredirect' => $preventredirect, 161 'component' => $component, 162 ]; 163 164 $optparams = ['frame', 'export', 'embed', 'copyright']; 165 foreach ($optparams as $optparam) { 166 if (!empty($config->$optparam)) { 167 $params[$optparam] = $config->$optparam; 168 } 169 } 170 $fileurl = new \moodle_url('/h5p/embed.php', $params); 171 172 $template = new \stdClass(); 173 $template->embedurl = $fileurl->out(false); 174 175 $result = $OUTPUT->render_from_template('core_h5p/h5pembed', $template); 176 $result .= self::get_resize_code(); 177 return $result; 178 } 179 180 /** 181 * Get the error messages stored in our H5P framework. 182 * 183 * @return stdClass with framework error messages. 184 */ 185 public function get_messages(): \stdClass { 186 return helper::get_messages($this->messages, $this->factory); 187 } 188 189 /** 190 * Create the H5PIntegration variable that will be included in the page. This variable is used as the 191 * main H5P config variable. 192 */ 193 public function add_assets_to_page() { 194 global $PAGE; 195 196 $cid = $this->get_cid(); 197 $systemcontext = \context_system::instance(); 198 199 $disable = array_key_exists('disable', $this->content) ? $this->content['disable'] : core::DISABLE_NONE; 200 $displayoptions = $this->core->getDisplayOptionsForView($disable, $this->h5pid); 201 202 $contenturl = \moodle_url::make_pluginfile_url($systemcontext->id, \core_h5p\file_storage::COMPONENT, 203 \core_h5p\file_storage::CONTENT_FILEAREA, $this->h5pid, null, null); 204 $exporturl = $this->get_export_settings($displayoptions[ core::DISPLAY_OPTION_DOWNLOAD ]); 205 $xapiobject = item_activity::create_from_id($this->context->id); 206 $contentsettings = [ 207 'library' => core::libraryToString($this->content['library']), 208 'fullScreen' => $this->content['library']['fullscreen'], 209 'exportUrl' => ($exporturl instanceof \moodle_url) ? $exporturl->out(false) : '', 210 'embedCode' => $this->get_embed_code($this->url->out(), 211 $displayoptions[ core::DISPLAY_OPTION_EMBED ]), 212 'resizeCode' => self::get_resize_code(), 213 'title' => $this->content['slug'], 214 'displayOptions' => $displayoptions, 215 'url' => $xapiobject->get_data()->id, 216 'contentUrl' => $contenturl->out(), 217 'metadata' => $this->content['metadata'], 218 'contentUserData' => [0 => ['state' => '{}']] 219 ]; 220 // Get the core H5P assets, needed by the H5P classes to render the H5P content. 221 $settings = $this->get_assets(); 222 $settings['contents'][$cid] = array_merge($settings['contents'][$cid], $contentsettings); 223 224 // Print JavaScript settings to page. 225 $PAGE->requires->data_for_js('H5PIntegration', $settings, true); 226 } 227 228 /** 229 * Outputs H5P wrapper HTML. 230 * 231 * @return string The HTML code to display this H5P content. 232 */ 233 public function output(): string { 234 global $OUTPUT, $USER; 235 236 $template = new \stdClass(); 237 $template->h5pid = $this->h5pid; 238 if ($this->embedtype === 'div') { 239 $h5phtml = $OUTPUT->render_from_template('core_h5p/h5pdiv', $template); 240 } else { 241 $h5phtml = $OUTPUT->render_from_template('core_h5p/h5piframe', $template); 242 } 243 244 // Trigger capability_assigned event. 245 \core_h5p\event\h5p_viewed::create([ 246 'objectid' => $this->h5pid, 247 'userid' => $USER->id, 248 'context' => $this->get_context(), 249 'other' => [ 250 'url' => $this->url->out(), 251 'time' => time() 252 ] 253 ])->trigger(); 254 255 return $h5phtml; 256 } 257 258 /** 259 * Get the title of the H5P content to display. 260 * 261 * @return string the title 262 */ 263 public function get_title(): string { 264 return $this->content['title']; 265 } 266 267 /** 268 * Get the context where the .h5p file belongs. 269 * 270 * @return context The context. 271 */ 272 public function get_context(): \context { 273 return $this->context; 274 } 275 276 /** 277 * Delete an H5P package. 278 * 279 * @param stdClass $content The H5P package to delete. 280 */ 281 private function delete_h5p(\stdClass $content) { 282 $h5pstorage = $this->factory->get_storage(); 283 // Add an empty slug to the content if it's not defined, because the H5P library requires this field exists. 284 // It's not used when deleting a package, so the real slug value is not required at this point. 285 $content->slug = $content->slug ?? ''; 286 $h5pstorage->deletePackage( (array) $content); 287 } 288 289 /** 290 * Export path for settings 291 * 292 * @param bool $downloadenabled Whether the option to export the H5P content is enabled. 293 * 294 * @return \moodle_url|null The URL of the exported file. 295 */ 296 private function get_export_settings(bool $downloadenabled): ?\moodle_url { 297 298 if (!$downloadenabled) { 299 return null; 300 } 301 302 $systemcontext = \context_system::instance(); 303 $slug = $this->content['slug'] ? $this->content['slug'] . '-' : ''; 304 // We have to build the right URL. 305 // Depending the request was made through webservice/pluginfile.php or pluginfile.php. 306 if (strpos($this->url, '/webservice/pluginfile.php')) { 307 $url = \moodle_url::make_webservice_pluginfile_url( 308 $systemcontext->id, 309 \core_h5p\file_storage::COMPONENT, 310 \core_h5p\file_storage::EXPORT_FILEAREA, 311 '', 312 '', 313 "{$slug}{$this->content['id']}.h5p" 314 ); 315 } else { 316 // If the request is made by tokenpluginfile.php we need to indicates to generate a token for current user. 317 $includetoken = false; 318 if (strpos($this->url, '/tokenpluginfile.php')) { 319 $includetoken = true; 320 } 321 $url = \moodle_url::make_pluginfile_url( 322 $systemcontext->id, 323 \core_h5p\file_storage::COMPONENT, 324 \core_h5p\file_storage::EXPORT_FILEAREA, 325 '', 326 '', 327 "{$slug}{$this->content['id']}.h5p", 328 false, 329 $includetoken 330 ); 331 } 332 333 return $url; 334 } 335 336 /** 337 * Get the identifier for the H5P content, to be used in the arrays as index. 338 * 339 * @return string The identifier. 340 */ 341 private function get_cid(): string { 342 return 'cid-' . $this->h5pid; 343 } 344 345 /** 346 * Get the core H5P assets, including all core H5P JavaScript and CSS. 347 * 348 * @return Array core H5P assets. 349 */ 350 private function get_assets(): array { 351 // Get core assets. 352 $settings = helper::get_core_assets(); 353 // Added here because in the helper we don't have the h5p content id. 354 $settings['moodleLibraryPaths'] = $this->core->get_dependency_roots($this->h5pid); 355 // Add also the Moodle component where the results will be tracked. 356 $settings['moodleComponent'] = $this->component; 357 if (!empty($settings['moodleComponent'])) { 358 $settings['reportingIsEnabled'] = true; 359 } 360 361 $cid = $this->get_cid(); 362 // The filterParameters function should be called before getting the dependencyfiles because it rebuild content 363 // dependency cache and export file. 364 $settings['contents'][$cid]['jsonContent'] = $this->get_filtered_parameters(); 365 366 $files = $this->get_dependency_files(); 367 if ($this->embedtype === 'div') { 368 $systemcontext = \context_system::instance(); 369 $h5ppath = "/pluginfile.php/{$systemcontext->id}/core_h5p"; 370 371 // Schedule JavaScripts for loading through Moodle. 372 foreach ($files['scripts'] as $script) { 373 $url = $script->path . $script->version; 374 375 // Add URL prefix if not external. 376 $isexternal = strpos($script->path, '://'); 377 if ($isexternal === false) { 378 $url = $h5ppath . $url; 379 } 380 $settings['loadedJs'][] = $url; 381 $this->jsrequires[] = new \moodle_url($isexternal ? $url : $CFG->wwwroot . $url); 382 } 383 384 // Schedule stylesheets for loading through Moodle. 385 foreach ($files['styles'] as $style) { 386 $url = $style->path . $style->version; 387 388 // Add URL prefix if not external. 389 $isexternal = strpos($style->path, '://'); 390 if ($isexternal === false) { 391 $url = $h5ppath . $url; 392 } 393 $settings['loadedCss'][] = $url; 394 $this->cssrequires[] = new \moodle_url($isexternal ? $url : $CFG->wwwroot . $url); 395 } 396 397 } else { 398 // JavaScripts and stylesheets will be loaded through h5p.js. 399 $settings['contents'][$cid]['scripts'] = $this->core->getAssetsUrls($files['scripts']); 400 $settings['contents'][$cid]['styles'] = $this->core->getAssetsUrls($files['styles']); 401 } 402 return $settings; 403 } 404 405 /** 406 * Get filtered parameters, modifying them by the renderer if the theme implements the h5p_alter_filtered_parameters function. 407 * 408 * @return string Filtered parameters. 409 */ 410 private function get_filtered_parameters(): string { 411 global $PAGE; 412 413 $safeparams = $this->core->filterParameters($this->content); 414 $decodedparams = json_decode($safeparams); 415 $h5poutput = $PAGE->get_renderer('core_h5p'); 416 $h5poutput->h5p_alter_filtered_parameters( 417 $decodedparams, 418 $this->content['library']['name'], 419 $this->content['library']['majorVersion'], 420 $this->content['library']['minorVersion'] 421 ); 422 $safeparams = json_encode($decodedparams); 423 424 return $safeparams; 425 } 426 427 /** 428 * Finds library dependencies of view 429 * 430 * @return array Files that the view has dependencies to 431 */ 432 private function get_dependency_files(): array { 433 global $PAGE; 434 435 $preloadeddeps = $this->core->loadContentDependencies($this->h5pid, 'preloaded'); 436 $files = $this->core->getDependenciesFiles($preloadeddeps); 437 438 // Add additional asset files if required. 439 $h5poutput = $PAGE->get_renderer('core_h5p'); 440 $h5poutput->h5p_alter_scripts($files['scripts'], $preloadeddeps, $this->embedtype); 441 $h5poutput->h5p_alter_styles($files['styles'], $preloadeddeps, $this->embedtype); 442 443 return $files; 444 } 445 446 /** 447 * Resizing script for settings 448 * 449 * @return string The HTML code with the resize script. 450 */ 451 private static function get_resize_code(): string { 452 global $OUTPUT; 453 454 $template = new \stdClass(); 455 $template->resizeurl = autoloader::get_h5p_core_library_url('js/h5p-resizer.js'); 456 457 return $OUTPUT->render_from_template('core_h5p/h5presize', $template); 458 } 459 460 /** 461 * Embed code for settings 462 * 463 * @param string $url The URL of the .h5p file. 464 * @param bool $embedenabled Whether the option to embed the H5P content is enabled. 465 * 466 * @return string The HTML code to reuse this H5P content in a different place. 467 */ 468 private function get_embed_code(string $url, bool $embedenabled): string { 469 global $OUTPUT; 470 471 if ( ! $embedenabled) { 472 return ''; 473 } 474 475 $template = new \stdClass(); 476 $template->embedurl = self::get_embed_url($url, $this->component)->out(false); 477 478 return $OUTPUT->render_from_template('core_h5p/h5pembed', $template); 479 } 480 481 /** 482 * Get the encoded URL for embeding this H5P content. 483 * @param string $url The URL of the .h5p file. 484 * @param string $component optional Moodle component to send xAPI tracking 485 * 486 * @return \moodle_url The embed URL. 487 */ 488 public static function get_embed_url(string $url, string $component = ''): \moodle_url { 489 $params = ['url' => $url]; 490 if (!empty($component)) { 491 // If component is not empty, it will be passed too, in order to allow tracking too. 492 $params['component'] = $component; 493 } 494 495 return new \moodle_url('/h5p/embed.php', $params); 496 } 497 498 /** 499 * Return the info export file for Mobile App. 500 * 501 * @return array 502 */ 503 public function get_export_file(): array { 504 // Get the export url. 505 $exporturl = $this->get_export_settings(true); 506 // Get the filename of the export url. 507 $path = $exporturl->out_as_local_url(); 508 $parts = explode('/', $path); 509 $filename = array_pop($parts); 510 // Get the required info from the export file to be able to get the export file by third apps. 511 $file = helper::get_export_info($filename, $exporturl); 512 513 return $file; 514 } 515 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body