--- /dev/null
+<?php
+
+namespace Drupal\media\Plugin\Field\FieldFormatter;
+
+use Drupal\Core\Cache\CacheableMetadata;
+use Drupal\Core\Config\ConfigFactoryInterface;
+use Drupal\Core\Field\FieldDefinitionInterface;
+use Drupal\Core\Field\FieldItemListInterface;
+use Drupal\Core\Field\FormatterBase;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Logger\LoggerChannelFactoryInterface;
+use Drupal\Core\Messenger\MessengerInterface;
+use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
+use Drupal\Core\Url;
+use Drupal\media\Entity\MediaType;
+use Drupal\media\IFrameUrlHelper;
+use Drupal\media\OEmbed\Resource;
+use Drupal\media\OEmbed\ResourceException;
+use Drupal\media\OEmbed\ResourceFetcherInterface;
+use Drupal\media\OEmbed\UrlResolverInterface;
+use Drupal\media\Plugin\media\Source\OEmbedInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Plugin implementation of the 'oembed' formatter.
+ *
+ * @internal
+ * This is an internal part of the oEmbed system and should only be used by
+ * oEmbed-related code in Drupal core.
+ *
+ * @FieldFormatter(
+ * id = "oembed",
+ * label = @Translation("oEmbed content"),
+ * field_types = {
+ * "link",
+ * "string",
+ * "string_long",
+ * },
+ * )
+ */
+class OEmbedFormatter extends FormatterBase implements ContainerFactoryPluginInterface {
+
+ /**
+ * The messenger service.
+ *
+ * @var \Drupal\Core\Messenger\MessengerInterface
+ */
+ protected $messenger;
+
+ /**
+ * The oEmbed resource fetcher.
+ *
+ * @var \Drupal\media\OEmbed\ResourceFetcherInterface
+ */
+ protected $resourceFetcher;
+
+ /**
+ * The oEmbed URL resolver service.
+ *
+ * @var \Drupal\media\OEmbed\UrlResolverInterface
+ */
+ protected $urlResolver;
+
+ /**
+ * The logger service.
+ *
+ * @var \Psr\Log\LoggerInterface
+ */
+ protected $logger;
+
+ /**
+ * The media settings config.
+ *
+ * @var \Drupal\Core\Config\ImmutableConfig
+ */
+ protected $config;
+
+ /**
+ * The iFrame URL helper service.
+ *
+ * @var \Drupal\media\IFrameUrlHelper
+ */
+ protected $iFrameUrlHelper;
+
+ /**
+ * Constructs an OEmbedFormatter instance.
+ *
+ * @param string $plugin_id
+ * The plugin ID for the formatter.
+ * @param mixed $plugin_definition
+ * The plugin implementation definition.
+ * @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
+ * The definition of the field to which the formatter is associated.
+ * @param array $settings
+ * The formatter settings.
+ * @param string $label
+ * The formatter label display setting.
+ * @param string $view_mode
+ * The view mode.
+ * @param array $third_party_settings
+ * Any third party settings.
+ * @param \Drupal\Core\Messenger\MessengerInterface $messenger
+ * The messenger service.
+ * @param \Drupal\media\OEmbed\ResourceFetcherInterface $resource_fetcher
+ * The oEmbed resource fetcher service.
+ * @param \Drupal\media\OEmbed\UrlResolverInterface $url_resolver
+ * The oEmbed URL resolver service.
+ * @param \Drupal\Core\Logger\LoggerChannelFactoryInterface $logger_factory
+ * The logger factory service.
+ * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
+ * The config factory service.
+ * @param \Drupal\media\IFrameUrlHelper $iframe_url_helper
+ * The iFrame URL helper service.
+ */
+ public function __construct($plugin_id, $plugin_definition, FieldDefinitionInterface $field_definition, array $settings, $label, $view_mode, array $third_party_settings, MessengerInterface $messenger, ResourceFetcherInterface $resource_fetcher, UrlResolverInterface $url_resolver, LoggerChannelFactoryInterface $logger_factory, ConfigFactoryInterface $config_factory, IFrameUrlHelper $iframe_url_helper) {
+ parent::__construct($plugin_id, $plugin_definition, $field_definition, $settings, $label, $view_mode, $third_party_settings);
+ $this->messenger = $messenger;
+ $this->resourceFetcher = $resource_fetcher;
+ $this->urlResolver = $url_resolver;
+ $this->logger = $logger_factory->get('media');
+ $this->config = $config_factory->get('media.settings');
+ $this->iFrameUrlHelper = $iframe_url_helper;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
+ return new static(
+ $plugin_id,
+ $plugin_definition,
+ $configuration['field_definition'],
+ $configuration['settings'],
+ $configuration['label'],
+ $configuration['view_mode'],
+ $configuration['third_party_settings'],
+ $container->get('messenger'),
+ $container->get('media.oembed.resource_fetcher'),
+ $container->get('media.oembed.url_resolver'),
+ $container->get('logger.factory'),
+ $container->get('config.factory'),
+ $container->get('media.oembed.iframe_url_helper')
+ );
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public static function defaultSettings() {
+ return [
+ 'max_width' => 0,
+ 'max_height' => 0,
+ ] + parent::defaultSettings();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function viewElements(FieldItemListInterface $items, $langcode) {
+ $element = [];
+ $max_width = $this->getSetting('max_width');
+ $max_height = $this->getSetting('max_height');
+
+ foreach ($items as $delta => $item) {
+ $main_property = $item->getFieldDefinition()->getFieldStorageDefinition()->getMainPropertyName();
+ $value = $item->{$main_property};
+
+ if (empty($value)) {
+ continue;
+ }
+
+ try {
+ $resource_url = $this->urlResolver->getResourceUrl($value, $max_width, $max_height);
+ $resource = $this->resourceFetcher->fetchResource($resource_url);
+ }
+ catch (ResourceException $exception) {
+ $this->logger->error("Could not retrieve the remote URL (@url).", ['@url' => $value]);
+ continue;
+ }
+
+ if ($resource->getType() === Resource::TYPE_LINK) {
+ $element[$delta] = [
+ '#title' => $resource->getTitle(),
+ '#type' => 'link',
+ '#url' => Url::fromUri($value),
+ ];
+ }
+ elseif ($resource->getType() === Resource::TYPE_PHOTO) {
+ $element[$delta] = [
+ '#theme' => 'image',
+ '#uri' => $resource->getUrl()->toString(),
+ '#width' => $max_width ?: $resource->getWidth(),
+ '#height' => $max_height ?: $resource->getHeight(),
+ ];
+ }
+ else {
+ $url = Url::fromRoute('media.oembed_iframe', [], [
+ 'query' => [
+ 'url' => $value,
+ 'max_width' => $max_width,
+ 'max_height' => $max_height,
+ 'hash' => $this->iFrameUrlHelper->getHash($value, $max_width, $max_height),
+ ],
+ ]);
+
+ $domain = $this->config->get('iframe_domain');
+ if ($domain) {
+ $url->setOption('base_url', $domain);
+ }
+
+ // Render videos and rich content in an iframe for security reasons.
+ // @see: https://oembed.com/#section3
+ $element[$delta] = [
+ '#type' => 'html_tag',
+ '#tag' => 'iframe',
+ '#attributes' => [
+ 'src' => $url->toString(),
+ 'frameborder' => 0,
+ 'scrolling' => FALSE,
+ 'allowtransparency' => TRUE,
+ 'width' => $max_width ?: $resource->getWidth(),
+ 'height' => $max_height ?: $resource->getHeight(),
+ ],
+ ];
+
+ CacheableMetadata::createFromObject($resource)
+ ->addCacheTags($this->config->getCacheTags())
+ ->applyTo($element[$delta]);
+ }
+ }
+ return $element;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function settingsForm(array $form, FormStateInterface $form_state) {
+ return parent::settingsForm($form, $form_state) + [
+ 'max_width' => [
+ '#type' => 'number',
+ '#title' => $this->t('Maximum width'),
+ '#default_value' => $this->getSetting('max_width'),
+ '#size' => 5,
+ '#maxlength' => 5,
+ '#field_suffix' => $this->t('pixels'),
+ '#min' => 0,
+ ],
+ 'max_height' => [
+ '#type' => 'number',
+ '#title' => $this->t('Maximum height'),
+ '#default_value' => $this->getSetting('max_height'),
+ '#size' => 5,
+ '#maxlength' => 5,
+ '#field_suffix' => $this->t('pixels'),
+ '#min' => 0,
+ ],
+ ];
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function settingsSummary() {
+ $summary = parent::settingsSummary();
+ if ($this->getSetting('max_width') && $this->getSetting('max_height')) {
+ $summary[] = $this->t('Maximum size: %max_width x %max_height pixels', [
+ '%max_width' => $this->getSetting('max_width'),
+ '%max_height' => $this->getSetting('max_height'),
+ ]);
+ }
+ elseif ($this->getSetting('max_width')) {
+ $summary[] = $this->t('Maximum width: %max_width pixels', [
+ '%max_width' => $this->getSetting('max_width'),
+ ]);
+ }
+ elseif ($this->getSetting('max_height')) {
+ $summary[] = $this->t('Maximum height: %max_height pixels', [
+ '%max_height' => $this->getSetting('max_height'),
+ ]);
+ }
+ return $summary;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public static function isApplicable(FieldDefinitionInterface $field_definition) {
+ if ($field_definition->getTargetEntityTypeId() !== 'media') {
+ return FALSE;
+ }
+
+ if (parent::isApplicable($field_definition)) {
+ $media_type = $field_definition->getTargetBundle();
+
+ if ($media_type) {
+ $media_type = MediaType::load($media_type);
+ return $media_type && $media_type->getSource() instanceof OEmbedInterface;
+ }
+ }
+ return FALSE;
+ }
+
+}