Newer
Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
<?php
namespace Drupal\media\Plugin\Validation\Constraint;
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
use Drupal\Core\Logger\LoggerChannelFactoryInterface;
use Drupal\media\OEmbed\ProviderException;
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;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
/**
* Validates oEmbed resource URLs.
*
* @internal
* This is an internal part of the oEmbed system and should only be used by
* oEmbed-related code in Drupal core.
*/
class OEmbedResourceConstraintValidator extends ConstraintValidator implements ContainerInjectionInterface {
/**
* The oEmbed URL resolver service.
*
* @var \Drupal\media\OEmbed\UrlResolverInterface
*/
protected $urlResolver;
/**
* The resource fetcher service.
*
* @var \Drupal\media\OEmbed\ResourceFetcherInterface
*/
protected $resourceFetcher;
/**
* The logger service.
*
* @var \Drupal\Core\Logger\LoggerChannelInterface
*/
protected $logger;
/**
* Constructs a new OEmbedResourceConstraintValidator.
*
* @param \Drupal\media\OEmbed\UrlResolverInterface $url_resolver
* The oEmbed URL resolver service.
* @param \Drupal\media\OEmbed\ResourceFetcherInterface $resource_fetcher
* The resource fetcher service.
* @param \Drupal\Core\Logger\LoggerChannelFactoryInterface $logger_factory
* The logger service.
*/
public function __construct(UrlResolverInterface $url_resolver, ResourceFetcherInterface $resource_fetcher, LoggerChannelFactoryInterface $logger_factory) {
$this->urlResolver = $url_resolver;
$this->resourceFetcher = $resource_fetcher;
$this->logger = $logger_factory->get('media');
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('media.oembed.url_resolver'),
$container->get('media.oembed.resource_fetcher'),
$container->get('logger.factory')
);
}
/**
* {@inheritdoc}
*/
public function validate($value, Constraint $constraint) {
/** @var \Drupal\media\MediaInterface $media */
$media = $value->getEntity();
/** @var \Drupal\media\Plugin\media\Source\OEmbedInterface $source */
$source = $media->getSource();
if (!($source instanceof OEmbedInterface)) {
throw new \LogicException('Media source must implement ' . OEmbedInterface::class);
}
$url = $source->getSourceFieldValue($media);
// Ensure that the URL matches a provider.
try {
$provider = $this->urlResolver->getProviderByUrl($url);
}
catch (ResourceException $e) {
$this->handleException($e, $constraint->unknownProviderMessage);
return;
}
catch (ProviderException $e) {
$this->handleException($e, $constraint->providerErrorMessage);
return;
}
// Ensure that the provider is allowed.
if (!in_array($provider->getName(), $source->getProviders(), TRUE)) {
$this->context->addViolation($constraint->disallowedProviderMessage, [
'@name' => $provider->getName(),
]);
return;
}
// Verify that resource fetching works, because some URLs might match
// the schemes but don't support oEmbed.
try {
$endpoints = $provider->getEndpoints();
$resource_url = reset($endpoints)->buildResourceUrl($url);
$this->resourceFetcher->fetchResource($resource_url);
}
catch (ResourceException $e) {
$this->handleException($e, $constraint->invalidResourceMessage);
}
}
/**
* Handles exceptions that occur during validation.
*
* @param \Exception $e
* The caught exception.
* @param string $error_message
* (optional) The error message to set as a constraint violation.
*/
protected function handleException(\Exception $e, $error_message = NULL) {
if ($error_message) {
$this->context->addViolation($error_message);
}
// The oEmbed system makes heavy use of exception wrapping, so log the
// entire exception chain to help with troubleshooting.
do {
// @todo If $e is a ProviderException or ResourceException, log additional
// debugging information contained in those exceptions in
// https://www.drupal.org/project/drupal/issues/2972846.
$this->logger->error($e->getMessage());
$e = $e->getPrevious();
} while ($e);
}
}