Skip to content

Commit

Permalink
MDL-38158 core_media: Convert media players to new plugin type
Browse files Browse the repository at this point in the history
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
  • Loading branch information
marinaglancy committed Nov 4, 2016
1 parent 3c73b26 commit fab1123
Show file tree
Hide file tree
Showing 76 changed files with 3,526 additions and 4,408 deletions.
1 change: 0 additions & 1 deletion .eslintignore
Expand Up @@ -24,7 +24,6 @@ lib/htmlpurifier/
lib/jabber/
lib/minify/matthiasmullie-minify/
lib/minify/matthiasmullie-pathconverter/
lib/flowplayer/
lib/pear/Auth/RADIUS.php
lib/pear/Crypt/CHAP.php
lib/pear/HTML/Common.php
Expand Down
1 change: 0 additions & 1 deletion .stylelintignore
Expand Up @@ -25,7 +25,6 @@ lib/htmlpurifier/
lib/jabber/
lib/minify/matthiasmullie-minify/
lib/minify/matthiasmullie-pathconverter/
lib/flowplayer/
lib/pear/Auth/RADIUS.php
lib/pear/Crypt/CHAP.php
lib/pear/HTML/Common.php
Expand Down
79 changes: 79 additions & 0 deletions admin/media.php
@@ -0,0 +1,79 @@
<?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/>.

/**
* Enrol config manipulation script.
*
* @package core
* @subpackage media
* @copyright 2016 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/

define('NO_OUTPUT_BUFFERING', true);

require_once('../config.php');
require_once($CFG->libdir.'/adminlib.php');

$action = required_param('action', PARAM_ALPHANUMEXT);
$media = required_param('media', PARAM_PLUGIN);
$confirm = optional_param('confirm', 0, PARAM_BOOL);

$PAGE->set_url('/admin/media.php');
$PAGE->set_context(context_system::instance());

require_login();
require_capability('moodle/site:config', context_system::instance());
require_sesskey();

$plugins = core_plugin_manager::instance()->get_plugins_of_type('media');
$sortorder = array_values(\core\plugininfo\media::get_enabled_plugins());

$return = new moodle_url('/admin/settings.php', array('section' => 'managemediaplayers'));

if (!array_key_exists($media, $plugins)) {
redirect($return);
}

switch ($action) {
case 'disable':
$plugins[$media]->set_enabled(false);
break;

case 'enable':
$plugins[$media]->set_enabled(true);
break;

case 'up':
if (($pos = array_search($media, $sortorder)) > 0) {
$tmp = $sortorder[$pos - 1];
$sortorder[$pos - 1] = $sortorder[$pos];
$sortorder[$pos] = $tmp;
\core\plugininfo\media::set_enabled_plugins($sortorder);
}
break;

case 'down':
if ((($pos = array_search($media, $sortorder)) !== false) && ($pos < count($sortorder) - 1)) {
$tmp = $sortorder[$pos + 1];
$sortorder[$pos + 1] = $sortorder[$pos];
$sortorder[$pos] = $tmp;
\core\plugininfo\media::set_enabled_plugins($sortorder);
}
break;
}

redirect($return);
43 changes: 0 additions & 43 deletions admin/settings/appearance.php
Expand Up @@ -196,49 +196,6 @@
$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));
Expand Down
22 changes: 22 additions & 0 deletions admin/settings/plugins.php
Expand Up @@ -215,6 +215,28 @@
$plugin->load_settings($ADMIN, 'filtersettings', $hassiteconfig);
}

// Media players.
$ADMIN->add('modules', new admin_category('mediaplayers', new lang_string('type_media_plural', 'plugin')));
$temp = new admin_settingpage('managemediaplayers', new lang_string('managemediaplayers', 'media'));
$temp->add(new admin_setting_heading('mediaformats', get_string('mediaformats', 'core_media'),
format_text(get_string('mediaformats_desc', 'core_media'), FORMAT_MARKDOWN)));
$temp->add(new admin_setting_managemediaplayers());
$temp->add(new admin_setting_heading('managemediaplayerscommonheading', new lang_string('commonsettings', 'admin'), ''));
$temp->add(new admin_setting_configtext('media_default_width',
new lang_string('defaultwidth', 'core_media'), new lang_string('defaultwidthdesc', 'core_media'),
400, PARAM_INT, 10));
$temp->add(new admin_setting_configtext('media_default_height',
new lang_string('defaultheight', 'core_media'), new lang_string('defaultheightdesc', 'core_media'),
300, PARAM_INT, 10));
$ADMIN->add('mediaplayers', $temp);

$plugins = core_plugin_manager::instance()->get_plugins_of_type('media');
core_collator::asort_objects_by_property($plugins, 'displayname');
foreach ($plugins as $plugin) {
/** @var \core\plugininfo\media $plugin */
$plugin->load_settings($ADMIN, 'mediaplayers', $hassiteconfig);
}

// Data format settings.
$ADMIN->add('modules', new admin_category('dataformatsettings', new lang_string('dataformats')));
$temp = new admin_settingpage('managedataformats', new lang_string('managedataformats'));
Expand Down
29 changes: 6 additions & 23 deletions filter/mediaplugin/dev/perftest.php
Expand Up @@ -36,32 +36,13 @@

// 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());
Expand Down Expand Up @@ -168,6 +149,8 @@ function filter_mediaplugin_perf_stop($name) {
}
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();
95 changes: 73 additions & 22 deletions filter/mediaplugin/filter.php
Expand Up @@ -42,10 +42,6 @@
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;
Expand All @@ -55,16 +51,11 @@ public function filter($text, array $options = array()) {
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);

Expand All @@ -76,32 +67,41 @@ public function filter($text, array $options = array()) {
}

// 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.
Expand All @@ -124,6 +124,8 @@ public function filter($text, array $options = array()) {
* @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])) {
Expand All @@ -137,30 +139,79 @@ private function callback(array $matches) {
}

// 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;
}
}

0 comments on commit fab1123

Please sign in to comment.