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