See Release Notes
Long Term Support Release
Differences Between: [Versions 310 and 401] [Versions 311 and 401] [Versions 39 and 401]
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 * Manager for media files 19 * 20 * @package core_media 21 * @copyright 2016 Marina Glancy 22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 */ 24 25 defined('MOODLE_INTERNAL') || die(); 26 27 /** 28 * Manager for media files. 29 * 30 * Used in file resources, media filter, and any other places that need to 31 * output embedded media. 32 * 33 * Usage: 34 * $manager = core_media_manager::instance(); 35 * 36 * 37 * @package core_media 38 * @copyright 2016 Marina Glancy 39 * @author 2011 The Open University 40 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 41 */ 42 final class core_media_manager { 43 /** 44 * Option: Disable text link fallback. 45 * 46 * Use this option if you are going to print a visible link anyway so it is 47 * pointless to have one as fallback. 48 * 49 * To enable, set value to true. 50 */ 51 const OPTION_NO_LINK = 'nolink'; 52 53 /** 54 * Option: When embedding, if there is no matching embed, do not use the 55 * default link fallback player; instead return blank. 56 * 57 * This is different from OPTION_NO_LINK because this option still uses the 58 * fallback link if there is some kind of embedding. Use this option if you 59 * are going to check if the return value is blank and handle it specially. 60 * 61 * To enable, set value to true. 62 */ 63 const OPTION_FALLBACK_TO_BLANK = 'embedorblank'; 64 65 /** 66 * Option: Enable players which are only suitable for use when we trust the 67 * user who embedded the content. 68 * 69 * In the past, this option enabled the SWF player (which was removed). 70 * However, this setting will remain because it might be used by third-party plugins. 71 * 72 * To enable, set value to true. 73 */ 74 const OPTION_TRUSTED = 'trusted'; 75 76 /** 77 * Option: Put a div around the output (if not blank) so that it displays 78 * as a block using the 'resourcecontent' CSS class. 79 * 80 * To enable, set value to true. 81 */ 82 const OPTION_BLOCK = 'block'; 83 84 /** 85 * Option: When the request for media players came from a text filter this option will contain the 86 * original HTML snippet, usually one of the tags: <a> or <video> or <audio> 87 * 88 * Players that support other HTML5 features such as tracks may find them in this option. 89 */ 90 const OPTION_ORIGINAL_TEXT = 'originaltext'; 91 92 /** @var array Array of available 'player' objects */ 93 private $players; 94 95 /** @var string Regex pattern for links which may contain embeddable content */ 96 private $embeddablemarkers; 97 98 /** @var core_media_manager caches a singleton instance */ 99 static private $instance; 100 101 /** @var moodle_page page this instance was initialised for */ 102 private $page; 103 104 /** 105 * Returns a singleton instance of a manager 106 * 107 * Note as of Moodle 3.3, this will call setup for you. 108 * 109 * @return core_media_manager 110 */ 111 public static function instance($page = null) { 112 // Use the passed $page if given, otherwise the $PAGE global. 113 if (!$page) { 114 global $PAGE; 115 $page = $PAGE; 116 } 117 if (self::$instance === null || ($page && self::$instance->page !== $page)) { 118 self::$instance = new self($page); 119 } 120 return self::$instance; 121 } 122 123 /** 124 * Construct a new core_media_manager instance 125 * 126 * @param moodle_page $page The page we are going to add requirements to. 127 * @see core_media_manager::instance() 128 */ 129 private function __construct($page) { 130 if ($page) { 131 $this->page = $page; 132 $players = $this->get_players(); 133 foreach ($players as $player) { 134 $player->setup($page); 135 } 136 } else { 137 debugging('Could not determine the $PAGE. Media plugins will not be set up', DEBUG_DEVELOPER); 138 } 139 } 140 141 /** 142 * @deprecated since Moodle 3.3. The setup is now done in ::instance() so there is no need to call this. 143 */ 144 public function setup() { 145 throw new coding_exception('core_media_manager::setup() can not be used any more because it is done in ::instance()'); 146 } 147 148 /** 149 * Resets cached singleton instance. To be used after $CFG->media_plugins_sortorder is modified 150 */ 151 public static function reset_caches() { 152 self::$instance = null; 153 } 154 155 /** 156 * Obtains the list of core_media_player objects currently in use to render 157 * items. 158 * 159 * The list is in rank order (highest first) and does not include players 160 * which are disabled. 161 * 162 * @return core_media_player[] Array of core_media_player objects in rank order 163 */ 164 private function get_players() { 165 // Save time by only building the list once. 166 if (!$this->players) { 167 $sortorder = \core\plugininfo\media::get_enabled_plugins(); 168 169 $this->players = []; 170 foreach ($sortorder as $name) { 171 $classname = "media_" . $name . "_plugin"; 172 if (class_exists($classname)) { 173 $this->players[] = new $classname(); 174 } 175 } 176 } 177 return $this->players; 178 } 179 180 /** 181 * Renders a media file (audio or video) using suitable embedded player. 182 * 183 * See embed_alternatives function for full description of parameters. 184 * This function calls through to that one. 185 * 186 * When using this function you can also specify width and height in the 187 * URL by including ?d=100x100 at the end. If specified in the URL, this 188 * will override the $width and $height parameters. 189 * 190 * @param moodle_url $url Full URL of media file 191 * @param string $name Optional user-readable name to display in download link 192 * @param int $width Width in pixels (optional) 193 * @param int $height Height in pixels (optional) 194 * @param array $options Array of key/value pairs 195 * @return string HTML content of embed 196 */ 197 public function embed_url(moodle_url $url, $name = '', $width = 0, $height = 0, 198 $options = array()) { 199 200 // Get width and height from URL if specified (overrides parameters in 201 // function call). 202 $rawurl = $url->out(false); 203 if (preg_match('/[?#]d=([\d]{1,4}%?)x([\d]{1,4}%?)/', $rawurl, $matches)) { 204 $width = $matches[1]; 205 $height = $matches[2]; 206 $url = new moodle_url(str_replace($matches[0], '', $rawurl)); 207 } 208 209 // Defer to array version of function. 210 return $this->embed_alternatives(array($url), $name, $width, $height, $options); 211 } 212 213 /** 214 * Renders media files (audio or video) using suitable embedded player. 215 * The list of URLs should be alternative versions of the same content in 216 * multiple formats. If there is only one format it should have a single 217 * entry. 218 * 219 * If the media files are not in a supported format, this will give students 220 * a download link to each format. The download link uses the filename 221 * unless you supply the optional name parameter. 222 * 223 * Width and height are optional. If specified, these are suggested sizes 224 * and should be the exact values supplied by the user, if they come from 225 * user input. These will be treated as relating to the size of the video 226 * content, not including any player control bar. 227 * 228 * For audio files, height will be ignored. For video files, a few formats 229 * work if you specify only width, but in general if you specify width 230 * you must specify height as well. 231 * 232 * The $options array is passed through to the core_media_player classes 233 * that render the object tag. The keys can contain values from 234 * core_media::OPTION_xx. 235 * 236 * @param array $alternatives Array of moodle_url to media files 237 * @param string $name Optional user-readable name to display in download link 238 * @param int $width Width in pixels (optional) 239 * @param int $height Height in pixels (optional) 240 * @param array $options Array of key/value pairs 241 * @return string HTML content of embed 242 */ 243 public function embed_alternatives($alternatives, $name = '', $width = 0, $height = 0, 244 $options = array()) { 245 246 // Get list of player plugins. 247 $players = $this->get_players(); 248 249 // Set up initial text which will be replaced by first player that 250 // supports any of the formats. 251 $out = core_media_player::PLACEHOLDER; 252 253 // Loop through all players that support any of these URLs. 254 foreach ($players as $player) { 255 $supported = $player->list_supported_urls($alternatives, $options); 256 if ($supported) { 257 // Embed. 258 $text = $player->embed($supported, $name, $width, $height, $options); 259 260 // Put this in place of the 'fallback' slot in the previous text. 261 $out = str_replace(core_media_player::PLACEHOLDER, $text, $out); 262 263 // Check if we need to continue looking for players. 264 if (strpos($out, core_media_player::PLACEHOLDER) === false) { 265 break; 266 } 267 } 268 } 269 270 if (!empty($options[self::OPTION_FALLBACK_TO_BLANK]) && $out === core_media_player::PLACEHOLDER) { 271 // In case of OPTION_FALLBACK_TO_BLANK and no player matched do not fallback to link, just return empty string. 272 return ''; 273 } 274 275 // Remove 'fallback' slot from final version and return it. 276 $fallback = $this->fallback_to_link($alternatives, $name, $options); 277 $out = str_replace(core_media_player::PLACEHOLDER, $fallback, $out); 278 $out = str_replace(core_media_player::LINKPLACEHOLDER, $fallback, $out); 279 if (!empty($options[self::OPTION_BLOCK]) && $out !== '') { 280 $out = html_writer::tag('div', $out, array('class' => 'resourcecontent')); 281 } 282 return $out; 283 } 284 285 /** 286 * Returns links to the specified URLs unless OPTION_NO_LINK is passed. 287 * 288 * @param array $urls URLs of media files 289 * @param string $name Display name; '' to use default 290 * @param array $options Options array 291 * @return string HTML code for embed 292 */ 293 private function fallback_to_link($urls, $name, $options) { 294 // If link is turned off, return empty. 295 if (!empty($options[self::OPTION_NO_LINK])) { 296 return ''; 297 } 298 299 // Build up link content. 300 $output = ''; 301 foreach ($urls as $url) { 302 if (strval($name) !== '' && $output === '') { 303 $title = $name; 304 } else { 305 $title = $this->get_filename($url); 306 } 307 $printlink = html_writer::link($url, $title, array('class' => 'mediafallbacklink')); 308 if ($output) { 309 // Where there are multiple available formats, there are fallback links 310 // for all formats, separated by /. 311 $output .= ' / '; 312 } 313 $output .= $printlink; 314 } 315 return $output; 316 } 317 318 /** 319 * Checks whether a file can be embedded. If this returns true you will get 320 * an embedded player; if this returns false, you will just get a download 321 * link. 322 * 323 * This is a wrapper for can_embed_urls. 324 * 325 * @param moodle_url $url URL of media file 326 * @param array $options Options (same as when embedding) 327 * @return bool True if file can be embedded 328 */ 329 public function can_embed_url(moodle_url $url, $options = array()) { 330 return $this->can_embed_urls(array($url), $options); 331 } 332 333 /** 334 * Checks whether a file can be embedded. If this returns true you will get 335 * an embedded player; if this returns false, you will just get a download 336 * link. 337 * 338 * @param array $urls URL of media file and any alternatives (moodle_url) 339 * @param array $options Options (same as when embedding) 340 * @return bool True if file can be embedded 341 */ 342 public function can_embed_urls(array $urls, $options = array()) { 343 // Check all players to see if any of them support it. 344 foreach ($this->get_players() as $player) { 345 // First player that supports it, return true. 346 if ($player->list_supported_urls($urls, $options)) { 347 return true; 348 } 349 } 350 return false; 351 } 352 353 /** 354 * Obtains a list of markers that can be used in a regular expression when 355 * searching for URLs that can be embedded by any player type. 356 * 357 * This string is used to improve peformance of regex matching by ensuring 358 * that the (presumably C) regex code can do a quick keyword check on the 359 * URL part of a link to see if it matches one of these, rather than having 360 * to go into PHP code for every single link to see if it can be embedded. 361 * 362 * @return string String suitable for use in regex such as '(\.mp4|\.flv)' 363 */ 364 public function get_embeddable_markers() { 365 if (empty($this->embeddablemarkers)) { 366 $markers = ''; 367 foreach ($this->get_players() as $player) { 368 foreach ($player->get_embeddable_markers() as $marker) { 369 if ($markers !== '') { 370 $markers .= '|'; 371 } 372 $markers .= preg_quote($marker); 373 } 374 } 375 $this->embeddablemarkers = $markers; 376 } 377 return $this->embeddablemarkers; 378 } 379 380 /** 381 * Given a string containing multiple URLs separated by #, this will split 382 * it into an array of moodle_url objects suitable for using when calling 383 * embed_alternatives. 384 * 385 * Note that the input string should NOT be html-escaped (i.e. if it comes 386 * from html, call html_entity_decode first). 387 * 388 * @param string $combinedurl String of 1 or more alternatives separated by # 389 * @param int $width Output variable: width (will be set to 0 if not specified) 390 * @param int $height Output variable: height (0 if not specified) 391 * @return array Array of 1 or more moodle_url objects 392 */ 393 public function split_alternatives($combinedurl, &$width, &$height) { 394 global $CFG; 395 $urls = explode('#', $combinedurl); 396 $width = 0; 397 $height = 0; 398 $returnurls = array(); 399 400 foreach ($urls as $url) { 401 $matches = null; 402 403 // You can specify the size as a separate part of the array like 404 // #d=640x480 without actually including a url in it. 405 if (preg_match('/^d=([\d]{1,4})x([\d]{1,4})$/i', $url, $matches)) { 406 $width = $matches[1]; 407 $height = $matches[2]; 408 continue; 409 } 410 411 // Can also include the ?d= as part of one of the URLs (if you use 412 // more than one they will be ignored except the last). 413 if (preg_match('/\?d=([\d]{1,4})x([\d]{1,4})$/i', $url, $matches)) { 414 $width = $matches[1]; 415 $height = $matches[2]; 416 417 // Trim from URL. 418 $url = str_replace($matches[0], '', $url); 419 } 420 421 // Clean up url. 422 $url = fix_utf8($url); 423 include_once($CFG->dirroot . '/lib/validateurlsyntax.php'); 424 if (!validateUrlSyntax($url, 's?H?S?F?R?E?u-P-a?I?p?f?q?r?')) { 425 continue; 426 } 427 428 // Turn it into moodle_url object. 429 $returnurls[] = new moodle_url($url); 430 } 431 432 return $returnurls; 433 } 434 435 /** 436 * Returns the file extension for a URL. 437 * @param moodle_url $url URL 438 */ 439 public function get_extension(moodle_url $url) { 440 // Note: Does not use core_text (. is UTF8-safe). 441 $filename = self::get_filename($url); 442 $dot = strrpos($filename, '.'); 443 if ($dot === false) { 444 return ''; 445 } else { 446 return strtolower(substr($filename, $dot + 1)); 447 } 448 } 449 450 /** 451 * Obtains the filename from the moodle_url. 452 * @param moodle_url $url URL 453 * @return string Filename only (not escaped) 454 */ 455 public function get_filename(moodle_url $url) { 456 // Use the 'file' parameter if provided (for links created when 457 // slasharguments was off). If not present, just use URL path. 458 $path = $url->get_param('file'); 459 if (!$path) { 460 $path = $url->get_path(); 461 } 462 463 // Remove everything before last / if present. Does not use textlib as / is UTF8-safe. 464 $slash = strrpos($path, '/'); 465 if ($slash !== false) { 466 $path = substr($path, $slash + 1); 467 } 468 return $path; 469 } 470 471 /** 472 * Guesses MIME type for a moodle_url based on file extension. 473 * @param moodle_url $url URL 474 * @return string MIME type 475 */ 476 public function get_mimetype(moodle_url $url) { 477 return mimeinfo('type', $this->get_filename($url)); 478 } 479 480 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body