diff --git a/.eslintignore b/.eslintignore index 68293b94f93..4761eff5c3c 100644 --- a/.eslintignore +++ b/.eslintignore @@ -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 diff --git a/.stylelintignore b/.stylelintignore index 383c5204377..ff3155b4063 100644 --- a/.stylelintignore +++ b/.stylelintignore @@ -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 diff --git a/admin/media.php b/admin/media.php new file mode 100644 index 00000000000..d6886a080fb --- /dev/null +++ b/admin/media.php @@ -0,0 +1,79 @@ +. + +/** + * 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); diff --git a/admin/settings/appearance.php b/admin/settings/appearance.php index 7ee3f7004c3..398c777282a 100644 --- a/admin/settings/appearance.php +++ b/admin/settings/appearance.php @@ -196,49 +196,6 @@ preferences,moodle|/user/preferences.php|preferences', $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)); diff --git a/admin/settings/plugins.php b/admin/settings/plugins.php index 2dbff19d87e..bd759a706cd 100644 --- a/admin/settings/plugins.php +++ b/admin/settings/plugins.php @@ -215,6 +215,28 @@ if ($hassiteconfig) { $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')); diff --git a/filter/mediaplugin/dev/perftest.php b/filter/mediaplugin/dev/perftest.php index 1c16a3037d1..2d09c50f856 100644 --- a/filter/mediaplugin/dev/perftest.php +++ b/filter/mediaplugin/dev/perftest.php @@ -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(); \ No newline at end of file diff --git a/filter/mediaplugin/filter.php b/filter/mediaplugin/filter.php index 0b1dce6e099..9e4ed55f600 100644 --- a/filter/mediaplugin/filter.php +++ b/filter/mediaplugin/filter.php @@ -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, '') === false) { - // Performance shortcut - if not tag, nothing can match. + if (stripos($text, '') === false && stripos($text, '') === false && stripos($text, '') === false) { + // Performance shortcut - if there are no , or 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 tag. - $re = '~]*href="([^"]*(?:' . $this->embedmarkers . ')[^"]*)"[^>]*>([^>]*)~is'; + $embedmarkers = core_media_manager::instance()->get_embeddable_markers(); + $re = '~]*href="([^"]*(?:' . $embedmarkers . ')[^"]*)"[^>]*>([^>]*)~is'; $newtext = ''; $validtag = ''; + $tagname = ''; $sizeofmatches = count($matches); // We iterate through the given string to find valid 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('||', $tag) && !empty($validtag)) { + if (preg_match('||', $tag) && !empty($validtag)) { $validtag .= $tag; // Given we now have a valid 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('/]*/', $tag) && $sizeofmatches > 1) { - // Looking for a starting 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 Manage media players'; diff --git a/filter/mediaplugin/settings.php b/filter/mediaplugin/settings.php new file mode 100644 index 00000000000..8c71e5ad218 --- /dev/null +++ b/filter/mediaplugin/settings.php @@ -0,0 +1,34 @@ +. + +/** + * 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); + +} diff --git a/filter/mediaplugin/styles.css b/filter/mediaplugin/styles.css index c6c41de7fd1..75a63ce380d 100644 --- a/filter/mediaplugin/styles.css +++ b/filter/mediaplugin/styles.css @@ -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; } diff --git a/filter/mediaplugin/tests/filter_test.php b/filter/mediaplugin/tests/filter_test.php index 13173eee4b2..512fb1f41e1 100644 --- a/filter/mediaplugin/tests/filter_test.php +++ b/filter/mediaplugin/tests/filter_test.php @@ -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 { 'test mp3', 'test ogg', 'test mpg', - 'test', + 'test', 'test file', 'test file', 'test file', @@ -72,7 +60,7 @@ class filter_mediaplugin_testcase extends advanced_testcase { 'test file', 'test flv', 'test file', - 'test mp3', + 'test ogg', 'test mp3', 'test mp3', 'test mp3', @@ -83,7 +71,7 @@ class filter_mediaplugin_testcase extends advanced_testcase { 'test mp3 + href="http://moodle.org/testfile/test.wav">test wav ', 'youtube\'s', // 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 = 'test.mp3'. $longurl; $filter = $filterplugin->filter($precededlongurl); - $this->assertEquals(1, substr_count($filter, 'M.util.add_audio_player')); + $this->assertEquals(1, substr_count($filter, '')); $this->assertContains($longurl, $filter); // Testing for cases where: to be filtered content has 6+ text afterwards. diff --git a/lang/en/deprecated.txt b/lang/en/deprecated.txt index 3aa78e6ed66..42cf26a6c9f 100644 --- a/lang/en/deprecated.txt +++ b/lang/en/deprecated.txt @@ -35,3 +35,6 @@ downloadtsv,core_table downloadxhtml,core_table invalidpersistent,core_competency revealpassword,core_form +mediasettings,core_media +legacyheading,core_media +legacyheading_desc,core_media diff --git a/lang/en/media.php b/lang/en/media.php index 5ad57387db9..be6847fef0f 100644 --- a/lang/en/media.php +++ b/lang/en/media.php @@ -22,30 +22,21 @@ * @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['audioextensions'] = 'Audio: {$a}'; +$string['defaultwidth'] = 'Default width'; +$string['defaultwidthdesc'] = 'Default width of video or other embedded object if no width is specified and player is not able to pick the real video width'; +$string['defaultheight'] = 'Default height'; +$string['defaultheightdesc'] = 'Default height of video or other embedded object if no height is specified and player is not able to pick the real video height'; +$string['extensions'] = 'Extensions: {$a}'; +$string['managemediaplayers'] = 'Manage media players'; $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['supports'] = 'Supports'; +$string['videoextensions'] = 'Video: {$a}'; + +// Deprecated since Moodle 3.2. $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 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.'; diff --git a/lang/en/plugin.php b/lang/en/plugin.php index ff64fd338f0..8e1d5e93313 100644 --- a/lang/en/plugin.php +++ b/lang/en/plugin.php @@ -141,6 +141,8 @@ $string['type_gradingform'] = 'Advanced grading method'; $string['type_gradingform_plural'] = 'Advanced grading methods'; $string['type_local'] = 'Local plugin'; $string['type_local_plural'] = 'Local plugins'; +$string['type_media'] = 'Media player'; +$string['type_media_plural'] = 'Media players'; $string['type_message'] = 'Messaging output'; $string['type_message_plural'] = 'Messaging outputs'; $string['type_mnetservice'] = 'MNet service'; diff --git a/lib/adminlib.php b/lib/adminlib.php index 515a6c2511e..da825e150bf 100644 --- a/lib/adminlib.php +++ b/lib/adminlib.php @@ -7158,6 +7158,225 @@ class admin_page_managefilters extends admin_externalpage { } } +/** + * Special class for media player plugins management. + * + * @copyright 2016 Marina Glancy + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class admin_setting_managemediaplayers extends admin_setting { + /** + * Calls parent::__construct with specific arguments + */ + public function __construct() { + $this->nosave = true; + parent::__construct('managemediaplayers', get_string('managemediaplayers', 'media'), '', ''); + } + + /** + * Always returns true, does nothing + * + * @return true + */ + public function get_setting() { + return true; + } + + /** + * Always returns true, does nothing + * + * @return true + */ + public function get_defaultsetting() { + return true; + } + + /** + * Always returns '', does not write anything + * + * @param mixed $data + * @return string Always returns '' + */ + public function write_setting($data) { + // Do not write any setting. + return ''; + } + + /** + * Checks if $query is one of the available enrol plugins + * + * @param string $query The string to search for + * @return bool Returns true if found, false if not + */ + public function is_related($query) { + if (parent::is_related($query)) { + return true; + } + + $query = core_text::strtolower($query); + $plugins = core_plugin_manager::instance()->get_plugins_of_type('media'); + foreach ($plugins as $name => $plugin) { + $localised = $plugin->displayname; + if (strpos(core_text::strtolower($name), $query) !== false) { + return true; + } + if (strpos(core_text::strtolower($localised), $query) !== false) { + return true; + } + } + return false; + } + + /** + * Sort plugins so enabled plugins are displayed first and all others are displayed in the end sorted by rank. + * @return \core\plugininfo\media[] + */ + protected function get_sorted_plugins() { + $pluginmanager = core_plugin_manager::instance(); + + $plugins = $pluginmanager->get_plugins_of_type('media'); + $enabledplugins = $pluginmanager->get_enabled_plugins('media'); + + // Sort plugins so enabled plugins are displayed first and all others are displayed in the end sorted by rank. + \core_collator::asort_objects_by_method($plugins, 'get_rank', \core_collator::SORT_NUMERIC); + + $order = array_values($enabledplugins); + $order = array_merge($order, array_diff(array_reverse(array_keys($plugins)), $order)); + + $sortedplugins = array(); + foreach ($order as $name) { + $sortedplugins[$name] = $plugins[$name]; + } + + return $sortedplugins; + } + + /** + * Builds the XHTML to display the control + * + * @param string $data Unused + * @param string $query + * @return string + */ + public function output_html($data, $query='') { + global $CFG, $OUTPUT, $DB, $PAGE; + + // Display strings. + $strup = get_string('up'); + $strdown = get_string('down'); + $strsettings = get_string('settings'); + $strenable = get_string('enable'); + $strdisable = get_string('disable'); + $struninstall = get_string('uninstallplugin', 'core_admin'); + $strversion = get_string('version'); + $strname = get_string('name'); + $strsupports = get_string('supports', 'core_media'); + + $pluginmanager = core_plugin_manager::instance(); + + $plugins = $this->get_sorted_plugins(); + $enabledplugins = $pluginmanager->get_enabled_plugins('media'); + + $return = $OUTPUT->box_start('generalbox mediaplayersui'); + + $table = new html_table(); + $table->head = array($strname, $strsupports, $strversion, + $strenable, $strup.'/'.$strdown, $strsettings, $struninstall); + $table->colclasses = array('leftalign', 'leftalign', 'centeralign', + 'centeralign', 'centeralign', 'centeralign', 'centeralign'); + $table->id = 'mediaplayerplugins'; + $table->attributes['class'] = 'admintable generaltable'; + $table->data = array(); + + // Iterate through media plugins and add to the display table. + $updowncount = 1; + $url = new moodle_url('/admin/media.php', array('sesskey' => sesskey())); + $printed = array(); + $spacer = $OUTPUT->pix_icon('spacer', '', 'moodle', array('class' => 'iconsmall')); + + $usedextensions = []; + foreach ($plugins as $name => $plugin) { + $url->param('media', $name); + $plugininfo = $pluginmanager->get_plugin_info('media_'.$name); + $version = $plugininfo->versiondb; + $supports = $plugininfo->supports($usedextensions); + + // Hide/show links. + $class = ''; + if (!$plugininfo->is_installed_and_upgraded()) { + $hideshow = ''; + $enabled = false; + $displayname = ''.$name.''; + } else { + $enabled = $plugininfo->is_enabled(); + if ($enabled) { + $hideshow = html_writer::link(new moodle_url($url, array('action' => 'disable')), + $OUTPUT->pix_icon('t/hide', $strdisable, 'moodle', array('class' => 'iconsmall'))); + } else { + $hideshow = html_writer::link(new moodle_url($url, array('action' => 'enable')), + $OUTPUT->pix_icon('t/show', $strenable, 'moodle', array('class' => 'iconsmall'))); + $class = 'dimmed_text'; + } + $displayname = $plugin->displayname; + if (get_string_manager()->string_exists('pluginname_help', 'media_' . $name)) { + $displayname .= ' ' . $OUTPUT->help_icon('pluginname', 'media_' . $name); + } + } + if ($PAGE->theme->resolve_image_location('icon', 'media_' . $name, false)) { + $icon = $OUTPUT->pix_icon('icon', '', 'media_' . $name, array('class' => 'icon pluginicon')); + } else { + $icon = $OUTPUT->pix_icon('spacer', '', 'moodle', array('class' => 'icon pluginicon noicon')); + } + + // Up/down link (only if enrol is enabled). + $updown = ''; + if ($enabled) { + if ($updowncount > 1) { + $updown = html_writer::link(new moodle_url($url, array('action' => 'up')), + $OUTPUT->pix_icon('t/up', $strup, 'moodle', array('class' => 'iconsmall'))); + } else { + $updown = $spacer; + } + if ($updowncount < count($enabledplugins)) { + $updown .= html_writer::link(new moodle_url($url, array('action' => 'down')), + $OUTPUT->pix_icon('t/down', $strdown, 'moodle', array('class' => 'iconsmall'))); + } else { + $updown .= $spacer; + } + ++$updowncount; + } + + $uninstall = ''; + $status = $plugininfo->get_status(); + if ($status === core_plugin_manager::PLUGIN_STATUS_MISSING) { + $uninstall = get_string('status_missing', 'core_plugin') . '
'; + } + if ($status === core_plugin_manager::PLUGIN_STATUS_NEW) { + $uninstall = get_string('status_new', 'core_plugin'); + } else if ($uninstallurl = $pluginmanager->get_uninstall_url('media_'.$name, 'manage')) { + $uninstall .= html_writer::link($uninstallurl, $struninstall); + } + + $settings = ''; + if ($plugininfo->get_settings_url()) { + $settings = html_writer::link($plugininfo->get_settings_url(), $strsettings); + } + + // Add a row to the table. + $row = new html_table_row(array($icon.$displayname, $supports, $version, $hideshow, $updown, $settings, $uninstall)); + if ($class) { + $row->attributes['class'] = $class; + } + $table->data[] = $row; + + $printed[$name] = true; + } + + $return .= html_writer::table($table); + $return .= $OUTPUT->box_end(); + return highlight($query, $return); + } +} /** * Initialise admin page - this function does require login and permission diff --git a/lib/classes/component.php b/lib/classes/component.php index e1adcff2895..de5c3081d16 100644 --- a/lib/classes/component.php +++ b/lib/classes/component.php @@ -454,7 +454,7 @@ $cache = '.var_export($cache, true).'; 'langconfig' => null, 'license' => null, 'mathslib' => null, - 'media' => null, + 'media' => $CFG->dirroot.'/media', 'message' => $CFG->dirroot.'/message', 'mimetypes' => null, 'mnet' => $CFG->dirroot.'/mnet', @@ -502,6 +502,7 @@ $cache = '.var_export($cache, true).'; 'enrol' => $CFG->dirroot.'/enrol', 'message' => $CFG->dirroot.'/message/output', 'block' => $CFG->dirroot.'/blocks', + 'media' => $CFG->dirroot.'/media/player', 'filter' => $CFG->dirroot.'/filter', 'editor' => $CFG->dirroot.'/lib/editor', 'format' => $CFG->dirroot.'/course/format', diff --git a/lib/classes/filetypes.php b/lib/classes/filetypes.php index bb2d2ab2659..28ffaf5de19 100644 --- a/lib/classes/filetypes.php +++ b/lib/classes/filetypes.php @@ -140,7 +140,7 @@ abstract class core_filetypes { 'mht' => array('type' => 'message/rfc822', 'icon' => 'archive'), 'mhtml' => array('type' => 'message/rfc822', 'icon' => 'archive'), 'mov' => array('type' => 'video/quicktime', 'icon' => 'quicktime', - 'groups' => array('video', 'web_video'), 'string' => 'video'), + 'groups' => array('video', 'web_video', 'html_video'), 'string' => 'video'), 'movie' => array('type' => 'video/x-sgi-movie', 'icon' => 'quicktime', 'groups' => array('video'), 'string' => 'video'), 'mw' => array('type' => 'application/maple', 'icon' => 'math'), 'mws' => array('type' => 'application/maple', 'icon' => 'math'), @@ -260,7 +260,7 @@ abstract class core_filetypes { 'tsv' => array('type' => 'text/tab-separated-values', 'icon' => 'text'), 'txt' => array('type' => 'text/plain', 'icon' => 'text', 'defaulticon' => true), 'vtt' => array('type' => 'text/vtt', 'icon' => 'text', 'groups' => array('html_track')), - 'wav' => array('type' => 'audio/wav', 'icon' => 'wav', 'groups' => array('audio'. 'html_audio', 'web_audio'), + 'wav' => array('type' => 'audio/wav', 'icon' => 'wav', 'groups' => array('audio', 'html_audio', 'web_audio'), 'string' => 'audio'), 'webm' => array('type' => 'video/webm', 'icon' => 'video', 'groups' => array('html_video', 'video', 'web_video'), 'string' => 'video'), diff --git a/lib/classes/plugin_manager.php b/lib/classes/plugin_manager.php index 286e9307c85..8d7e37670a9 100644 --- a/lib/classes/plugin_manager.php +++ b/lib/classes/plugin_manager.php @@ -1815,6 +1815,10 @@ class core_plugin_manager { 'memberships', 'profile', 'toolproxy', 'toolsettings' ), + 'media' => array( + 'html5audio', 'html5video', 'swf', 'videojs', 'vimeo', 'youtube' + ), + 'message' => array( 'airnotifier', 'email', 'jabber', 'popup' ), diff --git a/lib/classes/plugininfo/media.php b/lib/classes/plugininfo/media.php new file mode 100644 index 00000000000..8bd067bc858 --- /dev/null +++ b/lib/classes/plugininfo/media.php @@ -0,0 +1,188 @@ +. + +/** + * Defines classes used for plugin info. + * + * @package core + * @copyright 2016 Marina Glancy + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +namespace core\plugininfo; + +defined('MOODLE_INTERNAL') || die(); + +/** + * Class for media plugins + * + * @package core + * @copyright 2016 Marina Glancy + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class media extends base { + + public function is_uninstall_allowed() { + return true; + } + + /** + * Get the name for the settings section. + * + * @return string + */ + public function get_settings_section_name() { + return 'mediasetting' . $this->name; + } + + /** + * Load the global settings for a particular availability plugin (if there are any) + * + * @param \part_of_admin_tree $adminroot + * @param string $parentnodename + * @param bool $hassiteconfig + */ + public function load_settings(\part_of_admin_tree $adminroot, $parentnodename, $hassiteconfig) { + global $CFG, $USER, $DB, $OUTPUT, $PAGE; // In case settings.php wants to refer to them. + $ADMIN = $adminroot; // May be used in settings.php. + $plugininfo = $this; // Also can be used inside settings.php + $availability = $this; // Also to be used inside settings.php. + + if (!$this->is_installed_and_upgraded()) { + return; + } + + if (!$hassiteconfig) { + return; + } + + $section = $this->get_settings_section_name(); + + $settings = null; + if (file_exists($this->full_path('settings.php'))) { + $settings = new \admin_settingpage($section, $this->displayname, 'moodle/site:config', $this->is_enabled() === false); + include($this->full_path('settings.php')); // This may also set $settings to null. + } + if ($settings) { + $ADMIN->add($parentnodename, $settings); + } + } + + /** + * Return URL used for management of plugins of this type. + * @return \moodle_url + */ + public static function get_manage_url() { + return new \moodle_url('/admin/settings.php', array('section' => 'managemediaplayers')); + } + + public static function get_enabled_plugins() { + global $CFG; + + $order = (!empty($CFG->media_plugins_sortorder)) ? explode(',', $CFG->media_plugins_sortorder) : []; + if ($order) { + $plugins = \core_plugin_manager::instance()->get_installed_plugins('media'); + $order = array_intersect($order, array_keys($plugins)); + } + return array_combine($order, $order); + } + + /** + * Sets the current plugin as enabled or disabled + * When enabling tries to guess the sortorder based on default rank returned by the plugin. + * @param bool $newstate + */ + public function set_enabled($newstate = true) { + $enabled = self::get_enabled_plugins(); + if (array_key_exists($this->name, $enabled) == $newstate) { + // Nothing to do. + return; + } + if ($newstate) { + // Enable media plugin. + $plugins = \core_plugin_manager::instance()->get_plugins_of_type('media'); + if (!array_key_exists($this->name, $plugins)) { + // Can not be enabled. + return; + } + $rank = $this->get_rank(); + $position = 0; + // Insert before the first enabled plugin which default rank is smaller than the default rank of this one. + foreach ($enabled as $playername) { + if (($player = $plugins[$playername]) && ($rank > $player->get_rank())) { + break; + } + $position++; + } + array_splice($enabled, $position, 0, [$this->name]); + self::set_enabled_plugins($enabled); + } else { + // Disable media plugin. + unset($enabled[$this->name]); + self::set_enabled_plugins($enabled); + } + } + + /** + * Set the list of enabled media players in the specified sort order + * To be used when changing settings or in unit tests + * @param string|array $list list of plugin names without frankenstyle prefix - comma-separated string or an array + */ + public static function set_enabled_plugins($list) { + if (empty($list)) { + $list = []; + } else if (!is_array($list)) { + $list = explode(',', $list); + } + if ($list) { + $plugins = \core_plugin_manager::instance()->get_installed_plugins('media'); + $list = array_intersect($list, array_keys($plugins)); + } + set_config('media_plugins_sortorder', join(',', $list)); + \core_plugin_manager::reset_caches(); + \core_media_manager::reset_caches(); + } + + /** + * Returns the default rank of this plugin for default sort order + * @return int + */ + public function get_rank() { + $classname = '\media_'.$this->name.'_plugin'; + if (class_exists($classname)) { + $object = new $classname(); + return $object->get_rank(); + } + return 0; + } + + /** + * Returns human-readable string of supported file/link types for the "Manage media players" page + * @param array $extensions + * @return string + */ + public function supports(&$extensions) { + $classname = '\media_'.$this->name.'_plugin'; + if (class_exists($classname)) { + $object = new $classname(); + $result = $object->supports($extensions); + foreach ($object->get_supported_extensions() as $ext) { + $extensions[$ext] = $ext; + } + return $result; + } + return ''; + } +} diff --git a/lib/classes/useragent.php b/lib/classes/useragent.php index effe291ab51..8fb7a7cfc59 100644 --- a/lib/classes/useragent.php +++ b/lib/classes/useragent.php @@ -316,6 +316,10 @@ class core_useragent { // Internet Explorer. return self::check_ie_version($version); + case 'Edge': + // Microsoft Edge. + return self::check_edge_version($version); + case 'Firefox': // Mozilla Firefox browsers. return self::check_firefox_version($version); @@ -1054,4 +1058,85 @@ class core_useragent { } return false; } + + /** + * Checks if current browser supports files with give extension as