MDL-29624 Media embedding system, part 0: Add get_path() to moodle_url
[moodle.git] / filter / mediaplugin / filter.php
CommitLineData
4317f92f 1<?php
35716b86 2// This file is part of Moodle - http://moodle.org/
6de17fde 3//
35716b86
PS
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.
6de17fde 8//
35716b86
PS
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.
6de17fde 13//
35716b86
PS
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 * Media plugin filtering
19 *
20 * This filter will replace any links to a media file with
21 * a media plugin that plays that media inline
22 *
23 * @package filter
24 * @subpackage mediaplugin
25 * @copyright 2004 onwards Martin Dougiamas {@link http://moodle.com}
26 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
27 */
6de17fde 28
35716b86 29defined('MOODLE_INTERNAL') || die();
6de17fde 30
7e64d361 31require_once($CFG->libdir.'/filelib.php');
32
fcd2cbaf
PS
33if (!defined('FILTER_MEDIAPLUGIN_VIDEO_WIDTH')) {
34 /**
35 * Default media width, some plugins may use automatic sizes or accept resize parameters.
36 * This can be defined in config.php.
37 */
38 define('FILTER_MEDIAPLUGIN_VIDEO_WIDTH', 400);
39}
40
41if (!defined('FILTER_MEDIAPLUGIN_VIDEO_HEIGHT')) {
42 /**
43 * Default video height, plugins that know aspect ration
44 * should calculate it themselves using the FILTER_MEDIAPLUGIN_VIDEO_HEIGHT
45 * This can be defined in config.php.
46 */
47 define('FILTER_MEDIAPLUGIN_VIDEO_HEIGHT', 300);
48}
49
50
51//TODO: we should use /u modifier in regex, unfortunately it may not work properly on some misconfigured servers, see lib/filter/urltolink/filter.php ...
52
53//TODO: we should migrate to proper config_plugin settings ...
54
55
56/**
57 * Automatic media embedding filter class.
58 *
59 * It is highly recommended to configure servers to be compatible with our slasharguments,
60 * otherwise the "?d=600x400" may not work.
61 *
62 * @package filter
63 * @subpackage mediaplugin
64 * @copyright 2004 onwards Martin Dougiamas {@link http://moodle.com}
65 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
66 */
35716b86 67class filter_mediaplugin extends moodle_text_filter {
fcd2cbaf 68
dcfffe30 69 function filter($text, array $options = array()) {
fcd2cbaf
PS
70 global $CFG;
71
72 if (!is_string($text) or empty($text)) {
9e3f34d1 73 // non string data can not be filtered anyway
74 return $text;
75 }
e68789c5 76 if (stripos($text, '</a>') === false) {
ce8df92d 77 // performance shortcut - all regexes below end with the </a> tag,
e68789c5
PS
78 // if not present nothing can match
79 return $text;
80 }
9e3f34d1 81
fcd2cbaf
PS
82 $newtext = $text; // we need to return the original value if regex fails!
83
84 // YouTube and Vimeo are great because the files are not served by Moodle server
85
86 if (!empty($CFG->filter_mediaplugin_enable_youtube)) {
a3516c49 87 $search = '/<a\s[^>]*href="(https?:\/\/www\.youtube(-nocookie)?\.com)\/watch\?v=([a-z0-9\-_]+)[^"#]*(#d=([\d]{1,4})x([\d]{1,4}))?"[^>]*>([^>]*)<\/a>/is';
fcd2cbaf
PS
88 $newtext = preg_replace_callback($search, 'filter_mediaplugin_youtube_callback', $newtext);
89
a3516c49 90 $search = '/<a\s[^>]*href="(https?:\/\/www\.youtube(-nocookie)?\.com)\/v\/([a-z0-9\-_]+)[^"#]*(#d=([\d]{1,4})x([\d]{1,4}))?[^>]*>([^>]*)<\/a>/is';
fcd2cbaf
PS
91 $newtext = preg_replace_callback($search, 'filter_mediaplugin_youtube_callback', $newtext);
92
a3516c49 93 $search = '/<a\s[^>]*href="(https?:\/\/www\.youtube(-nocookie)?\.com)\/view_play_list\?p=([a-z0-9\-_]+)[^"#]*(#d=([\d]{1,4})x([\d]{1,4}))?[^>]*>([^>]*)<\/a>/is';
fcd2cbaf
PS
94 $newtext = preg_replace_callback($search, 'filter_mediaplugin_youtube_playlist_callback', $newtext);
95
a3516c49 96 $search = '/<a\s[^>]*href="(https?:\/\/www\.youtube(-nocookie)?\.com)\/p\/([a-z0-9\-_]+)[^"#]*(#d=([\d]{1,4})x([\d]{1,4}))?[^>]*>([^>]*)<\/a>/is';
fcd2cbaf
PS
97 $newtext = preg_replace_callback($search, 'filter_mediaplugin_youtube_playlist_callback', $newtext);
98 }
99
100 if (!empty($CFG->filter_mediaplugin_enable_vimeo)) {
a3516c49 101 $search = '/<a\s[^>]*href="http:\/\/vimeo\.com\/([0-9]+)[^"#]*(#d=([\d]{1,4})x([\d]{1,4}))?[^>]*>([^>]*)<\/a>/is';
fcd2cbaf
PS
102 $newtext = preg_replace_callback($search, 'filter_mediaplugin_vimeo_callback', $newtext);
103 }
104
105
106 // HTML 5 audio and video tags are the future! If only if vendors decided to use just one audio and video format...
107
108 if (!empty($CFG->filter_mediaplugin_enable_html5audio)) {
109 $search = '/<a\s[^>]*href="([^"#\?]+\.(ogg|oga|aac|m4a)([#\?][^"]*)?)"[^>]*>([^>]*)<\/a>/is';
110 $newtext = preg_replace_callback($search, 'filter_mediaplugin_html5audio_callback', $newtext);
9e3f34d1 111 }
112
fcd2cbaf
PS
113 if (!empty($CFG->filter_mediaplugin_enable_html5video)) {
114 $search = '/<a\s[^>]*href="([^"#\?]+\.(m4v|webm|ogv|mp4)([#\?][^"]*)?)"[^>]*>([^>]*)<\/a>/is';
115 $newtext = preg_replace_callback($search, 'filter_mediaplugin_html5video_callback', $newtext);
ce5dc36e
RW
116 }
117
fcd2cbaf
PS
118
119 // Flash stuff
120
121 if (!empty($CFG->filter_mediaplugin_enable_mp3)) {
122 $search = '/<a\s[^>]*href="([^"#\?]+\.mp3)"[^>]*>([^>]*)<\/a>/is';
123 $newtext = preg_replace_callback($search, 'filter_mediaplugin_mp3_callback', $newtext);
ce5dc36e 124 }
8e7fecd9 125
fcd2cbaf 126 if ((!empty($options['noclean']) or !empty($CFG->allowobjectembed)) and !empty($CFG->filter_mediaplugin_enable_swf)) {
a3516c49 127 $search = '/<a\s[^>]*href="([^"#\?]+\.swf)([#\?]d=([\d]{1,4})x([\d]{1,4}))?"[^>]*>([^>]*)<\/a>/is';
35716b86 128 $newtext = preg_replace_callback($search, 'filter_mediaplugin_swf_callback', $newtext);
9e3f34d1 129 }
130
8e7fecd9 131 if (!empty($CFG->filter_mediaplugin_enable_flv)) {
fcd2cbaf 132 $search = '/<a\s[^>]*href="([^"#\?]+\.(flv|f4v)([#\?][^"]*)?)"[^>]*>([^>]*)<\/a>/is';
35716b86 133 $newtext = preg_replace_callback($search, 'filter_mediaplugin_flv_callback', $newtext);
9e3f34d1 134 }
135
46936592 136
fcd2cbaf 137 // The rest of legacy formats - these should not be used if possible
a184479a 138
fcd2cbaf 139 if (!empty($CFG->filter_mediaplugin_enable_wmp)) {
a3516c49 140 $search = '/<a\s[^>]*href="([^"#\?]+\.(wmv|avi))(\?d=([\d]{1,4})x([\d]{1,4}))?"[^>]*>([^>]*)<\/a>/is';
35716b86 141 $newtext = preg_replace_callback($search, 'filter_mediaplugin_wmp_callback', $newtext);
9e3f34d1 142 }
143
fcd2cbaf
PS
144 if (!empty($CFG->filter_mediaplugin_enable_qt)) {
145 // HTML5 filtering may steal mpeg 4 formats
a3516c49 146 $search = '/<a\s[^>]*href="([^"#\?]+\.(mpg|mpeg|mov|mp4|m4v|m4a))(\?d=([\d]{1,4})x([\d]{1,4}))?"[^>]*>([^>]*)<\/a>/is';
35716b86 147 $newtext = preg_replace_callback($search, 'filter_mediaplugin_qt_callback', $newtext);
9e3f34d1 148 }
149
fcd2cbaf
PS
150 if (!empty($CFG->filter_mediaplugin_enable_rm)) {
151 // hopefully nobody is using this any more!!
152 // rpm is redhat packaging format these days, it is better to prevent these in default installs
9e3f34d1 153
fcd2cbaf 154 $search = '/<a\s[^>]*href="([^"#\?]+\.(ra|ram|rm|rv))"[^>]*>([^>]*)<\/a>/is';
35716b86 155 $newtext = preg_replace_callback($search, 'filter_mediaplugin_real_callback', $newtext);
9e3f34d1 156 }
157
9e3f34d1 158
fcd2cbaf
PS
159 if (empty($newtext) or $newtext === $text) {
160 // error or not filtered
161 unset($newtext);
162 return $text;
9e3f34d1 163 }
164
9e3f34d1 165
fcd2cbaf
PS
166 return $newtext;
167 }
168}
469aa3ba 169
9e3f34d1 170
fcd2cbaf
PS
171///===========================
172/// utility functions
3f5192c0 173
7f86bee3
PS
174/**
175 * Get mimetype of given url, useful for # alternative urls.
176 *
177 * @private
178 * @param string $url
179 * @return string $mimetype
180 */
181function filter_mediaplugin_get_mimetype($url) {
182 $matches = null;
183 if (preg_match("|^(.*)/[a-z]*file.php(\?file=)?(/[^&\?#]*)|", $url, $matches)) {
184 // remove the special moodle file serving hacks so that the *file.php is ignored
185 $url = $matches[1].$matches[3];
186 } else {
187 $url = preg_replace('/[#\?].*$/', '', $url);
188 }
189
190 $mimetype = mimeinfo('type', $url);
191
192 return $mimetype;
193}
3f5192c0 194
fcd2cbaf
PS
195/**
196 * Parse list of alternative URLs
197 * @param string $url urls separated with '#', size specified as ?d=640x480 or #d=640x480
7f86bee3
PS
198 * @param int $defaultwidth
199 * @param int $defaultheight
fcd2cbaf
PS
200 * @return array (urls, width, height)
201 */
202function filter_mediaplugin_parse_alternatives($url, $defaultwidth = 0, $defaultheight = 0) {
203 $urls = explode('#', $url);
204 $width = $defaultwidth;
205 $height = $defaultheight;
206 $returnurls = array();
207
208 foreach ($urls as $url) {
209 $matches = null;
210
a3516c49 211 if (preg_match('/^d=([\d]{1,4})x([\d]{1,4})$/i', $url, $matches)) { // #d=640x480
fcd2cbaf
PS
212 $width = $matches[1];
213 $height = $matches[2];
214 continue;
c159f28b 215 }
a3516c49 216 if (preg_match('/\?d=([\d]{1,4})x([\d]{1,4})$/i', $url, $matches)) { // old style file.ext?d=640x480
fcd2cbaf
PS
217 $width = $matches[1];
218 $height = $matches[2];
219 $url = str_replace($matches[0], '', $url);
9e3f34d1 220 }
221
fcd2cbaf
PS
222 $url = str_replace('&amp;', '&', $url);
223 $url = clean_param($url, PARAM_URL);
224 if (empty($url)) {
225 continue;
9e3f34d1 226 }
227
fcd2cbaf
PS
228 $returnurls[] = $url;
229 }
230
231 return array($returnurls, $width, $height);
232}
233
234/**
235 * Should the current tag be ignored in this filter?
236 * @param string $tag
237 * @return bool
238 */
239function filter_mediaplugin_ignore($tag) {
240 if (preg_match('/class="[^"]*nomediaplugin/i', $tag)) {
241 return true;
242 } else {
243 false;
d07851e1 244 }
6de17fde 245}
246
7e64d361 247///===========================
248/// callback filter functions
249
088ccb43 250
fcd2cbaf
PS
251/**
252 * Replace audio links with audio tag.
253 *
254 * @param array $link
255 * @return string
256 */
257function filter_mediaplugin_html5audio_callback(array $link) {
258 global $CFG;
7e64d361 259
fcd2cbaf
PS
260 if (filter_mediaplugin_ignore($link[0])) {
261 return $link[0];
262 }
263
264 $info = trim($link[4]);
265 if (empty($info) or strpos($info, 'http') === 0) {
266 $info = get_string('fallbackaudio', 'filter_mediaplugin');
267 }
268
269 list($urls, $ignorewidth, $ignoredheight) = filter_mediaplugin_parse_alternatives($link[1]);
7e64d361 270
fcd2cbaf
PS
271 $fallbackurl = null;
272 $fallbackmime = null;
273 $sources = array();
274 $fallbacklink = null;
7e64d361 275
fcd2cbaf 276 foreach ($urls as $url) {
7f86bee3 277 $mimetype = filter_mediaplugin_get_mimetype($url);
fcd2cbaf
PS
278 if (strpos($mimetype, 'audio/') !== 0) {
279 continue;
280 }
281 $sources[] = html_writer::tag('source', '', array('src' => $url, 'type' => $mimetype));
282
283 if ($fallbacklink === null) {
ce8df92d 284 $fallbacklink = html_writer::link($url.'#', $info); // the extra '#' prevents linking in mp3 filter below
fcd2cbaf
PS
285 }
286 if ($fallbackurl === null) {
287 if ($mimetype === 'audio/mp3' or $mimetype === 'audio/aac') {
288 $fallbackurl = str_replace('&', '&amp;', $url);
289 $fallbackmime = $mimetype;
290 }
291 }
292 }
293 if (!$sources) {
294 return $link[0];
3a42ad12 295 }
82afb587 296
fcd2cbaf
PS
297 if ($fallbackmime !== null) {
298 // fallback to quicktime
299 $fallback = <<<OET
300<object classid="clsid:02BF25D5-8C17-4B23-BC80-D3488ABDDC6B" codebase="http://www.apple.com/qtactivex/qtplugin.cab" width="200" height="20">
301 <param name="pluginspage" value="http://www.apple.com/quicktime/download/" />
302 <param name="src" value="$fallbackurl" />
303 <param name="controller" value="true" />
304 <param name="loop" value="false" />
305 <param name="autoplay" value="false" />
306 <param name="autostart" value="false" />
307 <param name="scale" value="aspect" />
308 $fallbacklink
309<!--[if !IE]>-->
310 <object data="$fallbackurl" type="$fallbackmime" width="200" height="20">
311 <param name="src" value="$fallbackurl" />
312 <param name="pluginurl" value="http://www.apple.com/quicktime/download/" />
313 <param name="controller" value="true" />
314 <param name="loop" value="false" />
315 <param name="autoplay" value="false" />
316 <param name="autostart" value="false" />
317 <param name="scale" value="aspect" />
318 $fallbacklink
319 </object>
320<!--<![endif]-->
321</object>
3a42ad12 322OET;
fcd2cbaf
PS
323 } else {
324 $fallback = $fallbacklink;
325 }
3a42ad12 326
fcd2cbaf
PS
327 $sources = implode("\n", $sources);
328 $title = s($info);
329
330 // audio players are supposed to be inline elements
331 $output = <<<OET
332<audio controls="true" width="200" class="mediaplugin mediaplugin_html5audio" preload="no" title="$title">
333$sources
334$fallback
335</audio>
336OET;
82afb587 337
338 return $output;
7e64d361 339}
340
fcd2cbaf
PS
341/**
342 * Replace ogg video links with video tag.
343 *
344 * Please note this is not going to work in all browsers,
345 * it is also not xhtml strict.
346 *
347 * @param array $link
348 * @return string
349 */
350function filter_mediaplugin_html5video_callback(array $link) {
ce5dc36e 351
fcd2cbaf
PS
352 if (filter_mediaplugin_ignore($link[0])) {
353 return $link[0];
354 }
355
356 $info = trim($link[4]);
357 if (empty($info) or strpos($info, 'http') === 0) {
358 $info = get_string('fallbackvideo', 'filter_mediaplugin');
359 }
360
361 list($urls, $width, $height) = filter_mediaplugin_parse_alternatives($link[1], FILTER_MEDIAPLUGIN_VIDEO_WIDTH, 0);
362
363 $fallbackurl = null;
364 $fallbackmime = null;
365 $sources = array();
366 $fallbacklink = null;
367
368 foreach ($urls as $url) {
7f86bee3 369 $mimetype = filter_mediaplugin_get_mimetype($url);
fcd2cbaf
PS
370 if (strpos($mimetype, 'video/') !== 0) {
371 continue;
372 }
373 $source = html_writer::tag('source', '', array('src' => $url, 'type' => $mimetype));
374 if ($mimetype === 'video/mp4') {
375 // better add m4v as first source, it might be a bit more compatible with problematic browsers
376 array_unshift($sources, $source);
377 } else {
378 $sources[] = $source;
379 }
380
381 if ($fallbacklink === null) {
ce8df92d 382 $fallbacklink = html_writer::link($url.'#', $info); // the extra '#' prevents linking in mp3 filter below
fcd2cbaf
PS
383 }
384 if ($fallbackurl === null) {
385 if ($mimetype === 'video/mp4') {
386 $fallbackurl = str_replace('&', '&amp;', $url);
387 $fallbackmime = $mimetype;
388 }
389 }
390 }
391 if (!$sources) {
392 return $link[0];
393 }
394
395 if ($fallbackmime !== null) {
396 $qtheight = ($height == 0) ? FILTER_MEDIAPLUGIN_VIDEO_HEIGHT : ($height + 15);
397 // fallback to quicktime
398 $fallback = <<<OET
399<object classid="clsid:02BF25D5-8C17-4B23-BC80-D3488ABDDC6B" codebase="http://www.apple.com/qtactivex/qtplugin.cab" width="$width" height="$qtheight">
400 <param name="pluginspage" value="http://www.apple.com/quicktime/download/" />
401 <param name="src" value="$fallbackurl" />
402 <param name="controller" value="true" />
403 <param name="loop" value="false" />
404 <param name="autoplay" value="false" />
405 <param name="autostart" value="false" />
406 <param name="scale" value="aspect" />
407 $fallbacklink
408<!--[if !IE]>-->
409 <object data="$fallbackurl" type="$fallbackmime" width="$width" height="$qtheight">
410 <param name="src" value="$fallbackurl" />
411 <param name="pluginurl" value="http://www.apple.com/quicktime/download/" />
412 <param name="controller" value="true" />
413 <param name="loop" value="false" />
414 <param name="autoplay" value="false" />
415 <param name="autostart" value="false" />
416 <param name="scale" value="aspect" />
417 $fallbacklink
418 </object>
419<!--<![endif]-->
420</object>
421OET;
422 } else {
423 $fallback = $fallbacklink;
424 }
425
426 $sources = implode("\n", $sources);
427 $title = s($info);
428
429 if (empty($height)) {
430 // automatic height
431 $size = "width=\"$width\"";
432 } else {
433 $size = "width=\"$width\" height=\"$height\"";
434 }
ce5dc36e 435
ce5dc36e 436 $output = <<<OET
fcd2cbaf
PS
437<span class="mediaplugin mediaplugin_html5video">
438<video controls="true" $size preload="metadata" title="$title">
439$sources
440$fallback
441</video>
442</span>
ce5dc36e 443OET;
8e7fecd9 444
ce5dc36e
RW
445 return $output;
446}
447
fcd2cbaf
PS
448/**
449 * Replace mp3 links with small audio player.
450 *
451 * @param $link
452 * @return string
453 */
454function filter_mediaplugin_mp3_callback($link) {
ce5dc36e 455 static $count = 0;
fcd2cbaf
PS
456
457 if (filter_mediaplugin_ignore($link[0])) {
458 return $link[0];
459 }
460
ce5dc36e 461 $count++;
fcd2cbaf 462 $id = 'filter_mp3_'.time().'_'.$count; //we need something unique because it might be stored in text cache
ce5dc36e 463
fcd2cbaf
PS
464 $url = $link[1];
465 $rawurl = str_replace('&amp;', '&', $url);
466
467 $info = trim($link[2]);
468 if (empty($info) or strpos($info, 'http') === 0) {
469 $info = get_string('mp3audio', 'filter_mediaplugin');
470
471 }
472 $printlink = html_writer::link($rawurl, $info, array('class'=>'mediafallbacklink'));
473
474 //note: when flash or javascript not available only the $printlink is displayed,
475 // audio players are supposed to be inline elements
476
477 $output = html_writer::tag('span', $printlink, array('id'=>$id, 'class'=>'mediaplugin mediaplugin_mp3'));
478 $output .= html_writer::script(js_writer::function_call('M.util.add_audio_player', array($id, $rawurl, true))); // we can not use standard JS init because this may be cached
ce5dc36e
RW
479
480 return $output;
481}
482
fcd2cbaf
PS
483/**
484 * Replace swf links with embedded flash objects.
485 *
486 * Please note this is not a secure and is recommended to be disabled on production systems.
487 *
488 * @deprecated
489 * @param $link
490 * @return string
491 */
35716b86 492function filter_mediaplugin_swf_callback($link) {
7e64d361 493
fcd2cbaf
PS
494 if (filter_mediaplugin_ignore($link[0])) {
495 return $link[0];
496 }
497
498 $width = empty($link[3]) ? FILTER_MEDIAPLUGIN_VIDEO_WIDTH : $link[3];
499 $height = empty($link[4]) ? FILTER_MEDIAPLUGIN_VIDEO_HEIGHT : $link[4];
7e64d361 500
fcd2cbaf
PS
501 $url = $link[1];
502 $rawurl = str_replace('&amp;', '&', $url);
82afb587 503
fcd2cbaf
PS
504 $info = trim($link[5]);
505 if (empty($info) or strpos($info, 'http') === 0) {
506 $info = get_string('flashanimation', 'filter_mediaplugin');
507
508 }
509 $printlink = html_writer::link($rawurl, $info, array('class'=>'mediafallbacklink'));
35716b86 510
fcd2cbaf
PS
511 $output = <<<OET
512<span class="mediaplugin mediaplugin_swf">
513 <object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" width="$width" height="$height">
514 <param name="movie" value="$url" />
515 <param name="autoplay" value="true" />
516 <param name="loop" value="true" />
517 <param name="controller" value="true" />
518 <param name="scale" value="aspect" />
519 <param name="base" value="." />
520 <param name="allowscriptaccess" value="never" />
521<!--[if !IE]>-->
522 <object type="application/x-shockwave-flash" data="$url" width="$width" height="$height">
523 <param name="controller" value="true" />
524 <param name="autoplay" value="true" />
525 <param name="loop" value="true" />
526 <param name="scale" value="aspect" />
527 <param name="base" value="." />
528 <param name="allowscriptaccess" value="never" />
529<!--<![endif]-->
530$printlink
531<!--[if !IE]>-->
532 </object>
533<!--<![endif]-->
534 </object>
535</span>
536OET;
82afb587 537
538 return $output;
fcd2cbaf 539
7e64d361 540}
541
fcd2cbaf
PS
542/**
543 * Replace flv links with flow player.
544 *
545 * @param $link
546 * @return string
547 */
35716b86 548function filter_mediaplugin_flv_callback($link) {
7e64d361 549 static $count = 0;
fcd2cbaf
PS
550
551 if (filter_mediaplugin_ignore($link[0])) {
552 return $link[0];
553 }
554
7e64d361 555 $count++;
fcd2cbaf
PS
556 $id = 'filter_flv_'.time().'_'.$count; //we need something unique because it might be stored in text cache
557
558 list($urls, $width, $height) = filter_mediaplugin_parse_alternatives($link[1], 0, 0);
559
560 $autosize = false;
561 if (!$width and !$height) {
562 $width = FILTER_MEDIAPLUGIN_VIDEO_WIDTH;
563 $height = FILTER_MEDIAPLUGIN_VIDEO_HEIGHT;
564 $autosize = true;
565 }
566
567 $flashurl = null;
568 $sources = array();
569
570 foreach ($urls as $url) {
7f86bee3 571 $mimetype = filter_mediaplugin_get_mimetype($url);
fcd2cbaf
PS
572 if (strpos($mimetype, 'video/') !== 0) {
573 continue;
574 }
575 $source = html_writer::tag('source', '', array('src' => $url, 'type' => $mimetype));
576 if ($mimetype === 'video/mp4') {
577 // better add m4v as first source, it might be a bit more compatible with problematic browsers
578 array_unshift($sources, $source);
579 } else {
580 $sources[] = $source;
581 }
582
583 if ($flashurl === null) {
7f86bee3 584 $flashurl = $url;
fcd2cbaf
PS
585 }
586 }
587 if (!$sources) {
588 return $link[0];
589 }
590
591 $info = trim($link[4]);
592 if (empty($info) or strpos($info, 'http') === 0) {
593 $info = get_string('fallbackvideo', 'filter_mediaplugin');
594 }
595 $printlink = html_writer::link($flashurl.'#', $info, array('class'=>'mediafallbacklink')); // the '#' prevents the QT filter
596
597 $title = s($info);
598
599 if (count($sources) > 1) {
600 $sources = implode("\n", $sources);
601
602 // html 5 fallback
603 $printlink = <<<OET
604<video controls="true" width="$width" height="$height" preload="metadata" title="$title">
605$sources
606$printlink
607</video>
608<noscript><br />
609$printlink
610</noscript>
611OET;
612 }
613
614 // note: no need to print "this is flv link" because it is printed automatically if JS or Flash not available
615
616 $output = html_writer::tag('span', $printlink, array('id'=>$id, 'class'=>'mediaplugin mediaplugin_flv'));
afd1c47c 617 $output .= html_writer::script(js_writer::function_call('M.util.add_video_player', array($id, addslashes_js($flashurl), $width, $height, $autosize))); // we can not use standard JS init because this may be cached
3a42ad12 618
82afb587 619 return $output;
7e64d361 620}
621
fcd2cbaf
PS
622/**
623 * Replace real media links with real player.
624 *
625 * Note: hopefully nobody is using this obsolete format any more.
626 *
627 * @deprectated
628 * @param $link
629 * @return string
630 */
631function filter_mediaplugin_real_callback($link) {
632
633 if (filter_mediaplugin_ignore($link[0])) {
634 return $link[0];
635 }
636
637 $url = $link[1];
638 $rawurl = str_replace('&amp;', '&', $url);
639
640 //Note: the size is hardcoded intentionally because this does not work anyway!
641
642 $width = FILTER_MEDIAPLUGIN_VIDEO_WIDTH;
643 $height = FILTER_MEDIAPLUGIN_VIDEO_HEIGHT;
644
645 $info = trim($link[3]);
646 if (empty($info) or strpos($info, 'http') === 0) {
647 $info = get_string('fallbackvideo', 'filter_mediaplugin');
648 }
649 $printlink = html_writer::link($rawurl, $info, array('class'=>'mediafallbacklink'));
650
651 return <<<OET
652<span class="mediaplugin mediaplugin_real">
653 $printlink <br />
654 <object title="$info" classid="clsid:CFCDAA03-8BE4-11cf-B84B-0020AFBBCCFA" data="$url" width="$width" height="$height"">
655 <param name="src" value="$url" />
656 <param name="controls" value="All" />
657<!--[if !IE]>-->
658 <object title="$info" type="audio/x-pn-realaudio-plugin" data="$url" width="$width" height="$height">
659 <param name="src" value="$url" />
660 <param name="controls" value="All" />
661<!--<![endif]-->
662<!--[if !IE]>-->
663 </object>
664<!--<![endif]-->
665 </object>
666</span>
667OET;
7e64d361 668}
669
4d8bccf4 670/**
fcd2cbaf
PS
671 * Change links to YouTube into embedded YouTube videos
672 *
673 * Note: resizing via url is not supported, user can click the fullscreen button instead
674 *
675 * @param $link
676 * @return string
4d8bccf4 677 */
fcd2cbaf
PS
678function filter_mediaplugin_youtube_callback($link) {
679 global $CFG;
680
681 if (filter_mediaplugin_ignore($link[0])) {
682 return $link[0];
683 }
684
685 $site = $link[1];
686 $videoid = $link[3];
687
a3516c49 688 $info = trim($link[7]);
fcd2cbaf
PS
689 if (empty($info) or strpos($info, 'http') === 0) {
690 $info = get_string('siteyoutube', 'filter_mediaplugin');
691 }
692 $info = s($info);
693
a3516c49
PS
694 $width = empty($link[5]) ? FILTER_MEDIAPLUGIN_VIDEO_WIDTH : $link[5];
695 $height = empty($link[6]) ? FILTER_MEDIAPLUGIN_VIDEO_HEIGHT : $link[6];
fcd2cbaf 696
c4b491a4 697 if (empty($CFG->xmlstrictheaders)) {
fcd2cbaf
PS
698 return <<<OET
699<iframe title="$info" width="$width" height="$height" src="$site/embed/$videoid?rel=0" frameborder="0" allowfullscreen></iframe>
700OET;
701 }
702
703 //NOTE: we can not use any link fallback because it breaks built-in player on iOS devices
704
705 $output = <<<OET
706<span class="mediaplugin mediaplugin_youtube">
707<object title="$info" type="application/x-shockwave-flash" data="$site/v/$videoid&amp;fs=1&amp;rel=0" width="$width" height="$height">
708 <param name="movie" value="$site/v/$videoid&amp;fs=1&amp;rel=0" />
709 <param name="FlashVars" value="playerMode=embedded" />
710 <param name="allowFullScreen" value="true" />
711</object>
712</span>
713OET;
714
715 return $output;
4d8bccf4 716}
717
469aa3ba 718/**
fcd2cbaf
PS
719 * Change YouTube playlist into embedded YouTube playlist videos
720 *
721 * Note: resizing via url is not supported, user can click the fullscreen button instead
722 *
723 * @param $link
724 * @return string
469aa3ba 725 */
fcd2cbaf
PS
726function filter_mediaplugin_youtube_playlist_callback($link) {
727 global $CFG;
728
729 if (filter_mediaplugin_ignore($link[0])) {
730 return $link[0];
731 }
732
733 $site = $link[1];
734 $playlist = $link[3];
735
a3516c49 736 $info = trim($link[7]);
fcd2cbaf
PS
737 if (empty($info) or strpos($info, 'http') === 0) {
738 $info = get_string('siteyoutube', 'filter_mediaplugin');
739 }
740 $printlink = html_writer::link("$site/view_play_list\?p=$playlist", $info, array('class'=>'mediafallbacklink'));
741 $info = s($info);
742
a3516c49
PS
743 $width = empty($link[5]) ? FILTER_MEDIAPLUGIN_VIDEO_WIDTH : $link[5];
744 $height = empty($link[6]) ? FILTER_MEDIAPLUGIN_VIDEO_HEIGHT : $link[6];
fcd2cbaf
PS
745
746 // TODO: iframe HTML 5 video not implemented and object does work on iOS devices
747
748 $output = <<<OET
749<span class="mediaplugin mediaplugin_youtube">
750<object title="$info" type="application/x-shockwave-flash" data="$site/p/$playlist&amp;fs=1&amp;rel=0" width="$width" height="$height">
751 <param name="movie" value="$site/v/$playlist&amp;fs=1&amp;rel=0" />
752 <param name="FlashVars" value="playerMode=embedded" />
753 <param name="allowFullScreen" value="true" />
754$printlink</object>
755</span>
756OET;
757
758 return $output;
469aa3ba
RW
759}
760
c159f28b 761/**
fcd2cbaf
PS
762 * Change links to Vimeo into embedded Vimeo videos
763 *
764 * @param $link
765 * @return string
c159f28b 766 */
fcd2cbaf
PS
767function filter_mediaplugin_vimeo_callback($link) {
768 global $CFG;
769
770 if (filter_mediaplugin_ignore($link[0])) {
771 return $link[0];
772 }
773
774 $videoid = $link[1];
a3516c49 775 $info = s(strip_tags($link[5]));
fcd2cbaf
PS
776
777 //Note: resizing via url is not supported, user can click the fullscreen button instead
778 // iframe embedding is not xhtml strict but it is the only option that seems to work on most devices
779
a3516c49
PS
780 $width = empty($link[3]) ? FILTER_MEDIAPLUGIN_VIDEO_WIDTH : $link[3];
781 $height = empty($link[4]) ? FILTER_MEDIAPLUGIN_VIDEO_HEIGHT : $link[4];
fcd2cbaf
PS
782
783 $output = <<<OET
784<span class="mediaplugin mediaplugin_vimeo">
785<iframe title="$info" src="http://player.vimeo.com/video/$videoid" width="$width" height="$height" frameborder="0"></iframe>
786</span>
787OET;
788
789 return $output;
c159f28b 790}
791
7e64d361 792/**
793 * Embed video using window media player if available
fcd2cbaf
PS
794 *
795 * This does not work much outside of IE, hopefully not many ppl use it these days.
796 *
797 * @param $link
798 * @return string
7e64d361 799 */
fcd2cbaf
PS
800function filter_mediaplugin_wmp_callback($link) {
801
802 if (filter_mediaplugin_ignore($link[0])) {
803 return $link[0];
804 }
805
806 $url = $link[1];
807 $rawurl = str_replace('&amp;', '&', $url);
808
809 $info = trim($link[6]);
810 if (empty($info) or strpos($info, 'http') === 0) {
811 $info = get_string('fallbackvideo', 'filter_mediaplugin');
812 }
813 $printlink = html_writer::link($rawurl, $info, array('class'=>'mediafallbacklink'));
814
3f5192c0 815 if (empty($link[4]) or empty($link[5])) {
7e64d361 816 $mpsize = '';
fcd2cbaf 817 $size = 'width="'.FILTER_MEDIAPLUGIN_VIDEO_WIDTH.'" height="'.(FILTER_MEDIAPLUGIN_VIDEO_HEIGHT+64).'"';
7e64d361 818 $autosize = 'true';
819 } else {
fcd2cbaf
PS
820 $size = 'width="'.$link[4].'" height="'.($link[5] + 15).'"';
821 $mpsize = 'width="'.$link[4].'" height="'.($link[5] + 64).'"';
7e64d361 822 $autosize = 'false';
823 }
7f86bee3 824 $mimetype = filter_mediaplugin_get_mimetype($url);
fcd2cbaf
PS
825
826
827
828 return <<<OET
829<span class="mediaplugin mediaplugin_wmp">
830$printlink <br />
831<object classid="CLSID:6BF52A52-394A-11d3-B153-00C04F79FAA6" $mpsize standby="Loading Microsoft(R) Windows(R) Media Player components..." type="application/x-oleobject">
832 <param name="Filename" value="$url" />
833 <param name="src" value="$url" />
834 <param name="url" value="$url" />
7e64d361 835 <param name="ShowControls" value="true" />
836 <param name="AutoRewind" value="true" />
fcd2cbaf
PS
837 <param name="AutoStart" value="false" />
838 <param name="Autosize" value="$autosize" />
7e64d361 839 <param name="EnableContextMenu" value="true" />
840 <param name="TransparentAtStart" value="false" />
841 <param name="AnimationAtStart" value="false" />
842 <param name="ShowGotoBar" value="false" />
843 <param name="EnableFullScreenControls" value="true" />
fcd2cbaf 844 <param name="uimode" value="full" />
7e64d361 845<!--[if !IE]>-->
fcd2cbaf
PS
846 <object data="$url" type="$mimetype" $size>
847 <param name="src" value="$url" />
7e64d361 848 <param name="controller" value="true" />
fcd2cbaf
PS
849 <param name="autoplay" value="false" />
850 <param name="autostart" value="false" />
7e64d361 851 <param name="resize" value="scale" />
852 </object>
853<!--<![endif]-->
fcd2cbaf
PS
854</object></span>
855OET;
7e64d361 856}
857
fcd2cbaf
PS
858/**
859 * Replace quicktime links with quicktime player.
860 *
861 * You need to install a quicktime player, it is not available for all browsers+OS combinations.
862 *
863 * @param $link
864 * @return string
865 */
866function filter_mediaplugin_qt_callback($link) {
867
868 if (filter_mediaplugin_ignore($link[0])) {
869 return $link[0];
870 }
871
872 $url = $link[1];
873 $rawurl = str_replace('&amp;', '&', $url);
874
875 $info = trim($link[6]);
876 if (empty($info) or strpos($info, 'http') === 0) {
877 $info = get_string('fallbackvideo', 'filter_mediaplugin');
878 }
879 $printlink = html_writer::link($rawurl, $info, array('class'=>'mediafallbacklink'));
880
3f5192c0 881 if (empty($link[4]) or empty($link[5])) {
fcd2cbaf 882 $size = 'width="'.FILTER_MEDIAPLUGIN_VIDEO_WIDTH.'" height="'.(FILTER_MEDIAPLUGIN_VIDEO_HEIGHT+15).'"';
7e64d361 883 } else {
fcd2cbaf 884 $size = 'width="'.$link[4].'" height="'.($link[5]+15).'"';
7e64d361 885 }
7f86bee3 886 $mimetype = filter_mediaplugin_get_mimetype($url);
7e64d361 887
fcd2cbaf
PS
888 // this is the safest fallback for incomplete or missing browser support for this format
889 return <<<OET
890<span class="mediaplugin mediaplugin_qt">
891$printlink <br />
892<object classid="clsid:02BF25D5-8C17-4B23-BC80-D3488ABDDC6B" codebase="http://www.apple.com/qtactivex/qtplugin.cab" $size>
7e64d361 893 <param name="pluginspage" value="http://www.apple.com/quicktime/download/" />
fcd2cbaf 894 <param name="src" value="$url" />
d42df9da 895 <param name="controller" value="true" />
7e64d361 896 <param name="loop" value="true" />
fcd2cbaf
PS
897 <param name="autoplay" value="false" />
898 <param name="autostart" value="false" />
7e64d361 899 <param name="scale" value="aspect" />
900<!--[if !IE]>-->
fcd2cbaf
PS
901 <object data="$url" type="$mimetype" $size>
902 <param name="src" value="$url" />
7e64d361 903 <param name="pluginurl" value="http://www.apple.com/quicktime/download/" />
904 <param name="controller" value="true" />
905 <param name="loop" value="true" />
fcd2cbaf
PS
906 <param name="autoplay" value="false" />
907 <param name="autostart" value="false" />
7e64d361 908 <param name="scale" value="aspect" />
909 </object>
910<!--<![endif]-->
fcd2cbaf
PS
911</object></span>
912OET;
7e64d361 913}
6de17fde 914