443df18098824ef50e99b07cf5a2a715b980b88a
[yaffs-website] / web / core / modules / media / src / Plugin / Field / FieldFormatter / OEmbedFormatter.php
1 <?php
2
3 namespace Drupal\media\Plugin\Field\FieldFormatter;
4
5 use Drupal\Core\Cache\CacheableMetadata;
6 use Drupal\Core\Config\ConfigFactoryInterface;
7 use Drupal\Core\Field\FieldDefinitionInterface;
8 use Drupal\Core\Field\FieldItemListInterface;
9 use Drupal\Core\Field\FormatterBase;
10 use Drupal\Core\Form\FormStateInterface;
11 use Drupal\Core\Logger\LoggerChannelFactoryInterface;
12 use Drupal\Core\Messenger\MessengerInterface;
13 use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
14 use Drupal\Core\Url;
15 use Drupal\media\Entity\MediaType;
16 use Drupal\media\IFrameUrlHelper;
17 use Drupal\media\OEmbed\Resource;
18 use Drupal\media\OEmbed\ResourceException;
19 use Drupal\media\OEmbed\ResourceFetcherInterface;
20 use Drupal\media\OEmbed\UrlResolverInterface;
21 use Drupal\media\Plugin\media\Source\OEmbedInterface;
22 use Symfony\Component\DependencyInjection\ContainerInterface;
23
24 /**
25  * Plugin implementation of the 'oembed' formatter.
26  *
27  * @internal
28  *   This is an internal part of the oEmbed system and should only be used by
29  *   oEmbed-related code in Drupal core.
30  *
31  * @FieldFormatter(
32  *   id = "oembed",
33  *   label = @Translation("oEmbed content"),
34  *   field_types = {
35  *     "link",
36  *     "string",
37  *     "string_long",
38  *   },
39  * )
40  */
41 class OEmbedFormatter extends FormatterBase implements ContainerFactoryPluginInterface {
42
43   /**
44    * The messenger service.
45    *
46    * @var \Drupal\Core\Messenger\MessengerInterface
47    */
48   protected $messenger;
49
50   /**
51    * The oEmbed resource fetcher.
52    *
53    * @var \Drupal\media\OEmbed\ResourceFetcherInterface
54    */
55   protected $resourceFetcher;
56
57   /**
58    * The oEmbed URL resolver service.
59    *
60    * @var \Drupal\media\OEmbed\UrlResolverInterface
61    */
62   protected $urlResolver;
63
64   /**
65    * The logger service.
66    *
67    * @var \Psr\Log\LoggerInterface
68    */
69   protected $logger;
70
71   /**
72    * The media settings config.
73    *
74    * @var \Drupal\Core\Config\ImmutableConfig
75    */
76   protected $config;
77
78   /**
79    * The iFrame URL helper service.
80    *
81    * @var \Drupal\media\IFrameUrlHelper
82    */
83   protected $iFrameUrlHelper;
84
85   /**
86    * Constructs an OEmbedFormatter instance.
87    *
88    * @param string $plugin_id
89    *   The plugin ID for the formatter.
90    * @param mixed $plugin_definition
91    *   The plugin implementation definition.
92    * @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
93    *   The definition of the field to which the formatter is associated.
94    * @param array $settings
95    *   The formatter settings.
96    * @param string $label
97    *   The formatter label display setting.
98    * @param string $view_mode
99    *   The view mode.
100    * @param array $third_party_settings
101    *   Any third party settings.
102    * @param \Drupal\Core\Messenger\MessengerInterface $messenger
103    *   The messenger service.
104    * @param \Drupal\media\OEmbed\ResourceFetcherInterface $resource_fetcher
105    *   The oEmbed resource fetcher service.
106    * @param \Drupal\media\OEmbed\UrlResolverInterface $url_resolver
107    *   The oEmbed URL resolver service.
108    * @param \Drupal\Core\Logger\LoggerChannelFactoryInterface $logger_factory
109    *   The logger factory service.
110    * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
111    *   The config factory service.
112    * @param \Drupal\media\IFrameUrlHelper $iframe_url_helper
113    *   The iFrame URL helper service.
114    */
115   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) {
116     parent::__construct($plugin_id, $plugin_definition, $field_definition, $settings, $label, $view_mode, $third_party_settings);
117     $this->messenger = $messenger;
118     $this->resourceFetcher = $resource_fetcher;
119     $this->urlResolver = $url_resolver;
120     $this->logger = $logger_factory->get('media');
121     $this->config = $config_factory->get('media.settings');
122     $this->iFrameUrlHelper = $iframe_url_helper;
123   }
124
125   /**
126    * {@inheritdoc}
127    */
128   public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
129     return new static(
130       $plugin_id,
131       $plugin_definition,
132       $configuration['field_definition'],
133       $configuration['settings'],
134       $configuration['label'],
135       $configuration['view_mode'],
136       $configuration['third_party_settings'],
137       $container->get('messenger'),
138       $container->get('media.oembed.resource_fetcher'),
139       $container->get('media.oembed.url_resolver'),
140       $container->get('logger.factory'),
141       $container->get('config.factory'),
142       $container->get('media.oembed.iframe_url_helper')
143     );
144   }
145
146   /**
147    * {@inheritdoc}
148    */
149   public static function defaultSettings() {
150     return [
151       'max_width' => 0,
152       'max_height' => 0,
153     ] + parent::defaultSettings();
154   }
155
156   /**
157    * {@inheritdoc}
158    */
159   public function viewElements(FieldItemListInterface $items, $langcode) {
160     $element = [];
161     $max_width = $this->getSetting('max_width');
162     $max_height = $this->getSetting('max_height');
163
164     foreach ($items as $delta => $item) {
165       $main_property = $item->getFieldDefinition()->getFieldStorageDefinition()->getMainPropertyName();
166       $value = $item->{$main_property};
167
168       if (empty($value)) {
169         continue;
170       }
171
172       try {
173         $resource_url = $this->urlResolver->getResourceUrl($value, $max_width, $max_height);
174         $resource = $this->resourceFetcher->fetchResource($resource_url);
175       }
176       catch (ResourceException $exception) {
177         $this->logger->error("Could not retrieve the remote URL (@url).", ['@url' => $value]);
178         continue;
179       }
180
181       if ($resource->getType() === Resource::TYPE_LINK) {
182         $element[$delta] = [
183           '#title' => $resource->getTitle(),
184           '#type' => 'link',
185           '#url' => Url::fromUri($value),
186         ];
187       }
188       elseif ($resource->getType() === Resource::TYPE_PHOTO) {
189         $element[$delta] = [
190           '#theme' => 'image',
191           '#uri' => $resource->getUrl()->toString(),
192           '#width' => $max_width ?: $resource->getWidth(),
193           '#height' => $max_height ?: $resource->getHeight(),
194         ];
195       }
196       else {
197         $url = Url::fromRoute('media.oembed_iframe', [], [
198           'query' => [
199             'url' => $value,
200             'max_width' => $max_width,
201             'max_height' => $max_height,
202             'hash' => $this->iFrameUrlHelper->getHash($value, $max_width, $max_height),
203           ],
204         ]);
205
206         $domain = $this->config->get('iframe_domain');
207         if ($domain) {
208           $url->setOption('base_url', $domain);
209         }
210
211         // Render videos and rich content in an iframe for security reasons.
212         // @see: https://oembed.com/#section3
213         $element[$delta] = [
214           '#type' => 'html_tag',
215           '#tag' => 'iframe',
216           '#attributes' => [
217             'src' => $url->toString(),
218             'frameborder' => 0,
219             'scrolling' => FALSE,
220             'allowtransparency' => TRUE,
221             'width' => $max_width ?: $resource->getWidth(),
222             'height' => $max_height ?: $resource->getHeight(),
223           ],
224         ];
225
226         CacheableMetadata::createFromObject($resource)
227           ->addCacheTags($this->config->getCacheTags())
228           ->applyTo($element[$delta]);
229       }
230     }
231     return $element;
232   }
233
234   /**
235    * {@inheritdoc}
236    */
237   public function settingsForm(array $form, FormStateInterface $form_state) {
238     return parent::settingsForm($form, $form_state) + [
239       'max_width' => [
240         '#type' => 'number',
241         '#title' => $this->t('Maximum width'),
242         '#default_value' => $this->getSetting('max_width'),
243         '#size' => 5,
244         '#maxlength' => 5,
245         '#field_suffix' => $this->t('pixels'),
246         '#min' => 0,
247       ],
248       'max_height' => [
249         '#type' => 'number',
250         '#title' => $this->t('Maximum height'),
251         '#default_value' => $this->getSetting('max_height'),
252         '#size' => 5,
253         '#maxlength' => 5,
254         '#field_suffix' => $this->t('pixels'),
255         '#min' => 0,
256       ],
257     ];
258   }
259
260   /**
261    * {@inheritdoc}
262    */
263   public function settingsSummary() {
264     $summary = parent::settingsSummary();
265     if ($this->getSetting('max_width') && $this->getSetting('max_height')) {
266       $summary[] = $this->t('Maximum size: %max_width x %max_height pixels', [
267         '%max_width' => $this->getSetting('max_width'),
268         '%max_height' => $this->getSetting('max_height'),
269       ]);
270     }
271     elseif ($this->getSetting('max_width')) {
272       $summary[] = $this->t('Maximum width: %max_width pixels', [
273         '%max_width' => $this->getSetting('max_width'),
274       ]);
275     }
276     elseif ($this->getSetting('max_height')) {
277       $summary[] = $this->t('Maximum height: %max_height pixels', [
278         '%max_height' => $this->getSetting('max_height'),
279       ]);
280     }
281     return $summary;
282   }
283
284   /**
285    * {@inheritdoc}
286    */
287   public static function isApplicable(FieldDefinitionInterface $field_definition) {
288     if ($field_definition->getTargetEntityTypeId() !== 'media') {
289       return FALSE;
290     }
291
292     if (parent::isApplicable($field_definition)) {
293       $media_type = $field_definition->getTargetBundle();
294
295       if ($media_type) {
296         $media_type = MediaType::load($media_type);
297         return $media_type && $media_type->getSource() instanceof OEmbedInterface;
298       }
299     }
300     return FALSE;
301   }
302
303 }