From 87d26b47433311ee6e9bfc0824813e31c0272657 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sam=20M=C3=B8ller?= Date: Thu, 23 Sep 2021 13:24:54 +0200 Subject: [PATCH] MDL-72607 media_vimeo: Fix vimeo URL parser Fix vimeo URL parser to support the new vimeo URL format with privacy settings (the video with password or domain restricted setting). For example "https://vimeo.com/123456789/2bffff0000" the plugin must parse the URL "https://player.vimeo.com/video/123456789?h=2bffff0000" to the iframe source to make it compatible with the new vimeo URL format. See more about vimeo video privacy settings at https://vimeo.com/blog/post/video-privacy-explained/ --- media/player/vimeo/classes/plugin.php | 33 +++++++- media/player/vimeo/tests/player_test.php | 100 +++++++++++++++++++++++ 2 files changed, 130 insertions(+), 3 deletions(-) diff --git a/media/player/vimeo/classes/plugin.php b/media/player/vimeo/classes/plugin.php index 80a8601b23cd0..8908ad68c5a7e 100644 --- a/media/player/vimeo/classes/plugin.php +++ b/media/player/vimeo/classes/plugin.php @@ -34,7 +34,7 @@ */ class media_vimeo_plugin extends core_media_player_external { protected function embed_external(moodle_url $url, $name, $width, $height, $options) { - $videoid = $this->matches[1]; + $videoid = $this->get_video_id(); $info = s($name); // Note: resizing via url is not supported, user can click the fullscreen @@ -53,6 +53,33 @@ protected function embed_external(moodle_url $url, $name, $width, $height, $opti return $output; } + /** + * Get Vimeo video ID. + * @return string + */ + protected function get_video_id(): string { + return $this->get_video_id_with_code() ?? $this->matches[1] ?? ''; + } + + /** + * Get video id with code. + * @return string|null If NULL then the URL does not contain the code. + */ + protected function get_video_id_with_code(): ?string { + $id = $this->matches[2] ?? null; + + if (!empty($id)) { + $code = $this->matches[3] ?? null; + if (!empty($code)) { + return "{$id}?h={$code}"; + } + + return $id; + } + + return null; + } + /** * Returns regular expression to match vimeo URLs. * @return string @@ -60,8 +87,8 @@ protected function embed_external(moodle_url $url, $name, $width, $height, $opti protected function get_regex() { // Initial part of link. $start = '~^https?://vimeo\.com/'; - // Middle bit: either watch?v= or v/. - $middle = '([0-9]+)'; + // Middle bit: either 123456789 or 123456789/abdef12345. + $middle = '(([0-9]+)/([0-9a-f]+)|[0-9]+)'; return $start . $middle . core_media_player_external::END_LINK_REGEX_PART; } diff --git a/media/player/vimeo/tests/player_test.php b/media/player/vimeo/tests/player_test.php index 3e669891f11dc..889285c35db13 100644 --- a/media/player/vimeo/tests/player_test.php +++ b/media/player/vimeo/tests/player_test.php @@ -131,4 +131,104 @@ public function test_embed_media() { $this->assertMatchesRegularExpression('~~', $content); $this->assertMatchesRegularExpression('~width="123" height="35"~', $content); } + + /** + * Test embedding without media filter (for example for displaying URL resorce) + * and test that player plugin is parsing the URL with the code. + */ + public function test_embed_url_with_code() { + global $CFG; + + $url = new moodle_url('https://vimeo.com/1176321/abcdef12345'); + + $manager = core_media_manager::instance(); + $embedoptions = array( + core_media_manager::OPTION_TRUSTED => true, + core_media_manager::OPTION_BLOCK => true, + ); + + $this->assertTrue($manager->can_embed_url($url, $embedoptions)); + $content = $manager->embed_url($url, 'Test & file', 0, 0, $embedoptions); + + // Video source URL is contains the new vimeo embedded URL format. + $this->assertStringContainsString('player.vimeo.com/video/1176321?h=abcdef12345', $content); + + $this->assertMatchesRegularExpression('~mediaplugin_vimeo~', $content); + $this->assertMatchesRegularExpression('~~', $content); + $this->assertMatchesRegularExpression('~width="' . $CFG->media_default_width . '" height="' . + $CFG->media_default_height . '"~', $content); + + // Repeat sending the specific size to the manager. + $content = $manager->embed_url($url, 'New file', 123, 50, $embedoptions); + $this->assertMatchesRegularExpression('~width="123" height="50"~', $content); + } + + /** + * Test that mediaplugin filter replaces a link to the supported file with media tag + * and test that player plugin is parsing the URL with the code. + * + * filter_mediaplugin is enabled by default. + */ + public function test_embed_link_with_code() { + global $CFG; + $url = new moodle_url('https://vimeo.com/1176321/abcdef12345'); + $text = html_writer::link($url, 'Watch this one'); + $content = format_text($text, FORMAT_HTML); + + // Video source URL is contains the new vimeo embedded URL format. + $this->assertStringContainsString('player.vimeo.com/video/1176321?h=abcdef12345', $content); + + $this->assertMatchesRegularExpression('~mediaplugin_vimeo~', $content); + $this->assertMatchesRegularExpression('~~', $content); + $this->assertMatchesRegularExpression('~width="' . $CFG->media_default_width . '" height="' . + $CFG->media_default_height . '"~', $content); + } + + /** + * Test that mediaplugin filter adds player code on top of ~', $content); + $this->assertDoesNotMatchRegularExpression('~assertDoesNotMatchRegularExpression('~Unsupported text~', $content); + $this->assertDoesNotMatchRegularExpression('~ tag. + $text = ''; + $content = format_text($text, FORMAT_HTML); + $this->assertMatchesRegularExpression('~mediaplugin_vimeo~', $content); + $this->assertMatchesRegularExpression('~~', $content); + $this->assertMatchesRegularExpression('~width="123" height="35"~', $content); + } + + /** + * Test that mediaplugin filter skip the process when the URL is invalid. + */ + public function test_skip_invalid_url_format_with_code() { + $url = new moodle_url('https://vimeo.com/_________/abcdef12345s'); + $text = html_writer::link($url, 'Invalid Vimeo URL'); + $content = format_text($text, FORMAT_HTML); + + $this->assertStringNotContainsString('player.vimeo.com/video/_________?h=abcdef12345s', $content); + $this->assertDoesNotMatchRegularExpression('~mediaplugin_vimeo~', $content); + $this->assertDoesNotMatchRegularExpression('~~', $content); + } }