create youtube playlist filter. Fixed regex, removed addslash_js, use variable param...
[moodle.git] / filter / mediaplugin / filter.php
1 <?php
3 // This file is part of Moodle - http://moodle.org/
4 //
5 // Moodle is free software: you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation, either version 3 of the License, or
8 // (at your option) any later version.
9 //
10 // Moodle is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
18 /**
19  *  Media plugin filtering
20  *
21  *  This filter will replace any links to a media file with
22  *  a media plugin that plays that media inline
23  *
24  * @package    filter
25  * @subpackage mediaplugin
26  * @copyright  2004 onwards Martin Dougiamas  {@link http://moodle.com}
27  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
28  */
30 defined('MOODLE_INTERNAL') || die();
32 require_once($CFG->libdir.'/filelib.php');
34 class filter_mediaplugin extends moodle_text_filter {
35     private $eolas_fix_applied = false;
36     function filter($text, array $options = array()) {
37         global $CFG, $PAGE;
38         // You should never modify parameters passed to a method or function, it's BAD practice. Create a copy instead.
39         // The reason is that you must always be able to refer to the original parameter that was passed.
40         // For this reason, I changed $text = preg_replace(..,..,$text) into $newtext = preg.... (NICOLAS CONNAULT)
41         // Thanks to Pablo Etcheverry for pointing this out! MDL-10177
43         // We're using the UFO technique for flash to attain XHTML Strict 1.0
44         // See: http://www.bobbyvandersluis.com/ufo/
45         if (!is_string($text)) {
46             // non string data can not be filtered anyway
47             return $text;
48         }
49         $newtext = $text; // fullclone is slow and not needed here
51         if (!empty($CFG->filter_mediaplugin_enable_mp3)) {
52             $search =   '/<a[^>]*?href="([^<]+\.mp3)"[^>]*>.*?<\/a>/is';
53             $newtext = preg_replace_callback($search, 'filter_mediaplugin_mp3_callback', $newtext);
54         }
56         if (!empty($CFG->filter_mediaplugin_enable_ogg)) {
57             $search =   '/<a[^>]*?href="([^<]+\.ogg)"[^>]*>.*?<\/a>/is';
58             $newtext = preg_replace_callback($search, 'filter_mediaplugin_ogg_callback', $newtext);
59         }
61         if (!empty($CFG->filter_mediaplugin_enable_ogv)) {
62             $search =   '/<a[^>]*?href="([^<]+\.ogv)"[^>]*>.*?<\/a>/is';
63             $newtext = preg_replace_callback($search, 'filter_mediaplugin_ogv_callback', $newtext);
64         }
66         if (!empty($CFG->filter_mediaplugin_enable_swf)) {
67             $search = '/<a[^>]*?href="([^<]+\.swf)(\?d=([\d]{1,3}%?)x([\d]{1,3}%?))?"[^>]*>.*?<\/a>/is';
68             $newtext = preg_replace_callback($search, 'filter_mediaplugin_swf_callback', $newtext);
69         }
71         if (!empty($CFG->filter_mediaplugin_enable_flv)) {
72             $search = '/<a[^>]*?href="([^<]+\.flv)(\?d=([\d]{1,3}%?)x([\d]{1,3}%?))?"[^>]*>.*?<\/a>/is';
73             $newtext = preg_replace_callback($search, 'filter_mediaplugin_flv_callback', $newtext);
74         }
76         if (!empty($CFG->filter_mediaplugin_enable_mov)) {
77             $search = '/<a[^>]*?href="([^<]+\.mov)(\?d=([\d]{1,3}%?)x([\d]{1,3}%?))?"[^>]*>.*?<\/a>/is';
78             $newtext = preg_replace_callback($search, 'filter_mediaplugin_qt_callback', $newtext);
80             $search = '/<a[^>]*?href="([^<]+\.mp4)(\?d=([\d]{1,4}%?)x([\d]{1,4}%?))?"[^>]*>.*?<\/a>/is';
81             $newtext = preg_replace_callback($search, 'filter_mediaplugin_qt_callback', $newtext);
83             $search = '/<a[^>]*?href="([^<]+\.m4v)(\?d=([\d]{1,4}%?)x([\d]{1,4}%?))?"[^>]*>.*?<\/a>/is';
84             $newtext = preg_replace_callback($search, 'filter_mediaplugin_qt_callback', $newtext);
86             $search = '/<a[^>]*?href="([^<]+\.m4a)(\?d=([\d]{1,4}%?)x([\d]{1,4}%?))?"[^>]*>.*?<\/a>/is';
87             $newtext = preg_replace_callback($search, 'filter_mediaplugin_qt_callback', $newtext);
88         }
90         if (!empty($CFG->filter_mediaplugin_enable_wmv)) {
91             $search = '/<a[^>]*?href="([^<]+\.wmv)(\?d=([\d]{1,3}%?)x([\d]{1,3}%?))?"[^>]*>.*?<\/a>/is';
92             $newtext = preg_replace_callback($search, 'filter_mediaplugin_wmp_callback', $newtext);
93         }
95         if (!empty($CFG->filter_mediaplugin_enable_mpg)) {
96             $search = '/<a[^>]*?href="([^<]+\.mpe?g)(\?d=([\d]{1,3}%?)x([\d]{1,3}%?))?"[^>]*>.*?<\/a>/is';
97             $newtext = preg_replace_callback($search, 'filter_mediaplugin_qt_callback', $newtext);
98         }
100         if (!empty($CFG->filter_mediaplugin_enable_avi)) {
101             $search = '/<a[^>]*?href="([^<]+\.avi)(\?d=([\d]{1,3}%?)x([\d]{1,3}%?))?"[^>]*>.*?<\/a>/is';
102             $newtext = preg_replace_callback($search, 'filter_mediaplugin_wmp_callback', $newtext);
103         }
105         if (!empty($CFG->filter_mediaplugin_enable_ram)) {
106             $search = '/<a[^>]*?href="([^<]+\.ram)"[^>]*>.*?<\/a>/is';
107             $newtext = preg_replace_callback($search, 'filter_mediaplugin_real_callback', $newtext);
108         }
110         if (!empty($CFG->filter_mediaplugin_enable_rpm)) {
111             $search = '/<a[^>]*?href="([^<]+\.rpm)"[^>]*>.*?<\/a>/is';
112             $newtext = preg_replace_callback($search, 'filter_mediaplugin_real_callback', $newtext);
113         }
115         if (!empty($CFG->filter_mediaplugin_enable_rm)) {
116             $search = '/<a[^>]*?href="([^<]+\.rm)"[^>]*>.*?<\/a>/is';
117             $newtext = preg_replace_callback($search, 'filter_mediaplugin_real_callback', $newtext);
118         }
120         if (!empty($CFG->filter_mediaplugin_enable_youtube)) {
121             //see MDL-23903 for description of recent changes to this regex
122             //$search = '/<a.*?href="([^<]*)youtube.com\/watch\?v=([^"]*)"[^>]*>(.*?)<\/a>/is';
123             $search = '/<a[^>]*href="([^<]*?)youtube.com\/watch\?v=([^"]*)"[^>]*>(.*?)<\/a>/is';
124             $newtext = preg_replace_callback($search, 'filter_mediaplugin_youtube_callback', $newtext);
126             $search = '/<a[^>]*href="([^<]*)youtube.com\/v\/([^"]*)"[^>]*>(.*?)<\/a>/is';
127             $newtext = preg_replace_callback($search, 'filter_mediaplugin_youtube_callback', $newtext);
129             $search = '/<a(\s+[^>]+?)?\s+href="((([^"]+)youtube\.com)\/view_play_list\?p=([^"]*))"[^>]*>(.*?)<\/a>/is';
130             $newtext = preg_replace_callback($search, 'filter_mediaplugin_youtube_playlist_callback', $newtext);
131         }
133         if (!empty($CFG->filter_mediaplugin_enable_img)) {
134             $search = '/<a[^>]*?href="([^<]+\.jpg)"[^>]*>(.*?)<\/a>/is';
135             $newtext = preg_replace_callback($search, 'filter_mediaplugin_img_callback', $newtext);
136             $search = '/<a[^>]*?href="([^<]+\.png)"[^>]*>(.*?)<\/a>/is';
137             $newtext = preg_replace_callback($search, 'filter_mediaplugin_img_callback', $newtext);
138             $search = '/<a[^>]*?href="([^<]+\.gif)"[^>]*>(.*?)<\/a>/is';
139             $newtext = preg_replace_callback($search, 'filter_mediaplugin_img_callback', $newtext);
140         }
142         if (empty($newtext) or $newtext === $text) {
143             // error or not filtered
144             unset($newtext);
145             return $text;
146         }
148         if (!$this->eolas_fix_applied) {
149             $PAGE->requires->js('/filter/mediaplugin/eolas_fix.js');
150             $this->eolas_fix_applied = true;
151         }
153         return $newtext;
154     }
157 ///===========================
158 /// callback filter functions
160 function filter_mediaplugin_mp3_callback($link) {
161     global $CFG, $OUTPUT, $PAGE;
163     $c = $OUTPUT->filter_mediaplugin_colors();   // You can set this up in your theme/xxx/config.php
165     static $count = 0;
166     $count++;
167     $id = 'filter_mp3_'.time().$count; //we need something unique because it might be stored in text cache
169     $url = addslashes_js($link[1]);
171     $playerpath = $CFG->wwwroot.'/filter/mediaplugin/mp3player.swf';
172     $audioplayerpath = $CFG->wwwroot .'/filter/mediaplugin/flowplayer.audio.swf';
173     $colors = explode('&', $c);
174     $playercolors = array();
175     foreach ($colors as $color) {
176         $color = explode('=', $color);
177         $playercolors[$color[0]] = $color[1];
178     }
180     $output = <<<OET
181     <span class="mediaplugin mediaplugin_mp3" id="$id"></span>
182     <noscript><div>
183     <object width="100" height="15" id="nonjsmp3plugin" name="undefined" data="$playerpath" type="application/x-shockwave-flash">
184     <param name="movie" value="$playerpath" />
185     <param name="allowfullscreen" value="false" />
186     <param name="allowscriptaccess" value="always" />
187     <param name="flashvars" value='config={"plugins": {"controls": {
188                                                             "fullscreen": false,
189                                                             "height": 15,
190                                                             "autoHide": false,
191                                                             "all": false,
192                                                             "play": true,
193                                                             "pause": true,
194                                                             "scrubber": true
195                                                             },
196                                                        "audio": {"url": "$audioplayerpath"}
197                                                       },
198                                            "clip":{"url":"$url",
199                                                    "autoPlay": false},
200                                            "content":{"url":"$playerpath"}}}' />
201     </object>
202     </div></noscript>
203 OET;
205     $jsoutput = create_flowplayer($id, $url, 'mp3', $playercolors);
206     $output .= $jsoutput;
208     return $output;
211 function filter_mediaplugin_ogg_callback($link) {
212     global $CFG, $OUTPUT, $PAGE;
214     static $count = 0;
215     $count++;
216     $id = 'filter_ogg_'.time().$count; //we need something unique because it might be stored in text cache
218     $url = addslashes_js($link[1]);
219     $printlink = html_writer::link($url, get_string('oggaudio', 'filter_mediaplugin'));
220     $unsupportedplugins = get_string('unsupportedplugins', 'filter_mediaplugin', $printlink);
221     $output = <<<OET
222     <audio id="$id" src="$url" controls="true" width="100">
223         $unsupportedplugins
224     </audio>
225 OET;
227     return $output;
230 function filter_mediaplugin_ogv_callback($link) {
231     global $CFG, $OUTPUT, $PAGE;
233     static $count = 0;
234     $count++;
235     $id = 'filter_ogv_'.time().$count; //we need something unique because it might be stored in text cache
237     $url = addslashes_js($link[1]);
238     $printlink = html_writer::link($url, get_string('ogvvideo', 'filter_mediaplugin'));
239     $unsupportedplugins = get_string('unsupportedplugins', 'filter_mediaplugin', $printlink);
240     $output = <<<OET
241     <video id="$id" src="$url" controls="true" width="600" >
242         $unsupportedplugins
243     </video>
244 OET;
246     return $output;
249 function filter_mediaplugin_swf_callback($link) {
250     global $PAGE;
251     static $count = 0;
252     $count++;
253     $id = 'filter_swf_'.time().$count; //we need something unique because it might be stored in text cache
255     $width  = empty($link[3]) ? '400' : $link[3];
256     $height = empty($link[4]) ? '300' : $link[4];
257     $url = addslashes_js($link[1]);
259     $args = Array();
260     $args['movie'] = $url;
261     $args['width'] = $width;
262     $args['height'] = $height;
263     $args['majorversion'] = 6;
264     $args['build'] = 40;
265     $args['allowscriptaccess'] = 'never';
266     $args['quality'] = 'high';
268     $jsoutput = create_ufo_inline($id, $args);
270     $output = $link[0].'<span class="mediaplugin mediaplugin_swf" id="'.$id.'">('.get_string('flashanimation', 'filter_mediaplugin').')</span>'.$jsoutput;
272     return $output;
275 function filter_mediaplugin_flv_callback($link) {
276     global $CFG, $PAGE;
278     static $count = 0;
279     $count++;
280     $id = 'filter_flv_'.time().$count; //we need something unique because it might be stored in text cache
282     $width  = empty($link[3]) ? '480' : $link[3];
283     $height = empty($link[4]) ? '360' : $link[4];
284     $url = addslashes_js($link[1]);
286     $playerpath = $CFG->wwwroot.'/filter/mediaplugin/flvplayer.swf';
288     $output = <<<EOT
289     <span class="mediaplugin mediaplugin_flv" id="$id"></span>
290     <noscript><div>
291     <object width="800" height="600" id="undefined" name="undefined" data="$playerpath" type="application/x-shockwave-flash">
292     <param name="movie" value="$playerpath" />
293     <param name="allowfullscreen" value="true" />
294     <param name="allowscriptaccess" value="always" />
295     <param name="flashvars" value='config={"clip":{"url":"$url",
296                                                    "autoPlay": false},
297                                            "content":{"url":"$playerpath"}}}' />
298     </object>
299     </div></noscript>
300 EOT;
302     $jsoutput = create_flowplayer($id, $url, 'flv');
303     $output .= $jsoutput;
304     return $output;
307 function filter_mediaplugin_real_callback($link, $autostart=false) {
308     $url = addslashes_js($link[1]);
309     $mimetype = mimeinfo('type', $url);
310     $autostart = $autostart ? 'true' : 'false';
312 // embed kept for now see MDL-8674
313     return $link[0].
314 '<span class="mediaplugin mediaplugin_real">
315 <script type="text/javascript">
316 //<![CDATA[
317 document.write(\'<object classid="clsid:CFCDAA03-8BE4-11cf-B84B-0020AFBBCCFA" width="240" height="180">\\
318   <param name="src" value="'.$url.'" />\\
319   <param name="autostart" value="'.$autostart.'" />\\
320   <param name="controls" value="imagewindow" />\\
321   <param name="console" value="video" />\\
322   <param name="loop" value="true" />\\
323   <embed src="'.$url.'" width="240" height="180" loop="true" type="'.$mimetype.'" controls="imagewindow" console="video" autostart="'.$autostart.'" />\\
324   </object><br />\\
325   <object classid="clsid:CFCDAA03-8BE4-11cf-B84B-0020AFBBCCFA" width="240" height="30">\\
326   <param name="src" value="'.$url.'" />\\
327   <param name="autostart" value="'.$autostart.'" />\\
328   <param name="controls" value="ControlPanel" />\\
329   <param name="console" value="video" />\\
330   <embed src="'.$url.'" width="240" height="30" controls="ControlPanel" type="'.$mimetype.'" console="video" autostart="'.$autostart.'" />\\
331   </object>\');
332 //]]>
333 </script></span>';
336 /**
337  * Change links to Youtube into embedded Youtube videos
338  */
339 function filter_mediaplugin_youtube_callback($link, $autostart=false) {
341     $site = addslashes_js($link[1]);
342     $url = addslashes_js($link[2]);
343     $info = addslashes_js(strip_tags($link[3]));//strip out html tags as they won't work in the title attribute
345     return '<object title="'.$info.'"
346                     class="mediaplugin mediaplugin_youtube" type="application/x-shockwave-flash"
347                     data="'.$site.'youtube.com/v/'.$url.'&amp;fs=1&amp;rel=0" width="425" height="344">'.
348            '<param name="movie" value="'.$site.'youtube.com/v/'.$url.'&amp;fs=1&amp;rel=0" />'.
349            '<param name="FlashVars" value="playerMode=embedded" />'.
350            '<param name="wmode" value="transparent" />'.
351            '<param name="allowFullScreen" value="true" />'.
352            '</object>';
355 /**
356  * Change Youtube playlist into embedded Youtube playlist videos
357  */
358 function filter_mediaplugin_youtube_playlist_callback($link, $autostart=false) {
360     $site = s($link[4]);
361     $param = s($link[5]);
362     $info = s($link[6]);
364     return '<object title="'.$info.'"
365                     class="mediaplugin mediaplugin_youtube" type="application/x-shockwave-flash"
366                     data="'.$site.'youtube.com/p/'.$param.'&amp;fs=1&amp;rel=0" width="400" height="320">'.
367            '<param name="movie" value="'.$site.'youtube.com/p/'.$param.'&amp;fs=1&amp;rel=0" />'.
368            '<param name="FlashVars" value="playerMode=embedded" />'.
369            '<param name="wmode" value="transparent" />'.
370            '<param name="allowFullScreen" value="true" />'.
371            '</object>';
374 /**
375  * Change links to images into embedded images
376  */
377 function filter_mediaplugin_img_callback($link, $autostart=false) {
378     $url = addslashes_js($link[1]);
379     $info = addslashes_js($link[2]);
381     return '<img class="mediaplugin mediaplugin_img" alt="" title="'.$info.'" src="'.$url.'" />';
384 /**
385  * Embed video using window media player if available
386  */
387 function filter_mediaplugin_wmp_callback($link, $autostart=false) {
388     $url = $link[1];
389     if (empty($link[3]) or empty($link[4])) {
390         $mpsize = '';
391         $size = 'width="300" height="260"';
392         $autosize = 'true';
393     } else {
394         $size = 'width="'.$link[3].'" height="'.$link[4].'"';
395         $mpsize = $size;
396         $autosize = 'false';
397     }
398     $mimetype = mimeinfo('type', $url);
399     $autostart = $autostart ? 'true' : 'false';
401     return $link[0].
402 '<span class="mediaplugin mediaplugin_wmp">
403 <object classid="CLSID:6BF52A52-394A-11d3-B153-00C04F79FAA6" '.$mpsize.'
404   standby="Loading Microsoft(R) Windows(R) Media Player components..."
405   type="application/x-oleobject">
406  <param name="Filename" value="'.$url.'" />
407  <param name="src" value="'.$url.'" />
408  <param name="url" value="'.$url.'" />
409  <param name="ShowControls" value="true" />
410  <param name="AutoRewind" value="true" />
411  <param name="AutoStart" value="'.$autostart.'" />
412  <param name="Autosize" value="'.$autosize.'" />
413  <param name="EnableContextMenu" value="true" />
414  <param name="TransparentAtStart" value="false" />
415  <param name="AnimationAtStart" value="false" />
416  <param name="ShowGotoBar" value="false" />
417  <param name="EnableFullScreenControls" value="true" />
418 <!--[if !IE]>-->
419   <object data="'.$url.'" type="'.$mimetype.'" '.$size.'>
420    <param name="src" value="'.$url.'" />
421    <param name="controller" value="true" />
422    <param name="autoplay" value="'.$autostart.'" />
423    <param name="autostart" value="'.$autostart.'" />
424    <param name="resize" value="scale" />
425   </object>
426 <!--<![endif]-->
427 </object></span>';
430 function filter_mediaplugin_qt_callback($link, $autostart=false) {
431     $url = $link[1];
432     if (empty($link[3]) or empty($link[4])) {
433         $size = 'width="440" height="315"';
434     } else {
435         $size = 'width="'.$link[3].'" height="'.$link[4].'"';
436     }
437     $mimetype = mimeinfo('type', $url);
438     $autostart = $autostart ? 'true' : 'false';
440     return $link[0].
441 '<span class="mediaplugin mediaplugin_qt">
442 <object classid="clsid:02BF25D5-8C17-4B23-BC80-D3488ABDDC6B"
443   codebase="http://www.apple.com/qtactivex/qtplugin.cab" '.$size.'>
444  <param name="pluginspage" value="http://www.apple.com/quicktime/download/" />
445  <param name="src" value="'.$url.'" />
446  <param name="controller" value="true" />
447  <param name="loop" value="true" />
448  <param name="autoplay" value="'.$autostart.'" />
449  <param name="autostart" value="'.$autostart.'" />
450  <param name="scale" value="aspect" />
451 <!--[if !IE]>-->
452   <object data="'.$url.'" type="'.$mimetype.'" '.$size.'>
453    <param name="src" value="'.$url.'" />
454    <param name="pluginurl" value="http://www.apple.com/quicktime/download/" />
455    <param name="controller" value="true" />
456    <param name="loop" value="true" />
457    <param name="autoplay" value="'.$autostart.'" />
458    <param name="autostart" value="'.$autostart.'" />
459    <param name="scale" value="aspect" />
460   </object>
461 <!--<![endif]-->
462 </object></span>';