Verified Commit 904e8cd9 authored by Théodore Biadala's avatar Théodore Biadala
Browse files

Issue #3514079 by grimreaper, larowlan, nicoloye, acbramley, nod_: Render...

Issue #3514079 by grimreaper, larowlan, nicoloye, acbramley, nod_: Render contextual links taking the current page theme into account
parent 8ac2dac7
Loading
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -9,6 +9,11 @@ drupal.contextual-links:
    theme:
      css/contextual.theme.css: {}
      css/contextual.icons.theme.css: {}
  drupalSettings:
    # These placeholder values will be set by
    # \Drupal\contextual\Element\ContextualLinksPlaceholder::preRenderPlaceholder().
    contextual:
      theme: null
  dependencies:
    - core/jquery
    - core/drupal
+6 −0
Original line number Diff line number Diff line
parameters:
  contextual.skip_procedural_hook_scan: true
services:
  theme.negotiator.contextual_links:
    class: Drupal\contextual\Theme\ContextualLinksNegotiator
    autowire: true
    tags:
      - { name: theme_negotiator }
+9 −3
Original line number Diff line number Diff line
@@ -156,7 +156,9 @@
      const uncachedIDs = [];
      const uncachedTokens = [];
      ids.forEach((contextualID) => {
        const html = storage.getItem(`Drupal.contextual.${contextualID.id}`);
        const html = storage.getItem(
          `Drupal.contextual.${contextualID.id}.${drupalSettings.contextual.theme}`,
        );
        if (html?.length) {
          // Initialize after the current execution cycle, to make the AJAX
          // request for retrieving the uncached contextual links as soon as
@@ -184,7 +186,8 @@
        uncachedIDs.forEach((id) => data.append('ids[]', id));
        uncachedTokens.forEach((id) => data.append('tokens[]', id));

        const req = new Request(Drupal.url('contextual/render'), {
        const controllerUrl = `contextual/render?theme=${drupalSettings.contextual.theme}`;
        const req = new Request(Drupal.url(controllerUrl), {
          method: 'POST',
          body: data,
        });
@@ -194,7 +197,10 @@
          .then((json) =>
            Object.entries(json).forEach(([contextualID, html]) => {
              // Store the metadata.
              storage.setItem(`Drupal.contextual.${contextualID}`, html);
              storage.setItem(
                `Drupal.contextual.${contextualID}.${drupalSettings.contextual.theme}`,
                html,
              );
              // If the rendered contextual links are empty, then the current
              // user does not have permission to access the associated links:
              // don't render anything.
+5 −0
Original line number Diff line number Diff line
@@ -3,6 +3,7 @@
namespace Drupal\contextual\Element;

use Drupal\Component\Utility\Crypt;
use Drupal\Core\Cache\CacheableMetadata;
use Drupal\Core\Site\Settings;
use Drupal\Core\Template\Attribute;
use Drupal\Core\Render\Attribute\RenderElement;
@@ -51,6 +52,10 @@ public static function preRenderPlaceholder(array $element) {
      'data-drupal-ajax-container' => '',
    ]);
    $element['#markup'] = new FormattableMarkup('<div@attributes></div>', ['@attributes' => $attribute]);
    $element['#attached']['drupalSettings']['contextual']['theme'] = \Drupal::service('theme.manager')->getActiveTheme()->getName();
    $cache = CacheableMetadata::createFromRenderArray($element);
    $cache->addCacheContexts(['theme']);
    $cache->applyTo($element);

    return $element;
  }
+38 −0
Original line number Diff line number Diff line
<?php

namespace Drupal\contextual\Theme;

use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\Theme\ThemeNegotiatorInterface;
use Drupal\Core\Extension\ThemeHandlerInterface;
use Symfony\Component\HttpFoundation\RequestStack;

/**
 * Set the theme according to the parameter passed to the controller.
 */
final class ContextualLinksNegotiator implements ThemeNegotiatorInterface {

  public function __construct(
    protected readonly RouteMatchInterface $route_match,
    protected readonly RequestStack $requestStack,
    protected readonly ThemeHandlerInterface $themeHandler,
    protected readonly ConfigFactoryInterface $configFactory,
  ) {}

  public function applies(RouteMatchInterface $route_match): bool {
    return $route_match->getRouteName() === 'contextual.render';
  }

  public function determineActiveTheme(RouteMatchInterface $route_match): string {
    $request = $this->requestStack->getCurrentRequest();
    $theme = $request?->query->get('theme', '') ?? '';

    if ($this->themeHandler->themeExists($theme)) {
      return $theme;
    }

    return $this->configFactory->get('system.theme')->get('default');
  }

}
Loading