You are here

public function OEmbedIframeController::render in Drupal 10

Same name and namespace in other branches
  1. 8 core/modules/media/src/Controller/OEmbedIframeController.php \Drupal\media\Controller\OEmbedIframeController::render()
  2. 9 core/modules/media/src/Controller/OEmbedIframeController.php \Drupal\media\Controller\OEmbedIframeController::render()

Renders an oEmbed resource.

Parameters

\Symfony\Component\HttpFoundation\Request $request: The request object.

Return value

\Symfony\Component\HttpFoundation\Response The response object.

Throws

\Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException Will be thrown if either

  • the 'hash' parameter does not match the expected hash of the 'url' parameter;
  • the iframe_domain is set in media.settings and does not match the host in the request.
1 string reference to 'OEmbedIframeController::render'
media.routing.yml in core/modules/media/media.routing.yml
core/modules/media/media.routing.yml

File

core/modules/media/src/Controller/OEmbedIframeController.php, line 125

Class

OEmbedIframeController
Controller which renders an oEmbed resource in a bare page (without blocks).

Namespace

Drupal\media\Controller

Code

public function render(Request $request) {

  // @todo Move domain check logic to a separate method.
  $allowed_domain = \Drupal::config('media.settings')
    ->get('iframe_domain');
  if ($allowed_domain) {
    $allowed_host = parse_url($allowed_domain, PHP_URL_HOST);
    $host = parse_url($request
      ->getSchemeAndHttpHost(), PHP_URL_HOST);
    if ($allowed_host !== $host) {
      throw new AccessDeniedHttpException('This resource is not available');
    }
  }
  $url = $request->query
    ->get('url');
  $max_width = $request->query
    ->getInt('max_width');
  $max_height = $request->query
    ->getInt('max_height');

  // Hash the URL and max dimensions, and ensure it is equal to the hash
  // parameter passed in the query string.
  $hash = $this->iFrameUrlHelper
    ->getHash($url, $max_width, $max_height);
  if (!hash_equals($hash, $request->query
    ->get('hash', ''))) {
    throw new AccessDeniedHttpException('This resource is not available');
  }

  // Return a response instead of a render array so that the frame content
  // will not have all the blocks and page elements normally rendered by
  // Drupal.
  $response = new HtmlResponse('', HtmlResponse::HTTP_OK, [
    'Content-Type' => 'text/html; charset=UTF-8',
  ]);
  $response
    ->addCacheableDependency(Url::createFromRequest($request));
  try {
    $resource_url = $this->urlResolver
      ->getResourceUrl($url, $max_width, $max_height);
    $resource = $this->resourceFetcher
      ->fetchResource($resource_url);
    $placeholder_token = Crypt::randomBytesBase64(55);

    // Render the content in a new render context so that the cacheability
    // metadata of the rendered HTML will be captured correctly.
    $element = [
      '#theme' => 'media_oembed_iframe',
      '#resource' => $resource,
      // Even though the resource HTML is untrusted, IFrameMarkup::create()
      // will create a trusted string. The only reason this is okay is
      // because we are serving it in an iframe, which will mitigate the
      // potential dangers of displaying third-party markup.
      '#media' => IFrameMarkup::create($resource
        ->getHtml()),
      '#cache' => [
        // Add the 'rendered' cache tag as this response is not processed by
        // \Drupal\Core\Render\MainContent\HtmlRenderer::renderResponse().
        'tags' => [
          'rendered',
        ],
      ],
      '#attached' => [
        'html_response_attachment_placeholders' => [
          'styles' => '<css-placeholder token="' . $placeholder_token . '">',
        ],
        'library' => [
          'media/oembed.frame',
        ],
      ],
      '#placeholder_token' => $placeholder_token,
    ];
    $context = new RenderContext();
    $content = $this->renderer
      ->executeInRenderContext($context, function () use ($element) {
      return $this->renderer
        ->render($element);
    });
    $response
      ->setContent($content)
      ->setAttachments($element['#attached'])
      ->addCacheableDependency($resource)
      ->addCacheableDependency(CacheableMetadata::createFromRenderArray($element));

    // Modules and themes implementing hook_media_oembed_iframe_preprocess()
    // can add additional #cache and #attachments to a render array. If this
    // occurs, the render context won't be empty, and we need to ensure the
    // added metadata is bubbled up to the response.
    // @see \Drupal\Core\Theme\ThemeManager::render()
    if (!$context
      ->isEmpty()) {
      $bubbleable_metadata = $context
        ->pop();
      assert($bubbleable_metadata instanceof BubbleableMetadata);
      $response
        ->addCacheableDependency($bubbleable_metadata);
      $response
        ->addAttachments($bubbleable_metadata
        ->getAttachments());
    }
  } catch (ResourceException $e) {

    // Prevent the response from being cached.
    $response
      ->setMaxAge(0);

    // The oEmbed system makes heavy use of exception wrapping, so log the
    // entire exception chain to help with troubleshooting.
    do {

      // @todo Log additional information from ResourceException, to help with
      // debugging, in https://www.drupal.org/project/drupal/issues/2972846.
      $this->logger
        ->error($e
        ->getMessage());
      $e = $e
        ->getPrevious();
    } while ($e);
  }
  return $response;
}