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
This commit is contained in:
sam marshall 2011-12-13 17:08:34 +00:00
parent ffe4de973b
commit daefd6eb97
17 changed files with 2462 additions and 925 deletions

View File

@ -115,6 +115,49 @@ if ($hassiteconfig) { // speedup for non-admins, add all caps used on this page
$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 File

@ -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 File

@ -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 File

@ -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 File

@ -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 @@ if (!defined('FILTER_MEDIAPLUGIN_VIDEO_HEIGHT')) {
* @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 (!$this->mediarenderer) {
$this->mediarenderer = $PAGE->get_renderer('core', 'media');
$this->embedmarkers = $this->mediarenderer->get_embeddable_markers();
}
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 (!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);
/**
* 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];
}
$url = str_replace('&amp;', '&', $url);
$url = clean_param($url, PARAM_URL);
if (empty($url)) {
continue;
// Get name.
$name = trim($matches[2]);
if (empty($name) or strpos($name, 'http') === 0) {
$name = ''; // Use default name.
}
$returnurls[] = $url;
}
// Split provided URL into alternatives.
$urls = core_media::split_alternatives($matches[1], $width, $height);
return array($returnurls, $width, $height);
}
$options = array();
/**
* 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;
// Allow SWF (or not).
if ($this->trusted) {
$options[core_media::OPTION_TRUSTED] = true;
}
$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
}
if ($fallbackurl === null) {
if ($mimetype === 'audio/mp3' or $mimetype === 'audio/aac') {
$fallbackurl = str_replace('&', '&amp;', $url);
$fallbackmime = $mimetype;
}
}
}
if (!$sources) {
return $link[0];
}
// 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;
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;
}
// 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);
$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;
}
/**
* 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) {
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);
// If something was embedded, return it, otherwise return original.
if ($result !== '') {
return $result;
} else {
$sources[] = $source;
}
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;
}
return $matches[0];
}
}
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
$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
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);
} else {
$sources[] = $source;
}
if ($flashurl === null) {
$flashurl = $url;
}
}
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 File

@ -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 File

@ -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 File

@ -37,16 +37,16 @@ class filter_mediaplugin_testcase extends advanced_testcase {
$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 File

@ -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)

51
lang/en/media.php Normal file
View File

@ -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.';

1232
lib/medialib.php Normal file

File diff suppressed because it is too large Load Diff

View File

@ -7707,6 +7707,7 @@ function get_core_subsystems() {
'langconfig' => NULL,
'license' => NULL,
'mathslib' => NULL,
'media' => 'media',
'message' => 'message',
'mimetypes' => NULL,
'mnet' => 'mnet',

View File

@ -2901,3 +2901,257 @@ class core_renderer_ajax extends core_renderer {
*/
public function heading($text, $level = 2, $classes = 'main', $id = null) {}
}
/**
* Renderer for media files.
*
* Used in file resources, media filter, and any other places that need to
* output embedded media.
*
* @copyright 2011 The Open University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class core_media_renderer extends plugin_renderer_base {
/** @var array Array of available 'player' objects */
private $players;
/** @var string Regex pattern for links which may contain embeddable content */
private $embeddablemarkers;
/**
* Constructor requires medialib.php.
*
* This is needed in the constructor (not later) so that you can use the
* constants and static functions that are defined in core_media class
* before you call renderer functions.
*/
public function __construct() {
global $CFG;
require_once($CFG->libdir . '/medialib.php');
}
/**
* Obtains the list of core_media_player objects currently in use to render
* items.
*
* The list is in rank order (highest first) and does not include players
* which are disabled.
*
* @return array Array of core_media_player objects in rank order
*/
protected function get_players() {
global $CFG;
// Save time by only building the list once
if (!$this->players) {
// Get raw list of players.
$players = $this->get_players_raw();
// Chuck all the ones that are disabled.
foreach ($players as $key => $player) {
if (!$player->is_enabled()) {
unset($players[$key]);
}
}
// Sort in rank order (highest first).
usort($players, array('core_media_player', 'compare_by_rank'));
$this->players = $players;
}
return $this->players;
}
/**
* Obtains a raw list of player objects that includes objects regardless
* of whether they are disabled or not, and without sorting.
*
* You can override this in a subclass if you need to add additional
* players.
*
* The return array is be indexed by player name to make it easier to
* remove players in a subclass.
*
* @return array $players Array of core_media_player objects in any order
*/
protected function get_players_raw() {
return array(
'vimeo' => new core_media_player_vimeo(),
'youtube' => new core_media_player_youtube(),
'youtube_playlist' => new core_media_player_youtube_playlist(),
'html5video' => new core_media_player_html5video(),
'html5audio' => new core_media_player_html5audio(),
'mp3' => new core_media_player_mp3(),
'flv' => new core_media_player_flv(),
'wmp' => new core_media_player_wmp(),
'qt' => new core_media_player_qt(),
'rm' => new core_media_player_rm(),
'swf' => new core_media_player_swf(),
'link' => new core_media_player_link(),
);
}
/**
* Renders a media file (audio or video) using suitable embedded player.
*
* See embed_alternatives function for full description of parameters.
* This function calls through to that one.
*
* When using this function you can also specify width and height in the
* URL by including ?d=100x100 at the end. If specified in the URL, this
* will override the $width and $height parameters.
*
* @param moodle_url $url Full URL of media file
* @param string $name Optional user-readable name to display in download link
* @param int $width Width in pixels (optional)
* @param int $height Height in pixels (optional)
* @param array $options Array of key/value pairs
* @return string HTML content of embed
*/
public function embed_url(moodle_url $url, $name = '', $width = 0, $height = 0,
$options = array()) {
// Get width and height from URL if specified (overrides parameters in
// function call).
$rawurl = $url->out(false);
if (preg_match('/[?#]d=([\d]{1,4}%?)x([\d]{1,4}%?)/', $rawurl, $matches)) {
$width = $matches[1];
$height = $matches[2];
$url = new moodle_url(str_replace($matches[0], '', $rawurl));
}
// Defer to array version of function.
return $this->embed_alternatives(array($url), $name, $width, $height, $options);
}
/**
* Renders media files (audio or video) using suitable embedded player.
* The list of URLs should be alternative versions of the same content in
* multiple formats. If there is only one format it should have a single
* entry.
*
* If the media files are not in a supported format, this will give students
* a download link to each format. The download link uses the filename
* unless you supply the optional name parameter.
*
* Width and height are optional. If specified, these are suggested sizes
* and should be the exact values supplied by the user, if they come from
* user input. These will be treated as relating to the size of the video
* content, not including any player control bar.
*
* For audio files, height will be ignored. For video files, a few formats
* work if you specify only width, but in general if you specify width
* you must specify height as well.
*
* The $options array is passed through to the core_media_player classes
* that render the object tag. The keys can contain values from
* core_media::OPTION_xx.
*
* @param array $alternatives Array of moodle_url to media files
* @param string $name Optional user-readable name to display in download link
* @param int $width Width in pixels (optional)
* @param int $height Height in pixels (optional)
* @param array $options Array of key/value pairs
* @return string HTML content of embed
*/
public function embed_alternatives($alternatives, $name = '', $width = 0, $height = 0,
$options = array()) {
// Get list of player plugins (will also require the library).
$players = $this->get_players();
// Set up initial text which will be replaced by first player that
// supports any of the formats.
$out = core_media_player::PLACEHOLDER;
// Loop through all players that support any of these URLs.
foreach ($players as $player) {
// Option: When no other player matched, don't do the default link player.
if (!empty($options[core_media::OPTION_FALLBACK_TO_BLANK]) &&
$player->get_rank() === 0 && $out === core_media_player::PLACEHOLDER) {
continue;
}
$supported = $player->list_supported_urls($alternatives, $options);
if ($supported) {
// Embed.
$text = $player->embed($supported, $name, $width, $height, $options);
// Put this in place of the 'fallback' slot in the previous text.
$out = str_replace(core_media_player::PLACEHOLDER, $text, $out);
}
}
// Remove 'fallback' slot from final version and return it.
$out = str_replace(core_media_player::PLACEHOLDER, '', $out);
if (!empty($options[core_media::OPTION_BLOCK]) && $out !== '') {
$out = html_writer::tag('div', $out, array('class' => 'resourcecontent'));
}
return $out;
}
/**
* Checks whether a file can be embedded. If this returns true you will get
* an embedded player; if this returns false, you will just get a download
* link.
*
* This is a wrapper for can_embed_urls.
*
* @param moodle_url $url URL of media file
* @param array $options Options (same as when embedding)
* @return bool True if file can be embedded
*/
public function can_embed_url(moodle_url $url, $options = array()) {
return $this->can_embed_urls(array($url), $options);
}
/**
* Checks whether a file can be embedded. If this returns true you will get
* an embedded player; if this returns false, you will just get a download
* link.
*
* @param array $urls URL of media file and any alternatives (moodle_url)
* @param array $options Options (same as when embedding)
* @return bool True if file can be embedded
*/
public function can_embed_urls(array $urls, $options = array()) {
// Check all players to see if any of them support it.
foreach ($this->get_players() as $player) {
// Link player (always last on list) doesn't count!
if ($player->get_rank() <= 0) {
break;
}
// First player that supports it, return true.
if ($player->list_supported_urls($urls, $options)) {
return true;
}
}
return false;
}
/**
* Obtains a list of markers that can be used in a regular expression when
* searching for URLs that can be embedded by any player type.
*
* This string is used to improve peformance of regex matching by ensuring
* that the (presumably C) regex code can do a quick keyword check on the
* URL part of a link to see if it matches one of these, rather than having
* to go into PHP code for every single link to see if it can be embedded.
*
* @return string String suitable for use in regex such as '(\.mp4|\.flv)'
*/
public function get_embeddable_markers() {
if (empty($this->embeddablemarkers)) {
$markers = '';
foreach ($this->get_players() as $player) {
foreach ($player->get_embeddable_markers() as $marker) {
if ($markers !== '') {
$markers .= '|';
}
$markers .= preg_quote($marker);
}
}
$this->embeddablemarkers = $markers;
}
return $this->embeddablemarkers;
}
}

608
lib/tests/medialib_test.php Normal file
View File

@ -0,0 +1,608 @@
<?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/>.
/**
* Test classes for handling embedded media (audio/video).
*
* @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();
global $CFG;
require_once($CFG->libdir . '/medialib.php');
/**
* Test script for media embedding.
*/
class medialib_testcase extends advanced_testcase {
/** @var array Files covered by test */
public static $includecoverage = array('lib/medialib.php', 'lib/outputrenderers.php');
/**
* Pre-test setup. Preserves $CFG.
*/
public function setUp() {
global $CFG;
parent::setUp();
// Reset CFG
$this->resetAfterTest(true);
// Consistent initial setup: all players disabled.
$CFG->core_media_enable_html5video = false;
$CFG->core_media_enable_html5audio = false;
$CFG->core_media_enable_mp3 = false;
$CFG->core_media_enable_flv = false;
$CFG->core_media_enable_wmp = false;
$CFG->core_media_enable_qt = false;
$CFG->core_media_enable_rm = false;
$CFG->core_media_enable_youtube = false;
$CFG->core_media_enable_vimeo = false;
$CFG->core_media_enable_swf = false;
// Strict headers turned off.
$CFG->xmlstrictheaders = false;
$_SERVER = array('HTTP_USER_AGENT' => '');
$this->pretend_to_be_safari();
}
/**
* Sets user agent to Safari.
*/
private function pretend_to_be_safari() {
// Pretend to be using Safari browser (must support mp4 for tests to work).
$_SERVER['HTTP_USER_AGENT'] = 'Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_8; de-at) ' .
'AppleWebKit/533.21.1 (KHTML, like Gecko) Version/5.0.5 Safari/533.21.1';
}
/**
* Sets user agent to Firefox.
*/
private function pretend_to_be_firefox() {
// Pretend to be using Firefox browser (must support ogg for tests to work).
$_SERVER['HTTP_USER_AGENT'] = 'Mozilla/5.0 (Windows NT 6.1; rv:8.0) Gecko/20100101 Firefox/8.0';
}
/**
* Post-test cleanup. Replaces old $CFG.
*/
public function tearDown() {
// Replace original user agent.
if (isset($this->realserver)) {
$_SERVER = $this->realserver;
}
parent::tearDown();
}
/**
* Test for the core_media_player is_enabled.
*/
public function test_is_enabled() {
global $CFG;
// Test enabled: unset, 0, 1.
$test = new core_media_player_test;
$this->assertFalse($test->is_enabled());
$CFG->core_media_enable_test = 0;
$this->assertFalse($test->is_enabled());
$CFG->core_media_enable_test = 1;
$this->assertTrue($test->is_enabled());
}
/**
* Test for the core_media_player list_supported_urls.
*/
public function test_list_supported_urls() {
global $CFG;
$test = new core_media_player_test;
// Some example URLs.
$supported1 = new moodle_url('http://example.org/1.test');
$supported2 = new moodle_url('http://example.org/2.TST');
$unsupported = new moodle_url('http://example.org/2.jpg');
// No URLs => none.
$result = $test->list_supported_urls(array());
$this->assertEquals(array(), $result);
// One supported URL => same.
$result = $test->list_supported_urls(array($supported1));
$this->assertEquals(array($supported1), $result);
// Two supported URLS => same.
$result = $test->list_supported_urls(array($supported1, $supported2));
$this->assertEquals(array($supported1, $supported2), $result);
// One unsupported => none.
$result = $test->list_supported_urls(array($unsupported));
$this->assertEquals(array(), $result);
// Two supported and one unsupported => same.
$result = $test->list_supported_urls(array($supported2, $unsupported, $supported1));
$this->assertEquals(array($supported2, $supported1), $result);
}
/**
* Test for core_media_renderer get_players
*/
public function test_get_players() {
global $CFG, $PAGE;
// All players are initially disabled (except link, which you can't).
$renderer = new core_media_renderer_test($PAGE, '');
$this->assertEquals('link', $renderer->get_players_test());
// A couple enabled, check the order.
$CFG->core_media_enable_html5audio = true;
$CFG->core_media_enable_mp3 = true;
$renderer = new core_media_renderer_test($PAGE, '');
$this->assertEquals('mp3, html5audio, link', $renderer->get_players_test());
}
/**
* Test for core_media_renderer can_embed_url
*/
public function test_can_embed_url() {
global $CFG, $PAGE;
// All players are initially disabled, so mp4 cannot be rendered.
$url = new moodle_url('http://example.org/test.mp4');
$renderer = new core_media_renderer_test($PAGE, '');
$this->assertFalse($renderer->can_embed_url($url));
// Enable QT player.
$CFG->core_media_enable_qt = true;
$renderer = new core_media_renderer_test($PAGE, '');
$this->assertTrue($renderer->can_embed_url($url));
// QT + html5.
$CFG->core_media_enable_html5video = true;
$renderer = new core_media_renderer_test($PAGE, '');
$this->assertTrue($renderer->can_embed_url($url));
// Only html5.
$CFG->core_media_enable_qt = false;
$renderer = new core_media_renderer_test($PAGE, '');
$this->assertTrue($renderer->can_embed_url($url));
// Only WMP.
$CFG->core_media_enable_html5video = false;
$CFG->core_media_enable_wmp = true;
$renderer = new core_media_renderer_test($PAGE, '');
$this->assertFalse($renderer->can_embed_url($url));
}
/**
* Test for core_media_renderer embed_url.
* Checks multiple format/fallback support.
*/
public function test_embed_url_fallbacks() {
global $CFG, $PAGE;
$url = new moodle_url('http://example.org/test.mp4');
// All plugins disabled, NOLINK option.
$renderer = new core_media_renderer_test($PAGE, '');
$t = $renderer->embed_url($url, 0, 0, '',
array(core_media::OPTION_NO_LINK => true));
// Completely empty.
$this->assertEquals('', $t);
// All plugins disabled but not NOLINK.
$renderer = new core_media_renderer_test($PAGE, '');
$t = $renderer->embed_url($url);
$this->assert_contents(false, false, true, $t);
// HTML5 plugin enabled.
$CFG->core_media_enable_html5video = true;
$renderer = new core_media_renderer_test($PAGE, '');
$t = $renderer->embed_url($url);
$this->assert_contents(false, true, true, $t);
// QT plugin enabled as well.
$CFG->core_media_enable_qt = true;
$renderer = new core_media_renderer_test($PAGE, '');
$t = $renderer->embed_url($url);
$this->assert_contents(true, true, true, $t);
// Nolink turned off, plugins still enabled.
$t = $renderer->embed_url($url, 0, 0, '',
array(core_media::OPTION_NO_LINK => true));
$this->assert_contents(true, true, false, $t);
}
/**
* Checks the contents of the resulting HTML code to ensure it used the
* correct embed method(s).
*
* @param bool $hasqt True if it should have QT embed code
* @param bool $hashtml5 True if it should have HTML5 embed code
* @param bool $haslink True if it should have a fallback link
* @param string $text HTML content
*/
private function assert_contents($hasqt, $hashtml5, $haslink, $text) {
// I tried to avoid making it specific to the exact details of the html
// code, picking out only single key strings that would let it check
// whether final code contains the right things.
$qt = 'qtplugin.cab';
$html5 = '</video>';
$link = 'mediafallbacklink';
$this->assertEquals($hasqt, self::str_contains($text, $qt));
$this->assertEquals($hashtml5, self::str_contains($text, $html5));
$this->assertEquals($haslink, self::str_contains($text, $link));
}
/**
* Test for core_media_renderer embed_url.
* Check SWF works including the special option required to enable it
*/
public function test_embed_url_swf() {
global $CFG, $PAGE;
$CFG->core_media_enable_swf = true;
$renderer = new core_media_renderer_test($PAGE, '');
// Without any options...
$url = new moodle_url('http://example.org/test.swf');
$t = $renderer->embed_url($url);
$this->assertFalse(self::str_contains($t, '</object>'));
// ...and with the 'no it's safe, I checked it' option.
$url = new moodle_url('http://example.org/test.swf');
$t = $renderer->embed_url($url, '', 0, 0, array(core_media::OPTION_TRUSTED => true));
$this->assertTrue(self::str_contains($t, '</object>'));
}
/**
* Test for core_media_renderer embed_url.
* Exercises all the basic formats not covered elsewhere.
*/
public function test_embed_url_other_formats() {
global $CFG, $PAGE;
// Enable all players and get renderer.
$CFG->core_media_enable_html5audio = true;
$CFG->core_media_enable_mp3 = true;
$CFG->core_media_enable_flv = true;
$CFG->core_media_enable_wmp = true;
$CFG->core_media_enable_rm = true;
$CFG->core_media_enable_youtube = true;
$CFG->core_media_enable_vimeo = true;
$renderer = new core_media_renderer_test($PAGE, '');
// Check each format one at a time. This is a basic check to be sure
// the HTML is included for files of the right type, not a test that
// the HTML itself is correct.
// Format: mp3.
$url = new moodle_url('http://example.org/test.mp3');
$t = $renderer->embed_url($url);
$this->assertTrue(self::str_contains($t, 'core_media_mp3_'));
// Format: flv.
$url = new moodle_url('http://example.org/test.flv');
$t = $renderer->embed_url($url);
$this->assertTrue(self::str_contains($t, 'core_media_flv_'));
// Format: wmp.
$url = new moodle_url('http://example.org/test.avi');
$t = $renderer->embed_url($url);
$this->assertTrue(self::str_contains($t, '6BF52A52-394A-11d3-B153-00C04F79FAA6'));
// Format: rm.
$url = new moodle_url('http://example.org/test.rm');
$t = $renderer->embed_url($url);
$this->assertTrue(self::str_contains($t, 'CFCDAA03-8BE4-11cf-B84B-0020AFBBCCFA'));
// Format: youtube.
$url = new moodle_url('http://www.youtube.com/watch?v=vyrwMmsufJc');
$t = $renderer->embed_url($url);
$this->assertTrue(self::str_contains($t, '</iframe>'));
$url = new moodle_url('http://www.youtube.com/v/vyrwMmsufJc');
$t = $renderer->embed_url($url);
$this->assertTrue(self::str_contains($t, '</iframe>'));
// Format: youtube playlist.
$url = new moodle_url('http://www.youtube.com/view_play_list?p=PL6E18E2927047B662');
$t = $renderer->embed_url($url);
$this->assertTrue(self::str_contains($t, '</object>'));
$url = new moodle_url('http://www.youtube.com/playlist?list=PL6E18E2927047B662');
$t = $renderer->embed_url($url);
$this->assertTrue(self::str_contains($t, '</object>'));
$url = new moodle_url('http://www.youtube.com/p/PL6E18E2927047B662');
$t = $renderer->embed_url($url);
$this->assertTrue(self::str_contains($t, '</object>'));
// Format: vimeo.
$url = new moodle_url('http://vimeo.com/1176321');
$t = $renderer->embed_url($url);
$this->assertTrue(self::str_contains($t, '</iframe>'));
// Format: html5audio.
$this->pretend_to_be_firefox();
$url = new moodle_url('http://example.org/test.ogg');
$t = $renderer->embed_url($url);
$this->assertTrue(self::str_contains($t, '</audio>'));
}
/**
* Test for core_media_renderer embed_url.
* Checks the EMBED_OR_BLANK option.
*/
public function test_embed_or_blank() {
global $CFG, $PAGE;
$CFG->core_media_enable_html5audio = true;
$this->pretend_to_be_firefox();
$renderer = new core_media_renderer_test($PAGE, '');
$options = array(core_media::OPTION_FALLBACK_TO_BLANK => true);
// Embed that does match something should still include the link too.
$url = new moodle_url('http://example.org/test.ogg');
$t = $renderer->embed_url($url, '', 0, 0, $options);
$this->assertTrue(self::str_contains($t, '</audio>'));
$this->assertTrue(self::str_contains($t, 'mediafallbacklink'));
// Embed that doesn't match something should be totally blank.
$url = new moodle_url('http://example.org/test.mp4');
$t = $renderer->embed_url($url, '', 0, 0, $options);
$this->assertEquals('', $t);
}
/**
* Test for core_media_renderer embed_url.
* Checks that size is passed through correctly to player objects and tests
* size support in html5video output.
*/
public function test_embed_url_size() {
global $CFG, $PAGE;
// Technically this could break in every format and they handle size
// in several different ways, but I'm too lazy to test it in every
// format, so let's just pick one to check the values get passed
// through.
$CFG->core_media_enable_html5video = true;
$renderer = new core_media_renderer_test($PAGE, '');
$url = new moodle_url('http://example.org/test.mp4');
// HTML5 default size - specifies core width and does not specify height.
$t = $renderer->embed_url($url);
$this->assertTrue(self::str_contains($t, 'width="' . CORE_MEDIA_VIDEO_WIDTH . '"'));
$this->assertFalse(self::str_contains($t, 'height'));
// HTML5 specified size - specifies both.
$t = $renderer->embed_url($url, '', '666', '101');
$this->assertTrue(self::str_contains($t, 'width="666"'));
$this->assertTrue(self::str_contains($t, 'height="101"'));
// HTML5 size specified in url, overrides call.
$url = new moodle_url('http://example.org/test.mp4?d=123x456');
$t = $renderer->embed_url($url, '', '666', '101');
$this->assertTrue(self::str_contains($t, 'width="123"'));
$this->assertTrue(self::str_contains($t, 'height="456"'));
}
/**
* Test for core_media_renderer embed_url.
* Checks that name is passed through correctly to player objects and tests
* name support in html5video output.
*/
public function test_embed_url_name() {
global $CFG, $PAGE;
// As for size this could break in every format but I'm only testing
// html5video.
$CFG->core_media_enable_html5video = true;
$renderer = new core_media_renderer_test($PAGE, '');
$url = new moodle_url('http://example.org/test.mp4');
// HTML5 default name - use filename.
$t = $renderer->embed_url($url);
$this->assertTrue(self::str_contains($t, 'title="test.mp4"'));
// HTML5 specified name - check escaping.
$t = $renderer->embed_url($url, 'frog & toad');
$this->assertTrue(self::str_contains($t, 'title="frog &amp; toad"'));
}
/**
* Test for core_media_renderer split_alternatives.
*/
public function test_split_alternatives() {
// Single URL - identical moodle_url.
$mp4 = 'http://example.org/test.mp4';
$result = core_media::split_alternatives($mp4, $w, $h);
$this->assertEquals($mp4, $result[0]->out(false));
// Width and height weren't specified.
$this->assertEquals(0, $w);
$this->assertEquals(0, $h);
// Two URLs - identical moodle_urls.
$webm = 'http://example.org/test.webm';
$result = core_media::split_alternatives("$mp4#$webm", $w, $h);
$this->assertEquals($mp4, $result[0]->out(false));
$this->assertEquals($webm, $result[1]->out(false));
// Two URLs plus dimensions.
$size = 'd=400x280';
$result = core_media::split_alternatives("$mp4#$webm#$size", $w, $h);
$this->assertEquals($mp4, $result[0]->out(false));
$this->assertEquals($webm, $result[1]->out(false));
$this->assertEquals(400, $w);
$this->assertEquals(280, $h);
// Two URLs plus legacy dimensions (use last one).
$result = core_media::split_alternatives("$mp4?d=1x1#$webm?$size", $w, $h);
$this->assertEquals($mp4, $result[0]->out(false));
$this->assertEquals($webm, $result[1]->out(false));
$this->assertEquals(400, $w);
$this->assertEquals(280, $h);
}
/**
* Test for core_media_renderer embed_alternatives (with multiple urls)
*/
public function test_embed_alternatives() {
global $PAGE, $CFG;
// Most aspects of this are same as single player so let's just try
// a single typical / complicated scenario.
// MP3, WebM and FLV.
$urls = array(
new moodle_url('http://example.org/test.mp4'),
new moodle_url('http://example.org/test.webm'),
new moodle_url('http://example.org/test.flv'),
);
// Enable html5 and flv.
$CFG->core_media_enable_html5video = true;
$CFG->core_media_enable_flv = true;
$renderer = new core_media_renderer_test($PAGE, '');
// Result should contain HTML5 with two sources + FLV.
$t = $renderer->embed_alternatives($urls);
// HTML5 sources - mp4, not flv or webm (not supported in Safari).
$this->assertTrue(self::str_contains($t, '<source src="http://example.org/test.mp4"'));
$this->assertFalse(self::str_contains($t, '<source src="http://example.org/test.webm"'));
$this->assertFalse(self::str_contains($t, '<source src="http://example.org/test.flv"'));
// FLV is before the video tag (indicating html5 is used as fallback to flv
// and not vice versa).
$this->assertTrue((bool)preg_match('~core_media_flv_.*<video~s', $t));
// Do same test with firefox and check we get the webm and not mp4.
$this->pretend_to_be_firefox();
$t = $renderer->embed_alternatives($urls);
// HTML5 sources - webm, not not flv or mp4 (not supported in Firefox).
$this->assertFalse(self::str_contains($t, '<source src="http://example.org/test.mp4"'));
$this->assertTrue(self::str_contains($t, '<source src="http://example.org/test.webm"'));
$this->assertFalse(self::str_contains($t, '<source src="http://example.org/test.flv"'));
}
/**
* Checks if text contains a given string. Should be PHP built-in function,
* but oddly isn't.
*
* @param string $text Text
* @param string $value Value that should be contained within the text
* @return bool True if value is contained
*/
public static function str_contains($text, $value) {
return strpos($text, $value) !== false;
}
/**
* Converts moodle_url array into a single comma-separated string for
* easier testing.
*
* @param array $urls Array of moodle_urls
* @return string String containing those URLs, comma-separated
*/
public static function string_urls($urls) {
$out = array();
foreach ($urls as $url) {
$out[] = $url->out(false);
}
return implode(',', $out);
}
/**
* Converts associative array into a semicolon-separated string for easier
* testing.
*
* @param array $options Associative array
* @return string String of form 'a=b;c=d'
*/
public static function string_options($options) {
$out = '';
foreach ($options as $key => $value) {
if ($out) {
$out .= ';';
}
$out .= "$key=$value";
}
return $out;
}
}
/**
* Media player stub for testing purposes.
*/
class core_media_player_test extends core_media_player {
/** @var array Array of supported extensions */
public $ext;
/** @var int Player rank */
public $rank;
/** @var int Arbitrary number */
public $num;
/**
* @param int $num Number (used in output)
* @param int $rank Player rank
* @param array $ext Array of supported extensions
*/
public function __construct($num = 1, $rank = 13, $ext = array('tst', 'test')) {
$this->ext = $ext;
$this->rank = $rank;
$this->num = $num;
}
public function embed($urls, $name, $width, $height, $options) {
return $this->num . ':' . medialib_test::string_urls($urls) .
",$name,$width,$height,<!--FALLBACK-->," . medialib_test::string_options($options);
}
public function get_supported_extensions() {
return $this->ext;
}
public function get_rank() {
return $this->rank;
}
}
/**
* Media renderer override for testing purposes.
*/
class core_media_renderer_test extends core_media_renderer {
/**
* Access list of players as string, shortening it by getting rid of
* repeated text.
* @return string Comma-separated list of players
*/
public function get_players_test() {
$players = $this->get_players();
$out = '';
foreach ($players as $player) {
if ($out) {
$out .= ', ';
}
$out .= str_replace('core_media_player_', '', get_class($player));
}
return $out;
}
}

View File

@ -12,7 +12,9 @@ Note:
debug messages and will produce fatal errors
API changes:
* send_stored_file() has changed its interface
* deleted several resourcelib_embed_* functions from resourcelib.php
=== 2.2 ===

View File

@ -5,12 +5,16 @@ information provided here is intended especially for developers.
=== 2.3 ===
required changes in code:
* define the capability mod/xxx:addinstance (and the corresponding lang string)
(unless your mod is a MOD_ARCHETYPE_SYSTEM).
* xxx_pluginfile() is now given the 7th parameter (hopefully the last one) that
contains additional options for the file serving. The array should be re-passed
to send_stored_file().
* most resourcelib_embed_* functions are replaced with core_media_renderer;
for an example, see mod/resource/locallib.php, resource_display_embed()
=== 2.2 ===

View File

@ -765,8 +765,13 @@ body.tag .managelink {padding: 5px;}
.dir-rtl .felement.feditor select {margin-right:18.75%;margin-left:auto;}
.dir-rtl .mform .fitem .felement {margin-right: 16%;margin-left:auto;}
/* Resourcelib mp3 player size: only width could be changed here, height hardcoded in JS */
.resourcecontent .resourcemediaplugin_mp3 object {height:25px; width: 600px}
/* Audio player size in 'block' mode (can only change width, height is hardcoded in JS) */
.resourcecontent .mediaplugin_mp3 object {height:25px; width: 600px}
.resourcecontent audio.mediaplugin_html5audio {width: 600px}
/* Audio player size in 'inline' mode (can only change width, as above) */
.mediaplugin_mp3 object {height:15px;width:300px}
audio.mediaplugin_html5audio {width: 300px}
/*
Fix for SubScript & SuperScript