Unverified Commit bb02eb5f authored by Lee Rowlands's avatar Lee Rowlands
Browse files

Issue #3080666 by phenaproxima, Upchuk, ieguskiza, larowlan, ravi.shankar,...

Issue #3080666 by phenaproxima, Upchuk, ieguskiza, larowlan, ravi.shankar, nikitagupta, zipymonkey, Bladedu, pameeela, b_sharpe, seanB, Spokje, mukesh.dev, Gauravmahlawat: oEmbed system doesn't work if thumbnail url does not have a file extension
parent dc9a09e8
Loading
Loading
Loading
Loading
+59 −13
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@
use GuzzleHttp\Exception\TransferException;
use Psr\Log\LoggerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\Mime\MimeTypes;

/**
 * Provides a media source plugin for oEmbed resources.
@@ -388,21 +389,11 @@ protected function getLocalThumbnailUri(Resource $resource) {
    if (!$remote_thumbnail_url) {
      return NULL;
    }
    $remote_thumbnail_url = $remote_thumbnail_url->toString();

    // Remove the query string, since we do not want to include it in the local
    // thumbnail URI.
    $local_thumbnail_url = parse_url($remote_thumbnail_url, PHP_URL_PATH);

    // Compute the local thumbnail URI, regardless of whether or not it exists.
    // Ensure that we can write to the local directory where thumbnails are
    // stored.
    $configuration = $this->getConfiguration();
    $directory = $configuration['thumbnails_directory'];
    $local_thumbnail_uri = "$directory/" . Crypt::hashBase64($local_thumbnail_url) . '.' . pathinfo($local_thumbnail_url, PATHINFO_EXTENSION);

    // If the local thumbnail already exists, return its URI.
    if (file_exists($local_thumbnail_uri)) {
      return $local_thumbnail_uri;
    }

    // The local thumbnail doesn't exist yet, so try to download it. First,
    // ensure that the destination directory is writable, and if it's not,
@@ -414,8 +405,20 @@ protected function getLocalThumbnailUri(Resource $resource) {
      return NULL;
    }

    // The local filename of the thumbnail is always a hash of its remote URL.
    // If a file with that name already exists in the thumbnails directory,
    // regardless of its extension, return its URI.
    $remote_thumbnail_url = $remote_thumbnail_url->toString();
    $hash = Crypt::hashBase64($remote_thumbnail_url);
    $files = $this->fileSystem->scanDirectory($directory, "/^$hash\..*/");
    if (count($files) > 0) {
      return reset($files)->uri;
    }

    // The local thumbnail doesn't exist yet, so we need to download it.
    $local_thumbnail_uri = $directory . DIRECTORY_SEPARATOR . $hash . '.' . $this->getThumbnailFileExtensionFromUrl($remote_thumbnail_url);
    try {
      $response = $this->httpClient->get($remote_thumbnail_url);
      $response = $this->httpClient->request('GET', $remote_thumbnail_url);
      if ($response->getStatusCode() === 200) {
        $this->fileSystem->saveData((string) $response->getBody(), $local_thumbnail_uri, FileSystemInterface::EXISTS_REPLACE);
        return $local_thumbnail_uri;
@@ -432,6 +435,49 @@ protected function getLocalThumbnailUri(Resource $resource) {
    return NULL;
  }

  /**
   * Tries to determine the file extension of a thumbnail.
   *
   * @param string $thumbnail_url
   *   The remote URL of the thumbnail.
   *
   * @return string|null
   *   The file extension, or NULL if it could not be determined.
   */
  protected function getThumbnailFileExtensionFromUrl(string $thumbnail_url): ?string {
    // First, try to glean the extension from the URL path.
    $path = parse_url($thumbnail_url, PHP_URL_PATH);
    if ($path) {
      $extension = strtolower(pathinfo($path, PATHINFO_EXTENSION));
      if ($extension) {
        return $extension;
      }
    }

    // If the URL didn't give us any clues about the file extension, make a HEAD
    // request to the thumbnail URL and see if the headers will give us a MIME
    // type.
    try {
      $content_type = $this->httpClient->request('HEAD', $thumbnail_url)
        ->getHeader('Content-Type');
    }
    catch (TransferException $e) {
      $this->logger->warning($e->getMessage());
    }

    // If there was no Content-Type header, there's nothing else we can do.
    if (empty($content_type)) {
      return NULL;
    }
    $extensions = MimeTypes::getDefault()->getExtensions(reset($content_type));
    if ($extensions) {
      return reset($extensions);
    }
    // If no file extension could be determined from the Content-Type header,
    // we're stumped.
    return NULL;
  }

  /**
   * {@inheritdoc}
   */
+4 −1
Original line number Diff line number Diff line
@@ -12,7 +12,10 @@
  <height>343</height>
  <html><h1>By the power of Grayskull, CollegeHumor works!</h1>
  </html>
  <thumbnail_url>internal:/core/misc/druplicon.png</thumbnail_url>
  <!-- The thumbnail URL does not contain a file extension, so we use
  this to test the oEmbed source plugin's thumbnail handling;
  see \Drupal\Tests\media\FunctionalJavascript\MediaSourceOEmbedVideoTest. -->
  <thumbnail_url>internal:/media_test_oembed/thumbnail</thumbnail_url>
  <thumbnail_width>88</thumbnail_width>
  <thumbnail_height>100</thumbnail_height>
</oembed>
+6 −0
Original line number Diff line number Diff line
@@ -4,3 +4,9 @@ media_test_oembed.resource.get:
    _controller: '\Drupal\media_test_oembed\Controller\ResourceController::get'
  requirements:
    _access: 'TRUE'
media_test_oembed.resource.thumbnail:
  path: '/media_test_oembed/thumbnail'
  defaults:
    _controller: '\Drupal\media_test_oembed\Controller\ResourceController::getThumbnailWithNoExtension'
  requirements:
    _access: 'TRUE'
+14 −1
Original line number Diff line number Diff line
@@ -2,6 +2,7 @@

namespace Drupal\media_test_oembed\Controller;

use Symfony\Component\HttpFoundation\BinaryFileResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

@@ -30,12 +31,24 @@ public function get(Request $request) {
    else {
      $content = file_get_contents($resources[$asset_url]);
      $response = new Response($content);
      $response->headers->set('Content-Type', 'application/json');
      $response->headers->set('Content-Type', 'application/' . pathinfo($resources[$asset_url], PATHINFO_EXTENSION));
    }

    return $response;
  }

  /**
   * Returns an example thumbnail file without an extension.
   *
   * @return \Symfony\Component\HttpFoundation\BinaryFileResponse
   *   The response.
   */
  public function getThumbnailWithNoExtension() {
    $response = new BinaryFileResponse('core/misc/druplicon.png');
    $response->headers->set('Content-Type', 'image/png');
    return $response;
  }

  /**
   * Maps an asset URL to a local fixture representing its oEmbed resource.
   *
+24 −0
Original line number Diff line number Diff line
@@ -4,6 +4,7 @@

use Drupal\Core\Session\AccountInterface;
use Drupal\media\Entity\Media;
use Drupal\media\Entity\MediaType;
use Drupal\media_test_oembed\Controller\ResourceController;
use Drupal\Tests\media\Traits\OEmbedTestTrait;
use Drupal\user\Entity\Role;
@@ -164,6 +165,29 @@ public function testMediaOEmbedVideoSource() {

    $assert_session->pageTextContains('The CollegeHumor provider is not allowed.');

    // Register a CollegeHumor video as a second oEmbed resource. Note that its
    // thumbnail URL does not have a file extension.
    $media_type = MediaType::load($media_type_id);
    $source_configuration = $media_type->getSource()->getConfiguration();
    $source_configuration['providers'][] = 'CollegeHumor';
    $media_type->getSource()->setConfiguration($source_configuration);
    $media_type->save();
    $video_url = 'http://www.collegehumor.com/video/40003213/let-not-get-a-drink-sometime';
    ResourceController::setResourceUrl($video_url, $this->getFixturesDirectory() . '/video_collegehumor.xml');

    // Create a new media item using a CollegeHumor video.
    $this->drupalGet("media/add/$media_type_id");
    $assert_session->fieldExists('Remote video URL')->setValue($video_url);
    $assert_session->buttonExists('Save')->press();

    /** @var \Drupal\media\MediaInterface $media */
    $media = Media::load(2);
    $thumbnail = $media->getSource()->getMetadata($media, 'thumbnail_uri');
    $this->assertFileExists($thumbnail);
    // Although the resource's thumbnail URL doesn't have a file extension, we
    // should have deduced the correct one.
    $this->assertStringEndsWith('.png', $thumbnail);

    // Test anonymous access to media via iframe.
    $this->drupalLogout();

Loading