Commit aa091959 authored by xjm's avatar xjm

Issue #3002608 by bnjmnm, tedbow, Wim Leers, samuel.mortenson, tim.plunkett,...

Issue #3002608 by bnjmnm, tedbow, Wim Leers, samuel.mortenson, tim.plunkett, xjm, lauriii, jibran, phenaproxima, effulgentsia: Remove contextual links not related to layout administration inside layout builder blocks
parent 1bfffca7
......@@ -147,6 +147,27 @@ function layout_builder_entity_view_alter(array &$build, EntityInterface $entity
}
}
}
$route_name = \Drupal::routeMatch()->getRouteName();
// If the entity is displayed within a Layout Builder block and the current
// route is in the Layout Builder UI, then remove all contextual link
// placeholders.
if ($display instanceof LayoutBuilderEntityViewDisplay && strpos($route_name, 'layout_builder.') === 0) {
unset($build['#contextual_links']);
}
}
/**
* Implements hook_entity_build_defaults_alter().
*/
function layout_builder_entity_build_defaults_alter(array &$build, EntityInterface $entity, $view_mode) {
// Contextual links are removed for entities viewed in Layout Builder's UI.
// The route.name.is_layout_builder_ui cache context accounts for this
// difference.
// @see layout_builder_entity_view_alter()
// @see \Drupal\layout_builder\Cache\LayoutBuilderUiCacheContext
$build['#cache']['contexts'][] = 'route.name.is_layout_builder_ui';
}
/**
......
......@@ -29,6 +29,11 @@ services:
arguments: ['@current_route_match']
tags:
- { name: cache.context}
cache_context.route.name.is_layout_builder_ui:
class: Drupal\layout_builder\Cache\LayoutBuilderUiCacheContext
arguments: ['@current_route_match']
tags:
- { name: cache.context }
layout_builder.sample_entity_generator:
class: Drupal\layout_builder\Entity\LayoutBuilderSampleEntityGenerator
arguments: ['@tempstore.shared', '@entity_type.manager']
......
<?php
namespace Drupal\layout_builder\Cache;
use Drupal\Core\Cache\Context\RouteNameCacheContext;
/**
* Determines if an entity is being viewed in the Layout Builder UI.
*
* Cache context ID: 'route.name.is_layout_builder_ui'.
*/
class LayoutBuilderUiCacheContext extends RouteNameCacheContext {
/**
* {@inheritdoc}
*/
public static function getLabel() {
return t('Layout Builder user interface');
}
/**
* {@inheritdoc}
*/
public function getContext() {
return 'is_layout_builder_ui.' . (int) (strpos($this->routeMatch->getRouteName(), 'layout_builder.') !== 0);
}
}
......@@ -11,6 +11,7 @@
use Drupal\layout_builder\Access\LayoutPreviewAccessAllowed;
use Drupal\layout_builder\Event\SectionComponentBuildRenderArrayEvent;
use Drupal\layout_builder\LayoutBuilderEvents;
use Drupal\views\Plugin\Block\ViewsBlock;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
/**
......@@ -90,6 +91,15 @@ public function onBuildRender(SectionComponentBuildRenderArrayEvent $event) {
if ($access->isAllowed()) {
$event->addCacheableDependency($block);
// @todo Revisit after https://www.drupal.org/node/3027653, as this will
// provide a better way to remove contextual links from Views blocks.
// Currently, doing this requires setting
// \Drupal\views\ViewExecutable::$showAdminLinks() to false before the
// Views block is built.
if ($block instanceof ViewsBlock && $event->inPreview()) {
$block->getViewExecutable()->setShowAdminLinks(FALSE);
}
$content = $block->build();
$is_content_empty = Element::isEmpty($content);
$is_placeholder_ready = $event->inPreview() && $block instanceof PreviewFallbackInterface;
......
langcode: en
status: true
dependencies:
config:
- core.entity_view_mode.node.teaser
module:
- node
- user
......@@ -175,3 +177,30 @@ display:
- 'user.node_grants:view'
- user.permissions
tags: { }
block_2:
display_plugin: block
id: block_2
display_title: 'Teaser block'
position: 2
display_options:
display_extenders: { }
display_description: ''
style:
type: default
options: { }
defaults:
style: false
row: false
row:
type: 'entity:node'
options:
relationship: none
view_mode: teaser
cache_metadata:
max-age: -1
contexts:
- 'languages:language_content'
- 'languages:language_interface'
- 'user.node_grants:view'
- user.permissions
tags: { }
<?php
namespace Drupal\Tests\layout_builder\FunctionalJavascript;
use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
use Drupal\Tests\system\Functional\Cache\AssertPageCacheContextsAndTagsTrait;
/**
* Test contextual links compatibility with the Layout Builder.
*
* @group layout_builder
*/
class ContextualLinksTest extends WebDriverTestBase {
use AssertPageCacheContextsAndTagsTrait;
/**
* {@inheritdoc}
*/
protected static $modules = [
'views',
'views_ui',
'layout_builder',
'layout_builder_views_test',
'layout_test',
'layout_builder_test_css_transitions',
'block',
'node',
'contextual',
];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$user = $this->drupalCreateUser([
'configure any layout',
'administer node display',
'administer node fields',
'access contextual links',
'administer nodes',
'bypass node access',
'administer views',
]);
$user->save();
$this->drupalLogin($user);
$this->createContentType(['type' => 'bundle_with_section_field']);
$this->createNode([
'type' => 'bundle_with_section_field',
'body' => [
[
'value' => 'The node body',
],
],
]);
}
/**
* Tests that the contextual links inside Layout Builder are removed.
*/
public function testContextualLinks() {
$page = $this->getSession()->getPage();
$field_ui_prefix = 'admin/structure/types/manage/bundle_with_section_field';
// Enable Layout Builder and overrides.
$this->drupalPostForm(
"$field_ui_prefix/display/default",
['layout[enabled]' => TRUE, 'layout[allow_custom]' => TRUE],
'Save'
);
$this->drupalGet('node/1/layout');
// Add a block that includes an entity contextual link.
$this->addBlock('Test Block View: Teaser block');
// Add a block that includes a views contextual link.
$this->addBlock('Recent content');
// Ensure the contextual links are correct before the layout is saved.
$this->assertCorrectContextualLinksInUi();
// Ensure the contextual links are correct when the Layout Builder is loaded
// after being saved.
$page->hasButton('Save layout');
$page->pressButton('Save layout');
$this->drupalGet('node/1/layout');
$this->assertCorrectContextualLinksInUi();
$this->drupalGet('node/1');
$this->assertCorrectContextualLinksInNode();
}
/**
* Adds block to the layout via Layout Builder's UI.
*
* @param string $block_name
* The block name as it appears in the Add Block form.
*/
protected function addBlock($block_name) {
$assert_session = $this->assertSession();
$page = $this->getSession()->getPage();
$assert_session->linkExists('Add Block');
$page->clickLink('Add Block');
$assert_session->assertWaitOnAjaxRequest();
$this->assertNotEmpty($assert_session->waitForElementVisible('css', "#drupal-off-canvas a:contains('$block_name')"));
$page->clickLink($block_name);
$this->assertNotEmpty($assert_session->waitForElementVisible('css', '[data-drupal-selector=\'edit-actions-submit\']'));
$page->pressButton('Add Block');
$this->waitForNoElement('#drupal-off-canvas');
$assert_session->assertWaitOnAjaxRequest();
}
/**
* Asserts the contextual links are correct in Layout Builder UI.
*/
protected function assertCorrectContextualLinksInUi() {
$assert_session = $this->assertSession();
$page = $this->getSession()->getPage();
$this->assertNotEmpty($assert_session->waitForElementVisible('css', '.block-views-blocktest-block-view-block-2'));
$layout_builder_specific_contextual_links = $page->findAll('css', '[data-contextual-id*=\'layout_builder_block:\']');
$this->assertNotEmpty($layout_builder_specific_contextual_links);
// Confirms Layout Builder contextual links are the only contextual links
// inside the Layout Builder UI.
$this->assertSameSize($layout_builder_specific_contextual_links, $page->findAll('css', '#layout-builder [data-contextual-id]'));
}
/**
* Asserts the contextual links are correct on the canonical entity route.
*/
protected function assertCorrectContextualLinksInNode() {
$assert_session = $this->assertSession();
$page = $this->getSession()->getPage();
$this->assertNotEmpty($assert_session->waitForElementVisible('css', '[data-contextual-id]'));
// Ensure that no Layout Builder contextual links are visible on node view.
$this->assertEmpty($page->findAll('css', '[data-contextual-id*=\'layout_builder_block:\']'));
// Ensure that the contextual links that are hidden in Layout Builder UI
// are visible on node view.
$this->assertNotEmpty($page->findAll('css', '.layout-content [data-contextual-id]'));
}
/**
* Waits for an element to be removed from the page.
*
* @param string $selector
* CSS selector.
* @param int $timeout
* (optional) Timeout in milliseconds, defaults to 10000.
*
* @todo Remove in https://www.drupal.org/node/2892440.
*/
protected function waitForNoElement($selector, $timeout = 10000) {
$condition = "(typeof jQuery !== 'undefined' && jQuery('$selector').length === 0)";
$this->assertJsCondition($condition, $timeout);
}
}
......@@ -213,4 +213,18 @@ protected function addContextualLinks(&$output, $block_type = 'block') {
}
}
/**
* Gets the view executable.
*
* @return \Drupal\views\ViewExecutable
* The view executable.
*
* @todo revisit after https://www.drupal.org/node/3027653. This method was
* added in https://www.drupal.org/node/3002608, but should not be
* necessary once block plugins can determine if they are being previewed.
*/
public function getViewExecutable() {
return $this->view;
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment