4

When using the layout builder module, the contextual links module is a hard dependency. This works quite well in the context of the layout builder. But I don't want those links anywhere else, because elsewhere they are not helpful at best, and completly mess up my frontend theme at worst.

I've already written a custom theme negotiator to force my admin theme in layout building mode.

But I don't know how to remove all contextual links when I'm in frontend. My current ugly hack is

function wt_cms_entity_view_alter(array &$build, EntityInterface $entity, EntityViewDisplayInterface $display) {
  /**
   * Suppress the contextual edit pen on frontend themes, we only want it
   * for layout builder in admin theme
   */
  if (isset($build['#contextual_links'])) {
    $routeName = \Drupal::routeMatch()->getRouteName();
    if (!in_array($routeName, ['layout_builder.overrides.node.view', 'layout_builder.defaults.node.view'])) {
      unset($build['#contextual_links']);
    }
  }
}

and an additional rule in my frontend CSS

.contextual-toolbar-tab, [data-contextual-id] {
  display: none !important;
}

This does at least hide the contextual edit pen in frontend, but it does not really remove the contextual link menu. (There still is an empty placeholder div).

I've also tried @Shreya Shetty's suggestion

function mymodule_contextual_links_alter(array &$links, $group, array $route_parameters) {
  $routeName = \Drupal::routeMatch()->getRouteName();  //incorrect, this always returns "contextual.render"
  if (!in_array($routeName, ['layout_builder.overrides.node.view', 'layout_builder.defaults.node.view'])) {
    $links = [];
  }
}

In contextual alter I've got other problems: The current $routeName is always contextual.render and not the route of the host page, so I've no "entry point" to specifically alter non-layout bulder-pages. It also has the same problem as the hook_entity_view_alter workaround: The menu items are suppressed, but the empty contextual placeholder divs are still rendered.

Can anyone tell me how to reliably and cleanly remove the contextual links? I don't want them anywhere, except in layout building mode.

5
  • did you try this drupal.stackexchange.com/questions/227330/… Commented May 11, 2020 at 6:26
  • Thanks for this link, but it didn't really help. Even if I unset all $links in hook_contextual_links_alter, the empty, contextual placeholder divs are still rendered. This is the same issue I'm having with my current solution in hook_entity_view_alter
    – Hudri
    Commented May 13, 2020 at 13:18
  • Does this answer your question? Alter contextual links
    – leymannx
    Commented May 13, 2020 at 14:35
  • Don't check on the route name, check on the $group like in the other answer, or maybe the route parameters, or even the links.
    – leymannx
    Commented May 13, 2020 at 14:36
  • I cannot use the parameters from hook_contextual_links_alter for my use case. Those parameters only contain information on the contextual link itself, but nothing on the context. E.g. the custom block configure link has exacly the same parameters in layout builder mode and in normal frontend view mode.
    – Hudri
    Commented May 14, 2020 at 7:12

1 Answer 1

1

It was a caching issue. This is my final code to cleanly hide all contextual links except in layout building mode:

use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
use Drupal\Core\Entity\EntityInterface;

function mymodule_page_attachments(array &$page) {
  $routeName = \Drupal::routeMatch()->getRouteName();
  if (!in_array($routeName, ['layout_builder.overrides.node.view', 'layout_builder.defaults.node.view'])) {
    $page['#attached']['library'] = array_diff($page['#attached']['library'], ['contextual/drupal.contextual-links', 'contextual/drupal.contextual-toolbar']);
  }
}

function mymodule_entity_view_alter(array &$build, EntityInterface $entity, EntityViewDisplayInterface $display) {
  $routeName = \Drupal::routeMatch()->getRouteName();
  if (!in_array($routeName, ['layout_builder.overrides.node.view', 'layout_builder.defaults.node.view'])) {
    unset($build['#contextual_links']);
  }
}

For the caching issue:
The changes above most likely will (after cache clearing) remove all contextual link entries, but leave an empty placeholder like <div data-contextual-id="..." data-contextual-token="..."></div> in your rendered HTML. To get rid off those empty divs, go to your browsers Developer console > Application > Session storage and clear all keys starting with Drupal.contextual. After clearing them, the empty placeholder divs are gone too, no more CSS hacking needed.

1
  • As my custom module's name started with an a (which comes before c of the contextual_links module) I also had to ensure my custom module's implementations of the page_attachments and entity_view_alter hooks were picked up last by implementing hook_module_implements_alter as well.
    – leymannx
    Commented May 25, 2022 at 20:44

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.