e1e61cab5b7c7f451e159738dfcc25351b7f9348
[moodle.git] / filter / mediaplugin / filter.php
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/>.
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  */
29 defined('MOODLE_INTERNAL') || die();
31 require_once($CFG->libdir.'/filelib.php');
33 if (!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 }
41 if (!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 }
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 ...
53 //TODO: we should migrate to proper config_plugin settings ...
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  */
67 class filter_mediaplugin extends moodle_text_filter {
69     function filter($text, array $options = array()) {
70         global $CFG;
72         if (!is_string($text) or empty($text)) {
73             // non string data can not be filtered anyway
74             return $text;
75         }
76         if (stripos($text, '</a>') === false) {
77             // performance shortcut - all regexes bellow end with the </a> tag,
78             // if not present nothing can match
79             return $text;
80         }
82         $newtext = $text; // we need to return the original value if regex fails!
84         // YouTube and Vimeo are great because the files are not served by Moodle server
86         if (!empty($CFG->filter_mediaplugin_enable_youtube)) {
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';
88             $newtext = preg_replace_callback($search, 'filter_mediaplugin_youtube_callback', $newtext);
90             $search = '/<a\s[^>]*href="(https?:\/\/www\.youtube(-nocookie)?\.com)\/v\/([a-z0-9\-_]+)[^"#]*(#d=([\d]{1,4})x([\d]{1,4}))?[^>]*>([^>]*)<\/a>/is';
91             $newtext = preg_replace_callback($search, 'filter_mediaplugin_youtube_callback', $newtext);
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';
94             $newtext = preg_replace_callback($search, 'filter_mediaplugin_youtube_playlist_callback', $newtext);
96             $search = '/<a\s[^>]*href="(https?:\/\/www\.youtube(-nocookie)?\.com)\/p\/([a-z0-9\-_]+)[^"#]*(#d=([\d]{1,4})x([\d]{1,4}))?[^>]*>([^>]*)<\/a>/is';
97             $newtext = preg_replace_callback($search, 'filter_mediaplugin_youtube_playlist_callback', $newtext);
98         }
100         if (!empty($CFG->filter_mediaplugin_enable_vimeo)) {
101             $search = '/<a\s[^>]*href="http:\/\/vimeo\.com\/([0-9]+)[^"#]*(#d=([\d]{1,4})x([\d]{1,4}))?[^>]*>([^>]*)<\/a>/is';
102             $newtext = preg_replace_callback($search, 'filter_mediaplugin_vimeo_callback', $newtext);
103         }
106         // HTML 5 audio and video tags are the future! If only if vendors decided to use just one audio and video format...
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);
111         }
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);
116         }
119         // Flash stuff
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);
124         }
126         if ((!empty($options['noclean']) or !empty($CFG->allowobjectembed)) and !empty($CFG->filter_mediaplugin_enable_swf)) {
127             $search = '/<a\s[^>]*href="([^"#\?]+\.swf)([#\?]d=([\d]{1,4})x([\d]{1,4}))?"[^>]*>([^>]*)<\/a>/is';
128             $newtext = preg_replace_callback($search, 'filter_mediaplugin_swf_callback', $newtext);
129         }
131         if (!empty($CFG->filter_mediaplugin_enable_flv)) {
132             $search = '/<a\s[^>]*href="([^"#\?]+\.(flv|f4v)([#\?][^"]*)?)"[^>]*>([^>]*)<\/a>/is';
133             $newtext = preg_replace_callback($search, 'filter_mediaplugin_flv_callback', $newtext);
134         }
137         // The rest of legacy formats - these should not be used if possible
139         if (!empty($CFG->filter_mediaplugin_enable_wmp)) {
140             $search = '/<a\s[^>]*href="([^"#\?]+\.(wmv|avi))(\?d=([\d]{1,4})x([\d]{1,4}))?"[^>]*>([^>]*)<\/a>/is';
141             $newtext = preg_replace_callback($search, 'filter_mediaplugin_wmp_callback', $newtext);
142         }
144         if (!empty($CFG->filter_mediaplugin_enable_qt)) {
145             // HTML5 filtering may steal mpeg 4 formats
146             $search = '/<a\s[^>]*href="([^"#\?]+\.(mpg|mpeg|mov|mp4|m4v|m4a))(\?d=([\d]{1,4})x([\d]{1,4}))?"[^>]*>([^>]*)<\/a>/is';
147             $newtext = preg_replace_callback($search, 'filter_mediaplugin_qt_callback', $newtext);
148         }
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
154             $search = '/<a\s[^>]*href="([^"#\?]+\.(ra|ram|rm|rv))"[^>]*>([^>]*)<\/a>/is';
155             $newtext = preg_replace_callback($search, 'filter_mediaplugin_real_callback', $newtext);
156         }
159         if (empty($newtext) or $newtext === $text) {
160             // error or not filtered
161             unset($newtext);
162             return $text;
163         }
166         return $newtext;
167     }
171 ///===========================
172 /// utility functions
174 /**
175  * Get mimetype of given url, useful for # alternative urls.
176  *
177  * @private
178  * @param string $url
179  * @return string $mimetype
180  */
181 function 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     }
190     $mimetype = mimeinfo('type', $url);
192     return $mimetype;
195 /**
196  * Parse list of alternative URLs
197  * @param string $url urls separated with '#', size specified as ?d=640x480 or #d=640x480
198  * @param int $defaultwidth
199  * @param int $defaultheight
200  * @return array (urls, width, height)
201  */
202 function filter_mediaplugin_parse_alternatives($url, $defaultwidth = 0, $defaultheight = 0) {
203     $urls = explode('#', $url);
204     $width  = $defaultwidth;
205     $height = $defaultheight;
206     $returnurls = array();
208     foreach ($urls as $url) {
209         $matches = null;
211         if (preg_match('/^d=([\d]{1,4})x([\d]{1,4})$/i', $url, $matches)) { // #d=640x480
212             $width  = $matches[1];
213             $height = $matches[2];
214             continue;
215         }
216         if (preg_match('/\?d=([\d]{1,4})x([\d]{1,4})$/i', $url, $matches)) { // old style file.ext?d=640x480
217             $width  = $matches[1];
218             $height = $matches[2];
219             $url = str_replace($matches[0], '', $url);
220         }
222         $url = str_replace('&amp;', '&', $url);
223         $url = clean_param($url, PARAM_URL);
224         if (empty($url)) {
225             continue;
226         }
228         $returnurls[] = $url;
229     }
231     return array($returnurls, $width, $height);
234 /**
235  * Should the current tag be ignored in this filter?
236  * @param string $tag
237  * @return bool
238  */
239 function filter_mediaplugin_ignore($tag) {
240     if (preg_match('/class="[^"]*nomediaplugin/i', $tag)) {
241         return true;
242     } else {
243         false;
244     }
247 ///===========================
248 /// callback filter functions
251 /**
252  * Replace audio links with audio tag.
253  *
254  * @param array $link
255  * @return string
256  */
257 function filter_mediaplugin_html5audio_callback(array $link) {
258     global $CFG;
260     if (filter_mediaplugin_ignore($link[0])) {
261         return $link[0];
262     }
264     $info = trim($link[4]);
265     if (empty($info) or strpos($info, 'http') === 0) {
266         $info = get_string('fallbackaudio', 'filter_mediaplugin');
267     }
269     list($urls, $ignorewidth, $ignoredheight) = filter_mediaplugin_parse_alternatives($link[1]);
271     $fallbackurl  = null;
272     $fallbackmime = null;
273     $sources      = array();
274     $fallbacklink = null;
276     foreach ($urls as $url) {
277         $mimetype = filter_mediaplugin_get_mimetype($url);
278         if (strpos($mimetype, 'audio/') !== 0) {
279             continue;
280         }
281         $sources[] = html_writer::tag('source', '', array('src' => $url, 'type' => $mimetype));
283         if ($fallbacklink === null) {
284             $fallbacklink = html_writer::link($url.'#', $info); // the extra '#' prevents linking in mp3 filter bellow
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];
295     }
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>
322 OET;
323     } else {
324         $fallback = $fallbacklink;
325     }
327     $sources = implode("\n", $sources);
328     $title = s($info);
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>
336 OET;
338     return $output;
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  */
350 function filter_mediaplugin_html5video_callback(array $link) {
352     if (filter_mediaplugin_ignore($link[0])) {
353         return $link[0];
354     }
356     $info = trim($link[4]);
357     if (empty($info) or strpos($info, 'http') === 0) {
358         $info = get_string('fallbackvideo', 'filter_mediaplugin');
359     }
361     list($urls, $width, $height) = filter_mediaplugin_parse_alternatives($link[1], FILTER_MEDIAPLUGIN_VIDEO_WIDTH, 0);
363     $fallbackurl  = null;
364     $fallbackmime = null;
365     $sources      = array();
366     $fallbacklink = null;
368     foreach ($urls as $url) {
369         $mimetype = filter_mediaplugin_get_mimetype($url);
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         }
381         if ($fallbacklink === null) {
382             $fallbacklink = html_writer::link($url.'#', $info); // the extra '#' prevents linking in mp3 filter bellow
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     }
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>
421 OET;
422     } else {
423         $fallback = $fallbacklink;
424     }
426     $sources = implode("\n", $sources);
427     $title = s($info);
429     if (empty($height)) {
430         // automatic height
431         $size = "width=\"$width\"";
432     } else {
433         $size = "width=\"$width\" height=\"$height\"";
434     }
436     $output = <<<OET
437 <span class="mediaplugin mediaplugin_html5video">
438 <video controls="true" $size preload="metadata" title="$title">
439 $sources
440 $fallback
441 </video>
442 </span>
443 OET;
445     return $output;
448 /**
449  * Replace mp3 links with small audio player.
450  *
451  * @param  $link
452  * @return string
453  */
454 function filter_mediaplugin_mp3_callback($link) {
455     static $count = 0;
457     if (filter_mediaplugin_ignore($link[0])) {
458         return $link[0];
459     }
461     $count++;
462     $id = 'filter_mp3_'.time().'_'.$count; //we need something unique because it might be stored in text cache
464     $url = $link[1];
465     $rawurl = str_replace('&amp;', '&', $url);
467     $info = trim($link[2]);
468     if (empty($info) or strpos($info, 'http') === 0) {
469         $info = get_string('mp3audio', 'filter_mediaplugin');
471     }
472     $printlink = html_writer::link($rawurl, $info, array('class'=>'mediafallbacklink'));
474     //note: when flash or javascript not available only the $printlink is displayed,
475     //      audio players are supposed to be inline elements
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
480     return $output;
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  */
492 function filter_mediaplugin_swf_callback($link) {
494     if (filter_mediaplugin_ignore($link[0])) {
495         return $link[0];
496     }
498     $width  = empty($link[3]) ? FILTER_MEDIAPLUGIN_VIDEO_WIDTH  : $link[3];
499     $height = empty($link[4]) ? FILTER_MEDIAPLUGIN_VIDEO_HEIGHT : $link[4];
501     $url = $link[1];
502     $rawurl = str_replace('&amp;', '&', $url);
504     $info = trim($link[5]);
505     if (empty($info) or strpos($info, 'http') === 0) {
506         $info = get_string('flashanimation', 'filter_mediaplugin');
508     }
509     $printlink = html_writer::link($rawurl, $info, array('class'=>'mediafallbacklink'));
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>
536 OET;
538     return $output;
542 /**
543  * Replace flv links with flow player.
544  *
545  * @param  $link
546  * @return string
547  */
548 function filter_mediaplugin_flv_callback($link) {
549     static $count = 0;
551     if (filter_mediaplugin_ignore($link[0])) {
552         return $link[0];
553     }
555     $count++;
556     $id = 'filter_flv_'.time().'_'.$count; //we need something unique because it might be stored in text cache
558     list($urls, $width, $height) = filter_mediaplugin_parse_alternatives($link[1], 0, 0);
560     $autosize = false;
561     if (!$width and !$height) {
562         $width    = FILTER_MEDIAPLUGIN_VIDEO_WIDTH;
563         $height   = FILTER_MEDIAPLUGIN_VIDEO_HEIGHT;
564         $autosize = true;
565     }
567     $flashurl = null;
568     $sources  = array();
570     foreach ($urls as $url) {
571         $mimetype = filter_mediaplugin_get_mimetype($url);
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         }
583         if ($flashurl === null) {
584             $flashurl  = $url;
585         }
586     }
587     if (!$sources) {
588         return $link[0];
589     }
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
597     $title = s($info);
599     if (count($sources) > 1) {
600         $sources = implode("\n", $sources);
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>
611 OET;
612     }
614     // note: no need to print "this is flv link" because it is printed automatically if JS or Flash not available
616     $output = html_writer::tag('span', $printlink, array('id'=>$id, 'class'=>'mediaplugin mediaplugin_flv'));
617     $output .= html_writer::script(js_writer::function_call('M.util.add_video_player', array($id, rawurlencode($flashurl), $width, $height, $autosize))); // we can not use standard JS init because this may be cached
619     return $output;
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  */
631 function filter_mediaplugin_real_callback($link) {
633     if (filter_mediaplugin_ignore($link[0])) {
634         return $link[0];
635     }
637     $url      = $link[1];
638     $rawurl   = str_replace('&amp;', '&', $url);
640     //Note: the size is hardcoded intentionally because this does not work anyway!
642     $width  = FILTER_MEDIAPLUGIN_VIDEO_WIDTH;
643     $height = FILTER_MEDIAPLUGIN_VIDEO_HEIGHT;
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'));
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>
667 OET;
670 /**
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
677  */
678 function filter_mediaplugin_youtube_callback($link) {
679     global $CFG;
681     if (filter_mediaplugin_ignore($link[0])) {
682         return $link[0];
683     }
685     $site    = $link[1];
686     $videoid = $link[3];
688     $info = trim($link[7]);
689     if (empty($info) or strpos($info, 'http') === 0) {
690         $info = get_string('siteyoutube', 'filter_mediaplugin');
691     }
692     $info = s($info);
694     $width  = empty($link[5]) ? FILTER_MEDIAPLUGIN_VIDEO_WIDTH  : $link[5];
695     $height = empty($link[6]) ? FILTER_MEDIAPLUGIN_VIDEO_HEIGHT : $link[6];
697     if (empty($CFG->xmlstrictheaders)) {
698         return <<<OET
699 <iframe title="$info" width="$width" height="$height" src="$site/embed/$videoid?rel=0" frameborder="0" allowfullscreen></iframe>
700 OET;
701     }
703     //NOTE: we can not use any link fallback because it breaks built-in player on iOS devices
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>
713 OET;
715     return $output;
718 /**
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
725  */
726 function filter_mediaplugin_youtube_playlist_callback($link) {
727     global $CFG;
729     if (filter_mediaplugin_ignore($link[0])) {
730         return $link[0];
731     }
733     $site     = $link[1];
734     $playlist = $link[3];
736     $info = trim($link[7]);
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);
743     $width  = empty($link[5]) ? FILTER_MEDIAPLUGIN_VIDEO_WIDTH  : $link[5];
744     $height = empty($link[6]) ? FILTER_MEDIAPLUGIN_VIDEO_HEIGHT : $link[6];
746     // TODO: iframe HTML 5 video not implemented and object does work on iOS devices
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>
756 OET;
758     return $output;
761 /**
762  * Change links to Vimeo into embedded Vimeo videos
763  *
764  * @param  $link
765  * @return string
766  */
767 function filter_mediaplugin_vimeo_callback($link) {
768     global $CFG;
770     if (filter_mediaplugin_ignore($link[0])) {
771         return $link[0];
772     }
774     $videoid = $link[1];
775     $info    = s(strip_tags($link[5]));
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
780     $width  = empty($link[3]) ? FILTER_MEDIAPLUGIN_VIDEO_WIDTH  : $link[3];
781     $height = empty($link[4]) ? FILTER_MEDIAPLUGIN_VIDEO_HEIGHT : $link[4];
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>
787 OET;
789     return $output;
792 /**
793  * Embed video using window media player if available
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
799  */
800 function filter_mediaplugin_wmp_callback($link) {
802     if (filter_mediaplugin_ignore($link[0])) {
803         return $link[0];
804     }
806     $url    = $link[1];
807     $rawurl = str_replace('&amp;', '&', $url);
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'));
815     if (empty($link[4]) or empty($link[5])) {
816         $mpsize = '';
817         $size = 'width="'.FILTER_MEDIAPLUGIN_VIDEO_WIDTH.'" height="'.(FILTER_MEDIAPLUGIN_VIDEO_HEIGHT+64).'"';
818         $autosize = 'true';
819     } else {
820         $size = 'width="'.$link[4].'" height="'.($link[5] + 15).'"';
821         $mpsize = 'width="'.$link[4].'" height="'.($link[5] + 64).'"';
822         $autosize = 'false';
823     }
824     $mimetype = filter_mediaplugin_get_mimetype($url);
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" />
835  <param name="ShowControls" value="true" />
836  <param name="AutoRewind" value="true" />
837  <param name="AutoStart" value="false" />
838  <param name="Autosize" value="$autosize" />
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" />
844  <param name="uimode" value="full" />
845 <!--[if !IE]>-->
846   <object data="$url" type="$mimetype" $size>
847    <param name="src" value="$url" />
848    <param name="controller" value="true" />
849    <param name="autoplay" value="false" />
850    <param name="autostart" value="false" />
851    <param name="resize" value="scale" />
852   </object>
853 <!--<![endif]-->
854 </object></span>
855 OET;
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  */
866 function filter_mediaplugin_qt_callback($link) {
868     if (filter_mediaplugin_ignore($link[0])) {
869         return $link[0];
870     }
872     $url    = $link[1];
873     $rawurl = str_replace('&amp;', '&', $url);
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'));
881     if (empty($link[4]) or empty($link[5])) {
882         $size = 'width="'.FILTER_MEDIAPLUGIN_VIDEO_WIDTH.'" height="'.(FILTER_MEDIAPLUGIN_VIDEO_HEIGHT+15).'"';
883     } else {
884         $size = 'width="'.$link[4].'" height="'.($link[5]+15).'"';
885     }
886     $mimetype = filter_mediaplugin_get_mimetype($url);
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>
893  <param name="pluginspage" value="http://www.apple.com/quicktime/download/" />
894  <param name="src" value="$url" />
895  <param name="controller" value="true" />
896  <param name="loop" value="true" />
897  <param name="autoplay" value="false" />
898  <param name="autostart" value="false" />
899  <param name="scale" value="aspect" />
900 <!--[if !IE]>-->
901   <object data="$url" type="$mimetype" $size>
902    <param name="src" value="$url" />
903    <param name="pluginurl" value="http://www.apple.com/quicktime/download/" />
904    <param name="controller" value="true" />
905    <param name="loop" value="true" />
906    <param name="autoplay" value="false" />
907    <param name="autostart" value="false" />
908    <param name="scale" value="aspect" />
909   </object>
910 <!--<![endif]-->
911 </object></span>
912 OET;