Initial Drupal 11 with DDEV setup
This commit is contained in:
182
web/core/modules/media/src/OEmbed/Endpoint.php
Normal file
182
web/core/modules/media/src/OEmbed/Endpoint.php
Normal file
@ -0,0 +1,182 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\media\OEmbed;
|
||||
|
||||
use Drupal\Component\Utility\UrlHelper;
|
||||
|
||||
/**
|
||||
* Value object for oEmbed provider endpoints.
|
||||
*
|
||||
* @internal
|
||||
* This class is an internal part of the oEmbed system and should only be
|
||||
* instantiated by instances of Drupal\media\OEmbed\Provider.
|
||||
*/
|
||||
class Endpoint {
|
||||
|
||||
/**
|
||||
* The endpoint's URL.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $url;
|
||||
|
||||
/**
|
||||
* The provider this endpoint belongs to.
|
||||
*
|
||||
* @var \Drupal\media\OEmbed\Provider
|
||||
*/
|
||||
protected $provider;
|
||||
|
||||
/**
|
||||
* List of URL schemes supported by the provider.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
protected $schemes;
|
||||
|
||||
/**
|
||||
* List of supported formats. Only 'json' and 'xml' are allowed.
|
||||
*
|
||||
* @var string[]
|
||||
*
|
||||
* @see https://oembed.com/#section2
|
||||
*/
|
||||
protected $formats;
|
||||
|
||||
/**
|
||||
* Whether the provider supports oEmbed discovery.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $supportsDiscovery;
|
||||
|
||||
/**
|
||||
* Endpoint constructor.
|
||||
*
|
||||
* @param string $url
|
||||
* The endpoint URL. May contain a "{format}" placeholder.
|
||||
* @param \Drupal\media\OEmbed\Provider $provider
|
||||
* The provider this endpoint belongs to.
|
||||
* @param string[] $schemes
|
||||
* List of URL schemes supported by the provider.
|
||||
* @param string[] $formats
|
||||
* List of supported formats. Can be "json", "xml" or both.
|
||||
* @param bool $supports_discovery
|
||||
* Whether the provider supports oEmbed discovery.
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
* If the endpoint URL is empty.
|
||||
*/
|
||||
public function __construct($url, Provider $provider, array $schemes = [], array $formats = [], $supports_discovery = FALSE) {
|
||||
$this->provider = $provider;
|
||||
$this->schemes = $schemes;
|
||||
|
||||
$this->formats = $formats = array_map('mb_strtolower', $formats);
|
||||
// Assert that only the supported formats are present.
|
||||
assert(array_diff($formats, ['json', 'xml']) == []);
|
||||
|
||||
// Use the first provided format to build the endpoint URL. If no formats
|
||||
// are provided, default to JSON.
|
||||
$this->url = str_replace('{format}', reset($this->formats) ?: 'json', $url);
|
||||
|
||||
if (!UrlHelper::isValid($this->url, TRUE) || !UrlHelper::isExternal($this->url)) {
|
||||
throw new \InvalidArgumentException('oEmbed endpoint must have a valid external URL');
|
||||
}
|
||||
|
||||
$this->supportsDiscovery = (bool) $supports_discovery;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the endpoint URL.
|
||||
*
|
||||
* The URL will be built with the first available format. If the endpoint
|
||||
* does not provide any formats, JSON will be used.
|
||||
*
|
||||
* @return string
|
||||
* The endpoint URL.
|
||||
*/
|
||||
public function getUrl() {
|
||||
return $this->url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the provider this endpoint belongs to.
|
||||
*
|
||||
* @return \Drupal\media\OEmbed\Provider
|
||||
* The provider object.
|
||||
*/
|
||||
public function getProvider() {
|
||||
return $this->provider;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns list of URL schemes supported by the provider.
|
||||
*
|
||||
* @return string[]
|
||||
* List of schemes.
|
||||
*/
|
||||
public function getSchemes() {
|
||||
return $this->schemes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns list of supported formats.
|
||||
*
|
||||
* @return string[]
|
||||
* List of formats.
|
||||
*/
|
||||
public function getFormats() {
|
||||
return $this->formats;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the provider supports oEmbed discovery.
|
||||
*
|
||||
* @return bool
|
||||
* Returns TRUE if the provides discovery, otherwise FALSE.
|
||||
*/
|
||||
public function supportsDiscovery() {
|
||||
return $this->supportsDiscovery;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to match a URL against the endpoint schemes.
|
||||
*
|
||||
* @param string $url
|
||||
* Media item URL.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if the URL matches against the endpoint schemes, otherwise FALSE.
|
||||
*/
|
||||
public function matchUrl($url) {
|
||||
foreach ($this->getSchemes() as $scheme) {
|
||||
// Convert scheme into a valid regular expression.
|
||||
$regexp = str_replace(['.', '*', '?'], ['\.', '.*', '\?'], $scheme);
|
||||
if (preg_match("|^$regexp$|", $url)) {
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds and returns the endpoint URL.
|
||||
*
|
||||
* In most situations this function should not be used. Your are probably
|
||||
* looking for \Drupal\media\OEmbed\UrlResolver::getResourceUrl(), because it
|
||||
* is alterable and also cached.
|
||||
*
|
||||
* @param string $url
|
||||
* The canonical media URL.
|
||||
*
|
||||
* @return string
|
||||
* URL of the oEmbed endpoint.
|
||||
*
|
||||
* @see \Drupal\media\OEmbed\UrlResolver::getResourceUrl()
|
||||
*/
|
||||
public function buildResourceUrl($url) {
|
||||
$query = ['url' => $url];
|
||||
return $this->getUrl() . '?' . UrlHelper::buildQuery($query);
|
||||
}
|
||||
|
||||
}
|
||||
100
web/core/modules/media/src/OEmbed/Provider.php
Normal file
100
web/core/modules/media/src/OEmbed/Provider.php
Normal file
@ -0,0 +1,100 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\media\OEmbed;
|
||||
|
||||
use Drupal\Component\Utility\UrlHelper;
|
||||
|
||||
/**
|
||||
* Value object for oEmbed providers.
|
||||
*/
|
||||
class Provider {
|
||||
|
||||
/**
|
||||
* The provider name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name;
|
||||
|
||||
/**
|
||||
* The provider URL.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $url;
|
||||
|
||||
/**
|
||||
* The provider endpoints.
|
||||
*
|
||||
* @var \Drupal\media\OEmbed\Endpoint[]
|
||||
*/
|
||||
protected $endpoints = [];
|
||||
|
||||
/**
|
||||
* Provider constructor.
|
||||
*
|
||||
* @param string $name
|
||||
* The provider name.
|
||||
* @param string $url
|
||||
* The provider URL.
|
||||
* @param array[] $endpoints
|
||||
* List of endpoints this provider exposes.
|
||||
*
|
||||
* @throws \Drupal\media\OEmbed\ProviderException
|
||||
*/
|
||||
public function __construct($name, $url, array $endpoints) {
|
||||
$this->name = $name;
|
||||
|
||||
if (!UrlHelper::isValid($url, TRUE) || !UrlHelper::isExternal($url)) {
|
||||
throw new ProviderException('Provider @name does not define a valid external URL.', $this);
|
||||
}
|
||||
$this->url = $url;
|
||||
|
||||
try {
|
||||
foreach ($endpoints as $endpoint) {
|
||||
$endpoint += ['formats' => [], 'schemes' => [], 'discovery' => FALSE];
|
||||
$this->endpoints[] = new Endpoint($endpoint['url'], $this, $endpoint['schemes'], $endpoint['formats'], $endpoint['discovery']);
|
||||
}
|
||||
}
|
||||
catch (\InvalidArgumentException) {
|
||||
// Just skip all the invalid endpoints.
|
||||
// @todo Log the exception message to help with debugging in
|
||||
// https://www.drupal.org/project/drupal/issues/2972846.
|
||||
}
|
||||
|
||||
if (empty($this->endpoints)) {
|
||||
throw new ProviderException('Provider @name does not define any valid endpoints.', $this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the provider name.
|
||||
*
|
||||
* @return string
|
||||
* Name of the provider.
|
||||
*/
|
||||
public function getName() {
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the provider URL.
|
||||
*
|
||||
* @return string
|
||||
* URL of the provider.
|
||||
*/
|
||||
public function getUrl() {
|
||||
return $this->url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the provider endpoints.
|
||||
*
|
||||
* @return \Drupal\media\OEmbed\Endpoint[]
|
||||
* List of endpoints this provider exposes.
|
||||
*/
|
||||
public function getEndpoints() {
|
||||
return $this->endpoints;
|
||||
}
|
||||
|
||||
}
|
||||
40
web/core/modules/media/src/OEmbed/ProviderException.php
Normal file
40
web/core/modules/media/src/OEmbed/ProviderException.php
Normal file
@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\media\OEmbed;
|
||||
|
||||
/**
|
||||
* Exception thrown if an oEmbed provider causes an error.
|
||||
*
|
||||
* @internal
|
||||
* This is an internal part of the oEmbed system and should only be used by
|
||||
* oEmbed-related code in Drupal core.
|
||||
*/
|
||||
class ProviderException extends \Exception {
|
||||
|
||||
/**
|
||||
* Information about the oEmbed provider which caused the exception.
|
||||
*
|
||||
* @var \Drupal\media\OEmbed\Provider
|
||||
*
|
||||
* @see \Drupal\media\OEmbed\ProviderRepositoryInterface::get()
|
||||
*/
|
||||
protected $provider;
|
||||
|
||||
/**
|
||||
* ProviderException constructor.
|
||||
*
|
||||
* @param string $message
|
||||
* The exception message. '@name' will be replaced with the provider name
|
||||
* if available, or '<unknown>' if not.
|
||||
* @param \Drupal\media\OEmbed\Provider $provider
|
||||
* (optional) The provider information.
|
||||
* @param \Throwable $previous
|
||||
* (optional) The previous exception, if any.
|
||||
*/
|
||||
public function __construct($message, ?Provider $provider = NULL, ?\Throwable $previous = NULL) {
|
||||
$this->provider = $provider;
|
||||
$message = str_replace('@name', $provider ? $provider->getName() : '<unknown>', $message);
|
||||
parent::__construct($message, 0, $previous);
|
||||
}
|
||||
|
||||
}
|
||||
161
web/core/modules/media/src/OEmbed/ProviderRepository.php
Normal file
161
web/core/modules/media/src/OEmbed/ProviderRepository.php
Normal file
@ -0,0 +1,161 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\media\OEmbed;
|
||||
|
||||
use Drupal\Component\Datetime\TimeInterface;
|
||||
use Drupal\Component\Serialization\Json;
|
||||
use Drupal\Core\Config\ConfigFactoryInterface;
|
||||
use Drupal\Core\KeyValueStore\KeyValueFactoryInterface;
|
||||
use Drupal\Core\Logger\LoggerChannelFactoryInterface;
|
||||
use GuzzleHttp\ClientInterface;
|
||||
use Psr\Http\Client\ClientExceptionInterface;
|
||||
|
||||
/**
|
||||
* Retrieves and caches information about oEmbed providers.
|
||||
*/
|
||||
class ProviderRepository implements ProviderRepositoryInterface {
|
||||
|
||||
/**
|
||||
* How long the provider data should be cached, in seconds.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $maxAge;
|
||||
|
||||
/**
|
||||
* The HTTP client.
|
||||
*
|
||||
* @var \GuzzleHttp\Client
|
||||
*/
|
||||
protected $httpClient;
|
||||
|
||||
/**
|
||||
* URL of a JSON document which contains a database of oEmbed providers.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $providersUrl;
|
||||
|
||||
/**
|
||||
* The time service.
|
||||
*
|
||||
* @var \Drupal\Component\Datetime\TimeInterface
|
||||
*/
|
||||
protected $time;
|
||||
|
||||
/**
|
||||
* The key-value store.
|
||||
*
|
||||
* @var \Drupal\Core\KeyValueStore\KeyValueStoreInterface
|
||||
*/
|
||||
protected $keyValue;
|
||||
|
||||
/**
|
||||
* The logger channel.
|
||||
*
|
||||
* @var \Drupal\Core\Logger\LoggerChannelInterface
|
||||
*/
|
||||
protected $logger;
|
||||
|
||||
/**
|
||||
* Constructs a ProviderRepository instance.
|
||||
*
|
||||
* @param \GuzzleHttp\ClientInterface $http_client
|
||||
* The HTTP client.
|
||||
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
|
||||
* The config factory service.
|
||||
* @param \Drupal\Component\Datetime\TimeInterface $time
|
||||
* The time service.
|
||||
* @param \Drupal\Core\KeyValueStore\KeyValueFactoryInterface $key_value_factory
|
||||
* The key-value store factory.
|
||||
* @param \Drupal\Core\Logger\LoggerChannelFactoryInterface $logger_factory
|
||||
* The logger channel factory.
|
||||
* @param int $max_age
|
||||
* (optional) How long the cache data should be kept. Defaults to a week.
|
||||
*/
|
||||
public function __construct(ClientInterface $http_client, ConfigFactoryInterface $config_factory, TimeInterface $time, KeyValueFactoryInterface $key_value_factory, LoggerChannelFactoryInterface $logger_factory, int $max_age = 604800) {
|
||||
$this->httpClient = $http_client;
|
||||
$this->providersUrl = $config_factory->get('media.settings')->get('oembed_providers_url');
|
||||
$this->time = $time;
|
||||
$this->maxAge = $max_age;
|
||||
$this->keyValue = $key_value_factory->get('media');
|
||||
$this->logger = $logger_factory->get('media');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getAll() {
|
||||
$current_time = $this->time->getCurrentTime();
|
||||
$stored = $this->keyValue->get('oembed_providers');
|
||||
// If we have stored data that hasn't yet expired, return that. We need to
|
||||
// store the data in a key-value store because, if the remote provider
|
||||
// database is unavailable, we'd rather return stale data than throw an
|
||||
// exception. This means we cannot use a normal cache backend or expirable
|
||||
// key-value store, since those could delete the stale data at any time.
|
||||
if ($stored && $stored['expires'] > $current_time) {
|
||||
return $stored['data'];
|
||||
}
|
||||
|
||||
try {
|
||||
$response = $this->httpClient->request('GET', $this->providersUrl);
|
||||
}
|
||||
catch (ClientExceptionInterface $e) {
|
||||
if (isset($stored['data'])) {
|
||||
// Use the stale data to fall back gracefully, but warn site
|
||||
// administrators that we used stale data.
|
||||
$this->logger->warning('Remote oEmbed providers could not be retrieved due to error: @error. Using previously stored data. This may contain out of date information.', [
|
||||
'@error' => $e->getMessage(),
|
||||
]);
|
||||
return $stored['data'];
|
||||
}
|
||||
// We have no previous data and the request failed.
|
||||
throw new ProviderException("Could not retrieve the oEmbed provider database from $this->providersUrl", NULL, $e);
|
||||
}
|
||||
|
||||
$providers = Json::decode((string) $response->getBody());
|
||||
|
||||
if (!is_array($providers) || empty($providers)) {
|
||||
if (isset($stored['data'])) {
|
||||
// Use the stale data to fall back gracefully, but as above, warn site
|
||||
// administrators that we used stale data.
|
||||
$this->logger->warning('Remote oEmbed providers database returned invalid or empty list. Using previously stored data. This may contain out of date information.');
|
||||
return $stored['data'];
|
||||
}
|
||||
// We have no previous data and the current data is corrupt.
|
||||
throw new ProviderException('Remote oEmbed providers database returned invalid or empty list.');
|
||||
}
|
||||
|
||||
$keyed_providers = [];
|
||||
foreach ($providers as $provider) {
|
||||
try {
|
||||
$name = (string) $provider['provider_name'];
|
||||
$keyed_providers[$name] = new Provider($provider['provider_name'], $provider['provider_url'], $provider['endpoints']);
|
||||
}
|
||||
catch (ProviderException $e) {
|
||||
// Skip invalid providers, but log the exception message to help with
|
||||
// debugging.
|
||||
$this->logger->warning($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
$this->keyValue->set('oembed_providers', [
|
||||
'data' => $keyed_providers,
|
||||
'expires' => $current_time + $this->maxAge,
|
||||
]);
|
||||
return $keyed_providers;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get($provider_name) {
|
||||
$providers = $this->getAll();
|
||||
|
||||
if (!isset($providers[$provider_name])) {
|
||||
throw new \InvalidArgumentException("Unknown provider '$provider_name'");
|
||||
}
|
||||
return $providers[$provider_name];
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\media\OEmbed;
|
||||
|
||||
/**
|
||||
* Defines an interface for a collection of oEmbed provider information.
|
||||
*
|
||||
* The provider repository is responsible for fetching information about all
|
||||
* available oEmbed providers, most likely pulled from the online database at
|
||||
* https://oembed.com/providers.json, and creating \Drupal\media\OEmbed\Provider
|
||||
* value objects for each provider.
|
||||
*/
|
||||
interface ProviderRepositoryInterface {
|
||||
|
||||
/**
|
||||
* Returns information on all available oEmbed providers.
|
||||
*
|
||||
* @return \Drupal\media\OEmbed\Provider[]
|
||||
* Returns an array of provider value objects, keyed by provider name.
|
||||
*
|
||||
* @throws \Drupal\media\OEmbed\ProviderException
|
||||
* If the oEmbed provider information cannot be retrieved.
|
||||
*/
|
||||
public function getAll();
|
||||
|
||||
/**
|
||||
* Returns information for a specific oEmbed provider.
|
||||
*
|
||||
* @param string $provider_name
|
||||
* The name of the provider.
|
||||
*
|
||||
* @return \Drupal\media\OEmbed\Provider
|
||||
* A value object containing information about the provider.
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
* If there is no known oEmbed provider with the specified name.
|
||||
*/
|
||||
public function get($provider_name);
|
||||
|
||||
}
|
||||
529
web/core/modules/media/src/OEmbed/Resource.php
Normal file
529
web/core/modules/media/src/OEmbed/Resource.php
Normal file
@ -0,0 +1,529 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\media\OEmbed;
|
||||
|
||||
use Drupal\Core\Cache\Cache;
|
||||
use Drupal\Core\Cache\CacheableDependencyInterface;
|
||||
use Drupal\Core\Cache\CacheableDependencyTrait;
|
||||
use Drupal\Core\Url;
|
||||
|
||||
/**
|
||||
* Value object representing an oEmbed resource.
|
||||
*
|
||||
* Data received from an oEmbed provider could be insecure. For example,
|
||||
* resources of the 'rich' type provide an HTML representation which is not
|
||||
* sanitized by this object in any way. Any values you retrieve from this object
|
||||
* should be treated as potentially dangerous user input and carefully validated
|
||||
* and sanitized before being displayed or otherwise manipulated by your code.
|
||||
*
|
||||
* Valid resource types are defined in the oEmbed specification and represented
|
||||
* by the TYPE_* constants in this class.
|
||||
*
|
||||
* @see https://oembed.com/#section2
|
||||
*
|
||||
* @internal
|
||||
* This class is an internal part of the oEmbed system and should only be
|
||||
* instantiated by
|
||||
* \Drupal\media\OEmbed\ResourceFetcherInterface::fetchResource().
|
||||
*/
|
||||
class Resource implements CacheableDependencyInterface {
|
||||
|
||||
use CacheableDependencyTrait;
|
||||
|
||||
/**
|
||||
* The resource type for link resources.
|
||||
*/
|
||||
const TYPE_LINK = 'link';
|
||||
|
||||
/**
|
||||
* The resource type for photo resources.
|
||||
*/
|
||||
const TYPE_PHOTO = 'photo';
|
||||
|
||||
/**
|
||||
* The resource type for rich resources.
|
||||
*/
|
||||
const TYPE_RICH = 'rich';
|
||||
|
||||
/**
|
||||
* The resource type for video resources.
|
||||
*/
|
||||
const TYPE_VIDEO = 'video';
|
||||
|
||||
/**
|
||||
* The resource type. Can be one of the static::TYPE_* constants.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $type;
|
||||
|
||||
/**
|
||||
* The resource provider.
|
||||
*
|
||||
* @var \Drupal\media\OEmbed\Provider
|
||||
*/
|
||||
protected $provider;
|
||||
|
||||
/**
|
||||
* A text title, describing the resource.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $title;
|
||||
|
||||
/**
|
||||
* The name of the author/owner of the resource.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $authorName;
|
||||
|
||||
/**
|
||||
* A URL for the author/owner of the resource.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $authorUrl;
|
||||
|
||||
/**
|
||||
* A URL to a thumbnail image representing the resource.
|
||||
*
|
||||
* The thumbnail must respect any maxwidth and maxheight parameters passed
|
||||
* to the oEmbed endpoint. If this parameter is present, thumbnail_width and
|
||||
* thumbnail_height must also be present.
|
||||
*
|
||||
* @var string
|
||||
*
|
||||
* @see \Drupal\media\OEmbed\UrlResolverInterface::getResourceUrl()
|
||||
* @see https://oembed.com/#section2
|
||||
*/
|
||||
protected $thumbnailUrl;
|
||||
|
||||
/**
|
||||
* The width of the thumbnail, in pixels.
|
||||
*
|
||||
* If this parameter is present, thumbnail_url and thumbnail_height must also
|
||||
* be present.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $thumbnailWidth;
|
||||
|
||||
/**
|
||||
* The height of the thumbnail, in pixels.
|
||||
*
|
||||
* If this parameter is present, thumbnail_url and thumbnail_width must also
|
||||
* be present.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $thumbnailHeight;
|
||||
|
||||
/**
|
||||
* The width of the resource, in pixels.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $width;
|
||||
|
||||
/**
|
||||
* The height of the resource, in pixels.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $height;
|
||||
|
||||
/**
|
||||
* The resource URL. Only applies to 'photo' and 'link' resources.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $url;
|
||||
|
||||
/**
|
||||
* The HTML representation of the resource.
|
||||
*
|
||||
* Only applies to 'rich' and 'video' resources.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $html;
|
||||
|
||||
/**
|
||||
* Resource constructor.
|
||||
*
|
||||
* @param \Drupal\media\OEmbed\Provider $provider
|
||||
* (optional) The resource provider.
|
||||
* @param string $title
|
||||
* (optional) A text title, describing the resource.
|
||||
* @param string $author_name
|
||||
* (optional) The name of the author/owner of the resource.
|
||||
* @param string $author_url
|
||||
* (optional) A URL for the author/owner of the resource.
|
||||
* @param int $cache_age
|
||||
* (optional) The suggested cache lifetime for this resource, in seconds.
|
||||
* @param string $thumbnail_url
|
||||
* (optional) A URL to a thumbnail image representing the resource. If this
|
||||
* parameter is present, $thumbnail_width and $thumbnail_height must also be
|
||||
* present.
|
||||
* @param int $thumbnail_width
|
||||
* (optional) The width of the thumbnail, in pixels. If this parameter is
|
||||
* present, $thumbnail_url and $thumbnail_height must also be present.
|
||||
* @param int $thumbnail_height
|
||||
* (optional) The height of the thumbnail, in pixels. If this parameter is
|
||||
* present, $thumbnail_url and $thumbnail_width must also be present.
|
||||
*/
|
||||
protected function __construct(?Provider $provider = NULL, $title = NULL, $author_name = NULL, $author_url = NULL, $cache_age = NULL, $thumbnail_url = NULL, $thumbnail_width = NULL, $thumbnail_height = NULL) {
|
||||
$this->provider = $provider;
|
||||
$this->title = $title;
|
||||
$this->authorName = $author_name;
|
||||
$this->authorUrl = $author_url;
|
||||
|
||||
if (isset($cache_age) && is_numeric($cache_age)) {
|
||||
// If the cache age is too big, it can overflow the 'expire' column of
|
||||
// database cache backends, causing SQL exceptions. To prevent that,
|
||||
// arbitrarily limit the cache age to 5 years. That should be enough.
|
||||
$this->cacheMaxAge = Cache::mergeMaxAges((int) $cache_age, 157680000);
|
||||
}
|
||||
|
||||
if ($thumbnail_url) {
|
||||
$this->thumbnailUrl = $thumbnail_url;
|
||||
$this->setThumbnailDimensions($thumbnail_width, $thumbnail_height);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a link resource.
|
||||
*
|
||||
* @param string $url
|
||||
* (optional) The URL of the resource.
|
||||
* @param \Drupal\media\OEmbed\Provider $provider
|
||||
* (optional) The resource provider.
|
||||
* @param string $title
|
||||
* (optional) A text title, describing the resource.
|
||||
* @param string $author_name
|
||||
* (optional) The name of the author/owner of the resource.
|
||||
* @param string $author_url
|
||||
* (optional) A URL for the author/owner of the resource.
|
||||
* @param int $cache_age
|
||||
* (optional) The suggested cache lifetime for this resource, in seconds.
|
||||
* @param string $thumbnail_url
|
||||
* (optional) A URL to a thumbnail image representing the resource. If this
|
||||
* parameter is present, $thumbnail_width and $thumbnail_height must also be
|
||||
* present.
|
||||
* @param int $thumbnail_width
|
||||
* (optional) The width of the thumbnail, in pixels. If this parameter is
|
||||
* present, $thumbnail_url and $thumbnail_height must also be present.
|
||||
* @param int $thumbnail_height
|
||||
* (optional) The height of the thumbnail, in pixels. If this parameter is
|
||||
* present, $thumbnail_url and $thumbnail_width must also be present.
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public static function link($url = NULL, ?Provider $provider = NULL, $title = NULL, $author_name = NULL, $author_url = NULL, $cache_age = NULL, $thumbnail_url = NULL, $thumbnail_width = NULL, $thumbnail_height = NULL) {
|
||||
$resource = new static($provider, $title, $author_name, $author_url, $cache_age, $thumbnail_url, $thumbnail_width, $thumbnail_height);
|
||||
$resource->type = self::TYPE_LINK;
|
||||
$resource->url = $url;
|
||||
|
||||
return $resource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a photo resource.
|
||||
*
|
||||
* @param string $url
|
||||
* The URL of the photo.
|
||||
* @param int $width
|
||||
* The width of the photo, in pixels.
|
||||
* @param int $height
|
||||
* (optional) The height of the photo, in pixels.
|
||||
* @param \Drupal\media\OEmbed\Provider $provider
|
||||
* (optional) The resource provider.
|
||||
* @param string $title
|
||||
* (optional) A text title, describing the resource.
|
||||
* @param string $author_name
|
||||
* (optional) The name of the author/owner of the resource.
|
||||
* @param string $author_url
|
||||
* (optional) A URL for the author/owner of the resource.
|
||||
* @param int $cache_age
|
||||
* (optional) The suggested cache lifetime for this resource, in seconds.
|
||||
* @param string $thumbnail_url
|
||||
* (optional) A URL to a thumbnail image representing the resource. If this
|
||||
* parameter is present, $thumbnail_width and $thumbnail_height must also be
|
||||
* present.
|
||||
* @param int $thumbnail_width
|
||||
* (optional) The width of the thumbnail, in pixels. If this parameter is
|
||||
* present, $thumbnail_url and $thumbnail_height must also be present.
|
||||
* @param int $thumbnail_height
|
||||
* (optional) The height of the thumbnail, in pixels. If this parameter is
|
||||
* present, $thumbnail_url and $thumbnail_width must also be present.
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public static function photo($url, $width, $height = NULL, ?Provider $provider = NULL, $title = NULL, $author_name = NULL, $author_url = NULL, $cache_age = NULL, $thumbnail_url = NULL, $thumbnail_width = NULL, $thumbnail_height = NULL) {
|
||||
if (empty($url)) {
|
||||
throw new \InvalidArgumentException('Photo resources must provide a URL.');
|
||||
}
|
||||
|
||||
$resource = static::link($url, $provider, $title, $author_name, $author_url, $cache_age, $thumbnail_url, $thumbnail_width, $thumbnail_height);
|
||||
$resource->type = self::TYPE_PHOTO;
|
||||
$resource->setDimensions($width, $height);
|
||||
|
||||
return $resource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a rich resource.
|
||||
*
|
||||
* @param string $html
|
||||
* The HTML representation of the resource.
|
||||
* @param int $width
|
||||
* The width of the resource, in pixels.
|
||||
* @param int $height
|
||||
* (optional) The height of the resource, in pixels.
|
||||
* @param \Drupal\media\OEmbed\Provider $provider
|
||||
* (optional) The resource provider.
|
||||
* @param string $title
|
||||
* (optional) A text title, describing the resource.
|
||||
* @param string $author_name
|
||||
* (optional) The name of the author/owner of the resource.
|
||||
* @param string $author_url
|
||||
* (optional) A URL for the author/owner of the resource.
|
||||
* @param int $cache_age
|
||||
* (optional) The suggested cache lifetime for this resource, in seconds.
|
||||
* @param string $thumbnail_url
|
||||
* (optional) A URL to a thumbnail image representing the resource. If this
|
||||
* parameter is present, $thumbnail_width and $thumbnail_height must also be
|
||||
* present.
|
||||
* @param int $thumbnail_width
|
||||
* (optional) The width of the thumbnail, in pixels. If this parameter is
|
||||
* present, $thumbnail_url and $thumbnail_height must also be present.
|
||||
* @param int $thumbnail_height
|
||||
* (optional) The height of the thumbnail, in pixels. If this parameter is
|
||||
* present, $thumbnail_url and $thumbnail_width must also be present.
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public static function rich($html, $width, $height = NULL, ?Provider $provider = NULL, $title = NULL, $author_name = NULL, $author_url = NULL, $cache_age = NULL, $thumbnail_url = NULL, $thumbnail_width = NULL, $thumbnail_height = NULL) {
|
||||
if (empty($html)) {
|
||||
throw new \InvalidArgumentException('The resource must provide an HTML representation.');
|
||||
}
|
||||
|
||||
$resource = new static($provider, $title, $author_name, $author_url, $cache_age, $thumbnail_url, $thumbnail_width, $thumbnail_height);
|
||||
$resource->type = self::TYPE_RICH;
|
||||
$resource->html = $html;
|
||||
$resource->setDimensions($width, $height);
|
||||
|
||||
return $resource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a video resource.
|
||||
*
|
||||
* @param string $html
|
||||
* The HTML required to display the video.
|
||||
* @param int $width
|
||||
* The width of the video, in pixels.
|
||||
* @param int $height
|
||||
* (optional) The height of the video, in pixels.
|
||||
* @param \Drupal\media\OEmbed\Provider $provider
|
||||
* (optional) The resource provider.
|
||||
* @param string $title
|
||||
* (optional) A text title, describing the resource.
|
||||
* @param string $author_name
|
||||
* (optional) The name of the author/owner of the resource.
|
||||
* @param string $author_url
|
||||
* (optional) A URL for the author/owner of the resource.
|
||||
* @param int $cache_age
|
||||
* (optional) The suggested cache lifetime for this resource, in seconds.
|
||||
* @param string $thumbnail_url
|
||||
* (optional) A URL to a thumbnail image representing the resource. If this
|
||||
* parameter is present, $thumbnail_width and $thumbnail_height must also be
|
||||
* present.
|
||||
* @param int $thumbnail_width
|
||||
* (optional) The width of the thumbnail, in pixels. If this parameter is
|
||||
* present, $thumbnail_url and $thumbnail_height must also be present.
|
||||
* @param int $thumbnail_height
|
||||
* (optional) The height of the thumbnail, in pixels. If this parameter is
|
||||
* present, $thumbnail_url and $thumbnail_width must also be present.
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public static function video($html, $width, $height = NULL, ?Provider $provider = NULL, $title = NULL, $author_name = NULL, $author_url = NULL, $cache_age = NULL, $thumbnail_url = NULL, $thumbnail_width = NULL, $thumbnail_height = NULL) {
|
||||
$resource = static::rich($html, $width, $height, $provider, $title, $author_name, $author_url, $cache_age, $thumbnail_url, $thumbnail_width, $thumbnail_height);
|
||||
$resource->type = self::TYPE_VIDEO;
|
||||
|
||||
return $resource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the resource type.
|
||||
*
|
||||
* @return string
|
||||
* The resource type. Will be one of the self::TYPE_* constants.
|
||||
*/
|
||||
public function getType() {
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the title of the resource.
|
||||
*
|
||||
* @return string|null
|
||||
* The title of the resource, if known.
|
||||
*/
|
||||
public function getTitle() {
|
||||
return $this->title;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the resource author.
|
||||
*
|
||||
* @return string|null
|
||||
* The name of the resource author, if known.
|
||||
*/
|
||||
public function getAuthorName() {
|
||||
return $this->authorName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the URL of the resource author.
|
||||
*
|
||||
* @return \Drupal\Core\Url|null
|
||||
* The absolute URL of the resource author, or NULL if none is provided.
|
||||
*/
|
||||
public function getAuthorUrl() {
|
||||
return $this->authorUrl ? Url::fromUri($this->authorUrl)->setAbsolute() : NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the resource provider, if known.
|
||||
*
|
||||
* @return \Drupal\media\OEmbed\Provider|null
|
||||
* The resource provider, or NULL if the provider is not known.
|
||||
*/
|
||||
public function getProvider() {
|
||||
return $this->provider;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the URL of the resource's thumbnail image.
|
||||
*
|
||||
* @return \Drupal\Core\Url|null
|
||||
* The absolute URL of the thumbnail image, or NULL if there isn't one.
|
||||
*/
|
||||
public function getThumbnailUrl() {
|
||||
return $this->thumbnailUrl ? Url::fromUri($this->thumbnailUrl)->setAbsolute() : NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the width of the resource's thumbnail image.
|
||||
*
|
||||
* @return int|null
|
||||
* The thumbnail width in pixels, or NULL if there is no thumbnail.
|
||||
*/
|
||||
public function getThumbnailWidth() {
|
||||
return $this->thumbnailWidth;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the height of the resource's thumbnail image.
|
||||
*
|
||||
* @return int|null
|
||||
* The thumbnail height in pixels, or NULL if there is no thumbnail.
|
||||
*/
|
||||
public function getThumbnailHeight() {
|
||||
return $this->thumbnailHeight;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the width of the resource.
|
||||
*
|
||||
* @return int|null
|
||||
* The width of the resource in pixels, or NULL if the resource has no
|
||||
* width.
|
||||
*/
|
||||
public function getWidth() {
|
||||
return $this->width;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the height of the resource.
|
||||
*
|
||||
* @return int|null
|
||||
* The height of the resource in pixels, or NULL if the resource has no
|
||||
* height.
|
||||
*/
|
||||
public function getHeight() {
|
||||
return $this->height;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the URL of the resource. Only applies to 'photo' resources.
|
||||
*
|
||||
* @return \Drupal\Core\Url|null
|
||||
* The resource URL, if it has one.
|
||||
*/
|
||||
public function getUrl() {
|
||||
if ($this->url) {
|
||||
return Url::fromUri($this->url)->setAbsolute();
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the HTML representation of the resource.
|
||||
*
|
||||
* Only applies to 'rich' and 'video' resources.
|
||||
*
|
||||
* @return string|null
|
||||
* The HTML representation of the resource, if it has one.
|
||||
*/
|
||||
public function getHtml() {
|
||||
return isset($this->html) ? (string) $this->html : NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the thumbnail dimensions.
|
||||
*
|
||||
* @param int $width
|
||||
* The width of the resource.
|
||||
* @param int $height
|
||||
* The height of the resource.
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
* If either $width or $height are not numbers greater than zero.
|
||||
*/
|
||||
protected function setThumbnailDimensions($width, $height) {
|
||||
$width = (int) $width;
|
||||
$height = (int) $height;
|
||||
|
||||
if ($width > 0 && $height > 0) {
|
||||
$this->thumbnailWidth = $width;
|
||||
$this->thumbnailHeight = $height;
|
||||
}
|
||||
else {
|
||||
throw new \InvalidArgumentException('The thumbnail dimensions must be numbers greater than zero.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the dimensions.
|
||||
*
|
||||
* @param int|null $width
|
||||
* The width of the resource.
|
||||
* @param int|null $height
|
||||
* The height of the resource.
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
* If either $width or $height are not numbers greater than zero.
|
||||
*/
|
||||
protected function setDimensions($width, $height) {
|
||||
if ((isset($width) && $width <= 0) || (isset($height) && $height <= 0)) {
|
||||
throw new \InvalidArgumentException('The dimensions must be NULL or numbers greater than zero.');
|
||||
}
|
||||
$this->width = isset($width) ? (int) $width : NULL;
|
||||
$this->height = isset($height) ? (int) $height : NULL;
|
||||
}
|
||||
|
||||
}
|
||||
67
web/core/modules/media/src/OEmbed/ResourceException.php
Normal file
67
web/core/modules/media/src/OEmbed/ResourceException.php
Normal file
@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\media\OEmbed;
|
||||
|
||||
/**
|
||||
* Exception thrown if an oEmbed resource cannot be fetched or parsed.
|
||||
*
|
||||
* @internal
|
||||
* This is an internal part of the oEmbed system and should only be used by
|
||||
* oEmbed-related code in Drupal core.
|
||||
*/
|
||||
class ResourceException extends \Exception {
|
||||
|
||||
/**
|
||||
* The URL of the resource.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $url;
|
||||
|
||||
/**
|
||||
* The resource data.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $data = [];
|
||||
|
||||
/**
|
||||
* ResourceException constructor.
|
||||
*
|
||||
* @param string $message
|
||||
* The exception message.
|
||||
* @param string $url
|
||||
* The URL of the resource. Can be the actual endpoint URL or the canonical
|
||||
* URL.
|
||||
* @param array $data
|
||||
* (optional) The raw resource data, if available.
|
||||
* @param \Throwable $previous
|
||||
* (optional) The previous exception, if any.
|
||||
*/
|
||||
public function __construct($message, $url, array $data = [], ?\Throwable $previous = NULL) {
|
||||
$this->url = $url;
|
||||
$this->data = $data;
|
||||
parent::__construct($message, 0, $previous);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the URL of the resource which caused the exception.
|
||||
*
|
||||
* @return string
|
||||
* The URL of the resource.
|
||||
*/
|
||||
public function getUrl() {
|
||||
return $this->url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the raw resource data, if available.
|
||||
*
|
||||
* @return array
|
||||
* The resource data.
|
||||
*/
|
||||
public function getData() {
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
}
|
||||
230
web/core/modules/media/src/OEmbed/ResourceFetcher.php
Normal file
230
web/core/modules/media/src/OEmbed/ResourceFetcher.php
Normal file
@ -0,0 +1,230 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\media\OEmbed;
|
||||
|
||||
use Drupal\Component\Serialization\Json;
|
||||
use Drupal\Core\Cache\CacheBackendInterface;
|
||||
use GuzzleHttp\ClientInterface;
|
||||
use GuzzleHttp\RequestOptions;
|
||||
use Psr\Http\Client\ClientExceptionInterface;
|
||||
|
||||
// cspell:ignore nocdata
|
||||
|
||||
/**
|
||||
* Fetches and caches oEmbed resources.
|
||||
*/
|
||||
class ResourceFetcher implements ResourceFetcherInterface {
|
||||
|
||||
/**
|
||||
* Constructs a ResourceFetcher object.
|
||||
*
|
||||
* @param \GuzzleHttp\ClientInterface $httpClient
|
||||
* The HTTP client.
|
||||
* @param \Drupal\media\OEmbed\ProviderRepositoryInterface $providers
|
||||
* The oEmbed provider repository service.
|
||||
* @param \Drupal\Core\Cache\CacheBackendInterface $cacheBackend
|
||||
* The cache backend.
|
||||
* @param int $timeout
|
||||
* The length of time to wait for the request before the request
|
||||
* should time out.
|
||||
*/
|
||||
public function __construct(
|
||||
protected ClientInterface $httpClient,
|
||||
protected ProviderRepositoryInterface $providers,
|
||||
protected CacheBackendInterface $cacheBackend,
|
||||
protected int $timeout = 5,
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function fetchResource($url) {
|
||||
$cache_id = "media:oembed_resource:$url";
|
||||
|
||||
$cached = $this->cacheBackend->get($cache_id);
|
||||
if ($cached) {
|
||||
return $this->createResource($cached->data, $url);
|
||||
}
|
||||
|
||||
try {
|
||||
$response = $this->httpClient->request('GET', $url, [
|
||||
RequestOptions::TIMEOUT => $this->timeout,
|
||||
]);
|
||||
}
|
||||
catch (ClientExceptionInterface $e) {
|
||||
throw new ResourceException('Could not retrieve the oEmbed resource.', $url, [], $e);
|
||||
}
|
||||
|
||||
[$format] = $response->getHeader('Content-Type');
|
||||
$content = (string) $response->getBody();
|
||||
|
||||
if (strstr($format, 'text/xml') || strstr($format, 'application/xml')) {
|
||||
$data = $this->parseResourceXml($content, $url);
|
||||
}
|
||||
// By default, try to parse the resource data as JSON.
|
||||
else {
|
||||
$data = Json::decode($content);
|
||||
|
||||
if (json_last_error() !== JSON_ERROR_NONE) {
|
||||
throw new ResourceException('Error decoding oEmbed resource: ' . json_last_error_msg(), $url);
|
||||
}
|
||||
}
|
||||
if (empty($data) || !is_array($data)) {
|
||||
throw new ResourceException('The oEmbed resource could not be decoded.', $url);
|
||||
}
|
||||
|
||||
$this->cacheBackend->set($cache_id, $data);
|
||||
|
||||
return $this->createResource($data, $url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a Resource object from raw resource data.
|
||||
*
|
||||
* @param array $data
|
||||
* The resource data returned by the provider.
|
||||
* @param string $url
|
||||
* The URL of the resource.
|
||||
*
|
||||
* @return \Drupal\media\OEmbed\Resource
|
||||
* A value object representing the resource.
|
||||
*
|
||||
* @throws \Drupal\media\OEmbed\ResourceException
|
||||
* If the resource cannot be created.
|
||||
*/
|
||||
protected function createResource(array $data, $url) {
|
||||
$data += [
|
||||
'title' => NULL,
|
||||
'author_name' => NULL,
|
||||
'author_url' => NULL,
|
||||
'provider_name' => NULL,
|
||||
'cache_age' => NULL,
|
||||
'thumbnail_url' => NULL,
|
||||
'thumbnail_width' => NULL,
|
||||
'thumbnail_height' => NULL,
|
||||
'width' => NULL,
|
||||
'height' => NULL,
|
||||
'url' => NULL,
|
||||
'html' => NULL,
|
||||
'version' => NULL,
|
||||
];
|
||||
|
||||
if ($data['version'] !== '1.0') {
|
||||
throw new ResourceException("Resource version must be '1.0'", $url, $data);
|
||||
}
|
||||
|
||||
// Prepare the arguments to pass to the factory method.
|
||||
$provider = $data['provider_name'] ? $this->providers->get($data['provider_name']) : NULL;
|
||||
|
||||
// The Resource object will validate the data we create it with and throw an
|
||||
// exception if anything looks wrong. For better debugging, catch those
|
||||
// exceptions and wrap them in a more specific and useful exception.
|
||||
try {
|
||||
switch ($data['type']) {
|
||||
case Resource::TYPE_LINK:
|
||||
return Resource::link(
|
||||
$data['url'],
|
||||
$provider,
|
||||
$data['title'],
|
||||
$data['author_name'],
|
||||
$data['author_url'],
|
||||
$data['cache_age'],
|
||||
$data['thumbnail_url'],
|
||||
$data['thumbnail_width'],
|
||||
$data['thumbnail_height']
|
||||
);
|
||||
|
||||
case Resource::TYPE_PHOTO:
|
||||
return Resource::photo(
|
||||
$data['url'],
|
||||
$data['width'],
|
||||
$data['height'],
|
||||
$provider,
|
||||
$data['title'],
|
||||
$data['author_name'],
|
||||
$data['author_url'],
|
||||
$data['cache_age'],
|
||||
$data['thumbnail_url'],
|
||||
$data['thumbnail_width'],
|
||||
$data['thumbnail_height']
|
||||
);
|
||||
|
||||
case Resource::TYPE_RICH:
|
||||
return Resource::rich(
|
||||
$data['html'],
|
||||
$data['width'],
|
||||
$data['height'],
|
||||
$provider,
|
||||
$data['title'],
|
||||
$data['author_name'],
|
||||
$data['author_url'],
|
||||
$data['cache_age'],
|
||||
$data['thumbnail_url'],
|
||||
$data['thumbnail_width'],
|
||||
$data['thumbnail_height']
|
||||
);
|
||||
|
||||
case Resource::TYPE_VIDEO:
|
||||
return Resource::video(
|
||||
$data['html'],
|
||||
$data['width'],
|
||||
$data['height'],
|
||||
$provider,
|
||||
$data['title'],
|
||||
$data['author_name'],
|
||||
$data['author_url'],
|
||||
$data['cache_age'],
|
||||
$data['thumbnail_url'],
|
||||
$data['thumbnail_width'],
|
||||
$data['thumbnail_height']
|
||||
);
|
||||
|
||||
default:
|
||||
throw new ResourceException('Unknown resource type: ' . $data['type'], $url, $data);
|
||||
}
|
||||
}
|
||||
catch (\InvalidArgumentException $e) {
|
||||
throw new ResourceException($e->getMessage(), $url, $data, $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses XML resource data.
|
||||
*
|
||||
* @param string $data
|
||||
* The raw XML for the resource.
|
||||
* @param string $url
|
||||
* The resource URL.
|
||||
*
|
||||
* @return array
|
||||
* The parsed resource data.
|
||||
*
|
||||
* @throws \Drupal\media\OEmbed\ResourceException
|
||||
* If the resource data could not be parsed.
|
||||
*/
|
||||
protected function parseResourceXml($data, $url) {
|
||||
// Enable userspace error handling.
|
||||
$was_using_internal_errors = libxml_use_internal_errors(TRUE);
|
||||
libxml_clear_errors();
|
||||
|
||||
$content = simplexml_load_string($data, 'SimpleXMLElement', LIBXML_NOCDATA);
|
||||
// Restore the previous error handling behavior.
|
||||
libxml_use_internal_errors($was_using_internal_errors);
|
||||
|
||||
$error = libxml_get_last_error();
|
||||
if ($error) {
|
||||
libxml_clear_errors();
|
||||
throw new ResourceException($error->message, $url);
|
||||
}
|
||||
elseif ($content === FALSE) {
|
||||
throw new ResourceException('The fetched resource could not be parsed.', $url);
|
||||
}
|
||||
|
||||
// Convert XML to JSON so that the parsed resource has a consistent array
|
||||
// structure, regardless of any XML attributes or quirks of the XML parser.
|
||||
$data = Json::encode($content);
|
||||
return Json::decode($data);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\media\OEmbed;
|
||||
|
||||
/**
|
||||
* Defines an interface for an oEmbed resource fetcher service.
|
||||
*
|
||||
* The resource fetcher's only responsibility is to retrieve oEmbed resource
|
||||
* data from an endpoint URL (i.e., as returned by
|
||||
* \Drupal\media\OEmbed\UrlResolverInterface::getResourceUrl()) and return a
|
||||
* \Drupal\media\OEmbed\Resource value object.
|
||||
*/
|
||||
interface ResourceFetcherInterface {
|
||||
|
||||
/**
|
||||
* Fetches an oEmbed resource.
|
||||
*
|
||||
* @param string $url
|
||||
* Endpoint-specific URL of the oEmbed resource.
|
||||
*
|
||||
* @return \Drupal\media\OEmbed\Resource
|
||||
* A resource object built from the oEmbed resource data.
|
||||
*
|
||||
* @see https://oembed.com/#section2
|
||||
*
|
||||
* @throws \Drupal\media\OEmbed\ResourceException
|
||||
* If the oEmbed endpoint is not reachable or the response returns an
|
||||
* unexpected Content-Type header.
|
||||
*/
|
||||
public function fetchResource($url);
|
||||
|
||||
}
|
||||
210
web/core/modules/media/src/OEmbed/UrlResolver.php
Normal file
210
web/core/modules/media/src/OEmbed/UrlResolver.php
Normal file
@ -0,0 +1,210 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\media\OEmbed;
|
||||
|
||||
use Drupal\Component\Utility\Html;
|
||||
use Drupal\Component\Utility\UrlHelper;
|
||||
use Drupal\Core\Cache\CacheBackendInterface;
|
||||
use Drupal\Core\Extension\ModuleHandlerInterface;
|
||||
use GuzzleHttp\ClientInterface;
|
||||
use Psr\Http\Client\ClientExceptionInterface;
|
||||
|
||||
// cspell:ignore omitscript
|
||||
|
||||
/**
|
||||
* Converts oEmbed media URLs into endpoint-specific resource URLs.
|
||||
*/
|
||||
class UrlResolver implements UrlResolverInterface {
|
||||
|
||||
/**
|
||||
* The HTTP client.
|
||||
*
|
||||
* @var \GuzzleHttp\Client
|
||||
*/
|
||||
protected $httpClient;
|
||||
|
||||
/**
|
||||
* The OEmbed provider repository service.
|
||||
*
|
||||
* @var \Drupal\media\OEmbed\ProviderRepositoryInterface
|
||||
*/
|
||||
protected $providers;
|
||||
|
||||
/**
|
||||
* The OEmbed resource fetcher service.
|
||||
*
|
||||
* @var \Drupal\media\OEmbed\ResourceFetcherInterface
|
||||
*/
|
||||
protected $resourceFetcher;
|
||||
|
||||
/**
|
||||
* The module handler service.
|
||||
*
|
||||
* @var \Drupal\Core\Extension\ModuleHandlerInterface
|
||||
*/
|
||||
protected $moduleHandler;
|
||||
|
||||
/**
|
||||
* Static cache of discovered oEmbed resource URLs, keyed by canonical URL.
|
||||
*
|
||||
* A discovered resource URL is the actual endpoint URL for a specific media
|
||||
* object, fetched from its canonical URL.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
protected $urlCache = [];
|
||||
|
||||
/**
|
||||
* The cache backend.
|
||||
*
|
||||
* @var \Drupal\Core\Cache\CacheBackendInterface
|
||||
*/
|
||||
protected $cacheBackend;
|
||||
|
||||
/**
|
||||
* Constructs a UrlResolver object.
|
||||
*
|
||||
* @param \Drupal\media\OEmbed\ProviderRepositoryInterface $providers
|
||||
* The oEmbed provider repository service.
|
||||
* @param \Drupal\media\OEmbed\ResourceFetcherInterface $resource_fetcher
|
||||
* The OEmbed resource fetcher service.
|
||||
* @param \GuzzleHttp\ClientInterface $http_client
|
||||
* The HTTP client.
|
||||
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
|
||||
* The module handler service.
|
||||
* @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend
|
||||
* The cache backend.
|
||||
*/
|
||||
public function __construct(ProviderRepositoryInterface $providers, ResourceFetcherInterface $resource_fetcher, ClientInterface $http_client, ModuleHandlerInterface $module_handler, CacheBackendInterface $cache_backend) {
|
||||
$this->providers = $providers;
|
||||
$this->resourceFetcher = $resource_fetcher;
|
||||
$this->httpClient = $http_client;
|
||||
$this->moduleHandler = $module_handler;
|
||||
$this->cacheBackend = $cache_backend;
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs oEmbed discovery and returns the endpoint URL if successful.
|
||||
*
|
||||
* @param string $url
|
||||
* The resource's URL.
|
||||
*
|
||||
* @return string|bool
|
||||
* URL of the oEmbed endpoint, or FALSE if the discovery was unsuccessful.
|
||||
*/
|
||||
protected function discoverResourceUrl($url) {
|
||||
try {
|
||||
$response = $this->httpClient->get($url);
|
||||
}
|
||||
catch (ClientExceptionInterface) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
$document = Html::load((string) $response->getBody());
|
||||
$xpath = new \DOMXPath($document);
|
||||
|
||||
return $this->findUrl($xpath, 'json') ?: $this->findUrl($xpath, 'xml');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to find the oEmbed URL in a DOM.
|
||||
*
|
||||
* @param \DOMXPath $xpath
|
||||
* Page HTML as DOMXPath.
|
||||
* @param string $format
|
||||
* Format of oEmbed resource. Possible values are 'json' and 'xml'.
|
||||
*
|
||||
* @return bool|string
|
||||
* A URL to an oEmbed resource or FALSE if not found.
|
||||
*/
|
||||
protected function findUrl(\DOMXPath $xpath, $format) {
|
||||
$result = $xpath->query("//link[@type='application/$format+oembed']");
|
||||
return $result->length ? $result->item(0)->getAttribute('href') : FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getProviderByUrl($url) {
|
||||
// Check the URL against every scheme of every endpoint of every provider
|
||||
// until we find a match.
|
||||
foreach ($this->providers->getAll() as $provider_info) {
|
||||
foreach ($provider_info->getEndpoints() as $endpoint) {
|
||||
if ($endpoint->matchUrl($url)) {
|
||||
return $provider_info;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$resource_url = $this->discoverResourceUrl($url);
|
||||
if ($resource_url) {
|
||||
return $this->resourceFetcher->fetchResource($resource_url)->getProvider();
|
||||
}
|
||||
|
||||
throw new ResourceException('No matching provider found.', $url);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getResourceUrl($url, $max_width = NULL, $max_height = NULL) {
|
||||
// Try to get the resource URL from the static cache.
|
||||
if (isset($this->urlCache[$url])) {
|
||||
return $this->urlCache[$url];
|
||||
}
|
||||
|
||||
// Try to get the resource URL from the persistent cache.
|
||||
$cache_id = "media:oembed_resource_url:$url:$max_width:$max_height";
|
||||
|
||||
$cached = $this->cacheBackend->get($cache_id);
|
||||
if ($cached) {
|
||||
$this->urlCache[$url] = $cached->data;
|
||||
return $this->urlCache[$url];
|
||||
}
|
||||
|
||||
$provider = $this->getProviderByUrl($url);
|
||||
|
||||
$resource_url = $this->getEndpointMatchingUrl($url, $provider);
|
||||
$parsed_url = UrlHelper::parse($resource_url);
|
||||
if ($max_width) {
|
||||
$parsed_url['query']['maxwidth'] = $max_width;
|
||||
}
|
||||
if ($max_height) {
|
||||
$parsed_url['query']['maxheight'] = $max_height;
|
||||
}
|
||||
// Let other modules alter the resource URL, because some oEmbed providers
|
||||
// provide extra parameters in the query string. For example, Instagram also
|
||||
// supports the 'omitscript' parameter.
|
||||
$this->moduleHandler->alter('oembed_resource_url', $parsed_url, $provider);
|
||||
$resource_url = $parsed_url['path'] . '?' . UrlHelper::buildQuery($parsed_url['query']);
|
||||
|
||||
$this->urlCache[$url] = $resource_url;
|
||||
$this->cacheBackend->set($cache_id, $resource_url);
|
||||
|
||||
return $resource_url;
|
||||
}
|
||||
|
||||
/**
|
||||
* For the given media item URL find an endpoint with schemes that match.
|
||||
*
|
||||
* @param string $url
|
||||
* The media URL used to lookup the matching endpoint.
|
||||
* @param \Drupal\media\OEmbed\Provider $provider
|
||||
* The oEmbed provider for the asset.
|
||||
*
|
||||
* @return string
|
||||
* The resource URL.
|
||||
*/
|
||||
protected function getEndpointMatchingUrl($url, Provider $provider) {
|
||||
$endpoints = $provider->getEndpoints();
|
||||
$resource_url = reset($endpoints)->buildResourceUrl($url);
|
||||
foreach ($endpoints as $endpoint) {
|
||||
if ($endpoint->matchUrl($url)) {
|
||||
$resource_url = $endpoint->buildResourceUrl($url);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return $resource_url ?? reset($endpoints)->buildResourceUrl($url);
|
||||
}
|
||||
|
||||
}
|
||||
45
web/core/modules/media/src/OEmbed/UrlResolverInterface.php
Normal file
45
web/core/modules/media/src/OEmbed/UrlResolverInterface.php
Normal file
@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\media\OEmbed;
|
||||
|
||||
/**
|
||||
* Defines the interface for the oEmbed URL resolver service.
|
||||
*
|
||||
* The URL resolver is responsible for converting oEmbed-compatible media asset
|
||||
* URLs into canonical resource URLs, at which an oEmbed representation of the
|
||||
* asset can be retrieved.
|
||||
*/
|
||||
interface UrlResolverInterface {
|
||||
|
||||
/**
|
||||
* Tries to determine the oEmbed provider for a media asset URL.
|
||||
*
|
||||
* @param string $url
|
||||
* The media asset URL.
|
||||
*
|
||||
* @return \Drupal\media\OEmbed\Provider
|
||||
* The oEmbed provider for the asset.
|
||||
*
|
||||
* @throws \Drupal\media\OEmbed\ResourceException
|
||||
* If the provider cannot be determined.
|
||||
* @throws \Drupal\media\OEmbed\ProviderException
|
||||
* If tne oEmbed provider causes an error.
|
||||
*/
|
||||
public function getProviderByUrl($url);
|
||||
|
||||
/**
|
||||
* Builds the resource URL for a media asset URL.
|
||||
*
|
||||
* @param string $url
|
||||
* The media asset URL.
|
||||
* @param int $max_width
|
||||
* (optional) Maximum width of the oEmbed resource, in pixels.
|
||||
* @param int $max_height
|
||||
* (optional) Maximum height of the oEmbed resource, in pixels.
|
||||
*
|
||||
* @return string
|
||||
* Returns the resource URL corresponding to the given media item URL.
|
||||
*/
|
||||
public function getResourceUrl($url, $max_width = NULL, $max_height = NULL);
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user