From 3a4430a3229d4ff3855006991f105f0c212880bb Mon Sep 17 00:00:00 2001 From: skaught <skaught@gmail.com> Date: Tue, 20 Jul 2021 11:28:24 -0400 Subject: [PATCH] 2913598-49 ally fixes to-render-not_container.patch --- .../src/Plugin/Filter/VideoEmbedWysiwyg.php | 24 +++++-- src/Plugin/Field/FieldFormatter/Video.php | 12 ++-- .../video_embed_field/Provider/Vimeo.php | 39 +++++------ .../video_embed_field/Provider/YouTube.php | 55 ++++++++++------ .../Provider/YouTubePlaylist.php | 2 +- src/ProviderPluginBase.php | 66 +++++++++++++++++-- src/ProviderPluginInterface.php | 17 ++++- templates/video-embed-iframe.html.twig | 6 +- .../Provider/MockProvider.php | 2 +- tests/src/Unit/ProviderUrlParseTest.php | 6 +- 10 files changed, 168 insertions(+), 61 deletions(-) diff --git a/modules/video_embed_wysiwyg/src/Plugin/Filter/VideoEmbedWysiwyg.php b/modules/video_embed_wysiwyg/src/Plugin/Filter/VideoEmbedWysiwyg.php index f0c50e7..61a9532 100644 --- a/modules/video_embed_wysiwyg/src/Plugin/Filter/VideoEmbedWysiwyg.php +++ b/modules/video_embed_wysiwyg/src/Plugin/Filter/VideoEmbedWysiwyg.php @@ -11,6 +11,7 @@ use Drupal\video_embed_field\Plugin\Field\FieldFormatter\Video; use Drupal\video_embed_field\ProviderManagerInterface; use Symfony\Component\DependencyInjection\ContainerInterface; use Drupal\Core\Render\RendererInterface; +use Drupal\Core\Path\CurrentPathStack; /** * The filter to turn tokens inserted into the WYSIWYG into videos. @@ -44,6 +45,12 @@ class VideoEmbedWysiwyg extends FilterBase implements ContainerFactoryPluginInte * @var \Drupal\Core\Session\AccountProxyInterface */ protected $currentUser; + /** + * The current user. + * + * @var \Drupal\Core\Path\CurrentPathStack + */ + protected $currentPath; /** * VideoEmbedWysiwyg constructor. @@ -61,18 +68,24 @@ class VideoEmbedWysiwyg extends FilterBase implements ContainerFactoryPluginInte * @param \Drupal\Core\Session\AccountProxyInterface $current_user * The current user. */ - public function __construct(array $configuration, $plugin_id, $plugin_definition, ProviderManagerInterface $provider_manager, RendererInterface $renderer, AccountProxyInterface $current_user) { + public function __construct(array $configuration, $plugin_id, $plugin_definition, ProviderManagerInterface $provider_manager, RendererInterface $renderer, AccountProxyInterface $current_user, CurrentPathStack $currentPath) { parent::__construct($configuration, $plugin_id, $plugin_definition); $this->providerManager = $provider_manager; $this->renderer = $renderer; $this->currentUser = $current_user; + $this->currentPath = $currentPath; } /** * {@inheritdoc} */ public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { - return new static($configuration, $plugin_id, $plugin_definition, $container->get('video_embed_field.provider_manager'), $container->get('renderer'), $container->get('current_user')); + return new static($configuration, $plugin_id, $plugin_definition, + $container->get('video_embed_field.provider_manager'), + $container->get('renderer'), + $container->get('current_user'), + $container->get('path.current'), + ); } /** @@ -81,14 +94,15 @@ class VideoEmbedWysiwyg extends FilterBase implements ContainerFactoryPluginInte public function process($text, $langcode) { $response = new FilterProcessResult($text); - + $startUnique = 1; foreach ($this->getValidMatches($text) as $source_text => $embed_data) { if (!$provider = $this->providerManager->loadProviderFromInput($embed_data['video_url'])) { continue; } - + $path = \ltrim($this->currentPath->getPath(), '/'); + $instance_id = 'youtube-' . Html::cleanCssIdentifier($path . '-' . Html::getUniqueId($provider->getVideoId())); $autoplay = $this->currentUser->hasPermission('never autoplay videos') ? FALSE : $embed_data['settings']['autoplay']; - $embed_code = $provider->renderEmbedCode($embed_data['settings']['width'], $embed_data['settings']['height'], $autoplay); + $embed_code = $provider->renderEmbedCode($embed_data['settings']['width'], $embed_data['settings']['height'], $autoplay, $instance_id); $embed_code = [ '#type' => 'container', diff --git a/src/Plugin/Field/FieldFormatter/Video.php b/src/Plugin/Field/FieldFormatter/Video.php index 361207a..940dc4d 100644 --- a/src/Plugin/Field/FieldFormatter/Video.php +++ b/src/Plugin/Field/FieldFormatter/Video.php @@ -92,21 +92,22 @@ class Video extends FormatterBase implements ContainerFactoryPluginInterface { $element = []; foreach ($items as $delta => $item) { $provider = $this->providerManager->loadProviderFromInput($item->value); - if (!$provider) { $element[$delta] = ['#theme' => 'video_embed_field_missing_provider']; } else { $autoplay = $this->currentUser->hasPermission('never autoplay videos') ? FALSE : $this->getSetting('autoplay'); - $element[$delta] = $provider->renderEmbedCode($this->getSetting('width'), $this->getSetting('height'), $autoplay); - $element[$delta]['#cache']['contexts'][] = 'user.permissions'; + $instance_id = 'youtube-embed-filter-'.$item->getEntity()->id().'-'.$delta; + $element[$delta] = $provider->renderEmbedCode($this->getSetting('width'), $this->getSetting('height'), $autoplay, $instance_id); + $element[$delta]['#cache']['max-age'] = 0; $element[$delta] = [ '#type' => 'container', - '#attributes' => ['class' => [Html::cleanCssIdentifier(sprintf('video-embed-field-provider-%s', $provider->getPluginId()))]], + '#attributes' => [ + 'class' => [Html::cleanCssIdentifier(sprintf('video-embed-field-provider-%s', $provider->getPluginId()))], + ], 'children' => $element[$delta], ]; - // For responsive videos, wrap each field item in it's own container. if ($this->getSetting('responsive')) { $element[$delta]['#attached']['library'][] = 'video_embed_field/responsive-video'; @@ -115,6 +116,7 @@ class Video extends FormatterBase implements ContainerFactoryPluginInterface { } } + return $element; } diff --git a/src/Plugin/video_embed_field/Provider/Vimeo.php b/src/Plugin/video_embed_field/Provider/Vimeo.php index 70c7f6b..799934b 100644 --- a/src/Plugin/video_embed_field/Provider/Vimeo.php +++ b/src/Plugin/video_embed_field/Provider/Vimeo.php @@ -17,25 +17,14 @@ class Vimeo extends ProviderPluginBase { /** * {@inheritdoc} */ - public function renderEmbedCode($width, $height, $autoplay) { - $iframe = [ - '#type' => 'video_embed_iframe', - '#provider' => 'vimeo', - '#url' => sprintf('https://player.vimeo.com/video/%s', $this->getVideoId()), - '#query' => [ - 'autoplay' => $autoplay, - ], - '#attributes' => [ - 'width' => $width, - 'height' => $height, - 'frameborder' => '0', - 'allowfullscreen' => 'allowfullscreen', - ], - ]; + public function renderEmbedCode($width, $height, $autoplay, $instance_id) { + $embed_code = parent::renderEmbedCode($width, $height, $autoplay, $instance_id); + $embed_code['#provider'] = 'vimeo'; + $embed_code['#url'] = $this->getProtocol() . '://player.vimeo.com/video/' . $this->getVideoId(); if ($time_index = $this->getTimeIndex()) { - $iframe['#fragment'] = sprintf('t=%s', $time_index); + $embed_code['#fragment'] = 't=' . $time_index; } - return $iframe; + return $embed_code; } /** @@ -48,11 +37,17 @@ class Vimeo extends ProviderPluginBase { /** * Get the vimeo oembed data. * - * @return array + * @return array|false * An array of data from the oembed endpoint. */ protected function oEmbedData() { - return json_decode(file_get_contents('http://vimeo.com/api/oembed.json?url=' . $this->getInput())); + $protocol = $this->getProtocol(); + $requestOembed = $this->httpClient->request('GET', $this->getProtocol() . '://vimeo.com/api/oembed.json?url=' . $this->getInput()); + if ($requestOembed->getStatusCode() != 200) { + return FALSE; + } + $data = \json_decode($requestOembed->getBody()->getContents()); + return $data; } /** @@ -66,7 +61,7 @@ class Vimeo extends ProviderPluginBase { /** * Get the time index from the URL. * - * @return string|FALSE + * @return string|false * A time index parameter to pass to the frame or FALSE if none is found. */ protected function getTimeIndex() { @@ -78,7 +73,9 @@ class Vimeo extends ProviderPluginBase { * {@inheritdoc} */ public function getName() { - return $this->oEmbedData()->title; + $stub = parent::getName(); + $title = (($this->oEmbedData() !== FALSE) ? $this->filterCharaters($this->oEmbedData()->title) . ' | ' : ''); + return $title . $stub; } } diff --git a/src/Plugin/video_embed_field/Provider/YouTube.php b/src/Plugin/video_embed_field/Provider/YouTube.php index 224edd2..39bf688 100644 --- a/src/Plugin/video_embed_field/Provider/YouTube.php +++ b/src/Plugin/video_embed_field/Provider/YouTube.php @@ -3,6 +3,7 @@ namespace Drupal\video_embed_field\Plugin\video_embed_field\Provider; use Drupal\video_embed_field\ProviderPluginBase; +use Drupal\Component\Utility\Html; /** * A YouTube provider plugin. @@ -17,23 +18,13 @@ class YouTube extends ProviderPluginBase { /** * {@inheritdoc} */ - public function renderEmbedCode($width, $height, $autoplay) { - $embed_code = [ - '#type' => 'video_embed_iframe', - '#provider' => 'youtube', - '#url' => sprintf('https://www.youtube.com/embed/%s', $this->getVideoId()), - '#query' => [ - 'autoplay' => $autoplay, - 'start' => $this->getTimeIndex(), - 'rel' => '0', - ], - '#attributes' => [ - 'width' => $width, - 'height' => $height, - 'frameborder' => '0', - 'allowfullscreen' => 'allowfullscreen', - ], - ]; + public function renderEmbedCode($width, $height, $autoplay, $instance_id) { + $embed_code = parent::renderEmbedCode($width, $height, $autoplay, $instance_id); + $yid = $this->getVideoId(); + $embed_code['#provider'] = 'youtube'; + $embed_code['#url'] = $this->getProtocol() . '://www.youtube.com/embed/' . $yid; + $embed_code['#query']['start'] = $this->getTimeIndex(); + $embed_code['#query']['rel'] = 0; if ($language = $this->getLanguagePreference()) { $embed_code['#query']['cc_lang_pref'] = $language; } @@ -59,7 +50,7 @@ class YouTube extends ProviderPluginBase { /** * Extract the language preference from the URL for use in closed captioning. * - * @return string|FALSE + * @return string|false * The language preference if one exists or FALSE if one could not be found. */ protected function getLanguagePreference() { @@ -71,7 +62,7 @@ class YouTube extends ProviderPluginBase { * {@inheritdoc} */ public function getRemoteThumbnailUrl() { - $url = 'http://img.youtube.com/vi/%s/%s.jpg'; + $url = $this->getProtocol() . '://img.youtube.com/vi/%s/%s.jpg'; $high_resolution = sprintf($url, $this->getVideoId(), 'maxresdefault'); $backup = sprintf($url, $this->getVideoId(), 'mqdefault'); try { @@ -91,4 +82,30 @@ class YouTube extends ProviderPluginBase { return isset($matches['id']) ? $matches['id'] : FALSE; } + /** + * Get the Youtube oEmbed data. + * + * @return array|false + * An array of data from the oEmbed endpoint. + */ + protected function oEmbedData() { + $protocol = $this->getProtocol(); + $requestOembed = $this->httpClient->request('GET', $protocol . '://www.youtube.com/oembed?url=' . $protocol . '://www.youtube.com/watch?v=' . $this->getVideoId() . '&format=json'); + if ($requestOembed->getStatusCode() != 200) { + return FALSE; + } + $data = \json_decode($requestOembed->getBody()->getContents()); + return $data; + } + + + /** + * {@inheritdoc} + */ + public function getName() { + $stub = parent::getName(); + $title = (($this->oEmbedData() !== FALSE) ? $this->filterCharaters($this->oEmbedData()->title) . ' | ' : ''); + return $title . $stub; + } + } diff --git a/src/Plugin/video_embed_field/Provider/YouTubePlaylist.php b/src/Plugin/video_embed_field/Provider/YouTubePlaylist.php index 1043c5a..52b3be1 100644 --- a/src/Plugin/video_embed_field/Provider/YouTubePlaylist.php +++ b/src/Plugin/video_embed_field/Provider/YouTubePlaylist.php @@ -17,7 +17,7 @@ class YouTubePlaylist extends ProviderPluginBase { /** * {@inheritdoc} */ - public function renderEmbedCode($width, $height, $autoplay) { + public function renderEmbedCode($width, $height, $autoplay, $instance_id) { return [ '#type' => 'video_embed_iframe', '#provider' => 'youtube_playlist', diff --git a/src/ProviderPluginBase.php b/src/ProviderPluginBase.php index c3c4b0d..2d790a9 100644 --- a/src/ProviderPluginBase.php +++ b/src/ProviderPluginBase.php @@ -7,6 +7,7 @@ use Drupal\Core\Plugin\ContainerFactoryPluginInterface; use Drupal\Core\Plugin\PluginBase; use GuzzleHttp\ClientInterface; use Symfony\Component\DependencyInjection\ContainerInterface; +use Drupal\Component\Utility\Html; /** * A base for the provider plugins. @@ -50,17 +51,17 @@ abstract class ProviderPluginBase extends PluginBase implements ProviderPluginIn * Create a plugin with the given input. * * @param array $configuration - * The configuration of the plugin. + * A configuration array containing information about the plugin instance. * @param string $plugin_id * The plugin id. * @param array $plugin_definition * The plugin definition. * @param \GuzzleHttp\ClientInterface $http_client - * An HTTP client. + * An HTTP client. * * @throws \Exception */ - public function __construct($configuration, $plugin_id, $plugin_definition, ClientInterface $http_client) { + public function __construct(array $configuration, $plugin_id, array $plugin_definition, ClientInterface $http_client) { parent::__construct($configuration, $plugin_id, $plugin_definition); if (!static::isApplicable($configuration['input'])) { throw new \Exception('Tried to create a video provider plugin with invalid input.'); @@ -76,7 +77,7 @@ abstract class ProviderPluginBase extends PluginBase implements ProviderPluginIn * @return string * The video ID. */ - protected function getVideoId() { + public function getVideoId() { return $this->videoId; } @@ -93,6 +94,39 @@ abstract class ProviderPluginBase extends PluginBase implements ProviderPluginIn return $this->fileSystem; } + /** + * Determine Protocol site is currenly using from server var. + * + * @return bool + */ + private function isSecure() { + return (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') || $_SERVER['SERVER_PORT'] == 443; + } + + /** + * Request Protocol site is currenly using. + * + * @return string + * The protocol currently used. + */ + public function getProtocol() { + return $this->isSecure() ? 'https' : 'http'; + } + + /** + * Transliterate and reduce (assuming Title) string from video provider data + * to html output. + * + * @return string + * an attribute ready string of text. + */ + public static function filterCharaters($str = '') { + $trans = \Drupal::transliteration(); + $langcode = \Drupal::languageManager()->getCurrentLanguage()->getId(); + $transformed = $trans->transliterate($str, $langcode); + return preg_replace('/[^a-zA-Z0-9_ -]/s', '', $transformed); + } + /** * Get the input which caused this plugin to be selected. * @@ -151,6 +185,30 @@ abstract class ProviderPluginBase extends PluginBase implements ProviderPluginIn } } + /** + * {@inheritdoc} + */ + public function renderEmbedCode($width, $height, $autoplay, $instance_id) { + $embed_code = [ + '#type' => 'video_embed_iframe', + '#provider' => '', + '#url' => '', + '#query' => [ + 'autoplay' => $autoplay, + ], + '#attributes' => [ + 'width' => $width, + 'height' => $height, + 'title' => $this->getName(), + 'name' => $instance_id, + 'id' => $instance_id, + 'frameborder' => '0', + 'allowfullscreen' => 'allowfullscreen', + ], + ]; + return $embed_code; + } + /** * {@inheritdoc} */ diff --git a/src/ProviderPluginInterface.php b/src/ProviderPluginInterface.php index 863feeb..b093c5c 100644 --- a/src/ProviderPluginInterface.php +++ b/src/ProviderPluginInterface.php @@ -1,6 +1,7 @@ <?php namespace Drupal\video_embed_field; + use Drupal\Component\Plugin\PluginInspectionInterface; /** @@ -72,11 +73,13 @@ interface ProviderPluginInterface extends PluginInspectionInterface { * The height of the video player. * @param bool $autoplay * If the video should autoplay. + * @param string $instance_id + * An instance_id to be used for name and id attributes of iframe. * * @return mixed * A renderable array of the embed code. */ - public function renderEmbedCode($width, $height, $autoplay); + public function renderEmbedCode($width, $height, $autoplay, $instance_id); /** * Get the ID of the video from user input. @@ -89,6 +92,18 @@ interface ProviderPluginInterface extends PluginInspectionInterface { */ public static function getIdFromInput($input); + /** + * Transliterate and reduce (assuming Title) string from video provider data + * to html output. + * + * @param string $input + * the input string (dirty) + * + * @return string + * an attribute ready string of text (clean) + */ + public static function filterCharaters($str); + /** * Get the name of the video. * diff --git a/templates/video-embed-iframe.html.twig b/templates/video-embed-iframe.html.twig index 353e0db..deac65a 100644 --- a/templates/video-embed-iframe.html.twig +++ b/templates/video-embed-iframe.html.twig @@ -4,4 +4,8 @@ * Display an iframe with alterable components. */ #} -<iframe{{ attributes }}{% if url is not empty %} src="{{ url }}{% if query is not empty %}?{{ query | url_encode }}{% endif %}{% if fragment is not empty %}#{{ fragment }}{% endif %}"{% endif %}></iframe> +{% spaceless %} +<iframe{{ attributes }}{% if url is not empty %} src="{{ url }}{% if query is not empty %}?{{ query | url_encode }}{% endif %}{% if fragment is not empty %}#{{ fragment }}{% endif %}"{% endif %}>{% if url is not empty %} + <a href={{ url }}{% if query is not empty %}?{{ query | url_encode }}{% endif %}{% if fragment is not empty %}#{{ fragment }}{% endif %}" target="_blank">{{ attributes.title }}</a> +{% endif %}</iframe> +{% endspaceless %} diff --git a/tests/modules/video_embed_field_mock_provider/src/Plugin/video_embed_field/Provider/MockProvider.php b/tests/modules/video_embed_field_mock_provider/src/Plugin/video_embed_field/Provider/MockProvider.php index da282f7..36b596d 100644 --- a/tests/modules/video_embed_field_mock_provider/src/Plugin/video_embed_field/Provider/MockProvider.php +++ b/tests/modules/video_embed_field_mock_provider/src/Plugin/video_embed_field/Provider/MockProvider.php @@ -33,7 +33,7 @@ class MockProvider implements ProviderPluginInterface { /** * {@inheritdoc} */ - public function renderEmbedCode($width, $height, $autoplay) { + public function renderEmbedCode($width, $height, $autoplay, $instance_id) { return [ '#markup' => 'Mock provider embed code.', ]; diff --git a/tests/src/Unit/ProviderUrlParseTest.php b/tests/src/Unit/ProviderUrlParseTest.php index 6f64b97..b79b265 100644 --- a/tests/src/Unit/ProviderUrlParseTest.php +++ b/tests/src/Unit/ProviderUrlParseTest.php @@ -199,7 +199,7 @@ class ProviderUrlParseTest extends UnitTestCase { $provider = new YouTube([ 'input' => $url, ], '', [], new MockHttpClient()); - $embed = $provider->renderEmbedCode(100, 100, TRUE); + $embed = $provider->renderEmbedCode(100, 100, TRUE, 'youtube-video'); $language = isset($embed['#query']['cc_lang_pref']) ? $embed['#query']['cc_lang_pref'] : FALSE; $this->assertEquals($expected, $language); } @@ -240,7 +240,7 @@ class ProviderUrlParseTest extends UnitTestCase { $provider = new YouTube([ 'input' => $url, ], '', [], new MockHttpClient()); - $embed = $provider->renderEmbedCode(100, 100, TRUE); + $embed = $provider->renderEmbedCode(100, 100, TRUE, 'youtube-video'); $this->assertEquals($expected, $embed['#query']['start']); } @@ -302,7 +302,7 @@ class ProviderUrlParseTest extends UnitTestCase { $this->assertEquals($exception_expected, $exception_triggered); if (!$exception_triggered) { - $embed = $provider->renderEmbedCode(100, 100, TRUE); + $embed = $provider->renderEmbedCode(100, 100, TRUE, 'youtube-video'); $this->assertEquals($expected, isset($embed['#fragment']) ? $embed['#fragment'] : FALSE); } } -- 2.20.1