MDL-38158 core_media: Convert media players to new plugin type

AMOS BEGIN
  MOV [siteyoutube,core_media],[pluginname,media_youtube]
  MOV [siteyoutube_desc,core_media],[pluginname_help,media_youtube]
  MOV [sitevimeo,core_media],[pluginname,media_vimeo]
  MOV [sitevimeo_desc,core_media],[pluginname_help,media_vimeo]
  MOV [html5audio,core_media],[pluginname,media_html5audio]
  MOV [html5audio_desc,core_media],[pluginname_help,media_html5audio]
  MOV [html5video,core_media],[pluginname,media_html5video]
  MOV [html5video_desc,core_media],[pluginname_help,media_html5video]
  MOV [flashanimation,core_media],[pluginname,media_swf]
  MOV [flashanimation_desc,core_media],[pluginname_help,media_swf]
AMOS END
This commit is contained in:
Marina Glancy 2016-09-15 15:52:41 +08:00
parent 3c73b26c4b
commit fab11235d8
76 changed files with 3524 additions and 4406 deletions

View file

@ -36,32 +36,13 @@ if (!is_siteadmin()) {
// Set up page.
$PAGE->set_context(context_system::instance());
$PAGE->set_url(new moodle_url('/filter/mediaplugin/perftest.php'));
$PAGE->set_url(new moodle_url('/filter/mediaplugin/dev/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;
// Enable all players.
$enabledmediaplugins = \core\plugininfo\media::get_enabled_plugins();
\core\plugininfo\media::set_enabled_plugins('vimeo,youtube,videojs,html5audio,html5video,swf');
// Create plugin.
$filterplugin = new filter_mediaplugin(null, array());
@ -168,6 +149,8 @@ foreach ($linksamples as $sample) {
}
filter_mediaplugin_perf_stop('One link (mp3)');
\core\plugininfo\media::set_enabled_plugins($enabledmediaplugins);
// End page.
echo html_writer::end_tag('ul');
print $OUTPUT->footer();

View file

@ -42,10 +42,6 @@ defined('MOODLE_INTERNAL') || die();
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;
public function filter($text, array $options = array()) {
global $CFG, $PAGE;
@ -55,16 +51,11 @@ class filter_mediaplugin extends moodle_text_filter {
return $text;
}
if (stripos($text, '</a>') === false) {
// Performance shortcut - if not </a> tag, nothing can match.
if (stripos($text, '</a>') === false && stripos($text, '</video>') === false && stripos($text, '</audio>') === false) {
// Performance shortcut - if there are no </a>, </video> or </audio> tags, nothing can match.
return $text;
}
if (!$this->mediarenderer) {
$this->mediarenderer = $PAGE->get_renderer('core', 'media');
$this->embedmarkers = $this->mediarenderer->get_embeddable_markers();
}
// Check SWF permissions.
$this->trusted = !empty($options['noclean']) or !empty($CFG->allowobjectembed);
@ -76,32 +67,41 @@ class filter_mediaplugin extends moodle_text_filter {
}
// Regex to find media extensions in an <a> tag.
$re = '~<a\s[^>]*href="([^"]*(?:' . $this->embedmarkers . ')[^"]*)"[^>]*>([^>]*)</a>~is';
$embedmarkers = core_media_manager::instance()->get_embeddable_markers();
$re = '~<a\s[^>]*href="([^"]*(?:' . $embedmarkers . ')[^"]*)"[^>]*>([^>]*)</a>~is';
$newtext = '';
$validtag = '';
$tagname = '';
$sizeofmatches = count($matches);
// We iterate through the given string to find valid <a> tags
// and build them so that the callback function can check it for
// embedded content. Then we rebuild the string.
foreach ($matches as $idx => $tag) {
if (preg_match('|</a>|', $tag) && !empty($validtag)) {
if (preg_match('|</'.$tagname.'>|', $tag) && !empty($validtag)) {
$validtag .= $tag;
// Given we now have a valid <a> tag to process it's time for
// ReDoS protection. Stop processing if a word is too large.
if (strlen($validtag) < 4096) {
$processed = preg_replace_callback($re, array($this, 'callback'), $validtag);
if ($tagname === 'a') {
$processed = preg_replace_callback($re, array($this, 'callback'), $validtag);
} else {
// For audio and video tags we just process them without precheck for embeddable markers.
$processed = $this->process_media_tag($validtag);
}
}
// Rebuilding the string with our new processed text.
$newtext .= !empty($processed) ? $processed : $validtag;
// Wipe it so we can catch any more instances to filter.
$validtag = '';
$processed = '';
} else if (preg_match('/<a\s[^>]*/', $tag) && $sizeofmatches > 1) {
// Looking for a starting <a> tag.
} else if (preg_match('/<(a|video|audio)\s[^>]*/', $tag, $tagmatches) && $sizeofmatches > 1 &&
(empty($validtag) || $tagname === strtolower($tagmatches[1]))) {
// Looking for a starting tag. Ignore tags embedded into each other.
$validtag = $tag;
$tagname = strtolower($tagmatches[1]);
} else {
// If we have a validtag add to that to process later,
// else add straight onto our newtext string.
@ -124,6 +124,8 @@ class filter_mediaplugin extends moodle_text_filter {
* @return string
*/
private function callback(array $matches) {
$mediamanager = core_media_manager::instance();
global $CFG, $PAGE;
// Check if we ignore it.
if (preg_match('/class="[^"]*nomediaplugin/i', $matches[0])) {
@ -137,30 +139,79 @@ class filter_mediaplugin extends moodle_text_filter {
}
// Split provided URL into alternatives.
$urls = core_media::split_alternatives($matches[1], $width, $height);
$urls = $mediamanager->split_alternatives($matches[1], $width, $height);
$options = array();
$options = [core_media_manager::OPTION_ORIGINAL_TEXT => $matches[0]];
return $this->embed_alternatives($urls, $name, $width, $height, $options);
}
/**
* Renders media files (audio or video) using suitable embedded player.
*
* Wrapper for {@link core_media_manager::embed_alternatives()}
*
* @param array $urls 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
*/
protected function embed_alternatives($urls, $name, $width, $height, $options) {
// Allow SWF (or not).
if ($this->trusted) {
$options[core_media::OPTION_TRUSTED] = true;
$options[core_media_manager::OPTION_TRUSTED] = true;
}
// 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;
$options[core_media_manager::OPTION_FALLBACK_TO_BLANK] = true;
// 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);
$result = core_media_manager::instance()->embed_alternatives($urls, $name, $width, $height, $options);
// If something was embedded, return it, otherwise return original.
if ($result !== '') {
return $result;
} else {
return $matches[0];
return $options[core_media_manager::OPTION_ORIGINAL_TEXT];
}
}
/**
* Replaces <video> or <audio> tag with processed contents
*
* @param string $fulltext complete HTML snipped "<video ...>...</video>" or "<audio ...>....</audio>"
* @return string
*/
protected function process_media_tag($fulltext) {
// Check if we ignore it.
if (preg_match('/^<[^>]*class="[^"]*nomediaplugin/im', $fulltext)) {
return $fulltext;
}
// Find all sources both as <video src=""> and as embedded <source> tags.
$urls = [];
if (preg_match('/^<[^>]*\bsrc="(.*?)"/im', $fulltext, $matches)) {
$urls[] = new moodle_url($matches[1]);
}
if (preg_match_all('/<source\b[^>]*\bsrc="(.*?)"/im', $fulltext, $matches)) {
foreach ($matches[1] as $url) {
$urls[] = new moodle_url($url);
}
}
// Extract width/height/title attributes and call embed_alternatives to find a suitable media player.
if ($urls) {
$options = [core_media_manager::OPTION_ORIGINAL_TEXT => $fulltext];
$width = core_media_player_native::get_attribute($fulltext, 'width', PARAM_INT);
$height = core_media_player_native::get_attribute($fulltext, 'height', PARAM_INT);
$name = core_media_player_native::get_attribute($fulltext, 'title');
return $this->embed_alternatives($urls, $name, $width, $height, $options);
}
return $fulltext;
}
}

View file

@ -25,4 +25,5 @@
$string['fallbackaudio'] = 'Audio link';
$string['fallbackvideo'] = 'Video link';
$string['filtername'] = 'Multimedia plugins';
$string['linktomedia'] = 'Multimedia plugins filter engages the existing media players to convert links to media files into embedded objects. '.
' It can also enhance &lt;video&gt; and &lt;audio&gt; tags with selected video and audio players.<br><a href="{$a}">Manage media players</a>';

View file

@ -0,0 +1,34 @@
<?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_mediaplugin
* @copyright 2016 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die;
if ($ADMIN->fulltree) {
$url = new moodle_url('/admin/settings.php', ['section' => 'managemediaplayers']);
$item = new admin_setting_heading('filter_mediaplugin/about',
'',
new lang_string('linktomedia', 'filter_mediaplugin', $url->out()));
$settings->add($item);
}

View file

@ -1,26 +1,13 @@
/**
* Filters
*/
.mediaplugin_html5audio,
.mediaplugin_html5video,
.mediaplugin_swf,
.mediaplugin_flv,
.mediaplugin_real,
.mediaplugin_youtube,
.mediaplugin_vimeo,
.mediaplugin_wmp,
.mediaplugin_qt {
.mediaplugin {
display: block;
margin-top: 5px;
margin-bottom: 5px;
text-align: center;
}
/*
* mp3 player colours -this read using JS and applied to swf audio flow player
* see http://flowplayer.org/documentation/skinning/controlbar.html?skin=default for more color properties,
* any property that ends with '...Color' is supported here.
*/
.mp3flowplayer_backgroundColor {
color: #000;
.mediaplugin > div {
margin: auto;
}

View file

@ -32,22 +32,10 @@ require_once($CFG->dirroot . '/filter/mediaplugin/filter.php'); // Include the c
class filter_mediaplugin_testcase extends advanced_testcase {
function test_filter_mediaplugin_link() {
global $CFG;
$this->resetAfterTest(true);
// we need to enable the plugins somehow
$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;
\core\plugininfo\media::set_enabled_plugins('vimeo,youtube,videojs,html5video,swf,html5audio');
$filterplugin = new filter_mediaplugin(null, array());
@ -64,7 +52,7 @@ class filter_mediaplugin_testcase extends advanced_testcase {
'<a href="http://moodle.org/testfile/test.mp3">test mp3</a>',
'<a href="http://moodle.org/testfile/test.ogg">test ogg</a>',
'<a id="movie player" class="center" href="http://moodle.org/testfile/test.mpg">test mpg</a>',
'<a href="http://moodle.org/testfile/test.ram">test</a>',
'<a href="http://moodle.org/testfile/test.webm">test</a>',
'<a href="http://www.youtube.com/watch?v=JghQgA2HMX8" class="href=css">test file</a>',
'<a href="http://www.youtube-nocookie.com/watch?v=JghQgA2HMX8" class="href=css">test file</a>',
'<a href="http://youtu.be/JghQgA2HMX8" class="href=css">test file</a>',
@ -72,7 +60,7 @@ class filter_mediaplugin_testcase extends advanced_testcase {
'<a class="youtube" href="http://www.youtube.com/watch?v=JghQgA2HMX8">test file</a>',
'<a class="_blanktarget" href="http://moodle.org/testfile/test.flv?d=100x100">test flv</a>',
'<a class="hrefcss" href="http://www.youtube.com/watch?v=JghQgA2HMX8">test file</a>',
'<a class="content" href="http://moodle.org/testfile/test.avi">test mp3</a>',
'<a class="content" href="http://moodle.org/testfile/test.ogg">test ogg</a>',
'<a id="audio" href="http://moodle.org/testfile/test.mp3">test mp3</a>',
'<a href="http://moodle.org/testfile/test.mp3">test mp3</a>',
'<a href="http://moodle.org/testfile/test.mp3">test mp3</a>',
@ -83,7 +71,7 @@ class filter_mediaplugin_testcase extends advanced_testcase {
'<a class="content"
href="http://moodle.org/testfile/test.avi">test mp3
href="http://moodle.org/testfile/test.wav">test wav
</a>',
'<a href="http://www.youtube.com/watch?v=JghQgA2HMX8?d=200x200" >youtube\'s</a>',
// Test a long URL under 4096 characters.
@ -139,7 +127,7 @@ class filter_mediaplugin_testcase extends advanced_testcase {
// Valid mediaurl followed by a longurl.
$precededlongurl = '<a href="http://moodle.org/testfile/test.mp3">test.mp3</a>'. $longurl;
$filter = $filterplugin->filter($precededlongurl);
$this->assertEquals(1, substr_count($filter, 'M.util.add_audio_player'));
$this->assertEquals(1, substr_count($filter, '</audio>'));
$this->assertContains($longurl, $filter);
// Testing for cases where: to be filtered content has 6+ text afterwards.