Permalink
Browse files

MDL-29624 Media embedding system, part 1: new API and filter changes

Includes new API in medialib.php and core_media_renderer in outputrenderers.php.
Enable/disable settings moved from filter to systemwide appearance page.
Filter changed to use new API.

AMOS BEGIN
 MOV [flashanimation,filter_mediaplugin],[flashanimation,core_media]
 MOV [flashanimation_help,filter_mediaplugin],[flashanimation_desc,core_media]
 MOV [flashvideo,filter_mediaplugin],[flashvideo,core_media]
 MOV [flashvideo_help,filter_mediaplugin],[flashvideo_desc,core_media]
 MOV [html5audio,filter_mediaplugin],[html5audio,core_media]
 MOV [html5audio_help,filter_mediaplugin],[html5audio_desc,core_media]
 MOV [html5video,filter_mediaplugin],[html5video,core_media]
 MOV [html5video_help,filter_mediaplugin],[html5video_desc,core_media]
 MOV [mp3audio,filter_mediaplugin],[mp3audio,core_media]
 MOV [mp3audio_help,filter_mediaplugin],[mp3audio_desc,core_media]
 MOV [legacyquicktime,filter_mediaplugin],[legacyquicktime,core_media]
 MOV [legacyquicktime_help,filter_mediaplugin],[legacyquicktime_desc,core_media]
 MOV [legacyreal,filter_mediaplugin],[legacyreal,core_media]
 MOV [legacyreal_help,filter_mediaplugin],[legacyreal_desc,core_media]
 MOV [legacywmp,filter_mediaplugin],[legacywmp,core_media]
 MOV [legacywmp_help,filter_mediaplugin],[legacywmp_desc,core_media]
 MOV [legacyheading,filter_mediaplugin],[legacyheading,core_media]
 MOV [legacyheading_help,filter_mediaplugin],[legacyheading_desc,core_media]
 MOV [sitevimeo,filter_mediaplugin],[sitevimeo,core_media]
 MOV [sitevimeo_help,filter_mediaplugin],[sitevimeo_desc,core_media]
 MOV [siteyoutube,filter_mediaplugin],[siteyoutube,core_media]
 MOV [siteyoutube_help,filter_mediaplugin],[siteyoutube_desc,core_media]
AMOS END
  • Loading branch information...
1 parent ffe4de9 commit daefd6eb9736473720749af4f2bff8ee1ae2a3e7 @sammarshallou sammarshallou committed Dec 13, 2011
View
43 admin/settings/appearance.php
@@ -115,6 +115,49 @@
$ADMIN->add('appearance', new admin_externalpage('resetemoticons', new lang_string('emoticonsreset', 'admin'),
new moodle_url('/admin/resetemoticons.php'), 'moodle/site:config', true));
+
+ // The "media" subpage.
+ $temp = new admin_settingpage('mediasettings', get_string('mediasettings', 'core_media'));
+
+ $temp->add(new admin_setting_heading('mediaformats', get_string('mediaformats', 'core_media'),
+ format_text(get_string('mediaformats_desc', 'core_media'), FORMAT_MARKDOWN)));
+
+ // External services.
+ $temp->add(new admin_setting_configcheckbox('core_media_enable_youtube',
+ get_string('siteyoutube', 'core_media'), get_string('siteyoutube_desc', 'core_media'), 1));
+ $temp->add(new admin_setting_configcheckbox('core_media_enable_vimeo',
+ get_string('sitevimeo', 'core_media'), get_string('sitevimeo_desc', 'core_media'), 0));
+
+ // Options which require Flash.
+ $temp->add(new admin_setting_configcheckbox('core_media_enable_mp3',
+ get_string('mp3audio', 'core_media'), get_string('mp3audio_desc', 'core_media'), 1));
+ $temp->add(new admin_setting_configcheckbox('core_media_enable_flv',
+ get_string('flashvideo', 'core_media'), get_string('flashvideo_desc', 'core_media'), 1));
+ $temp->add(new admin_setting_configcheckbox('core_media_enable_swf',
+ get_string('flashanimation', 'core_media'), get_string('flashanimation_desc', 'core_media'), 1));
+
+ // HTML 5 media.
+ // Audio now enabled by default so that it can provide a fallback for mp3 on devices without flash.
+ $temp->add(new admin_setting_configcheckbox('core_media_enable_html5audio',
+ get_string('html5audio', 'core_media'), get_string('html5audio_desc', 'core_media'), 1));
+ // Video now enabled by default so it can provide mp4 support.
+ $temp->add(new admin_setting_configcheckbox('core_media_enable_html5video',
+ get_string('html5video', 'core_media'), get_string('html5video_desc', 'core_media'), 1));
+
+ // Legacy players.
+ $temp->add(new admin_setting_heading('legacymediaformats',
+ get_string('legacyheading', 'core_media'), get_string('legacyheading_desc', 'core_media')));
+
+ $temp->add(new admin_setting_configcheckbox('core_media_enable_qt',
+ get_string('legacyquicktime', 'core_media'), get_string('legacyquicktime_desc', 'core_media'), 1));
+ $temp->add(new admin_setting_configcheckbox('core_media_enable_wmp',
+ get_string('legacywmp', 'core_media'), get_string('legacywmp_desc', 'core_media'), 1));
+ $temp->add(new admin_setting_configcheckbox('core_media_enable_rm',
+ get_string('legacyreal', 'core_media'), get_string('legacyreal_desc', 'core_media'), 1));
+
+ $ADMIN->add('appearance', $temp);
+
+
// "documentation" settingpage
$temp = new admin_settingpage('documentation', new lang_string('moodledocs'));
$temp->add(new admin_setting_configtext('docroot', new lang_string('docroot', 'admin'), new lang_string('configdocroot', 'admin'), 'http://docs.moodle.org', PARAM_URL));
View
1 filter/mediaplugin/db/install.php
@@ -1,5 +1,4 @@
<?php
-
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
View
21 filter/mediaplugin/db/upgrade.php
@@ -33,5 +33,26 @@ function xmldb_filter_mediaplugin_upgrade($oldversion) {
$dbman = $DB->get_manager();
+ if ($oldversion < 2011121200) {
+ // Move all the media enable setttings that are now handled by core media renderer.
+ foreach (array('html5video', 'html5audio', 'mp3', 'flv', 'wmp', 'qt', 'rm',
+ 'youtube', 'vimeo', 'swf') as $type) {
+ $existingkey = 'filter_mediaplugin_enable_' . $type;
+ if (array_key_exists($existingkey, $CFG)) {
+ set_config('core_media_enable_' . $type, $CFG->{$existingkey});
+ unset_config($existingkey);
+ }
+ }
+
+ // Override setting for html5 to turn it on (previous default was off; because
+ // of changes in the way fallbacks are handled, this is now unlikely to cause
+ // a problem, and is required for mobile a/v support on non-Flash devices, so
+ // this change is basically needed in order to maintain existing behaviour).
+ set_config('core_media_enable_html5video', 1);
+ set_config('core_media_enable_html5audio', 1);
+
+ upgrade_plugin_savepoint(true, 2011121200, 'filter', 'mediaplugin');
+ }
+
return true;
}
View
173 filter/mediaplugin/dev/perftest.php
@@ -0,0 +1,173 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Media filter performance test script.
+ *
+ * For developer test usage only. This can be used to compare performance if
+ * there are changes to the system in future.
+ *
+ * @copyright 2012 The Open University
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @package filter_mediaplugin
+ */
+
+require(dirname(__FILE__) . '/../../../config.php');
+require_once($CFG->dirroot . '/filter/mediaplugin/filter.php');
+
+// Only available to site admins.
+require_login();
+if (!is_siteadmin()) {
+ print_error('nopermissions', 'error', '', 'perftest');
+}
+
+// Set up page.
+$PAGE->set_context(context_system::instance());
+$PAGE->set_url(new moodle_url('/filter/mediaplugin/perftest.php'));
+$PAGE->set_heading($SITE->fullname);
+print $OUTPUT->header();
+
+// Hack setup to enable all players.
+$CFG->core_media_enable_youtube = 1;
+$CFG->core_media_enable_vimeo = 1;
+$CFG->core_media_enable_mp3 = 1;
+$CFG->core_media_enable_flv = 1;
+$CFG->core_media_enable_swf = 1;
+$CFG->core_media_enable_html5audio = 1;
+$CFG->core_media_enable_html5video = 1;
+$CFG->core_media_enable_qt = 1;
+$CFG->core_media_enable_wmp = 1;
+$CFG->core_media_enable_rm = 1;
+
+$CFG->filter_mediaplugin_enable_youtube = 1;
+$CFG->filter_mediaplugin_enable_vimeo = 1;
+$CFG->filter_mediaplugin_enable_mp3 = 1;
+$CFG->filter_mediaplugin_enable_flv = 1;
+$CFG->filter_mediaplugin_enable_swf = 1;
+$CFG->filter_mediaplugin_enable_html5audio = 1;
+$CFG->filter_mediaplugin_enable_html5video = 1;
+$CFG->filter_mediaplugin_enable_qt = 1;
+$CFG->filter_mediaplugin_enable_wmp = 1;
+$CFG->filter_mediaplugin_enable_rm = 1;
+
+// Create plugin.
+$filterplugin = new filter_mediaplugin(null, array());
+
+// Note: As this is a developer test page, language strings are not used: all
+// text is English-only.
+
+/**
+ * Starts time counter.
+ */
+function filter_mediaplugin_perf_start() {
+ global $filter_mediaplugin_starttime;
+ $filter_mediaplugin_starttime = microtime(true);
+}
+
+/**
+ * Ends and displays time counter.
+ * @param string $name Counter name to display
+ */
+function filter_mediaplugin_perf_stop($name) {
+ global $filter_mediaplugin_starttime;
+ $time = microtime(true) - $filter_mediaplugin_starttime;
+
+ echo html_writer::tag('li', $name . ': ' . html_writer::tag('strong', round($time, 2)) . 'ms');
+}
+
+// 1) Some sample text strings.
+// Note: These are from a random sample of real forum data. Just in case there
+// are any privacy concerns I have altered names as may be clear.
+$samples = array(
+ "<p>Hi,</p>&#13;\n<p>I've got myself 2 Heaney's \"The Burial at Thebes\"</p>",
+ "best mark iv heard so far v v good",
+ "<p>I have a script draft anyone want to look at it?",
+ "<p>Thanks for your input Legolas and Ghimli!</p>",
+ "<p>Just to say that I'm thinking of those of you who are working on TMA02.</p>",
+ "<p><strong>1.</strong> <strong>If someone asks you 'where do you come from?'</strong></p>",
+ "<p>With regards to Aragorn's question 'what would we do different'?</p>&#13;\n",
+ "<p>Just thought I'd drop a line to see how everyone is managing generally?</p>&#13;\n",
+ "<p>Feb '12 - Oct '12  AA100</p>&#13;\n<p>Nov '12 - April '13 - A150</p>&#13;\n",
+ "<p>So where does that leave the bible???</p>",
+);
+
+// 2) Combine sample text strings into one really big (20KB) string.
+$length = 0;
+$bigstring = '';
+$index = 0;
+while ($length < 20 * 1024) {
+ $bigstring .= $samples[$index];
+ $length += strlen($samples[$index]);
+ $index++;
+ if ($index >= count($samples)) {
+ $index = 0;
+ }
+}
+
+// 3) Make random samples from this. I did the following stats on recent forum
+// posts:
+// 0-199 characters approx 30%
+// 200-1999 approx 60%
+// 2000-19999 approx 10%.
+
+$samplebank = array();
+foreach (array(100 => 300, 1000 => 600, 10000 => 100) as $chars => $num) {
+ for ($i = 0; $i < $num; $i++) {
+ $start = rand(0, $length - $chars - 1);
+ $samplebank[] = substr($bigstring, $start, $chars);
+ }
+}
+
+echo html_writer::start_tag('ul');
+
+// First test: filter text that doesn't have any links.
+filter_mediaplugin_perf_start();
+foreach ($samplebank as $sample) {
+ $filterplugin->filter($sample);
+}
+filter_mediaplugin_perf_stop('No links');
+
+// Second test: filter text with one link added (that doesn't match).
+$link = '<a href="http://www.example.org/another/link/">Link</a>';
+$linksamples = array();
+foreach ($samplebank as $sample) {
+ // Make it the same length but with $link replacing the end part.
+ $linksamples[] = substr($sample, 0, -strlen($link)) . $link;
+}
+
+filter_mediaplugin_perf_start();
+foreach ($linksamples as $sample) {
+ $filterplugin->filter($sample);
+}
+filter_mediaplugin_perf_stop('One link (no match)');
+
+// Third test: filter text with one link added that does match (mp3).
+$link = '<a href="http://www.example.org/another/file.mp3">MP3 audio</a>';
+$linksamples = array();
+foreach ($samplebank as $sample) {
+ // Make it the same length but with $link replacing the end part.
+ $linksamples[] = substr($sample, 0, -strlen($link)) . $link;
+}
+
+filter_mediaplugin_perf_start();
+foreach ($linksamples as $sample) {
+ $filterplugin->filter($sample);
+}
+filter_mediaplugin_perf_stop('One link (mp3)');
+
+// End page.
+echo html_writer::end_tag('ul');
+print $OUTPUT->footer();
View
890 filter/mediaplugin/filter.php
@@ -28,31 +28,6 @@
defined('MOODLE_INTERNAL') || die();
-require_once($CFG->libdir.'/filelib.php');
-
-if (!defined('FILTER_MEDIAPLUGIN_VIDEO_WIDTH')) {
- /**
- * Default media width, some plugins may use automatic sizes or accept resize parameters.
- * This can be defined in config.php.
- */
- define('FILTER_MEDIAPLUGIN_VIDEO_WIDTH', 400);
-}
-
-if (!defined('FILTER_MEDIAPLUGIN_VIDEO_HEIGHT')) {
- /**
- * Default video height, plugins that know aspect ration
- * should calculate it themselves using the FILTER_MEDIAPLUGIN_VIDEO_HEIGHT
- * This can be defined in config.php.
- */
- define('FILTER_MEDIAPLUGIN_VIDEO_HEIGHT', 300);
-}
-
-
-//TODO: we should use /u modifier in regex, unfortunately it may not work properly on some misconfigured servers, see lib/filter/urltolink/filter.php ...
-
-//TODO: we should migrate to proper config_plugin settings ...
-
-
/**
* Automatic media embedding filter class.
*
@@ -65,850 +40,93 @@
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class filter_mediaplugin extends moodle_text_filter {
+ /** @var bool True if currently filtering trusted text */
+ private $trusted;
+ /** @var core_media_renderer Media renderer */
+ private $mediarenderer;
+ /** @var string Partial regex pattern indicating possible embeddable content */
+ private $embedmarkers;
- function filter($text, array $options = array()) {
- global $CFG;
+ public function filter($text, array $options = array()) {
+ global $CFG, $PAGE;
if (!is_string($text) or empty($text)) {
// non string data can not be filtered anyway
return $text;
}
+
if (stripos($text, '</a>') === false) {
- // performance shortcut - all regexes below end with the </a> tag,
- // if not present nothing can match
+ // Performance shortcut - if not </a> tag, nothing can match.
return $text;
}
- $newtext = $text; // we need to return the original value if regex fails!
-
- // YouTube and Vimeo are great because the files are not served by Moodle server
-
- if (!empty($CFG->filter_mediaplugin_enable_youtube)) {
- $search = '/<a\s[^>]*href="(https?:\/\/www\.youtube(-nocookie)?\.com)\/watch\?v=([a-z0-9\-_]+)[^"#]*(#d=([\d]{1,4})x([\d]{1,4}))?"[^>]*>([^>]*)<\/a>/is';
- $newtext = preg_replace_callback($search, 'filter_mediaplugin_youtube_callback', $newtext);
-
- $search = '/<a\s[^>]*href="(https?:\/\/www\.youtube(-nocookie)?\.com)\/v\/([a-z0-9\-_]+)[^"#]*(#d=([\d]{1,4})x([\d]{1,4}))?[^>]*>([^>]*)<\/a>/is';
- $newtext = preg_replace_callback($search, 'filter_mediaplugin_youtube_callback', $newtext);
-
- $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';
- $newtext = preg_replace_callback($search, 'filter_mediaplugin_youtube_playlist_callback', $newtext);
-
- $search = '/<a\s[^>]*href="(https?:\/\/www\.youtube(-nocookie)?\.com)\/p\/([a-z0-9\-_]+)[^"#]*(#d=([\d]{1,4})x([\d]{1,4}))?[^>]*>([^>]*)<\/a>/is';
- $newtext = preg_replace_callback($search, 'filter_mediaplugin_youtube_playlist_callback', $newtext);
- }
-
- if (!empty($CFG->filter_mediaplugin_enable_vimeo)) {
- $search = '/<a\s[^>]*href="http:\/\/vimeo\.com\/([0-9]+)[^"#]*(#d=([\d]{1,4})x([\d]{1,4}))?[^>]*>([^>]*)<\/a>/is';
- $newtext = preg_replace_callback($search, 'filter_mediaplugin_vimeo_callback', $newtext);
- }
-
-
- // HTML 5 audio and video tags are the future! If only if vendors decided to use just one audio and video format...
-
- if (!empty($CFG->filter_mediaplugin_enable_html5audio)) {
- $search = '/<a\s[^>]*href="([^"#\?]+\.(ogg|oga|aac|m4a)([#\?][^"]*)?)"[^>]*>([^>]*)<\/a>/is';
- $newtext = preg_replace_callback($search, 'filter_mediaplugin_html5audio_callback', $newtext);
- }
-
- if (!empty($CFG->filter_mediaplugin_enable_html5video)) {
- $search = '/<a\s[^>]*href="([^"#\?]+\.(m4v|webm|ogv|mp4)([#\?][^"]*)?)"[^>]*>([^>]*)<\/a>/is';
- $newtext = preg_replace_callback($search, 'filter_mediaplugin_html5video_callback', $newtext);
- }
-
-
- // Flash stuff
-
- if (!empty($CFG->filter_mediaplugin_enable_mp3)) {
- $search = '/<a\s[^>]*href="([^"#\?]+\.mp3)"[^>]*>([^>]*)<\/a>/is';
- $newtext = preg_replace_callback($search, 'filter_mediaplugin_mp3_callback', $newtext);
- }
-
- if ((!empty($options['noclean']) or !empty($CFG->allowobjectembed)) and !empty($CFG->filter_mediaplugin_enable_swf)) {
- $search = '/<a\s[^>]*href="([^"#\?]+\.swf)([#\?]d=([\d]{1,4})x([\d]{1,4}))?"[^>]*>([^>]*)<\/a>/is';
- $newtext = preg_replace_callback($search, 'filter_mediaplugin_swf_callback', $newtext);
+ if (!$this->mediarenderer) {
+ $this->mediarenderer = $PAGE->get_renderer('core', 'media');
+ $this->embedmarkers = $this->mediarenderer->get_embeddable_markers();
}
- if (!empty($CFG->filter_mediaplugin_enable_flv)) {
- $search = '/<a\s[^>]*href="([^"#\?]+\.(flv|f4v)([#\?][^"]*)?)"[^>]*>([^>]*)<\/a>/is';
- $newtext = preg_replace_callback($search, 'filter_mediaplugin_flv_callback', $newtext);
- }
-
-
- // The rest of legacy formats - these should not be used if possible
-
- if (!empty($CFG->filter_mediaplugin_enable_wmp)) {
- $search = '/<a\s[^>]*href="([^"#\?]+\.(wmv|avi))(\?d=([\d]{1,4})x([\d]{1,4}))?"[^>]*>([^>]*)<\/a>/is';
- $newtext = preg_replace_callback($search, 'filter_mediaplugin_wmp_callback', $newtext);
- }
-
- if (!empty($CFG->filter_mediaplugin_enable_qt)) {
- // HTML5 filtering may steal mpeg 4 formats
- $search = '/<a\s[^>]*href="([^"#\?]+\.(mpg|mpeg|mov|mp4|m4v|m4a))(\?d=([\d]{1,4})x([\d]{1,4}))?"[^>]*>([^>]*)<\/a>/is';
- $newtext = preg_replace_callback($search, 'filter_mediaplugin_qt_callback', $newtext);
- }
-
- if (!empty($CFG->filter_mediaplugin_enable_rm)) {
- // hopefully nobody is using this any more!!
- // rpm is redhat packaging format these days, it is better to prevent these in default installs
-
- $search = '/<a\s[^>]*href="([^"#\?]+\.(ra|ram|rm|rv))"[^>]*>([^>]*)<\/a>/is';
- $newtext = preg_replace_callback($search, 'filter_mediaplugin_real_callback', $newtext);
- }
+ // Check SWF permissions.
+ $this->trusted = !empty($options['noclean']) or !empty($CFG->allowobjectembed);
+ // Handle all links that contain any 'embeddable' marker text (it could
+ // do all links, but the embeddable markers thing should make it faster
+ // by meaning for most links it doesn't drop into PHP code).
+ $newtext = preg_replace_callback($re = '~<a\s[^>]*href="([^"]*(?:' .
+ $this->embedmarkers . ')[^"]*)"[^>]*>([^>]*)</a>~is',
+ array($this, 'callback'), $text);
if (empty($newtext) or $newtext === $text) {
// error or not filtered
- unset($newtext);
return $text;
}
-
return $newtext;
}
-}
-
-
-///===========================
-/// utility functions
-
-/**
- * Get mimetype of given url, useful for # alternative urls.
- *
- * @private
- * @param string $url
- * @return string $mimetype
- */
-function filter_mediaplugin_get_mimetype($url) {
- $matches = null;
- if (preg_match("|^(.*)/[a-z]*file.php(\?file=)?(/[^&\?#]*)|", $url, $matches)) {
- // remove the special moodle file serving hacks so that the *file.php is ignored
- $url = $matches[1].$matches[3];
- } else {
- $url = preg_replace('/[#\?].*$/', '', $url);
- }
-
- $mimetype = mimeinfo('type', $url);
-
- return $mimetype;
-}
-
-/**
- * Parse list of alternative URLs
- * @param string $url urls separated with '#', size specified as ?d=640x480 or #d=640x480
- * @param int $defaultwidth
- * @param int $defaultheight
- * @return array (urls, width, height)
- */
-function filter_mediaplugin_parse_alternatives($url, $defaultwidth = 0, $defaultheight = 0) {
- $urls = explode('#', $url);
- $width = $defaultwidth;
- $height = $defaultheight;
- $returnurls = array();
-
- foreach ($urls as $url) {
- $matches = null;
-
- if (preg_match('/^d=([\d]{1,4})x([\d]{1,4})$/i', $url, $matches)) { // #d=640x480
- $width = $matches[1];
- $height = $matches[2];
- continue;
- }
- if (preg_match('/\?d=([\d]{1,4})x([\d]{1,4})$/i', $url, $matches)) { // old style file.ext?d=640x480
- $width = $matches[1];
- $height = $matches[2];
- $url = str_replace($matches[0], '', $url);
- }
-
- $url = str_replace('&amp;', '&', $url);
- $url = clean_param($url, PARAM_URL);
- if (empty($url)) {
- continue;
- }
-
- $returnurls[] = $url;
- }
- return array($returnurls, $width, $height);
-}
-
-/**
- * Should the current tag be ignored in this filter?
- * @param string $tag
- * @return bool
- */
-function filter_mediaplugin_ignore($tag) {
- if (preg_match('/class="[^"]*nomediaplugin/i', $tag)) {
- return true;
- } else {
- false;
- }
-}
-
-///===========================
-/// callback filter functions
-
-
-/**
- * Replace audio links with audio tag.
- *
- * @param array $link
- * @return string
- */
-function filter_mediaplugin_html5audio_callback(array $link) {
- global $CFG;
-
- if (filter_mediaplugin_ignore($link[0])) {
- return $link[0];
- }
-
- $info = trim($link[4]);
- if (empty($info) or strpos($info, 'http') === 0) {
- $info = get_string('fallbackaudio', 'filter_mediaplugin');
- }
-
- list($urls, $ignorewidth, $ignoredheight) = filter_mediaplugin_parse_alternatives($link[1]);
-
- $fallbackurl = null;
- $fallbackmime = null;
- $sources = array();
- $fallbacklink = null;
-
- foreach ($urls as $url) {
- $mimetype = filter_mediaplugin_get_mimetype($url);
- if (strpos($mimetype, 'audio/') !== 0) {
- continue;
+ /**
+ * Replace link with embedded content, if supported.
+ *
+ * @param array $matches
+ * @return string
+ */
+ private function callback(array $matches) {
+ global $CFG, $PAGE;
+ // Check if we ignore it.
+ if (preg_match('/class="[^"]*nomediaplugin/i', $matches[0])) {
+ return $matches[0];
}
- $sources[] = html_writer::tag('source', '', array('src' => $url, 'type' => $mimetype));
- if ($fallbacklink === null) {
- $fallbacklink = html_writer::link($url.'#', $info); // the extra '#' prevents linking in mp3 filter below
+ // Get name.
+ $name = trim($matches[2]);
+ if (empty($name) or strpos($name, 'http') === 0) {
+ $name = ''; // Use default name.
}
- if ($fallbackurl === null) {
- if ($mimetype === 'audio/mp3' or $mimetype === 'audio/aac') {
- $fallbackurl = str_replace('&', '&amp;', $url);
- $fallbackmime = $mimetype;
- }
- }
- }
- if (!$sources) {
- return $link[0];
- }
-
- if ($fallbackmime !== null) {
- // fallback to quicktime
- $fallback = <<<OET
-<object classid="clsid:02BF25D5-8C17-4B23-BC80-D3488ABDDC6B" codebase="http://www.apple.com/qtactivex/qtplugin.cab" width="200" height="20">
- <param name="pluginspage" value="http://www.apple.com/quicktime/download/" />
- <param name="src" value="$fallbackurl" />
- <param name="controller" value="true" />
- <param name="loop" value="false" />
- <param name="autoplay" value="false" />
- <param name="autostart" value="false" />
- <param name="scale" value="aspect" />
- $fallbacklink
-<!--[if !IE]>-->
- <object data="$fallbackurl" type="$fallbackmime" width="200" height="20">
- <param name="src" value="$fallbackurl" />
- <param name="pluginurl" value="http://www.apple.com/quicktime/download/" />
- <param name="controller" value="true" />
- <param name="loop" value="false" />
- <param name="autoplay" value="false" />
- <param name="autostart" value="false" />
- <param name="scale" value="aspect" />
- $fallbacklink
- </object>
-<!--<![endif]-->
-</object>
-OET;
- } else {
- $fallback = $fallbacklink;
- }
-
- $sources = implode("\n", $sources);
- $title = s($info);
- // audio players are supposed to be inline elements
- $output = <<<OET
-<audio controls="true" width="200" class="mediaplugin mediaplugin_html5audio" preload="no" title="$title">
-$sources
-$fallback
-</audio>
-OET;
-
- return $output;
-}
+ // Split provided URL into alternatives.
+ $urls = core_media::split_alternatives($matches[1], $width, $height);
-/**
- * Replace ogg video links with video tag.
- *
- * Please note this is not going to work in all browsers,
- * it is also not xhtml strict.
- *
- * @param array $link
- * @return string
- */
-function filter_mediaplugin_html5video_callback(array $link) {
+ $options = array();
- if (filter_mediaplugin_ignore($link[0])) {
- return $link[0];
- }
-
- $info = trim($link[4]);
- if (empty($info) or strpos($info, 'http') === 0) {
- $info = get_string('fallbackvideo', 'filter_mediaplugin');
- }
-
- list($urls, $width, $height) = filter_mediaplugin_parse_alternatives($link[1], FILTER_MEDIAPLUGIN_VIDEO_WIDTH, 0);
-
- $fallbackurl = null;
- $fallbackmime = null;
- $sources = array();
- $fallbacklink = null;
-
- foreach ($urls as $url) {
- $mimetype = filter_mediaplugin_get_mimetype($url);
- if (strpos($mimetype, 'video/') !== 0) {
- continue;
- }
- $source = html_writer::tag('source', '', array('src' => $url, 'type' => $mimetype));
- if ($mimetype === 'video/mp4') {
- // better add m4v as first source, it might be a bit more compatible with problematic browsers
- array_unshift($sources, $source);
- } else {
- $sources[] = $source;
+ // Allow SWF (or not).
+ if ($this->trusted) {
+ $options[core_media::OPTION_TRUSTED] = true;
}
- if ($fallbacklink === null) {
- $fallbacklink = html_writer::link($url.'#', $info); // the extra '#' prevents linking in mp3 filter below
- }
- if ($fallbackurl === null) {
- if ($mimetype === 'video/mp4') {
- $fallbackurl = str_replace('&', '&amp;', $url);
- $fallbackmime = $mimetype;
- }
- }
- }
- if (!$sources) {
- return $link[0];
- }
-
- if ($fallbackmime !== null) {
- $qtheight = ($height == 0) ? FILTER_MEDIAPLUGIN_VIDEO_HEIGHT : ($height + 15);
- // fallback to quicktime
- $fallback = <<<OET
-<object classid="clsid:02BF25D5-8C17-4B23-BC80-D3488ABDDC6B" codebase="http://www.apple.com/qtactivex/qtplugin.cab" width="$width" height="$qtheight">
- <param name="pluginspage" value="http://www.apple.com/quicktime/download/" />
- <param name="src" value="$fallbackurl" />
- <param name="controller" value="true" />
- <param name="loop" value="false" />
- <param name="autoplay" value="false" />
- <param name="autostart" value="false" />
- <param name="scale" value="aspect" />
- $fallbacklink
-<!--[if !IE]>-->
- <object data="$fallbackurl" type="$fallbackmime" width="$width" height="$qtheight">
- <param name="src" value="$fallbackurl" />
- <param name="pluginurl" value="http://www.apple.com/quicktime/download/" />
- <param name="controller" value="true" />
- <param name="loop" value="false" />
- <param name="autoplay" value="false" />
- <param name="autostart" value="false" />
- <param name="scale" value="aspect" />
- $fallbacklink
- </object>
-<!--<![endif]-->
-</object>
-OET;
- } else {
- $fallback = $fallbacklink;
- }
-
- $sources = implode("\n", $sources);
- $title = s($info);
-
- if (empty($height)) {
- // automatic height
- $size = "width=\"$width\"";
- } else {
- $size = "width=\"$width\" height=\"$height\"";
- }
-
- $output = <<<OET
-<span class="mediaplugin mediaplugin_html5video">
-<video controls="true" $size preload="metadata" title="$title">
-$sources
-$fallback
-</video>
-</span>
-OET;
-
- return $output;
-}
-
-/**
- * Replace mp3 links with small audio player.
- *
- * @param $link
- * @return string
- */
-function filter_mediaplugin_mp3_callback($link) {
- static $count = 0;
-
- if (filter_mediaplugin_ignore($link[0])) {
- return $link[0];
- }
-
- $count++;
- $id = 'filter_mp3_'.time().'_'.$count; //we need something unique because it might be stored in text cache
-
- $url = $link[1];
- $rawurl = str_replace('&amp;', '&', $url);
-
- $info = trim($link[2]);
- if (empty($info) or strpos($info, 'http') === 0) {
- $info = get_string('mp3audio', 'filter_mediaplugin');
-
- }
- $printlink = html_writer::link($rawurl, $info, array('class'=>'mediafallbacklink'));
-
- //note: when flash or javascript not available only the $printlink is displayed,
- // audio players are supposed to be inline elements
+ // We could test whether embed is possible using can_embed, but to save
+ // time, let's just embed it with the 'fallback to blank' option which
+ // does most of the same stuff anyhow.
+ $options[core_media::OPTION_FALLBACK_TO_BLANK] = true;
- $output = html_writer::tag('span', $printlink, array('id'=>$id, 'class'=>'mediaplugin mediaplugin_mp3'));
- $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
+ // NOTE: Options are not passed through from filter because the 'embed'
+ // code does not recognise filter options (it's a different kind of
+ // option-space) as it can be used in non-filter situations.
+ $result = $this->mediarenderer->embed_alternatives($urls, $name, $width, $height, $options);
- return $output;
-}
-
-/**
- * Replace swf links with embedded flash objects.
- *
- * Please note this is not a secure and is recommended to be disabled on production systems.
- *
- * @deprecated
- * @param $link
- * @return string
- */
-function filter_mediaplugin_swf_callback($link) {
-
- if (filter_mediaplugin_ignore($link[0])) {
- return $link[0];
- }
-
- $width = empty($link[3]) ? FILTER_MEDIAPLUGIN_VIDEO_WIDTH : $link[3];
- $height = empty($link[4]) ? FILTER_MEDIAPLUGIN_VIDEO_HEIGHT : $link[4];
-
- $url = $link[1];
- $rawurl = str_replace('&amp;', '&', $url);
-
- $info = trim($link[5]);
- if (empty($info) or strpos($info, 'http') === 0) {
- $info = get_string('flashanimation', 'filter_mediaplugin');
-
- }
- $printlink = html_writer::link($rawurl, $info, array('class'=>'mediafallbacklink'));
-
- $output = <<<OET
-<span class="mediaplugin mediaplugin_swf">
- <object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" width="$width" height="$height">
- <param name="movie" value="$url" />
- <param name="autoplay" value="true" />
- <param name="loop" value="true" />
- <param name="controller" value="true" />
- <param name="scale" value="aspect" />
- <param name="base" value="." />
- <param name="allowscriptaccess" value="never" />
-<!--[if !IE]>-->
- <object type="application/x-shockwave-flash" data="$url" width="$width" height="$height">
- <param name="controller" value="true" />
- <param name="autoplay" value="true" />
- <param name="loop" value="true" />
- <param name="scale" value="aspect" />
- <param name="base" value="." />
- <param name="allowscriptaccess" value="never" />
-<!--<![endif]-->
-$printlink
-<!--[if !IE]>-->
- </object>
-<!--<![endif]-->
- </object>
-</span>
-OET;
-
- return $output;
-
-}
-
-/**
- * Replace flv links with flow player.
- *
- * @param $link
- * @return string
- */
-function filter_mediaplugin_flv_callback($link) {
- static $count = 0;
-
- if (filter_mediaplugin_ignore($link[0])) {
- return $link[0];
- }
-
- $count++;
- $id = 'filter_flv_'.time().'_'.$count; //we need something unique because it might be stored in text cache
-
- list($urls, $width, $height) = filter_mediaplugin_parse_alternatives($link[1], 0, 0);
-
- $autosize = false;
- if (!$width and !$height) {
- $width = FILTER_MEDIAPLUGIN_VIDEO_WIDTH;
- $height = FILTER_MEDIAPLUGIN_VIDEO_HEIGHT;
- $autosize = true;
- }
-
- $flashurl = null;
- $sources = array();
-
- foreach ($urls as $url) {
- $mimetype = filter_mediaplugin_get_mimetype($url);
- if (strpos($mimetype, 'video/') !== 0) {
- continue;
- }
- $source = html_writer::tag('source', '', array('src' => $url, 'type' => $mimetype));
- if ($mimetype === 'video/mp4') {
- // better add m4v as first source, it might be a bit more compatible with problematic browsers
- array_unshift($sources, $source);
+ // If something was embedded, return it, otherwise return original.
+ if ($result !== '') {
+ return $result;
} else {
- $sources[] = $source;
- }
-
- if ($flashurl === null) {
- $flashurl = $url;
+ return $matches[0];
}
}
- if (!$sources) {
- return $link[0];
- }
-
- $info = trim($link[4]);
- if (empty($info) or strpos($info, 'http') === 0) {
- $info = get_string('fallbackvideo', 'filter_mediaplugin');
- }
- $printlink = html_writer::link($flashurl.'#', $info, array('class'=>'mediafallbacklink')); // the '#' prevents the QT filter
-
- $title = s($info);
-
- if (count($sources) > 1) {
- $sources = implode("\n", $sources);
-
- // html 5 fallback
- $printlink = <<<OET
-<video controls="true" width="$width" height="$height" preload="metadata" title="$title">
-$sources
-$printlink
-</video>
-<noscript><br />
-$printlink
-</noscript>
-OET;
- }
-
- // note: no need to print "this is flv link" because it is printed automatically if JS or Flash not available
-
- $output = html_writer::tag('span', $printlink, array('id'=>$id, 'class'=>'mediaplugin mediaplugin_flv'));
- $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
-
- return $output;
}
-
-/**
- * Replace real media links with real player.
- *
- * Note: hopefully nobody is using this obsolete format any more.
- *
- * @deprectated
- * @param $link
- * @return string
- */
-function filter_mediaplugin_real_callback($link) {
-
- if (filter_mediaplugin_ignore($link[0])) {
- return $link[0];
- }
-
- $url = $link[1];
- $rawurl = str_replace('&amp;', '&', $url);
-
- //Note: the size is hardcoded intentionally because this does not work anyway!
-
- $width = FILTER_MEDIAPLUGIN_VIDEO_WIDTH;
- $height = FILTER_MEDIAPLUGIN_VIDEO_HEIGHT;
-
- $info = trim($link[3]);
- if (empty($info) or strpos($info, 'http') === 0) {
- $info = get_string('fallbackvideo', 'filter_mediaplugin');
- }
- $printlink = html_writer::link($rawurl, $info, array('class'=>'mediafallbacklink'));
-
- return <<<OET
-<span class="mediaplugin mediaplugin_real">
- $printlink <br />
- <object title="$info" classid="clsid:CFCDAA03-8BE4-11cf-B84B-0020AFBBCCFA" data="$url" width="$width" height="$height"">
- <param name="src" value="$url" />
- <param name="controls" value="All" />
-<!--[if !IE]>-->
- <object title="$info" type="audio/x-pn-realaudio-plugin" data="$url" width="$width" height="$height">
- <param name="src" value="$url" />
- <param name="controls" value="All" />
-<!--<![endif]-->
-<!--[if !IE]>-->
- </object>
-<!--<![endif]-->
- </object>
-</span>
-OET;
-}
-
-/**
- * Change links to YouTube into embedded YouTube videos
- *
- * Note: resizing via url is not supported, user can click the fullscreen button instead
- *
- * @param $link
- * @return string
- */
-function filter_mediaplugin_youtube_callback($link) {
- global $CFG;
-
- if (filter_mediaplugin_ignore($link[0])) {
- return $link[0];
- }
-
- $site = $link[1];
- $videoid = $link[3];
-
- $info = trim($link[7]);
- if (empty($info) or strpos($info, 'http') === 0) {
- $info = get_string('siteyoutube', 'filter_mediaplugin');
- }
- $info = s($info);
-
- $width = empty($link[5]) ? FILTER_MEDIAPLUGIN_VIDEO_WIDTH : $link[5];
- $height = empty($link[6]) ? FILTER_MEDIAPLUGIN_VIDEO_HEIGHT : $link[6];
-
- if (empty($CFG->xmlstrictheaders)) {
- return <<<OET
-<iframe title="$info" width="$width" height="$height" src="$site/embed/$videoid?rel=0" frameborder="0" allowfullscreen></iframe>
-OET;
- }
-
- //NOTE: we can not use any link fallback because it breaks built-in player on iOS devices
-
- $output = <<<OET
-<span class="mediaplugin mediaplugin_youtube">
-<object title="$info" type="application/x-shockwave-flash" data="$site/v/$videoid&amp;fs=1&amp;rel=0" width="$width" height="$height">
- <param name="movie" value="$site/v/$videoid&amp;fs=1&amp;rel=0" />
- <param name="FlashVars" value="playerMode=embedded" />
- <param name="allowFullScreen" value="true" />
-</object>
-</span>
-OET;
-
- return $output;
-}
-
-/**
- * Change YouTube playlist into embedded YouTube playlist videos
- *
- * Note: resizing via url is not supported, user can click the fullscreen button instead
- *
- * @param $link
- * @return string
- */
-function filter_mediaplugin_youtube_playlist_callback($link) {
- global $CFG;
-
- if (filter_mediaplugin_ignore($link[0])) {
- return $link[0];
- }
-
- $site = $link[1];
- $playlist = $link[3];
-
- $info = trim($link[7]);
- if (empty($info) or strpos($info, 'http') === 0) {
- $info = get_string('siteyoutube', 'filter_mediaplugin');
- }
- $printlink = html_writer::link("$site/view_play_list\?p=$playlist", $info, array('class'=>'mediafallbacklink'));
- $info = s($info);
-
- $width = empty($link[5]) ? FILTER_MEDIAPLUGIN_VIDEO_WIDTH : $link[5];
- $height = empty($link[6]) ? FILTER_MEDIAPLUGIN_VIDEO_HEIGHT : $link[6];
-
- // TODO: iframe HTML 5 video not implemented and object does work on iOS devices
-
- $output = <<<OET
-<span class="mediaplugin mediaplugin_youtube">
-<object title="$info" type="application/x-shockwave-flash" data="$site/p/$playlist&amp;fs=1&amp;rel=0" width="$width" height="$height">
- <param name="movie" value="$site/v/$playlist&amp;fs=1&amp;rel=0" />
- <param name="FlashVars" value="playerMode=embedded" />
- <param name="allowFullScreen" value="true" />
-$printlink</object>
-</span>
-OET;
-
- return $output;
-}
-
-/**
- * Change links to Vimeo into embedded Vimeo videos
- *
- * @param $link
- * @return string
- */
-function filter_mediaplugin_vimeo_callback($link) {
- global $CFG;
-
- if (filter_mediaplugin_ignore($link[0])) {
- return $link[0];
- }
-
- $videoid = $link[1];
- $info = s(strip_tags($link[5]));
-
- //Note: resizing via url is not supported, user can click the fullscreen button instead
- // iframe embedding is not xhtml strict but it is the only option that seems to work on most devices
-
- $width = empty($link[3]) ? FILTER_MEDIAPLUGIN_VIDEO_WIDTH : $link[3];
- $height = empty($link[4]) ? FILTER_MEDIAPLUGIN_VIDEO_HEIGHT : $link[4];
-
- $output = <<<OET
-<span class="mediaplugin mediaplugin_vimeo">
-<iframe title="$info" src="http://player.vimeo.com/video/$videoid" width="$width" height="$height" frameborder="0"></iframe>
-</span>
-OET;
-
- return $output;
-}
-
-/**
- * Embed video using window media player if available
- *
- * This does not work much outside of IE, hopefully not many ppl use it these days.
- *
- * @param $link
- * @return string
- */
-function filter_mediaplugin_wmp_callback($link) {
-
- if (filter_mediaplugin_ignore($link[0])) {
- return $link[0];
- }
-
- $url = $link[1];
- $rawurl = str_replace('&amp;', '&', $url);
-
- $info = trim($link[6]);
- if (empty($info) or strpos($info, 'http') === 0) {
- $info = get_string('fallbackvideo', 'filter_mediaplugin');
- }
- $printlink = html_writer::link($rawurl, $info, array('class'=>'mediafallbacklink'));
-
- if (empty($link[4]) or empty($link[5])) {
- $mpsize = '';
- $size = 'width="'.FILTER_MEDIAPLUGIN_VIDEO_WIDTH.'" height="'.(FILTER_MEDIAPLUGIN_VIDEO_HEIGHT+64).'"';
- $autosize = 'true';
- } else {
- $size = 'width="'.$link[4].'" height="'.($link[5] + 15).'"';
- $mpsize = 'width="'.$link[4].'" height="'.($link[5] + 64).'"';
- $autosize = 'false';
- }
- $mimetype = filter_mediaplugin_get_mimetype($url);
-
-
-
- return <<<OET
-<span class="mediaplugin mediaplugin_wmp">
-$printlink <br />
-<object classid="CLSID:6BF52A52-394A-11d3-B153-00C04F79FAA6" $mpsize standby="Loading Microsoft(R) Windows(R) Media Player components..." type="application/x-oleobject">
- <param name="Filename" value="$url" />
- <param name="src" value="$url" />
- <param name="url" value="$url" />
- <param name="ShowControls" value="true" />
- <param name="AutoRewind" value="true" />
- <param name="AutoStart" value="false" />
- <param name="Autosize" value="$autosize" />
- <param name="EnableContextMenu" value="true" />
- <param name="TransparentAtStart" value="false" />
- <param name="AnimationAtStart" value="false" />
- <param name="ShowGotoBar" value="false" />
- <param name="EnableFullScreenControls" value="true" />
- <param name="uimode" value="full" />
-<!--[if !IE]>-->
- <object data="$url" type="$mimetype" $size>
- <param name="src" value="$url" />
- <param name="controller" value="true" />
- <param name="autoplay" value="false" />
- <param name="autostart" value="false" />
- <param name="resize" value="scale" />
- </object>
-<!--<![endif]-->
-</object></span>
-OET;
-}
-
-/**
- * Replace quicktime links with quicktime player.
- *
- * You need to install a quicktime player, it is not available for all browsers+OS combinations.
- *
- * @param $link
- * @return string
- */
-function filter_mediaplugin_qt_callback($link) {
-
- if (filter_mediaplugin_ignore($link[0])) {
- return $link[0];
- }
-
- $url = $link[1];
- $rawurl = str_replace('&amp;', '&', $url);
-
- $info = trim($link[6]);
- if (empty($info) or strpos($info, 'http') === 0) {
- $info = get_string('fallbackvideo', 'filter_mediaplugin');
- }
- $printlink = html_writer::link($rawurl, $info, array('class'=>'mediafallbacklink'));
-
- if (empty($link[4]) or empty($link[5])) {
- $size = 'width="'.FILTER_MEDIAPLUGIN_VIDEO_WIDTH.'" height="'.(FILTER_MEDIAPLUGIN_VIDEO_HEIGHT+15).'"';
- } else {
- $size = 'width="'.$link[4].'" height="'.($link[5]+15).'"';
- }
- $mimetype = filter_mediaplugin_get_mimetype($url);
-
- // this is the safest fallback for incomplete or missing browser support for this format
- return <<<OET
-<span class="mediaplugin mediaplugin_qt">
-$printlink <br />
-<object classid="clsid:02BF25D5-8C17-4B23-BC80-D3488ABDDC6B" codebase="http://www.apple.com/qtactivex/qtplugin.cab" $size>
- <param name="pluginspage" value="http://www.apple.com/quicktime/download/" />
- <param name="src" value="$url" />
- <param name="controller" value="true" />
- <param name="loop" value="true" />
- <param name="autoplay" value="false" />
- <param name="autostart" value="false" />
- <param name="scale" value="aspect" />
-<!--[if !IE]>-->
- <object data="$url" type="$mimetype" $size>
- <param name="src" value="$url" />
- <param name="pluginurl" value="http://www.apple.com/quicktime/download/" />
- <param name="controller" value="true" />
- <param name="loop" value="true" />
- <param name="autoplay" value="false" />
- <param name="autostart" value="false" />
- <param name="scale" value="aspect" />
- </object>
-<!--<![endif]-->
-</object></span>
-OET;
-}
-
View
50 filter/mediaplugin/filtersettings.php
@@ -1,50 +0,0 @@
-<?php
-// This file is part of Moodle - http://moodle.org/
-//
-// Moodle is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// Moodle is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
-
-/**
- * Mediaplugin filter settings
- *
- * @package filter
- * @subpackage mediaplugin
- * @copyright 2017 Petr Skoda (http://skodak.org)
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-
-defined('MOODLE_INTERNAL') || die;
-
-if ($ADMIN->fulltree) {
-
- // External services
- $settings->add(new admin_setting_configcheckbox('filter_mediaplugin_enable_youtube', get_string('siteyoutube','filter_mediaplugin'), get_string('siteyoutube_help','filter_mediaplugin'), 1));
- $settings->add(new admin_setting_configcheckbox('filter_mediaplugin_enable_vimeo', get_string('sitevimeo','filter_mediaplugin'), get_string('sitevimeo_help','filter_mediaplugin'), 0));
-
- // these require flash
- $settings->add(new admin_setting_configcheckbox('filter_mediaplugin_enable_mp3', get_string('mp3audio','filter_mediaplugin'), get_string('mp3audio_help','filter_mediaplugin'), 1));
- $settings->add(new admin_setting_configcheckbox('filter_mediaplugin_enable_flv', get_string('flashvideo','filter_mediaplugin'), get_string('flashvideo_help','filter_mediaplugin'), 1));
- $settings->add(new admin_setting_configcheckbox('filter_mediaplugin_enable_swf', get_string('flashanimation','filter_mediaplugin'), get_string('flashanimation_help','filter_mediaplugin'), 1));
-
- // HTML 5 media
- $settings->add(new admin_setting_configcheckbox('filter_mediaplugin_enable_html5audio', get_string('html5audio','filter_mediaplugin'), get_string('html5audio_help','filter_mediaplugin'), 0)); // disabled because mp3 is much better choice
- $settings->add(new admin_setting_configcheckbox('filter_mediaplugin_enable_html5video', get_string('html5video','filter_mediaplugin'), get_string('html5video_help','filter_mediaplugin'), 0)); // disabled because flv with html5 fallback works better
-
- // legacy players
- $settings->add(new admin_setting_heading('legacymediaformats', get_string('legacyheading', 'filter_mediaplugin'), get_string('legacyheading_help', 'filter_mediaplugin')));
-
- $settings->add(new admin_setting_configcheckbox('filter_mediaplugin_enable_qt', get_string('legacyquicktime','filter_mediaplugin'), get_string('legacyquicktime_help','filter_mediaplugin'), 1));
- $settings->add(new admin_setting_configcheckbox('filter_mediaplugin_enable_wmp', get_string('legacywmp','filter_mediaplugin'), get_string('legacywmp_help','filter_mediaplugin'), 1));
- $settings->add(new admin_setting_configcheckbox('filter_mediaplugin_enable_rm', get_string('legacyreal','filter_mediaplugin'), get_string('legacyreal_help','filter_mediaplugin'), 1));
-
-}
View
24 filter/mediaplugin/lang/en/filter_mediaplugin.php
@@ -25,28 +25,4 @@
$string['fallbackaudio'] = 'Audio link';
$string['fallbackvideo'] = 'Video link';
$string['filtername'] = 'Multimedia plugins';
-$string['flashanimation'] = 'Flash animation';
-$string['flashanimation_help'] = 'Files with extension *.swf. For security reasons this filter is used only in trusted texts.';
-$string['flashvideo'] = 'Flash video';
-$string['flashvideo_help'] = 'Files with extension *.flv and *.f4v. Plays video clips using Flowplayer, requires Flash plugin and javascript. Uses HTML 5 video fallback if multiple sources specified.';
-$string['html5audio'] = 'HTML 5 audio';
-$string['html5audio_help'] = 'Audio files with extension *.ogg, *.aac and others. It is compatible with latest web browsers only, unfortunately there is no format that is supported by all browsers.
-Workaround is to specify fallbacks separated with # (ex: http://example.org/audio.aac#http://example.org/audio.aac#http://example.org/audio.mp3#), QuickTime player is used as a fallback for old browsers, fallback can be any audio type.';
-$string['html5video'] = 'HTML 5 video';
-$string['html5video_help'] = 'Video files with extension *.webm, *.m4v, *.ogv, *.mp4 and others. It is compatible with latest web browsers only, unfortunately there is no format that is supported by all browsers.
-Workaround is to specify fallbacks sources separated with # (ex: http://example.org/video.m4v#http://example.org/video.aac#http://example.org/video.ogv#d=640x480), QuickTime player is used as a fallback for old browsers.';
-$string['mp3audio'] = 'MP3 audio';
-$string['mp3audio_help'] = 'Files with extension *.mp3. Plays audio using Flowplayer, requires Flash plugin.';
-$string['legacyquicktime'] = 'QuickTime player';
-$string['legacyquicktime_help'] = 'Files with extension *.mov, *.mp4, *.m4a, *.mp4 and *.mpg. Requires QuickTime player or codecs.';
-$string['legacyreal'] = 'Real media player';
-$string['legacyreal_help'] = 'Files with extension *.rm, *.ra, *.ram, *.rp, *.rv. Requires RealPlayer.';
-$string['legacywmp'] = 'Windows media player';
-$string['legacywmp_help'] = 'Files with extension *.avi and *.wmv. Fully compatible with Internet Explorer in Windows, may be problematic in other browsers or operating systems.';
-$string['legacyheading'] = 'Legacy media players';
-$string['legacyheading_help'] = 'Following formats are not recommended for general usage, they are usually used in intranet installation with centrally managed clients.';
-$string['sitevimeo'] = 'Vimeo';
-$string['sitevimeo_help'] = 'Vimeo video sharing site.';
-$string['siteyoutube'] = 'YouTube';
-$string['siteyoutube_help'] = 'YouTube video sharing site, video and playlist links supported.';
View
20 filter/mediaplugin/tests/filter_test.php
@@ -37,16 +37,16 @@ function test_filter_mediaplugin_link() {
$this->resetAfterTest(true);
// we need to enable the plugins somehow
- $CFG->filter_mediaplugin_enable_youtube = 1;
- $CFG->filter_mediaplugin_enable_vimeo = 1;
- $CFG->filter_mediaplugin_enable_mp3 = 1;
- $CFG->filter_mediaplugin_enable_flv = 1;
- $CFG->filter_mediaplugin_enable_swf = 1;
- $CFG->filter_mediaplugin_enable_html5audio = 1;
- $CFG->filter_mediaplugin_enable_html5video = 1;
- $CFG->filter_mediaplugin_enable_qt = 1;
- $CFG->filter_mediaplugin_enable_wmp = 1;
- $CFG->filter_mediaplugin_enable_rm = 1;
+ $CFG->core_media_enable_youtube = 1;
+ $CFG->core_media_enable_vimeo = 1;
+ $CFG->core_media_enable_mp3 = 1;
+ $CFG->core_media_enable_flv = 1;
+ $CFG->core_media_enable_swf = 1;
+ $CFG->core_media_enable_html5audio = 1;
+ $CFG->core_media_enable_html5video = 1;
+ $CFG->core_media_enable_qt = 1;
+ $CFG->core_media_enable_wmp = 1;
+ $CFG->core_media_enable_rm = 1;
$filterplugin = new filter_mediaplugin(null, array());
View
4 filter/mediaplugin/version.php
@@ -25,6 +25,6 @@
defined('MOODLE_INTERNAL') || die();
-$plugin->version = 2011112900; // The current plugin version (Date: YYYYMMDDXX)
-$plugin->requires = 2011112900; // Requires this Moodle version
+$plugin->version = 2011121200; // The current plugin version (Date: YYYYMMDDXX)
+$plugin->requires = 2011120500; // Requires this Moodle version
$plugin->component = 'filter_mediaplugin'; // Full name of the plugin (used for diagnostics)
View
51 lang/en/media.php
@@ -0,0 +1,51 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Language strings for media embedding.
+ * @package core
+ * @subpackage media
+ * @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com}
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+$string['flashanimation'] = 'Flash animation';
+$string['flashanimation_desc'] = 'Files with extension *.swf. For security reasons this format is only embedded within trusted text.';
+$string['flashvideo'] = 'Flash video';
+$string['flashvideo_desc'] = 'Files with extension *.flv and *.f4v. Plays video clips using Flowplayer, requires Flash plugin and javascript.';
+$string['html5audio'] = 'HTML 5 audio';
+$string['html5audio_desc'] = 'Audio files with extension *.ogg, *.aac and *.mp3. Used primarily for mobile devices. (Format support depends on browser.)';
+$string['html5video'] = 'HTML 5 video';
+$string['html5video_desc'] = 'Video files with extension *.webm, *.m4v, *.ogv, *.mp4 and others. Used primarily for mobile devices. (Format support depends on browser.)';
+$string['mediaformats'] = 'Available players';
+$string['mediaformats_desc'] = 'When players are enabled in these settings, files can be embedded using the media filter (if enabled) or using a File or URL resources with the Embed option. When not enabled, these formats are not embedded and users can manually download or follow links to these resources.
+
+Where two players support the same format, enabling both increases compatibility across different devices such as mobile phones. It is possible to increase compatibility further by providing multiple files in different formats for a single audio or video clip.';
+$string['mediasettings'] = 'Media embedding';
+$string['mp3audio'] = 'MP3 audio';
+$string['mp3audio_desc'] = 'Files with extension *.mp3. Plays audio using Flowplayer, requires Flash plugin.';
+$string['legacyquicktime'] = 'QuickTime player';
+$string['legacyquicktime_desc'] = 'Files with extension *.mov, *.mp4, *.m4a, *.mp4 and *.mpg. Requires QuickTime player or codecs.';
+$string['legacyreal'] = 'Real media player';
+$string['legacyreal_desc'] = 'Files with extension *.rm, *.ra, *.ram, *.rp and *.rv. Requires RealPlayer.';
+$string['legacywmp'] = 'Windows media player';
+$string['legacywmp_desc'] = 'Files with extension *.avi and *.wmv. Fully compatible with Internet Explorer in Windows; may not work in other browsers or operating systems.';
+$string['legacyheading'] = 'Legacy media players';
+$string['legacyheading_desc'] = 'These players are not frequently used on the Web and require browser plugins that are less widely installed.';
+$string['sitevimeo'] = 'Vimeo';
+$string['sitevimeo_desc'] = 'Vimeo video sharing site.';
+$string['siteyoutube'] = 'YouTube';
+$string['siteyoutube_desc'] = 'YouTube video sharing site, video and playlist links supported.';
View
1,232 lib/medialib.php
@@ -0,0 +1,1232 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Classes for handling embedded media (mainly audio and video).
+ *
+ * These are used only from within the core media renderer.
+ *
+ * To embed media from Moodle code, do something like the following:
+ *
+ * $mediarenderer = $PAGE->get_renderer('core', 'media');
+ * echo $mediarenderer->embed_url(new moodle_url('http://example.org/a.mp3'));
+ *
+ * You do not need to require this library file manually. Getting the renderer
+ * (the first line above) requires this library file automatically.
+ *
+ * @package core_media
+ * @copyright 2012 The Open University
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+if (!defined('CORE_MEDIA_VIDEO_WIDTH')) {
+ /**
+ * Default video width if no width is specified; some players may do something
+ * more intelligent such as use real video width.
+ *
+ * May be defined in config.php if required.
+ */
+ define('CORE_MEDIA_VIDEO_WIDTH', 400);
+}
+if (!defined('CORE_MEDIA_VIDEO_HEIGHT')) {
+ /**
+ * Default video height. May be defined in config.php if required.
+ */
+ define('CORE_MEDIA_VIDEO_HEIGHT', 300);
+}
+if (!defined('CORE_MEDIA_AUDIO_WIDTH')) {
+ /**
+ * Default audio width if no width is specified.
+ *
+ * May be defined in config.php if required.
+ */
+ define('CORE_MEDIA_AUDIO_WIDTH', 300);
+}
+
+
+/**
+ * Constants and static utility functions for use with core_media_renderer.
+ *
+ * @copyright 2011 The Open University
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+abstract class core_media {
+ /**
+ * Option: Disable text link fallback.
+ *
+ * Use this option if you are going to print a visible link anyway so it is
+ * pointless to have one as fallback.
+ *
+ * To enable, set value to true.
+ */
+ const OPTION_NO_LINK = 'nolink';
+
+ /**
+ * Option: When embedding, if there is no matching embed, do not use the
+ * default link fallback player; instead return blank.
+ *
+ * This is different from OPTION_NO_LINK because this option still uses the
+ * fallback link if there is some kind of embedding. Use this option if you
+ * are going to check if the return value is blank and handle it specially.
+ *
+ * To enable, set value to true.
+ */
+ const OPTION_FALLBACK_TO_BLANK = 'embedorblank';
+
+ /**
+ * Option: Enable players which are only suitable for use when we trust the
+ * user who embedded the content.
+ *
+ * At present, this option enables the SWF player.
+ *
+ * To enable, set value to true.
+ */
+ const OPTION_TRUSTED = 'trusted';
+
+ /**
+ * Option: Put a div around the output (if not blank) so that it displays
+ * as a block using the 'resourcecontent' CSS class.
+ *
+ * To enable, set value to true.
+ */
+ const OPTION_BLOCK = 'block';
+
+ /**
+ * Given a string containing multiple URLs separated by #, this will split
+ * it into an array of moodle_url objects suitable for using when calling
+ * embed_alternatives.
+ *
+ * Note that the input string should NOT be html-escaped (i.e. if it comes
+ * from html, call html_entity_decode first).
+ *
+ * @param string $combinedurl String of 1 or more alternatives separated by #
+ * @param int $width Output variable: width (will be set to 0 if not specified)
+ * @param int $height Output variable: height (0 if not specified)
+ * @return array Array of 1 or more moodle_url objects
+ */
+ public static function split_alternatives($combinedurl, &$width, &$height) {
+ $urls = explode('#', $combinedurl);
+ $width = 0;
+ $height = 0;
+ $returnurls = array();
+
+ foreach ($urls as $url) {
+ $matches = null;
+
+ // You can specify the size as a separate part of the array like
+ // #d=640x480 without actually including a url in it.
+ if (preg_match('/^d=([\d]{1,4})x([\d]{1,4})$/i', $url, $matches)) {
+ $width = $matches[1];
+ $height = $matches[2];
+ continue;
+ }
+
+ // Can also include the ?d= as part of one of the URLs (if you use
+ // more than one they will be ignored except the last).
+ if (preg_match('/\?d=([\d]{1,4})x([\d]{1,4})$/i', $url, $matches)) {
+ $width = $matches[1];
+ $height = $matches[2];
+
+ // Trim from URL.
+ $url = str_replace($matches[0], '', $url);
+ }
+
+ // Clean up url.
+ $url = clean_param($url, PARAM_URL);
+ if (empty($url)) {
+ continue;
+ }
+
+ // Turn it into moodle_url object.
+ $returnurls[] = new moodle_url($url);
+ }
+
+ return $returnurls;
+ }
+
+ /**
+ * Returns the file extension for a URL.
+ * @param moodle_url $url URL
+ */
+ public static function get_extension(moodle_url $url) {
+ // Note: Does not use textlib (. is UTF8-safe).
+ $filename = self::get_filename($url);
+ $dot = strrpos($filename, '.');
+ if ($dot === false) {
+ return '';
+ } else {
+ return strtolower(substr($filename, $dot + 1));
+ }
+ }
+
+ /**
+ * Obtains the filename from the moodle_url.
+ * @param moodle_url $url URL
+ * @return string Filename only (not escaped)
+ */
+ public static function get_filename(moodle_url $url) {
+ $path = $url->get_path();
+ // Remove everything before last / if present. Does not use textlib as / is UTF8-safe.
+ $slash = strrpos($path, '/');
+ if ($slash !== false) {
+ $path = substr($path, $slash + 1);
+ }
+
+ return $path;
+ }
+
+ /**
+ * Guesses MIME type for a moodle_url based on file extension.
+ * @param moodle_url $url URL
+ * @return string MIME type
+ */
+ public static function get_mimetype(moodle_url $url) {
+ return mimeinfo('type', self::get_filename($url));
+ }
+}
+
+
+/**
+ * Base class for media players.
+ *
+ * Media players return embed HTML for a particular way of playing back audio
+ * or video (or another file type).
+ *
+ * In order to make the code more lightweight, this is not a plugin type
+ * (players cannot have their own settings, database tables, capabilities, etc).
+ * These classes are used only by core_media_renderer in outputrenderers.php.
+ * If you add a new class here (in core code) you must modify the
+ * get_players_raw function in that file to include it.
+ *
+ * If a Moodle installation wishes to add extra player objects they can do so
+ * by overriding that renderer in theme, and overriding the get_players_raw
+ * function. The new player class should then of course be defined within the
+ * custom theme or other suitable location, not in this file.
+ *
+ * @copyright 2011 The Open University
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+abstract class core_media_player {
+ /**
+ * Placeholder text used to indicate where the fallback content is placed
+ * within a result.
+ */
+ const PLACEHOLDER = '<!--FALLBACK-->';
+
+ /**
+ * Generates code required to embed the player.
+ *
+ * The returned code contains a placeholder comment '<!--FALLBACK-->'
+ * (constant core_media_player::PLACEHOLDER) which indicates the location
+ * where fallback content should be placed in the event that this type of
+ * player is not supported by user browser.
+ *
+ * The $urls parameter includes one or more alternative media formats that
+ * are supported by this player. It does not include formats that aren't
+ * supported (see list_supported_urls).
+ *
+ * The $options array contains key-value pairs. See OPTION_xx constants
+ * for documentation of standard option(s).
+ *
+ * @param array $urls URLs of media files
+ * @param string $name Display name; '' to use default
+ * @param int $width Optional width; 0 to use default
+ * @param int $height Optional height; 0 to use default
+ * @param array $options Options array
+ * @return string HTML code for embed
+ */
+ public abstract function embed($urls, $name, $width, $height, $options);
+
+ /**
+ * Gets the list of file extensions supported by this media player.
+ *
+ * Note: This is only required for the default implementation of
+ * list_supported_urls. If you override that function to determine
+ * supported URLs in some way other than by extension, then this function
+ * is not necessary.
+ *
+ * @return array Array of strings (extension not including dot e.g. 'mp3')
+ */
+ public function get_supported_extensions() {
+ return array();
+ }
+
+ /**
+ * Lists keywords that must be included in a url that can be embedded with
+ * this player. Any such keywords should be added to the array.
+ *
+ * For example if this player supports FLV and F4V files then it should add
+ * '.flv' and '.f4v' to the array. (The check is not case-sensitive.)
+ *
+ * Default handling calls the get_supported_extensions function and adds
+ * a dot to each of those values, so players only need to override this
+ * if they don't implement get_supported_extensions.
+ *
+ * This is used to improve performance when matching links in the media filter.
+ *
+ * @return array Array of keywords to add to the embeddable markers list
+ */
+ public function get_embeddable_markers() {
+ $markers = array();
+ foreach ($this->get_supported_extensions() as $extension) {
+ $markers[] = '.' . $extension;
+ }
+ return $markers;
+ }
+
+ /**
+ * Gets the ranking of this player. This is an integer used to decide which
+ * player to use (after applying other considerations such as which ones
+ * the user has disabled).
+ *
+ * Rank must be unique (no two players should have the same rank).
+ *
+ * Rank zero has a special meaning, indicating that this 'player' does not
+ * really embed the video.
+ *
+ * Rank is not a user-configurable value because it needs to be defined
+ * carefully in order to ensure that the embedding fallbacks actually work.
+ * It might be possible to have some user options which affect rank, but
+ * these would be best defined as e.g. checkboxes in settings that have
+ * a particular effect on the rank of a couple of plugins, rather than
+ * letting users generally alter rank.
+ *
+ * Note: Within medialib.php, players are listed in rank order (highest
+ * rank first).
+ *
+ * @return int Rank (higher is better)
+ */
+ public abstract function get_rank();
+
+ /**
+ * @return bool True if player is enabled
+ */
+ public function is_enabled() {
+ global $CFG;
+
+ // With the class core_media_player_html5video it is enabled
+ // based on $CFG->core_media_enable_html5video.
+ $setting = str_replace('_player_', '_enable_', get_class($this));
+ return !empty($CFG->{$setting});
+ }
+
+ /**
+ * Given a list of URLs, returns a reduced array containing only those URLs
+ * which are supported by this player. (Empty if none.)
+ * @param array $urls Array of moodle_url
+ * @param array $options Options (same as will be passed to embed)
+ * @return array Array of supported moodle_url
+ */
+ public function list_supported_urls(array $urls, array $options = array()) {
+ $extensions = $this->get_supported_extensions();
+ $result = array();
+ foreach ($urls as $url) {
+ if (in_array(core_media::get_extension($url), $extensions)) {
+ $result[] = $url;
+ }
+ }
+ return $result;
+ }
+
+ /**
+ * Obtains suitable name for media. Uses specified name if there is one,
+ * otherwise makes one up.
+ * @param string $name User-specified name ('' if none)
+ * @param array $urls Array of moodle_url used to make up name
+ * @return string Name
+ */
+ protected function get_name($name, $urls) {
+ // If there is a specified name, use that.
+ if ($name) {
+ return $name;
+ }
+
+ // Get filename of first URL.
+ $url = reset($urls);
+ $name = core_media::get_filename($url);
+
+ // If there is more than one url, strip the extension as we could be
+ // referring to a different one or several at once.
+ if (count($urls) > 1) {
+ $name = preg_replace('~\.[^.]*$~', '', $name);
+ }
+
+ return $name;
+ }
+
+ /**
+ * Compares by rank order, highest first. Used for sort functions.
+ * @param core_media_player $a Player A
+ * @param core_media_player $b Player B
+ * @return int Negative if A should go before B, positive for vice versa
+ */
+ public static function compare_by_rank(core_media_player $a, core_media_player $b) {
+ return $b->get_rank() - $a->get_rank();
+ }
+
+ /**
+ * Utility function that sets width and height to defaults if not specified
+ * as a parameter to the function (will be specified either if, (a) the calling
+ * code passed it, or (b) the URL included it).
+ * @param int $width Width passed to function (updated with final value)
+ * @param int $height Height passed to function (updated with final value)
+ */
+ protected static function pick_video_size(&$width, &$height) {
+ if (!$width) {
+ $width = CORE_MEDIA_VIDEO_WIDTH;
+ $height = CORE_MEDIA_VIDEO_HEIGHT;
+ }
+ }
+}
+
+
+/**
+ * Base class for players which handle external links (YouTube etc).
+ *
+ * As opposed to media files.
+ *
+ * @copyright 2011 The Open University
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+abstract class core_media_player_external extends core_media_player {
+ /**
+ * Array of matches from regular expression - subclass can assume these
+ * will be valid when the embed function is called, to save it rerunning
+ * the regex.
+ * @var array
+ */
+ protected $matches;
+
+ /**
+ * Part of a regular expression, including ending ~ symbol (note: these
+ * regexes use ~ instead of / because URLs and HTML code typically include
+ * / symbol and makes harder to read if you have to escape it).
+ * Matches the end part of a link after you have read the 'important' data
+ * including optional #d=400x300 at end of url, plus content of <a> tag,
+ * up to </a>.
+ * @var string
+ */
+ const END_LINK_REGEX_PART = '[^#]*(#d=([\d]{1,4})x([\d]{1,4}))?~si';
+
+ public function embed($urls, $name, $width, $height, $options) {
+ return $this->embed_external(reset($urls), $name, $width, $height, $options);
+ }
+
+ /**
+ * Obtains HTML code to embed the link.
+ * @param moodle_url $url Single URL to embed
+ * @param string $name Display name; '' to use default
+ * @param int $width Optional width; 0 to use default
+ * @param int $height Optional height; 0 to use default
+ * @param array $options Options array
+ * @return string HTML code for embed
+ */
+ protected abstract function embed_external(moodle_url $url, $name, $width, $height, $options);
+
+ public function list_supported_urls(array $urls, array $options = array()) {
+ // These only work with a SINGLE url (there is no fallback).
+ if (count($urls) != 1) {
+ return array();
+ }
+ $url = reset($urls);
+
+ // Check against regex.
+ if (preg_match($this->get_regex(), $url->out(false), $this->matches)) {
+ return array($url);
+ }
+
+ return array();
+ }
+
+ /**
+ * Returns regular expression used to match URLs that this player handles
+ * @return string PHP regular expression e.g. '~^https?://example.org/~'
+ */
+ protected function get_regex() {
+ return '~^unsupported~';
+ }
+
+ /**
+ * Annoyingly, preg_match $matches result does not always have the same
+ * number of parameters - it leaves out optional ones at the end. WHAT.
+ * Anyway, this function can be used to fix it.
+ * @param array $matches Array that should be adjusted
+ * @param int $count Number of capturing groups (=6 to make $matches[6] work)
+ */
+ protected static function fix_match_count(&$matches, $count) {
+ for ($i = count($matches); $i <= $count; $i++) {
+ $matches[$i] = false;
+ }
+ }
+}
+
+
+/**
+ * Player that embeds Vimeo links.
+ *
+ * @copyright 2011 The Open University
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class core_media_player_vimeo extends core_media_player_external {
+ protected function embed_external(moodle_url $url, $name, $width, $height, $options) {
+ $videoid = $this->matches[1];
+ $info = s($name);
+
+ // Note: resizing via url is not supported, user can click the fullscreen
+ // button instead. iframe embedding is not xhtml strict but it is the only
+ // option that seems to work on most devices.
+ self::pick_video_size($width, $height);
+
+ $output = <<<OET
+<span class="mediaplugin mediaplugin_vimeo">
+<iframe title="$info" src="http://player.vimeo.com/video/$videoid"
+ width="$width" height="$height" frameborder="0"></iframe>
+</span>
+OET;
+
+ return $output;
+ }
+
+ protected function get_regex() {
+ // Initial part of link.
+ $start = '~^http://vimeo\.com/';
+ // Middle bit: either watch?v= or v/.
+ $middle = '([0-9]+)';
+ return $start . $middle . core_media_player_external::END_LINK_REGEX_PART;
+ }
+
+ public function get_rank() {
+ return 1010;
+ }
+
+ public function get_embeddable_markers() {
+ return array('vimeo.com/');
+ }
+}
+
+/**
+ * Player that creates YouTube embedding.
+ *
+ * @copyright 2011 The Open University
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class core_media_player_youtube extends core_media_player_external {
+ protected function embed_external(moodle_url $url, $name, $width, $height, $options) {
+ global $CFG;
+
+ $site = $this->matches[1];
+ $videoid = $this->matches[3];
+
+ $info = trim($name);
+ if (empty($info) or strpos($info, 'http') === 0) {
+ $info = get_string('siteyoutube', 'core_media');
+ }
+ $info = s($info);
+
+ self::pick_video_size($width, $height);
+
+ if (empty($CFG->xmlstrictheaders)) {
+ return <<<OET
+<iframe title="$info" width="$width" height="$height"
+ src="$site/embed/$videoid?rel=0" frameborder="0" allowfullscreen></iframe>
+OET;
+ }
+
+ // NOTE: we can not use any link fallback because it breaks built-in
+ // player on iOS devices.
+ $output = <<<OET
+<span class="mediaplugin mediaplugin_youtube">
+<object title="$info" type="application/x-shockwave-flash"
+ data="$site/v/$videoid&amp;fs=1&amp;rel=0" width="$width" height="$height">
+ <param name="movie" value="$site/v/$videoid&amp;fs=1&amp;rel=0" />
+ <param name="FlashVars" value="playerMode=embedded" />
+ <param name="allowFullScreen" value="true" />
+</object>
+</span>
+OET;
+
+ return $output;
+ }
+
+ protected function get_regex() {
+ // Initial part of link.
+ $start = '~^(https?://www\.youtube(-nocookie)?\.com)/';
+ // Middle bit: either watch?v= or v/.
+ $middle = '(?:watch\?v=|v/)([a-z0-9\-_]+)';
+ return $start . $middle . core_media_player_external::END_LINK_REGEX_PART;
+ }
+
+ public function get_rank() {
+ // I decided to make the link-embedding ones (that don't handle file
+ // formats) have ranking in the 1000 range.
+ return 1001;
+ }
+
+ public function get_embeddable_markers() {
+ return array('youtube');
+ }
+}
+
+
+/**
+ * Player that creates YouTube playlist embedding.
+ *
+ * @copyright 2011 The Open University
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class core_media_player_youtube_playlist extends core_media_player_external {
+ public function is_enabled() {
+ global $CFG;
+ // Use the youtube on/off flag.
+ return $CFG->core_media_enable_youtube;
+ }
+
+ protected function embed_external(moodle_url $url, $name, $width, $height, $options) {
+ $site = $this->matches[1];
+ $playlist = $this->matches[3];
+
+ $info = trim($name);
+ if (empty($info) or strpos($info, 'http') === 0) {
+ $info = get_string('siteyoutube', 'core_media');
+ }
+ $info = s($info);
+
+ self::pick_video_size($width, $height);
+
+ // TODO: iframe HTML 5 video not implemented and object does not work
+ // on iOS devices.
+ $fallback = core_media_player::PLACEHOLDER;
+ $output = <<<OET
+<span class="mediaplugin mediaplugin_youtube">
+<object title="$info" type="application/x-shockwave-flash"
+ data="$site/p/$playlist&amp;fs=1&amp;rel=0" width="$width" height="$height">
+ <param name="movie" value="$site/v/$playlist&amp;fs=1&amp;rel=0" />
+ <param name="FlashVars" value="playerMode=embedded" />
+ <param name="allowFullScreen" value="true" />
+$fallback</object>
+</span>
+OET;
+
+ return $output;
+ }
+
+ protected function get_regex() {
+ // Initial part of link.
+ $start = '~^(https?://www\.youtube(-nocookie)?\.com)/';
+ // Middle bit: either view_play_list?p= or p/ (doesn't work on youtube) or playlist?list=.
+ $middle = '(?:view_play_list\?p=|p/|playlist\?list=)([a-z0-9\-_]+)';
+ return $start . $middle . core_media_player_external::END_LINK_REGEX_PART;
+ }
+
+ public function get_rank() {
+ // I decided to make the link-embedding ones (that don't handle file
+ // formats) have ranking in the 1000 range.
+ return 1000;
+ }
+
+ public function get_embeddable_markers() {
+ return array('youtube');
+ }
+}
+
+
+/**
+ * MP3 player inserted using JavaScript.
+ *
+ * @copyright 2011 The Open University
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class core_media_player_mp3 extends core_media_player {
+ public function embed($urls, $name, $width, $height, $options) {
+ // Use first url (there can actually be only one unless some idiot
+ // enters two mp3 files as alternatives).
+ $url = reset($urls);
+
+ // Unique id even across different http requests made at the same time
+ // (for AJAX, iframes).
+ $id = 'core_media_mp3_' . md5(time() . '_' . rand());
+
+ // When Flash or JavaScript are not available only the fallback is displayed,
+ // using span not div because players are inline elements.
+ $spanparams = array('id' => $id, 'class' => 'mediaplugin mediaplugin_mp3');
+ if ($width) {
+ $spanparams['style'] = 'width: ' . $width . 'px';
+ }
+ $output = html_writer::tag('span', core_media_player::PLACEHOLDER, $spanparams);
+ // We can not use standard JS init because this may be cached
+ // note: use 'small' size unless embedding in block mode.
+ $output .= html_writer::script(js_writer::function_call(
+ 'M.util.add_audio_player', array($id, $url->out(false),
+ empty($options[core_media::OPTION_BLOCK]))));
+
+ return $output;
+ }
+
+ public function get_supported_extensions() {
+ return array('mp3');
+ }
+
+ public function get_rank() {
+ return 80;
+ }
+}
+
+
+/**
+ * Flash video player inserted using JavaScript.
+ *
+ * @copyright 2011 The Open University
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class core_media_player_flv extends core_media_player {
+ public function embed($urls, $name, $width, $height, $options) {
+ // Use first url (there can actually be only one unless some idiot
+ // enters two mp3 files as alternatives).
+ $url = reset($urls);
+
+ // Unique id even across different http requests made at the same time
+ // (for