diff --git a/core/modules/outside_in/js/outside_in.es6.js b/core/modules/outside_in/js/outside_in.es6.js index 76bb872749..c73e535035 100644 --- a/core/modules/outside_in/js/outside_in.es6.js +++ b/core/modules/outside_in/js/outside_in.es6.js @@ -9,6 +9,8 @@ const itemsToToggleSelector = '[data-off-canvas-main-canvas], #toolbar-bar, [data-drupal-outsidein="editable"] a, [data-drupal-outsidein="editable"] button'; const contextualItemsSelector = '[data-contextual-id] a, [data-contextual-id] button'; const quickEditItemSelector = '[data-quickedit-entity-id]'; + const excludedLinksSelector = '[data-outside-in-exclude] [data-outside-in-edit]'; + /** * Reacts to contextual links being added. @@ -21,6 +23,10 @@ * @listens event:drupalContextualLinkAdded */ $(document).on('drupalContextualLinkAdded', (event, data) => { + // Remove contextual links that should be excluded. + // @see outside_in_block_view_alter(). + $(excludedLinksSelector).remove(); + // Bind Ajax behaviors to all items showing the class. // @todo Fix contextual links to work with use-ajax links in // https://www.drupal.org/node/2764931. diff --git a/core/modules/outside_in/js/outside_in.js b/core/modules/outside_in/js/outside_in.js index 95cf90e225..a27c40c7ce 100644 --- a/core/modules/outside_in/js/outside_in.js +++ b/core/modules/outside_in/js/outside_in.js @@ -11,8 +11,11 @@ var itemsToToggleSelector = '[data-off-canvas-main-canvas], #toolbar-bar, [data-drupal-outsidein="editable"] a, [data-drupal-outsidein="editable"] button'; var contextualItemsSelector = '[data-contextual-id] a, [data-contextual-id] button'; var quickEditItemSelector = '[data-quickedit-entity-id]'; + var excludedLinksSelector = '[data-outside-in-exclude] [data-outside-in-edit]'; $(document).on('drupalContextualLinkAdded', function (event, data) { + $(excludedLinksSelector).remove(); + Drupal.attachBehaviors(data.$el[0]); data.$el.find(blockConfigureSelector).on('click.outsidein', function () { diff --git a/core/modules/outside_in/outside_in.api.php b/core/modules/outside_in/outside_in.api.php new file mode 100644 index 0000000000..e24ce8ce75 --- /dev/null +++ b/core/modules/outside_in/outside_in.api.php @@ -0,0 +1,18 @@ + [], ]; + + if (_outside_in_block_is_excluded($build['#plugin_id']) && isset($build['#contextual_links']['block'])) { + // If this block should not be editable set an attribute which will be used + // to remove the link on the client side. + // The individual links are not available here to remove. + // @see outside_in.js + $build['#attributes']['data-outside-in-exclude'] = TRUE; + } + +} + +/** + * Determines if block plugin should be excluded. + * + * @param string $plugin_id + * The plugin id for the block. + * + * @return bool + * True if the blocks with this plugin id should be excluded. + */ +function _outside_in_block_is_excluded($plugin_id) { + $definition = \Drupal::service('plugin.manager.block')->getDefinition($plugin_id); + return !empty($definition['outside_in_exclude']); } /** @@ -101,7 +124,7 @@ function outside_in_entity_type_build(array &$entity_types) { */ function outside_in_preprocess_block(&$variables) { // The main system block does not contain the block contextual links. - if ($variables['plugin_id'] !== 'system_main_block') { + if (!_outside_in_block_is_excluded($variables['plugin_id'])) { // Add class and attributes to all blocks to allow Javascript to target. $variables['attributes']['class'][] = 'outside-in-editable'; $variables['attributes']['data-drupal-outsidein'] = 'editable'; @@ -145,12 +168,17 @@ function outside_in_block_alter(&$definitions) { $definitions['system_branding_block']['forms']['off_canvas'] = SystemBrandingOffCanvasForm::class; } - // Since menu blocks use derivatives, check the definition ID instead of - // relying on the plugin ID. + $exluded_blocks = ['page_title_block', 'system_main_block']; foreach ($definitions as &$definition) { + // Since menu blocks use derivatives, check the definition ID instead of + // relying on the plugin ID. if ($definition['id'] === 'system_menu_block') { $definition['forms']['off_canvas'] = SystemMenuOffCanvasForm::class; } + + if (in_array($definition['id'], $exluded_blocks)) { + $definition['outside_in_exclude'] = TRUE; + } } } diff --git a/core/modules/outside_in/tests/modules/outside_in_test/outside_in_test.info.yml b/core/modules/outside_in/tests/modules/outside_in_test/outside_in_test.info.yml new file mode 100644 index 0000000000..590f559329 --- /dev/null +++ b/core/modules/outside_in/tests/modules/outside_in_test/outside_in_test.info.yml @@ -0,0 +1,9 @@ +name: 'Settings Tray Test' +type: module +description: 'Provides Settings Tray test functionality.' +package: Testing +version: VERSION +core: 8.x +dependencies: + - block + - outside_in diff --git a/core/modules/outside_in/tests/modules/outside_in_test/src/Plugin/Block/ExcludedTestBlock.php b/core/modules/outside_in/tests/modules/outside_in_test/src/Plugin/Block/ExcludedTestBlock.php new file mode 100644 index 0000000000..08ff81c70e --- /dev/null +++ b/core/modules/outside_in/tests/modules/outside_in_test/src/Plugin/Block/ExcludedTestBlock.php @@ -0,0 +1,32 @@ + FALSE]; + } + + /** + * {@inheritdoc} + */ + public function build() { + return ['#markup' => '' . $this->t('Why did you exclude me?') . '']; + } + +} diff --git a/core/modules/outside_in/tests/src/FunctionalJavascript/OutsideInBlockFormTest.php b/core/modules/outside_in/tests/src/FunctionalJavascript/OutsideInBlockFormTest.php index 313e12dc01..8b5b4d94d7 100644 --- a/core/modules/outside_in/tests/src/FunctionalJavascript/OutsideInBlockFormTest.php +++ b/core/modules/outside_in/tests/src/FunctionalJavascript/OutsideInBlockFormTest.php @@ -35,6 +35,7 @@ class OutsideInBlockFormTest extends OutsideInJavascriptTestBase { // Add test module to override CSS pointer-events properties because they // cause test failures. 'outside_in_test_css', + 'outside_in_test', ]; /** @@ -498,4 +499,33 @@ protected function isLabelInputVisible() { return $this->getSession()->getPage()->find('css', static::LABEL_INPUT_SELECTOR)->isVisible(); } + /** + * Tests that title block does not have a outside_in contextual link. + */ + public function testBlockExcluded() { + $web_assert = $this->assertSession(); + + $non_excluded_block = $this->placeBlock('system_powered_by_block'); + $excluded_block_plugin_ids = ['page_title_block', 'system_main_block', 'outside_in_test_excluded']; + $block_selectors = []; + // Place blocks that should be excluded. + foreach ($excluded_block_plugin_ids as $excluded_block_plugin_id) { + // The block HTML 'id' attribute will be "block-[block_id]". + $block_selectors[] = $this->getBlockSelector($this->placeBlock($excluded_block_plugin_id)); + } + $this->drupalGet('user'); + // Assert that block has been marked as "editable" and contextual that + // should exist does. + $web_assert->elementExists('css', $this->getBlockSelector($non_excluded_block) . "[data-drupal-outsidein=\"editable\"]"); + // Check each block that should be excluded. + foreach ($block_selectors as $block_selector) { + // Assert block itself exists. + $web_assert->elementExists('css', $block_selector); + // Assert that exclude block has not been marked as "editable". + $web_assert->elementNotExists('css', "{$block_selector}[data-drupal-outsidein=\"editable\"]"); + // Assert that contextual that not should exist does not. + $web_assert->elementNotExists('css', "$block_selector [data-outside-in-edit]"); + } + } + }