~ubuntu-branches/ubuntu/trusty/moodle/trusty-proposed

« back to all changes in this revision

Viewing changes to lib/medialib.php

  • Committer: Package Import Robot
  • Author(s): Thijs Kinkhorst
  • Date: 2013-07-19 08:52:46 UTC
  • mfrom: (1.1.10)
  • Revision ID: package-import@ubuntu.com-20130719085246-yebwditc2exoap2r
Tags: 2.5.1-1
* New upstream version: 2.5.1.
  - Fixes security issues:
    CVE-2013-2242 CVE-2013-2243 CVE-2013-2244 CVE-2013-2245
    CVE-2013-2246
* Depend on apache2 instead of obsolete apache2-mpm-prefork.
* Use packaged libphp-phpmailer (closes: #429339), adodb,
  HTMLPurifier, PclZip.
* Update debconf translations, thanks Salvatore Merone, Pietro Tollot,
  Joe Hansen, Yuri Kozlov, Holger Wansing, Américo Monteiro,
  Adriano Rafael Gomes, victory, Michał Kułach.
  (closes: #716972, #716986, #717080, #717108, #717278)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
 * Classes for handling embedded media (mainly audio and video).
 
19
 *
 
20
 * These are used only from within the core media renderer.
 
21
 *
 
22
 * To embed media from Moodle code, do something like the following:
 
23
 *
 
24
 * $mediarenderer = $PAGE->get_renderer('core', 'media');
 
25
 * echo $mediarenderer->embed_url(new moodle_url('http://example.org/a.mp3'));
 
26
 *
 
27
 * You do not need to require this library file manually. Getting the renderer
 
28
 * (the first line above) requires this library file automatically.
 
29
 *
 
30
 * @package core_media
 
31
 * @copyright 2012 The Open University
 
32
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 
33
 */
 
34
 
 
35
defined('MOODLE_INTERNAL') || die();
 
36
 
 
37
if (!defined('CORE_MEDIA_VIDEO_WIDTH')) {
 
38
    // Default video width if no width is specified; some players may do something
 
39
    // more intelligent such as use real video width.
 
40
    // May be defined in config.php if required.
 
41
    define('CORE_MEDIA_VIDEO_WIDTH', 400);
 
42
}
 
43
if (!defined('CORE_MEDIA_VIDEO_HEIGHT')) {
 
44
    // Default video height. May be defined in config.php if required.
 
45
    define('CORE_MEDIA_VIDEO_HEIGHT', 300);
 
46
}
 
47
if (!defined('CORE_MEDIA_AUDIO_WIDTH')) {
 
48
    // Default audio width if no width is specified.
 
49
    // May be defined in config.php if required.
 
50
    define('CORE_MEDIA_AUDIO_WIDTH', 300);
 
51
}
 
52
 
 
53
 
 
54
/**
 
55
 * Constants and static utility functions for use with core_media_renderer.
 
56
 *
 
57
 * @copyright 2011 The Open University
 
58
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 
59
 */
 
60
abstract class core_media {
 
61
    /**
 
62
     * Option: Disable text link fallback.
 
63
     *
 
64
     * Use this option if you are going to print a visible link anyway so it is
 
65
     * pointless to have one as fallback.
 
66
     *
 
67
     * To enable, set value to true.
 
68
     */
 
69
    const OPTION_NO_LINK = 'nolink';
 
70
 
 
71
    /**
 
72
     * Option: When embedding, if there is no matching embed, do not use the
 
73
     * default link fallback player; instead return blank.
 
74
     *
 
75
     * This is different from OPTION_NO_LINK because this option still uses the
 
76
     * fallback link if there is some kind of embedding. Use this option if you
 
77
     * are going to check if the return value is blank and handle it specially.
 
78
     *
 
79
     * To enable, set value to true.
 
80
     */
 
81
    const OPTION_FALLBACK_TO_BLANK = 'embedorblank';
 
82
 
 
83
    /**
 
84
     * Option: Enable players which are only suitable for use when we trust the
 
85
     * user who embedded the content.
 
86
     *
 
87
     * At present, this option enables the SWF player.
 
88
     *
 
89
     * To enable, set value to true.
 
90
     */
 
91
    const OPTION_TRUSTED = 'trusted';
 
92
 
 
93
    /**
 
94
     * Option: Put a div around the output (if not blank) so that it displays
 
95
     * as a block using the 'resourcecontent' CSS class.
 
96
     *
 
97
     * To enable, set value to true.
 
98
     */
 
99
    const OPTION_BLOCK = 'block';
 
100
 
 
101
    /**
 
102
     * Given a string containing multiple URLs separated by #, this will split
 
103
     * it into an array of moodle_url objects suitable for using when calling
 
104
     * embed_alternatives.
 
105
     *
 
106
     * Note that the input string should NOT be html-escaped (i.e. if it comes
 
107
     * from html, call html_entity_decode first).
 
108
     *
 
109
     * @param string $combinedurl String of 1 or more alternatives separated by #
 
110
     * @param int $width Output variable: width (will be set to 0 if not specified)
 
111
     * @param int $height Output variable: height (0 if not specified)
 
112
     * @return array Array of 1 or more moodle_url objects
 
113
     */
 
114
    public static function split_alternatives($combinedurl, &$width, &$height) {
 
115
        $urls = explode('#', $combinedurl);
 
116
        $width = 0;
 
117
        $height = 0;
 
118
        $returnurls = array();
 
119
 
 
120
        foreach ($urls as $url) {
 
121
            $matches = null;
 
122
 
 
123
            // You can specify the size as a separate part of the array like
 
124
            // #d=640x480 without actually including a url in it.
 
125
            if (preg_match('/^d=([\d]{1,4})x([\d]{1,4})$/i', $url, $matches)) {
 
126
                $width  = $matches[1];
 
127
                $height = $matches[2];
 
128
                continue;
 
129
            }
 
130
 
 
131
            // Can also include the ?d= as part of one of the URLs (if you use
 
132
            // more than one they will be ignored except the last).
 
133
            if (preg_match('/\?d=([\d]{1,4})x([\d]{1,4})$/i', $url, $matches)) {
 
134
                $width  = $matches[1];
 
135
                $height = $matches[2];
 
136
 
 
137
                // Trim from URL.
 
138
                $url = str_replace($matches[0], '', $url);
 
139
            }
 
140
 
 
141
            // Clean up url.
 
142
            $url = clean_param($url, PARAM_URL);
 
143
            if (empty($url)) {
 
144
                continue;
 
145
            }
 
146
 
 
147
            // Turn it into moodle_url object.
 
148
            $returnurls[] = new moodle_url($url);
 
149
        }
 
150
 
 
151
        return $returnurls;
 
152
    }
 
153
 
 
154
    /**
 
155
     * Returns the file extension for a URL.
 
156
     * @param moodle_url $url URL
 
157
     */
 
158
    public static function get_extension(moodle_url $url) {
 
159
        // Note: Does not use textlib (. is UTF8-safe).
 
160
        $filename = self::get_filename($url);
 
161
        $dot = strrpos($filename, '.');
 
162
        if ($dot === false) {
 
163
            return '';
 
164
        } else {
 
165
            return strtolower(substr($filename, $dot + 1));
 
166
        }
 
167
    }
 
168
 
 
169
    /**
 
170
     * Obtains the filename from the moodle_url.
 
171
     * @param moodle_url $url URL
 
172
     * @return string Filename only (not escaped)
 
173
     */
 
174
    public static function get_filename(moodle_url $url) {
 
175
        global $CFG;
 
176
 
 
177
        // Use the 'file' parameter if provided (for links created when
 
178
        // slasharguments was off). If not present, just use URL path.
 
179
        $path = $url->get_param('file');
 
180
        if (!$path) {
 
181
            $path = $url->get_path();
 
182
        }
 
183
 
 
184
        // Remove everything before last / if present. Does not use textlib as / is UTF8-safe.
 
185
        $slash = strrpos($path, '/');
 
186
        if ($slash !== false) {
 
187
            $path = substr($path, $slash + 1);
 
188
        }
 
189
        return $path;
 
190
    }
 
191
 
 
192
    /**
 
193
     * Guesses MIME type for a moodle_url based on file extension.
 
194
     * @param moodle_url $url URL
 
195
     * @return string MIME type
 
196
     */
 
197
    public static function get_mimetype(moodle_url $url) {
 
198
        return mimeinfo('type', self::get_filename($url));
 
199
    }
 
200
}
 
201
 
 
202
 
 
203
/**
 
204
 * Base class for media players.
 
205
 *
 
206
 * Media players return embed HTML for a particular way of playing back audio
 
207
 * or video (or another file type).
 
208
 *
 
209
 * In order to make the code more lightweight, this is not a plugin type
 
210
 * (players cannot have their own settings, database tables, capabilities, etc).
 
211
 * These classes are used only by core_media_renderer in outputrenderers.php.
 
212
 * If you add a new class here (in core code) you must modify the
 
213
 * get_players_raw function in that file to include it.
 
214
 *
 
215
 * If a Moodle installation wishes to add extra player objects they can do so
 
216
 * by overriding that renderer in theme, and overriding the get_players_raw
 
217
 * function. The new player class should then of course be defined within the
 
218
 * custom theme or other suitable location, not in this file.
 
219
 *
 
220
 * @copyright 2011 The Open University
 
221
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 
222
 */
 
223
abstract class core_media_player {
 
224
    /**
 
225
     * Placeholder text used to indicate where the fallback content is placed
 
226
     * within a result.
 
227
     */
 
228
    const PLACEHOLDER = '<!--FALLBACK-->';
 
229
 
 
230
    /**
 
231
     * Generates code required to embed the player.
 
232
     *
 
233
     * The returned code contains a placeholder comment '<!--FALLBACK-->'
 
234
     * (constant core_media_player::PLACEHOLDER) which indicates the location
 
235
     * where fallback content should be placed in the event that this type of
 
236
     * player is not supported by user browser.
 
237
     *
 
238
     * The $urls parameter includes one or more alternative media formats that
 
239
     * are supported by this player. It does not include formats that aren't
 
240
     * supported (see list_supported_urls).
 
241
     *
 
242
     * The $options array contains key-value pairs. See OPTION_xx constants
 
243
     * for documentation of standard option(s).
 
244
     *
 
245
     * @param array $urls URLs of media files
 
246
     * @param string $name Display name; '' to use default
 
247
     * @param int $width Optional width; 0 to use default
 
248
     * @param int $height Optional height; 0 to use default
 
249
     * @param array $options Options array
 
250
     * @return string HTML code for embed
 
251
     */
 
252
    public abstract function embed($urls, $name, $width, $height, $options);
 
253
 
 
254
    /**
 
255
     * Gets the list of file extensions supported by this media player.
 
256
     *
 
257
     * Note: This is only required for the default implementation of
 
258
     * list_supported_urls. If you override that function to determine
 
259
     * supported URLs in some way other than by extension, then this function
 
260
     * is not necessary.
 
261
     *
 
262
     * @return array Array of strings (extension not including dot e.g. 'mp3')
 
263
     */
 
264
    public function get_supported_extensions() {
 
265
        return array();
 
266
    }
 
267
 
 
268
    /**
 
269
     * Lists keywords that must be included in a url that can be embedded with
 
270
     * this player. Any such keywords should be added to the array.
 
271
     *
 
272
     * For example if this player supports FLV and F4V files then it should add
 
273
     * '.flv' and '.f4v' to the array. (The check is not case-sensitive.)
 
274
     *
 
275
     * Default handling calls the get_supported_extensions function and adds
 
276
     * a dot to each of those values, so players only need to override this
 
277
     * if they don't implement get_supported_extensions.
 
278
     *
 
279
     * This is used to improve performance when matching links in the media filter.
 
280
     *
 
281
     * @return array Array of keywords to add to the embeddable markers list
 
282
     */
 
283
    public function get_embeddable_markers() {
 
284
        $markers = array();
 
285
        foreach ($this->get_supported_extensions() as $extension) {
 
286
            $markers[] = '.' . $extension;
 
287
        }
 
288
        return $markers;
 
289
    }
 
290
 
 
291
    /**
 
292
     * Gets the ranking of this player. This is an integer used to decide which
 
293
     * player to use (after applying other considerations such as which ones
 
294
     * the user has disabled).
 
295
     *
 
296
     * Rank must be unique (no two players should have the same rank).
 
297
     *
 
298
     * Rank zero has a special meaning, indicating that this 'player' does not
 
299
     * really embed the video.
 
300
     *
 
301
     * Rank is not a user-configurable value because it needs to be defined
 
302
     * carefully in order to ensure that the embedding fallbacks actually work.
 
303
     * It might be possible to have some user options which affect rank, but
 
304
     * these would be best defined as e.g. checkboxes in settings that have
 
305
     * a particular effect on the rank of a couple of plugins, rather than
 
306
     * letting users generally alter rank.
 
307
     *
 
308
     * Note: Within medialib.php, players are listed in rank order (highest
 
309
     * rank first).
 
310
     *
 
311
     * @return int Rank (higher is better)
 
312
     */
 
313
    public abstract function get_rank();
 
314
 
 
315
    /**
 
316
     * @return bool True if player is enabled
 
317
     */
 
318
    public function is_enabled() {
 
319
        global $CFG;
 
320
 
 
321
        // With the class core_media_player_html5video it is enabled
 
322
        // based on $CFG->core_media_enable_html5video.
 
323
        $setting = str_replace('_player_', '_enable_', get_class($this));
 
324
        return !empty($CFG->{$setting});
 
325
    }
 
326
 
 
327
    /**
 
328
     * Given a list of URLs, returns a reduced array containing only those URLs
 
329
     * which are supported by this player. (Empty if none.)
 
330
     * @param array $urls Array of moodle_url
 
331
     * @param array $options Options (same as will be passed to embed)
 
332
     * @return array Array of supported moodle_url
 
333
     */
 
334
    public function list_supported_urls(array $urls, array $options = array()) {
 
335
        $extensions = $this->get_supported_extensions();
 
336
        $result = array();
 
337
        foreach ($urls as $url) {
 
338
            if (in_array(core_media::get_extension($url), $extensions)) {
 
339
                $result[] = $url;
 
340
            }
 
341
        }
 
342
        return $result;
 
343
    }
 
344
 
 
345
    /**
 
346
     * Obtains suitable name for media. Uses specified name if there is one,
 
347
     * otherwise makes one up.
 
348
     * @param string $name User-specified name ('' if none)
 
349
     * @param array $urls Array of moodle_url used to make up name
 
350
     * @return string Name
 
351
     */
 
352
    protected function get_name($name, $urls) {
 
353
        // If there is a specified name, use that.
 
354
        if ($name) {
 
355
            return $name;
 
356
        }
 
357
 
 
358
        // Get filename of first URL.
 
359
        $url = reset($urls);
 
360
        $name = core_media::get_filename($url);
 
361
 
 
362
        // If there is more than one url, strip the extension as we could be
 
363
        // referring to a different one or several at once.
 
364
        if (count($urls) > 1) {
 
365
            $name = preg_replace('~\.[^.]*$~', '', $name);
 
366
        }
 
367
 
 
368
        return $name;
 
369
    }
 
370
 
 
371
    /**
 
372
     * Compares by rank order, highest first. Used for sort functions.
 
373
     * @param core_media_player $a Player A
 
374
     * @param core_media_player $b Player B
 
375
     * @return int Negative if A should go before B, positive for vice versa
 
376
     */
 
377
    public static function compare_by_rank(core_media_player $a, core_media_player $b) {
 
378
        return $b->get_rank() - $a->get_rank();
 
379
    }
 
380
 
 
381
    /**
 
382
     * Utility function that sets width and height to defaults if not specified
 
383
     * as a parameter to the function (will be specified either if, (a) the calling
 
384
     * code passed it, or (b) the URL included it).
 
385
     * @param int $width Width passed to function (updated with final value)
 
386
     * @param int $height Height passed to function (updated with final value)
 
387
     */
 
388
    protected static function pick_video_size(&$width, &$height) {
 
389
        if (!$width) {
 
390
            $width = CORE_MEDIA_VIDEO_WIDTH;
 
391
            $height = CORE_MEDIA_VIDEO_HEIGHT;
 
392
        }
 
393
    }
 
394
}
 
395
 
 
396
 
 
397
/**
 
398
 * Base class for players which handle external links (YouTube etc).
 
399
 *
 
400
 * As opposed to media files.
 
401
 *
 
402
 * @copyright 2011 The Open University
 
403
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 
404
 */
 
405
abstract class core_media_player_external extends core_media_player {
 
406
    /**
 
407
     * Array of matches from regular expression - subclass can assume these
 
408
     * will be valid when the embed function is called, to save it rerunning
 
409
     * the regex.
 
410
     * @var array
 
411
     */
 
412
    protected $matches;
 
413
 
 
414
    /**
 
415
     * Part of a regular expression, including ending ~ symbol (note: these
 
416
     * regexes use ~ instead of / because URLs and HTML code typically include
 
417
     * / symbol and makes harder to read if you have to escape it).
 
418
     * Matches the end part of a link after you have read the 'important' data
 
419
     * including optional #d=400x300 at end of url, plus content of <a> tag,
 
420
     * up to </a>.
 
421
     * @var string
 
422
     */
 
423
    const END_LINK_REGEX_PART = '[^#]*(#d=([\d]{1,4})x([\d]{1,4}))?~si';
 
424
 
 
425
    public function embed($urls, $name, $width, $height, $options) {
 
426
        return $this->embed_external(reset($urls), $name, $width, $height, $options);
 
427
    }
 
428
 
 
429
    /**
 
430
     * Obtains HTML code to embed the link.
 
431
     * @param moodle_url $url Single URL to embed
 
432
     * @param string $name Display name; '' to use default
 
433
     * @param int $width Optional width; 0 to use default
 
434
     * @param int $height Optional height; 0 to use default
 
435
     * @param array $options Options array
 
436
     * @return string HTML code for embed
 
437
     */
 
438
    protected abstract function embed_external(moodle_url $url, $name, $width, $height, $options);
 
439
 
 
440
    public function list_supported_urls(array $urls, array $options = array()) {
 
441
        // These only work with a SINGLE url (there is no fallback).
 
442
        if (count($urls) != 1) {
 
443
            return array();
 
444
        }
 
445
        $url = reset($urls);
 
446
 
 
447
        // Check against regex.
 
448
        if (preg_match($this->get_regex(), $url->out(false), $this->matches)) {
 
449
            return array($url);
 
450
        }
 
451
 
 
452
        return array();
 
453
    }
 
454
 
 
455
    /**
 
456
     * Returns regular expression used to match URLs that this player handles
 
457
     * @return string PHP regular expression e.g. '~^https?://example.org/~'
 
458
     */
 
459
    protected function get_regex() {
 
460
        return '~^unsupported~';
 
461
    }
 
462
 
 
463
    /**
 
464
     * Annoyingly, preg_match $matches result does not always have the same
 
465
     * number of parameters - it leaves out optional ones at the end. WHAT.
 
466
     * Anyway, this function can be used to fix it.
 
467
     * @param array $matches Array that should be adjusted
 
468
     * @param int $count Number of capturing groups (=6 to make $matches[6] work)
 
469
     */
 
470
    protected static function fix_match_count(&$matches, $count) {
 
471
        for ($i = count($matches); $i <= $count; $i++) {
 
472
            $matches[$i] = false;
 
473
        }
 
474
    }
 
475
}
 
476
 
 
477
 
 
478
/**
 
479
 * Player that embeds Vimeo links.
 
480
 *
 
481
 * @copyright 2011 The Open University
 
482
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 
483
 */
 
484
class core_media_player_vimeo extends core_media_player_external {
 
485
    protected function embed_external(moodle_url $url, $name, $width, $height, $options) {
 
486
        $videoid = $this->matches[1];
 
487
        $info = s($name);
 
488
 
 
489
        // Note: resizing via url is not supported, user can click the fullscreen
 
490
        // button instead. iframe embedding is not xhtml strict but it is the only
 
491
        // option that seems to work on most devices.
 
492
        self::pick_video_size($width, $height);
 
493
 
 
494
        $output = <<<OET
 
495
<span class="mediaplugin mediaplugin_vimeo">
 
496
<iframe title="$info" src="https://player.vimeo.com/video/$videoid"
 
497
  width="$width" height="$height" frameborder="0"></iframe>
 
498
</span>
 
499
OET;
 
500
 
 
501
        return $output;
 
502
    }
 
503
 
 
504
    protected function get_regex() {
 
505
        // Initial part of link.
 
506
        $start = '~^https?://vimeo\.com/';
 
507
        // Middle bit: either watch?v= or v/.
 
508
        $middle = '([0-9]+)';
 
509
        return $start . $middle . core_media_player_external::END_LINK_REGEX_PART;
 
510
    }
 
511
 
 
512
    public function get_rank() {
 
513
        return 1010;
 
514
    }
 
515
 
 
516
    public function get_embeddable_markers() {
 
517
        return array('vimeo.com/');
 
518
    }
 
519
}
 
520
 
 
521
/**
 
522
 * Player that creates YouTube embedding.
 
523
 *
 
524
 * @copyright 2011 The Open University
 
525
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 
526
 */
 
527
class core_media_player_youtube extends core_media_player_external {
 
528
    protected function embed_external(moodle_url $url, $name, $width, $height, $options) {
 
529
        $videoid = end($this->matches);
 
530
 
 
531
        $info = trim($name);
 
532
        if (empty($info) or strpos($info, 'http') === 0) {
 
533
            $info = get_string('siteyoutube', 'core_media');
 
534
        }
 
535
        $info = s($info);
 
536
 
 
537
        self::pick_video_size($width, $height);
 
538
 
 
539
        return <<<OET
 
540
<span class="mediaplugin mediaplugin_youtube">
 
541
<iframe title="$info" width="$width" height="$height"
 
542
  src="https://www.youtube.com/embed/$videoid?rel=0&wmode=transparent" frameborder="0" allowfullscreen="1"></iframe>
 
543
</span>
 
544
OET;
 
545
 
 
546
    }
 
547
 
 
548
    protected function get_regex() {
 
549
        // Regex for standard youtube link
 
550
         $link = '(youtube(-nocookie)?\.com/(?:watch\?v=|v/))';
 
551
        // Regex for shortened youtube link
 
552
        $shortlink = '((youtu|y2u)\.be/)';
 
553
 
 
554
        // Initial part of link.
 
555
         $start = '~^https?://(www\.)?(' . $link . '|' . $shortlink . ')';
 
556
        // Middle bit: Video key value
 
557
        $middle = '([a-z0-9\-_]+)';
 
558
        return $start . $middle . core_media_player_external::END_LINK_REGEX_PART;
 
559
    }
 
560
 
 
561
    public function get_rank() {
 
562
        // I decided to make the link-embedding ones (that don't handle file
 
563
        // formats) have ranking in the 1000 range.
 
564
        return 1001;
 
565
    }
 
566
 
 
567
    public function get_embeddable_markers() {
 
568
        return array('youtube.com', 'youtube-nocookie.com', 'youtu.be', 'y2u.be');
 
569
    }
 
570
}
 
571
 
 
572
 
 
573
/**
 
574
 * Player that creates YouTube playlist embedding.
 
575
 *
 
576
 * @copyright 2011 The Open University
 
577
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 
578
 */
 
579
class core_media_player_youtube_playlist extends core_media_player_external {
 
580
    public function is_enabled() {
 
581
        global $CFG;
 
582
        // Use the youtube on/off flag.
 
583
        return $CFG->core_media_enable_youtube;
 
584
    }
 
585
 
 
586
    protected function embed_external(moodle_url $url, $name, $width, $height, $options) {
 
587
        $site = $this->matches[1];
 
588
        $playlist = $this->matches[3];
 
589
 
 
590
        $info = trim($name);
 
591
        if (empty($info) or strpos($info, 'http') === 0) {
 
592
            $info = get_string('siteyoutube', 'core_media');
 
593
        }
 
594
        $info = s($info);
 
595
 
 
596
        self::pick_video_size($width, $height);
 
597
 
 
598
        return <<<OET
 
599
<span class="mediaplugin mediaplugin_youtube">
 
600
<iframe width="$width" height="$height" src="https://$site/embed/videoseries?list=$playlist" frameborder="0" allowfullscreen="1"></iframe>
 
601
</span>
 
602
OET;
 
603
    }
 
604
 
 
605
    protected function get_regex() {
 
606
        // Initial part of link.
 
607
        $start = '~^https?://(www\.youtube(-nocookie)?\.com)/';
 
608
        // Middle bit: either view_play_list?p= or p/ (doesn't work on youtube) or playlist?list=.
 
609
        $middle = '(?:view_play_list\?p=|p/|playlist\?list=)([a-z0-9\-_]+)';
 
610
        return $start . $middle . core_media_player_external::END_LINK_REGEX_PART;
 
611
    }
 
612
 
 
613
    public function get_rank() {
 
614
        // I decided to make the link-embedding ones (that don't handle file
 
615
        // formats) have ranking in the 1000 range.
 
616
        return 1000;
 
617
    }
 
618
 
 
619
    public function get_embeddable_markers() {
 
620
        return array('youtube');
 
621
    }
 
622
}
 
623
 
 
624
 
 
625
/**
 
626
 * MP3 player inserted using JavaScript.
 
627
 *
 
628
 * @copyright 2011 The Open University
 
629
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 
630
 */
 
631
class core_media_player_mp3 extends core_media_player {
 
632
    public function embed($urls, $name, $width, $height, $options) {
 
633
        // Use first url (there can actually be only one unless some idiot
 
634
        // enters two mp3 files as alternatives).
 
635
        $url = reset($urls);
 
636
 
 
637
        // Unique id even across different http requests made at the same time
 
638
        // (for AJAX, iframes).
 
639
        $id = 'core_media_mp3_' . md5(time() . '_' . rand());
 
640
 
 
641
        // When Flash or JavaScript are not available only the fallback is displayed,
 
642
        // using span not div because players are inline elements.
 
643
        $spanparams = array('id' => $id, 'class' => 'mediaplugin mediaplugin_mp3');
 
644
        if ($width) {
 
645
            $spanparams['style'] = 'width: ' . $width . 'px';
 
646
        }
 
647
        $output = html_writer::tag('span', core_media_player::PLACEHOLDER, $spanparams);
 
648
        // We can not use standard JS init because this may be cached
 
649
        // note: use 'small' size unless embedding in block mode.
 
650
        $output .= html_writer::script(js_writer::function_call(
 
651
                'M.util.add_audio_player', array($id, $url->out(false),
 
652
                empty($options[core_media::OPTION_BLOCK]))));
 
653
 
 
654
        return $output;
 
655
    }
 
656
 
 
657
    public function get_supported_extensions() {
 
658
        return array('mp3');
 
659
    }
 
660
 
 
661
    public function get_rank() {
 
662
        return 80;
 
663
    }
 
664
}
 
665
 
 
666
 
 
667
/**
 
668
 * Flash video player inserted using JavaScript.
 
669
 *
 
670
 * @copyright 2011 The Open University
 
671
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 
672
 */
 
673
class core_media_player_flv extends core_media_player {
 
674
    public function embed($urls, $name, $width, $height, $options) {
 
675
        // Use first url (there can actually be only one unless some idiot
 
676
        // enters two mp3 files as alternatives).
 
677
        $url = reset($urls);
 
678
 
 
679
        // Unique id even across different http requests made at the same time
 
680
        // (for AJAX, iframes).
 
681
        $id = 'core_media_flv_' . md5(time() . '_' . rand());
 
682
 
 
683
        // Compute width and height.
 
684
        $autosize = false;
 
685
        if (!$width && !$height) {
 
686
            $width = CORE_MEDIA_VIDEO_WIDTH;
 
687
            $height = CORE_MEDIA_VIDEO_HEIGHT;
 
688
            $autosize = true;
 
689
        }
 
690
 
 
691
        // Fallback span (will normally contain link).
 
692
        $output = html_writer::tag('span', core_media_player::PLACEHOLDER,
 
693
                array('id'=>$id, 'class'=>'mediaplugin mediaplugin_flv'));
 
694
        // We can not use standard JS init because this may be cached.
 
695
        $output .= html_writer::script(js_writer::function_call(
 
696
                'M.util.add_video_player', array($id, addslashes_js($url->out(false)),
 
697
                $width, $height, $autosize)));
 
698
        return $output;
 
699
    }
 
700
 
 
701
    public function get_supported_extensions() {
 
702
        return array('flv', 'f4v');
 
703
    }
 
704
 
 
705
    public function get_rank() {
 
706
        return 70;
 
707
    }
 
708
}
 
709
 
 
710
 
 
711
/**
 
712
 * Embeds Windows Media Player using object tag.
 
713
 *
 
714
 * @copyright 2011 The Open University
 
715
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 
716
 */
 
717
class core_media_player_wmp extends core_media_player {
 
718
    public function embed($urls, $name, $width, $height, $options) {
 
719
        // Get URL (we just use first, probably there is only one).
 
720
        $firsturl = reset($urls);
 
721
        $url = $firsturl->out(false);
 
722
 
 
723
        // Work out width.
 
724
        if (!$width || !$height) {
 
725
            // Object tag has default size.
 
726
            $mpsize = '';
 
727
            $size = 'width="' . CORE_MEDIA_VIDEO_WIDTH .
 
728
                    '" height="' . (CORE_MEDIA_VIDEO_HEIGHT+64) . '"';
 
729
            $autosize = 'true';
 
730
        } else {
 
731
            $size = 'width="' . $width . '" height="' . ($height + 15) . '"';
 
732
            $mpsize = 'width="' . $width . '" height="' . ($height + 64) . '"';
 
733
            $autosize = 'false';
 
734
        }
 
735
 
 
736
        // MIME type for object tag.
 
737
        $mimetype = core_media::get_mimetype($firsturl);
 
738
 
 
739
        $fallback = core_media_player::PLACEHOLDER;
 
740
 
 
741
        // Embed code.
 
742
        return <<<OET
 
743
<span class="mediaplugin mediaplugin_wmp">
 
744
    <object classid="CLSID:6BF52A52-394A-11d3-B153-00C04F79FAA6" $mpsize
 
745
            standby="Loading Microsoft(R) Windows(R) Media Player components..."
 
746
            type="application/x-oleobject">
 
747
        <param name="Filename" value="$url" />
 
748
        <param name="src" value="$url" />
 
749
        <param name="url" value="$url" />
 
750
        <param name="ShowControls" value="true" />
 
751
        <param name="AutoRewind" value="true" />
 
752
        <param name="AutoStart" value="false" />
 
753
        <param name="Autosize" value="$autosize" />
 
754
        <param name="EnableContextMenu" value="true" />
 
755
        <param name="TransparentAtStart" value="false" />
 
756
        <param name="AnimationAtStart" value="false" />
 
757
        <param name="ShowGotoBar" value="false" />
 
758
        <param name="EnableFullScreenControls" value="true" />
 
759
        <param name="uimode" value="full" />
 
760
        <!--[if !IE]>-->
 
761
        <object data="$url" type="$mimetype" $size>
 
762
            <param name="src" value="$url" />
 
763
            <param name="controller" value="true" />
 
764
            <param name="autoplay" value="false" />
 
765
            <param name="autostart" value="false" />
 
766
            <param name="resize" value="scale" />
 
767
        <!--<![endif]-->
 
768
            $fallback
 
769
        <!--[if !IE]>-->
 
770
        </object>
 
771
        <!--<![endif]-->
 
772
    </object>
 
773
</span>
 
774
OET;
 
775
    }
 
776
 
 
777
    public function get_supported_extensions() {
 
778
        return array('wmv', 'avi');
 
779
    }
 
780
 
 
781
    public function get_rank() {
 
782
        return 60;
 
783
    }
 
784
}
 
785
 
 
786
 
 
787
/**
 
788
 * Media player using object tag and QuickTime player.
 
789
 *
 
790
 * @copyright 2011 The Open University
 
791
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 
792
 */
 
793
class core_media_player_qt extends core_media_player {
 
794
    public function embed($urls, $name, $width, $height, $options) {
 
795
        // Show first URL.
 
796
        $firsturl = reset($urls);
 
797
        $url = $firsturl->out(true);
 
798
 
 
799
        // Work out size.
 
800
        if (!$width || !$height) {
 
801
            $size = 'width="' . CORE_MEDIA_VIDEO_WIDTH .
 
802
                    '" height="' . (CORE_MEDIA_VIDEO_HEIGHT + 15) . '"';
 
803
        } else {
 
804
            $size = 'width="' . $width . '" height="' . ($height + 15) . '"';
 
805
        }
 
806
 
 
807
        // MIME type for object tag.
 
808
        $mimetype = core_media::get_mimetype($firsturl);
 
809
 
 
810
        $fallback = core_media_player::PLACEHOLDER;
 
811
 
 
812
        // Embed code.
 
813
        return <<<OET
 
814
<span class="mediaplugin mediaplugin_qt">
 
815
    <object classid="clsid:02BF25D5-8C17-4B23-BC80-D3488ABDDC6B"
 
816
            codebase="http://www.apple.com/qtactivex/qtplugin.cab" $size>
 
817
        <param name="pluginspage" value="http://www.apple.com/quicktime/download/" />
 
818
        <param name="src" value="$url" />
 
819
        <param name="controller" value="true" />
 
820
        <param name="loop" value="true" />
 
821
        <param name="autoplay" value="false" />
 
822
        <param name="autostart" value="false" />
 
823
        <param name="scale" value="aspect" />
 
824
        <!--[if !IE]>-->
 
825
        <object data="$url" type="$mimetype" $size>
 
826
            <param name="src" value="$url" />
 
827
            <param name="pluginurl" value="http://www.apple.com/quicktime/download/" />
 
828
            <param name="controller" value="true" />
 
829
            <param name="loop" value="true" />
 
830
            <param name="autoplay" value="false" />
 
831
            <param name="autostart" value="false" />
 
832
            <param name="scale" value="aspect" />
 
833
        <!--<![endif]-->
 
834
            $fallback
 
835
        <!--[if !IE]>-->
 
836
        </object>
 
837
        <!--<![endif]-->
 
838
    </object>
 
839
</span>
 
840
OET;
 
841
    }
 
842
 
 
843
    public function get_supported_extensions() {
 
844
        return array('mpg', 'mpeg', 'mov', 'mp4', 'm4v', 'm4a');
 
845
    }
 
846
 
 
847
    public function get_rank() {
 
848
        return 50;
 
849
    }
 
850
}
 
851
 
 
852
 
 
853
/**
 
854
 * Media player using object tag and RealPlayer.
 
855
 *
 
856
 * Hopefully nobody is using this obsolete format any more!
 
857
 *
 
858
 * @copyright 2011 The Open University
 
859
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 
860
 */
 
861
class core_media_player_rm extends core_media_player {
 
862
    public function embed($urls, $name, $width, $height, $options) {
 
863
        // Show first URL.
 
864
        $firsturl = reset($urls);
 
865
        $url = $firsturl->out(true);
 
866
 
 
867
        // Get name to use as title.
 
868
        $info = s($this->get_name($name, $urls));
 
869
 
 
870
        // The previous version of this code has the following comment, which
 
871
        // I don't understand, but trust it is correct:
 
872
        // Note: the size is hardcoded intentionally because this does not work anyway!
 
873
        $width = CORE_MEDIA_VIDEO_WIDTH;
 
874
        $height = CORE_MEDIA_VIDEO_HEIGHT;
 
875
 
 
876
        $fallback = core_media_player::PLACEHOLDER;
 
877
        return <<<OET
 
878
<span class="mediaplugin mediaplugin_real">
 
879
    <object title="$info" classid="clsid:CFCDAA03-8BE4-11cf-B84B-0020AFBBCCFA"
 
880
            data="$url" width="$width" height="$height"">
 
881
        <param name="src" value="$url" />
 
882
        <param name="controls" value="All" />
 
883
        <!--[if !IE]>-->
 
884
        <object title="$info" type="audio/x-pn-realaudio-plugin"
 
885
                data="$url" width="$width" height="$height">
 
886
            <param name="src" value="$url" />
 
887
            <param name="controls" value="All" />
 
888
        <!--<![endif]-->
 
889
            $fallback
 
890
        <!--[if !IE]>-->
 
891
        </object>
 
892
        <!--<![endif]-->
 
893
  </object>
 
894
</span>
 
895
OET;
 
896
    }
 
897
 
 
898
    public function get_supported_extensions() {
 
899
        return array('ra', 'ram', 'rm', 'rv');
 
900
    }
 
901
 
 
902
    public function get_rank() {
 
903
        return 40;
 
904
    }
 
905
}
 
906
 
 
907
 
 
908
/**
 
909
 * Media player for Flash SWF files.
 
910
 *
 
911
 * This player contains additional security restriction: it will only be used
 
912
 * if you add option core_media_player_swf::ALLOW = true.
 
913
 *
 
914
 * Code should only set this option if it has verified that the data was
 
915
 * embedded by a trusted user (e.g. in trust text).
 
916
 *
 
917
 * @copyright 2011 The Open University
 
918
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 
919
 */
 
920
class core_media_player_swf extends core_media_player {
 
921
    public function embed($urls, $name, $width, $height, $options) {
 
922
        self::pick_video_size($width, $height);
 
923
 
 
924
        $firsturl = reset($urls);
 
925
        $url = $firsturl->out(true);
 
926
 
 
927
        $fallback = core_media_player::PLACEHOLDER;
 
928
        $output = <<<OET
 
929
<span class="mediaplugin mediaplugin_swf">
 
930
  <object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" width="$width" height="$height">
 
931
    <param name="movie" value="$url" />
 
932
    <param name="autoplay" value="true" />
 
933
    <param name="loop" value="true" />
 
934
    <param name="controller" value="true" />
 
935
    <param name="scale" value="aspect" />
 
936
    <param name="base" value="." />
 
937
    <param name="allowscriptaccess" value="never" />
 
938
<!--[if !IE]>-->
 
939
    <object type="application/x-shockwave-flash" data="$url" width="$width" height="$height">
 
940
      <param name="controller" value="true" />
 
941
      <param name="autoplay" value="true" />
 
942
      <param name="loop" value="true" />
 
943
      <param name="scale" value="aspect" />
 
944
      <param name="base" value="." />
 
945
      <param name="allowscriptaccess" value="never" />
 
946
<!--<![endif]-->
 
947
$fallback
 
948
<!--[if !IE]>-->
 
949
    </object>
 
950
<!--<![endif]-->
 
951
  </object>
 
952
</span>
 
953
OET;
 
954
 
 
955
        return $output;
 
956
    }
 
957
 
 
958
    public function get_supported_extensions() {
 
959
        return array('swf');
 
960
    }
 
961
 
 
962
    public function list_supported_urls(array $urls, array $options = array()) {
 
963
        // Not supported unless the creator is trusted.
 
964
        if (empty($options[core_media::OPTION_TRUSTED])) {
 
965
            return array();
 
966
        }
 
967
        return parent::list_supported_urls($urls, $options);
 
968
    }
 
969
 
 
970
    public function get_rank() {
 
971
        return 30;
 
972
    }
 
973
}
 
974
 
 
975
 
 
976
/**
 
977
 * Player that creates HTML5 <video> tag.
 
978
 *
 
979
 * @copyright 2011 The Open University
 
980
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 
981
 */
 
982
class core_media_player_html5video extends core_media_player {
 
983
    public function embed($urls, $name, $width, $height, $options) {
 
984
        // Special handling to make videos play on Android devices pre 2.3.
 
985
        // Note: I tested and 2.3.3 (in emulator) works without, is 533.1 webkit.
 
986
        $oldandroid = check_browser_version('WebKit Android') &&
 
987
                !check_browser_version('WebKit Android', '533.1');
 
988
 
 
989
        // Build array of source tags.
 
990
        $sources = array();
 
991
        foreach ($urls as $url) {
 
992
            $mimetype = core_media::get_mimetype($url);
 
993
            $source = html_writer::tag('source', '', array('src' => $url, 'type' => $mimetype));
 
994
            if ($mimetype === 'video/mp4') {
 
995
                if ($oldandroid) {
 
996
                    // Old Android fails if you specify the type param.
 
997
                    $source = html_writer::tag('source', '', array('src' => $url));
 
998
                }
 
999
 
 
1000
                // Better add m4v as first source, it might be a bit more
 
1001
                // compatible with problematic browsers.
 
1002
                array_unshift($sources, $source);
 
1003
            } else {
 
1004
                $sources[] = $source;
 
1005
            }
 
1006
        }
 
1007
 
 
1008
        $sources = implode("\n", $sources);
 
1009
        $title = s($this->get_name($name, $urls));
 
1010
 
 
1011
        if (!$width) {
 
1012
            // No width specified, use system default.
 
1013
            $width = CORE_MEDIA_VIDEO_WIDTH;
 
1014
        }
 
1015
 
 
1016
        if (!$height) {
 
1017
            // Let browser choose height automatically.
 
1018
            $size = "width=\"$width\"";
 
1019
        } else {
 
1020
            $size = "width=\"$width\" height=\"$height\"";
 
1021
        }
 
1022
 
 
1023
        $sillyscript = '';
 
1024
        $idtag = '';
 
1025
        if ($oldandroid) {
 
1026
            // Old Android does not support 'controls' option.
 
1027
            $id = 'core_media_html5v_' . md5(time() . '_' . rand());
 
1028
            $idtag = 'id="' . $id . '"';
 
1029
            $sillyscript = <<<OET
 
1030
<script type="text/javascript">
 
1031
document.getElementById('$id').addEventListener('click', function() {
 
1032
    this.play();
 
1033
}, false);
 
1034
</script>
 
1035
OET;
 
1036
        }
 
1037
 
 
1038
        $fallback = core_media_player::PLACEHOLDER;
 
1039
        return <<<OET
 
1040
<span class="mediaplugin mediaplugin_html5video">
 
1041
<video $idtag controls="true" $size preload="metadata" title="$title">
 
1042
    $sources
 
1043
    $fallback
 
1044
</video>
 
1045
$sillyscript
 
1046
</span>
 
1047
OET;
 
1048
    }
 
1049
 
 
1050
    public function get_supported_extensions() {
 
1051
        return array('m4v', 'webm', 'ogv', 'mp4');
 
1052
    }
 
1053
 
 
1054
    public function list_supported_urls(array $urls, array $options = array()) {
 
1055
        $extensions = $this->get_supported_extensions();
 
1056
        $result = array();
 
1057
        foreach ($urls as $url) {
 
1058
            $ext = core_media::get_extension($url);
 
1059
            if (in_array($ext, $extensions)) {
 
1060
                // Unfortunately html5 video does not handle fallback properly.
 
1061
                // https://www.w3.org/Bugs/Public/show_bug.cgi?id=10975
 
1062
                // That means we need to do browser detect and not use html5 on
 
1063
                // browsers which do not support the given type, otherwise users
 
1064
                // will not even see the fallback link.
 
1065
                // Based on http://en.wikipedia.org/wiki/HTML5_video#Table - this
 
1066
                // is a simplified version, does not take into account old browser
 
1067
                // versions or manual plugins.
 
1068
                if ($ext === 'ogv' || $ext === 'webm') {
 
1069
                    // Formats .ogv and .webm are not supported in IE or Safari.
 
1070
                    if (check_browser_version('MSIE') || check_browser_version('Safari')) {
 
1071
                        continue;
 
1072
                    }
 
1073
                } else {
 
1074
                    // Formats .m4v and .mp4 are not supported in Firefox or Opera.
 
1075
                    if (check_browser_version('Firefox') || check_browser_version('Opera')) {
 
1076
                        continue;
 
1077
                    }
 
1078
                }
 
1079
 
 
1080
                $result[] = $url;
 
1081
            }
 
1082
        }
 
1083
        return $result;
 
1084
    }
 
1085
 
 
1086
    public function get_rank() {
 
1087
        return 20;
 
1088
    }
 
1089
}
 
1090
 
 
1091
 
 
1092
/**
 
1093
 * Player that creates HTML5 <audio> tag.
 
1094
 *
 
1095
 * @copyright 2011 The Open University
 
1096
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 
1097
 */
 
1098
class core_media_player_html5audio extends core_media_player {
 
1099
    public function embed($urls, $name, $width, $height, $options) {
 
1100
 
 
1101
        // Build array of source tags.
 
1102
        $sources = array();
 
1103
        foreach ($urls as $url) {
 
1104
            $mimetype = core_media::get_mimetype($url);
 
1105
            $sources[] = html_writer::tag('source', '', array('src' => $url, 'type' => $mimetype));
 
1106
        }
 
1107
 
 
1108
        $sources = implode("\n", $sources);
 
1109
        $title = s($this->get_name($name, $urls));
 
1110
 
 
1111
        // Default to not specify size (so it can be changed in css).
 
1112
        $size = '';
 
1113
        if ($width) {
 
1114
            $size = 'width="' . $width . '"';
 
1115
        }
 
1116
 
 
1117
        $fallback = core_media_player::PLACEHOLDER;
 
1118
 
 
1119
        return <<<OET
 
1120
<audio controls="true" $size class="mediaplugin mediaplugin_html5audio" preload="no" title="$title">
 
1121
$sources
 
1122
$fallback
 
1123
</audio>
 
1124
OET;
 
1125
    }
 
1126
 
 
1127
    public function get_supported_extensions() {
 
1128
        return array('ogg', 'oga', 'aac', 'm4a', 'mp3');
 
1129
    }
 
1130
 
 
1131
    public function list_supported_urls(array $urls, array $options = array()) {
 
1132
        $extensions = $this->get_supported_extensions();
 
1133
        $result = array();
 
1134
        foreach ($urls as $url) {
 
1135
            $ext = core_media::get_extension($url);
 
1136
            if (in_array($ext, $extensions)) {
 
1137
                if ($ext === 'ogg' || $ext === 'oga') {
 
1138
                    // Formats .ogg and .oga are not supported in IE or Safari.
 
1139
                    if (check_browser_version('MSIE') || check_browser_version('Safari')) {
 
1140
                        continue;
 
1141
                    }
 
1142
                } else {
 
1143
                    // Formats .aac, .mp3, and .m4a are not supported in Firefox or Opera.
 
1144
                    if (check_browser_version('Firefox') || check_browser_version('Opera')) {
 
1145
                        continue;
 
1146
                    }
 
1147
                }
 
1148
                // Old Android versions (pre 2.3.3) 'support' audio tag but no codecs.
 
1149
                if (check_browser_version('WebKit Android') &&
 
1150
                        !check_browser_version('WebKit Android', '533.1')) {
 
1151
                    continue;
 
1152
                }
 
1153
 
 
1154
                $result[] = $url;
 
1155
            }
 
1156
        }
 
1157
        return $result;
 
1158
    }
 
1159
 
 
1160
    public function get_rank() {
 
1161
        return 10;
 
1162
    }
 
1163
}
 
1164
 
 
1165
 
 
1166
/**
 
1167
 * Special media player class that just puts a link.
 
1168
 *
 
1169
 * Always enabled, used as the last fallback.
 
1170
 *
 
1171
 * @copyright 2011 The Open University
 
1172
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 
1173
 */
 
1174
class core_media_player_link extends core_media_player {
 
1175
    public function embed($urls, $name, $width, $height, $options) {
 
1176
        // If link is turned off, return empty.
 
1177
        if (!empty($options[core_media::OPTION_NO_LINK])) {
 
1178
            return '';
 
1179
        }
 
1180
 
 
1181
        // Build up link content.
 
1182
        $output = '';
 
1183
        foreach ($urls as $url) {
 
1184
            $title = core_media::get_filename($url);
 
1185
            $printlink = html_writer::link($url, $title, array('class' => 'mediafallbacklink'));
 
1186
            if ($output) {
 
1187
                // Where there are multiple available formats, there are fallback links
 
1188
                // for all formats, separated by /.
 
1189
                $output .= ' / ';
 
1190
            }
 
1191
            $output .= $printlink;
 
1192
        }
 
1193
        return $output;
 
1194
    }
 
1195
 
 
1196
    public function list_supported_urls(array $urls, array $options = array()) {
 
1197
        // Supports all URLs.
 
1198
        return $urls;
 
1199
    }
 
1200
 
 
1201
    public function is_enabled() {
 
1202
        // Cannot be disabled.
 
1203
        return true;
 
1204
    }
 
1205
 
 
1206
    public function get_rank() {
 
1207
        return 0;
 
1208
    }
 
1209
}