bookish_admin-1.0.x-dev/modules/bookish_ckeditor/src/Plugin/Filter/BookishOEmbedFilter.php

modules/bookish_ckeditor/src/Plugin/Filter/BookishOEmbedFilter.php
<?php

namespace Drupal\bookish_ckeditor\Plugin\Filter;

use Drupal\Component\Serialization\Json;
use Drupal\Component\Utility\Html;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\filter\FilterProcessResult;
use Drupal\filter\Plugin\FilterBase;
use GuzzleHttp\ClientInterface;
use GuzzleHttp\RequestOptions;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Renders non-previewable OEmbed content.
 *
 * @Filter(
 *   id = "bookish_oembed_filter",
 *   title = @Translation("Display embedded OEmbed URLs if possible."),
 *   type = Drupal\filter\Plugin\FilterInterface::TYPE_TRANSFORM_REVERSIBLE
 * )
 */
class BookishOEmbedFilter extends FilterBase implements ContainerFactoryPluginInterface {

  /**
   * The HTTP client.
   *
   * @var \GuzzleHttp\Client
   */
  protected $httpClient;

  /**
   * An array of providers, copied from the CKEditor 5 codebase.
   *
   * @var string[]
   */
  protected $providers;

  /**
   * Constructs a BookishOEmbedFitler object.
   *
   * @param array $configuration
   *   A configuration array containing information about the plugin instance.
   * @param string $plugin_id
   *   The plugin_id for the plugin instance.
   * @param mixed $plugin_definition
   *   The plugin implementation definition.
   * @param \GuzzleHttp\ClientInterface $http_client
   *   The HTTP client.
   */
  public function __construct(array $configuration, $plugin_id, $plugin_definition, ClientInterface $http_client) {
    parent::__construct($configuration, $plugin_id, $plugin_definition);
    $this->httpClient = $http_client;
    // This list is taken from CKEditor 5.
    // @see https://github.com/ckeditor/ckeditor5/blob/30286f77b39526fce2856b03b9be0ba4cc91d1c7/packages/ckeditor5-media-embed/src/mediaembedediting.js
    $this->providers = [
      [
        'name' => 'dailymotion',
        'url' => '/^dailymotion\.com\/video\/(\w+)/',
      ],
      [
        'name' => 'spotify',
        'url' => [
          '/^open\.spotify\.com\/(artist\/\w+)/',
          '/^open\.spotify\.com\/(album\/\w+)/',
          '/^open\.spotify\.com\/(track\/\w+)/',
        ],
      ],
      [
        'name' => 'youtube',
        'url' => [
          '/^(?:m\.)?youtube\.com\/watch\?v=([\w-]+)/',
          '/^(?:m\.)?youtube\.com\/v\/([\w-]+)/',
          '/^youtube\.com\/embed\/([\w-]+)/',
          '/^youtu\.be\/([\w-]+)/',
        ],
      ],
      [
        'name' => 'vimeo',
        'url' => [
          '/^vimeo\.com\/(\d+)/',
          '/^vimeo\.com\/[^\/]+\/[^\/]+\/video\/(\d+)/',
          '/^vimeo\.com\/album\/[^\/]+\/video\/(\d+)/',
          '/^vimeo\.com\/channels\/[^\/]+\/(\d+)/',
          '/^vimeo\.com\/groups\/[^\/]+\/videos\/(\d+)/',
          '/^vimeo\.com\/ondemand\/[^\/]+\/(\d+)/',
          '/^player\.vimeo\.com\/video\/(\d+)/',
        ],
      ],
      [
        'name' => 'instagram',
        'url' => '/^instagram\.com\/p\/(\w+)/',
      ],
      [
        'name' => 'twitter',
      // \/ added for security reasons.
        'url' => '/^twitter\.com\//',
      ],
      [
        'name' => 'googleMaps',
        'url' => [
          '/^google\.com\/maps/',
          '/^goo\.gl\/maps/',
      // \/ added for security reasons.
          '/^maps\.google\.com\//',
      // \/ added for security reasons.
          '/^maps\.app\.goo\.gl\//',
        ],
      ],
      [
        'name' => 'flickr',
      // \/ added for security reasons.
        'url' => '/^flickr\.com\//',
      ],
      [
        'name' => 'facebook',
      // \/ added for security reasons.
        'url' => '/^facebook\.com\//',
      ],
    ];
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
    return new static(
      $configuration,
      $plugin_id,
      $plugin_definition,
      $container->get('http_client')
    );
  }

  /**
   * {@inheritdoc}
   */
  public function process($text, $langcode) {
    $result = new FilterProcessResult($text);

    if (stristr($text, '<oembed') !== FALSE) {
      $dom = Html::load($text);
      $xpath = new \DOMXPath($dom);
      foreach ($xpath->query('//oembed') as $node) {
        $url = $node->getAttribute('url');
        [$url, $provider] = $this->getUrlAndProvider($url);
        if (!$url || !$provider) {
          $data = ['html' => 'Cannot find provider for URL.'];
        }
        else {
          $data = $this->getOembedData($url, $provider);
        }
        $wrapper = $dom->createElement('div', '');
        $wrapper->setAttribute('class', 'bookish-oembed-wrapper bookish-oembed-wrapper-' . ($provider ? $provider['name'] : 'unknown'));
        $iframe = $dom->createElement('iframe', '');
        $iframe->setAttribute('class', 'bookish-oembed bookish-oembed-' . ($provider ? $provider['name'] : 'unknown'));
        $iframe->setAttribute('srcdoc', '<style>iframe { max-width: 100% !important; }</style>' . $data['html']);
        if (!empty($provider) && $provider['name'] === 'flickr') {
          $iframe->setAttribute('scrolling', 'no');
        }
        if (isset($data['width'])) {
          $iframe->setAttribute('width', $data['width']);
        }
        if (isset($data['height'])) {
          $iframe->setAttribute('height', $data['height']);
        }
        $iframe->setAttribute('sandbox', 'allow-scripts allow-same-origin allow-popups allow-popups-to-escape-sandbox');
        $wrapper->appendChild($iframe);
        $node->parentNode->replaceChild($wrapper, $node);
      }
      if (!empty($provider) && $provider['name'] === 'twitter') {
        $result->setAttachments([
          'library' => [
            'bookish_ckeditor/iframeSize',
          ],
        ]);
      }
      $result->setProcessedText(Html::serialize($dom));
    }

    return $result;
  }

  /**
   * Fetches the data for a given oEmbed URL.
   *
   * @param string $url
   *   A user-provided URL.
   * @param array $provider
   *   The provider information.
   *
   * @return string
   *   The fetched data.
   */
  protected function getOembedData($url, array $provider) {
    $url = 'https://' . $url;
    $query = [
      'url' => $url,
    ];
    $request_url = '';
    switch ($provider['name']) {
      case 'twitter':
        $request_url = 'https://publish.twitter.com/oembed';
        break;

      case 'flickr':
        $request_url = 'https://www.flickr.com/services/oembed/';
        $query['format'] = 'json';
        break;
    }
    if (!$request_url) {
      return ['html' => 'This provider is not supported.'];
    }

    try {
      $response = $this->httpClient->request('GET', $request_url, [
        RequestOptions::TIMEOUT => 5,
        'query' => $query,
      ]);
    }
    catch (\Exception $e) {
      return ['html' => 'Could not retrieve the oEmbed HTML.'];
    }

    $content = (string) $response->getBody();
    $data = Json::decode($content);

    if (json_last_error() !== JSON_ERROR_NONE) {
      return ['html' => 'Error decoding oEmbed data.'];
    }
    if (empty($data) || !is_array($data) || !isset($data['html']) || !is_string($data['html'])) {
      return ['html' => 'The oEmbed data does not have valid HTML.'];
    }

    return $data;
  }

  /**
   * Looks up a provider by URL.
   *
   * @param string $url
   *   A user-provided URL.
   *
   * @return array|bool
   *   The provider, or FALSE if one cannot be found.
   */
  protected function getProvider($url) {
    foreach ($this->providers as $provider) {
      $provider['url'] = (array) $provider['url'];
      foreach ($provider['url'] as $pattern) {
        if (preg_match($pattern, $url) === 1) {
          return $provider;
        }
      }
    }
    return FALSE;
  }

  /**
   * Gets the lookup URL and provider for a given URL.
   *
   * @param string $url
   *   A user-provided URL.
   *
   * @return array
   *   A tuple in the format [url,provider], [FALSE, FALSE] in case of error.
   *
   * @see https://github.com/ckeditor/ckeditor5/blob/30286f77b39526fce2856b03b9be0ba4cc91d1c7/packages/ckeditor5-media-embed/src/mediaregistry.js#L138
   */
  protected function getUrlAndProvider($url) {
    $url = preg_replace('|^https?://|', '', $url);
    if ($provider = $this->getProvider($url)) {
      return [$url, $provider];
    }
    $url = preg_replace('|^www\.|', '', $url);
    if ($provider = $this->getProvider($url)) {
      return [$url, $provider];
    }
    return [FALSE, FALSE];
  }

}

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc