diff --git a/core/modules/settings_tray/settings_tray.module b/core/modules/settings_tray/settings_tray.module index 48b08caaab..3d525ab310 100644 --- a/core/modules/settings_tray/settings_tray.module +++ b/core/modules/settings_tray/settings_tray.module @@ -11,6 +11,8 @@ use Drupal\settings_tray\Block\BlockEntityOffCanvasForm; use Drupal\settings_tray\Form\SystemBrandingOffCanvasForm; use Drupal\settings_tray\Form\SystemMenuOffCanvasForm; +use Drupal\block\BlockInterface; +use Drupal\block\Entity\Block; /** * Implements hook_help(). @@ -54,6 +56,23 @@ function settings_tray_contextual_links_view_alter(&$element, $items) { } } +/** + * Checks if a block has overrides. + * + * @param \Drupal\block\BlockInterface $block + * The block to check for overrides. + * + * @return bool + * TRUE if the block has overrides otherwise FALSE. + * + * @internal + */ +function _settings_tray_has_block_overrides(BlockInterface $block) { + // @todo Replace the following with $block->hasOverrides() in https://www.drupal.org/project/drupal/issues/2910353 + // and remove the need for this function. + return \Drupal::config($block->getEntityType()->getConfigPrefix() . '.' . $block->id())->hasOverrides(); +} + /** * Implements hook_block_view_alter(). */ @@ -93,10 +112,14 @@ function settings_tray_preprocess_block(&$variables) { $block_plugin_manager = \Drupal::service('plugin.manager.block'); /** @var \Drupal\Core\Block\BlockPluginInterface $block_plugin */ $block_plugin = $block_plugin_manager->createInstance($variables['plugin_id']); - if ($access_checker->accessBlockPlugin($block_plugin)->isAllowed()) { - // Add class and attributes to all blocks to allow Javascript to target. - $variables['attributes']['class'][] = 'settings-tray-editable'; - $variables['attributes']['data-drupal-settingstray'] = 'editable'; + if (isset($variables['elements']['#contextual_links']['block']['route_parameters']['block'])) { + $block_id = $variables['elements']['#contextual_links']['block']['route_parameters']['block']; + $block = Block::load($block_id); + if ($access_checker->accessBlockPlugin($block_plugin)->isAllowed() && !_settings_tray_has_block_overrides($block)) { + // Add class and attributes to all blocks to allow Javascript to target. + $variables['attributes']['class'][] = 'settings-tray-editable'; + $variables['attributes']['data-drupal-settingstray'] = 'editable'; + } } } @@ -196,3 +219,15 @@ function settings_tray_css_alter(&$css, AttachedAssetsInterface $assets) { $css[$path]['group'] = 200; } } + +/** + * Implements hook_contextual_links_alter(). + */ +function settings_tray_contextual_links_alter(array &$links, $group, array $route_parameters) { + if (isset($links['settings_tray.block_configure']['route_parameters']['block'])) { + $block = Block::load($links['settings_tray.block_configure']['route_parameters']['block']); + if (_settings_tray_has_block_overrides($block)) { + unset($links['settings_tray.block_configure']); + } + } +} diff --git a/core/modules/settings_tray/src/Form/SystemBrandingOffCanvasForm.php b/core/modules/settings_tray/src/Form/SystemBrandingOffCanvasForm.php index 0b4290e2fa..94c8052648 100644 --- a/core/modules/settings_tray/src/Form/SystemBrandingOffCanvasForm.php +++ b/core/modules/settings_tray/src/Form/SystemBrandingOffCanvasForm.php @@ -2,6 +2,7 @@ namespace Drupal\settings_tray\Form; +use Drupal\Core\Access\AccessResult; use Drupal\Core\Config\ConfigFactoryInterface; use Drupal\Core\DependencyInjection\ContainerInjectionInterface; use Drupal\Core\Form\FormStateInterface; @@ -63,10 +64,13 @@ public function buildConfigurationForm(array $form, FormStateInterface $form_sta unset($form['block_branding']['use_site_name']['#description'], $form['block_branding']['use_site_slogan']['#description']); $site_config = $this->configFactory->getEditable('system.site'); + // Load the immutable config to load the overrides. + $site_config_immutable = $this->configFactory->get('system.site'); $form['site_information'] = [ '#type' => 'details', '#title' => t('Site details'), '#open' => TRUE, + '#access' => AccessResult::allowedIf(!$site_config_immutable->hasOverrides('name') && !$site_config_immutable->hasOverrides('slogan')), ]; $form['site_information']['site_name'] = [ '#type' => 'textfield', @@ -95,11 +99,15 @@ public function validateConfigurationForm(array &$form, FormStateInterface $form * {@inheritdoc} */ public function submitConfigurationForm(array &$form, FormStateInterface $form_state) { - $site_info = $form_state->getValue('site_information'); - $this->configFactory->getEditable('system.site') - ->set('name', $site_info['site_name']) - ->set('slogan', $site_info['site_slogan']) - ->save(); + $site_config = $this->configFactory->get('system.site'); + if (AccessResult::allowedIf(!$site_config->hasOverrides('name') && !$site_config->hasOverrides('slogan'))->isAllowed()) { + $site_info = $form_state->getValue('site_information'); + $this->configFactory->getEditable('system.site') + ->set('name', $site_info['site_name']) + ->set('slogan', $site_info['site_slogan']) + ->save(); + } + $this->plugin->submitConfigurationForm($form, $form_state); } diff --git a/core/modules/settings_tray/src/Form/SystemMenuOffCanvasForm.php b/core/modules/settings_tray/src/Form/SystemMenuOffCanvasForm.php index 15d19a87f9..42f35b47bb 100644 --- a/core/modules/settings_tray/src/Form/SystemMenuOffCanvasForm.php +++ b/core/modules/settings_tray/src/Form/SystemMenuOffCanvasForm.php @@ -3,6 +3,7 @@ namespace Drupal\settings_tray\Form; use Drupal\Component\Plugin\PluginInspectionInterface; +use Drupal\Core\Access\AccessResult; use Drupal\Core\DependencyInjection\ContainerInjectionInterface; use Drupal\Core\Entity\EntityStorageInterface; use Drupal\Core\Entity\EntityTypeManagerInterface; @@ -87,6 +88,7 @@ public function buildConfigurationForm(array $form, FormStateInterface $form_sta '#type' => 'details', '#title' => $this->t('Edit menu %label', ['%label' => $this->menu->label()]), '#open' => TRUE, + '#access' => AccessResult::allowedIf(!$this->hasMenuOverrides()), ]; $form['entity_form'] += $this->getEntityForm($this->menu)->buildForm([], $form_state); @@ -115,7 +117,9 @@ public function buildConfigurationForm(array $form, FormStateInterface $form_sta */ public function validateConfigurationForm(array &$form, FormStateInterface $form_state) { $this->plugin->validateConfigurationForm($form, $form_state); - $this->getEntityForm($this->menu)->validateForm($form, $form_state); + if ($this->hasMenuOverrides()) { + $this->getEntityForm($this->menu)->validateForm($form, $form_state); + } } /** @@ -123,8 +127,10 @@ public function validateConfigurationForm(array &$form, FormStateInterface $form */ public function submitConfigurationForm(array &$form, FormStateInterface $form_state) { $this->plugin->submitConfigurationForm($form, $form_state); - $this->getEntityForm($this->menu)->submitForm($form, $form_state); - $this->menu->save(); + if ($this->hasMenuOverrides()) { + $this->getEntityForm($this->menu)->submitForm($form, $form_state); + $this->menu->save(); + } } /** @@ -147,7 +153,15 @@ protected function getEntityForm(MenuInterface $menu) { */ public function setPlugin(PluginInspectionInterface $plugin) { $this->plugin = $plugin; - $this->menu = $this->menuStorage->load($this->plugin->getDerivativeId()); + $this->menu = $this->menuStorage->loadOverrideFree($this->plugin->getDerivativeId()); + } + + /** + * @return bool + */ + protected function hasMenuOverrides() { + return \Drupal::config($this->menu->getEntityType() + ->getConfigPrefix() . '.' . $this->menu->id())->hasOverrides(); } } diff --git a/core/modules/settings_tray/tests/modules/settings_tray_override_test/settings_tray_override_test.info.yml b/core/modules/settings_tray/tests/modules/settings_tray_override_test/settings_tray_override_test.info.yml new file mode 100644 index 0000000000..89f9732feb --- /dev/null +++ b/core/modules/settings_tray/tests/modules/settings_tray_override_test/settings_tray_override_test.info.yml @@ -0,0 +1,7 @@ +name: 'Configuration override test for Settings Tray' +type: module +package: Testing +version: VERSION +core: 8.x +dependencies: + - settings_tray diff --git a/core/modules/settings_tray/tests/modules/settings_tray_override_test/settings_tray_override_test.services.yml b/core/modules/settings_tray/tests/modules/settings_tray_override_test/settings_tray_override_test.services.yml new file mode 100644 index 0000000000..6e5cb75731 --- /dev/null +++ b/core/modules/settings_tray/tests/modules/settings_tray_override_test/settings_tray_override_test.services.yml @@ -0,0 +1,5 @@ +services: + settings_tray_override_test.overrider: + class: Drupal\settings_tray_override_test\ConfigOverrider + tags: + - { name: config.factory.override } diff --git a/core/modules/settings_tray/tests/modules/settings_tray_override_test/src/ConfigOverrider.php b/core/modules/settings_tray/tests/modules/settings_tray_override_test/src/ConfigOverrider.php new file mode 100644 index 0000000000..0dcd9e608c --- /dev/null +++ b/core/modules/settings_tray/tests/modules/settings_tray_override_test/src/ConfigOverrider.php @@ -0,0 +1,58 @@ + ['settings' => ['label' => 'Now this will be the label.']]]; + } + if (in_array('system.site', $names)) { + if (\Drupal::state()->get('settings_tray_override_test.site_name')) { + $overrides = $overrides + ['system.site' => ['name' => 'Llama Fan Club']]; + } + } + if (in_array('system.menu.main', $names)) { + if (\Drupal::state()->get('settings_tray_override_test.menu')) { + $overrides = $overrides + ['system.menu.main' => ['label' => 'Labely label']]; + } + } + return $overrides; + } + + /** + * {@inheritdoc} + */ + public function getCacheSuffix() { + return 'ConfigOverrider'; + } + + /** + * {@inheritdoc} + */ + public function createConfigObject($name, $collection = StorageInterface::DEFAULT_COLLECTION) { + return NULL; + } + + /** + * {@inheritdoc} + */ + public function getCacheableMetadata($name) { + return new CacheableMetadata(); + } + +} diff --git a/core/modules/settings_tray/tests/src/FunctionalJavascript/SettingsTrayBlockFormTest.php b/core/modules/settings_tray/tests/src/FunctionalJavascript/SettingsTrayBlockFormTest.php index fcfecde400..626fcad769 100644 --- a/core/modules/settings_tray/tests/src/FunctionalJavascript/SettingsTrayBlockFormTest.php +++ b/core/modules/settings_tray/tests/src/FunctionalJavascript/SettingsTrayBlockFormTest.php @@ -5,6 +5,7 @@ use Drupal\block\Entity\Block; use Drupal\block_content\Entity\BlockContent; use Drupal\block_content\Entity\BlockContentType; +use Drupal\menu_link_content\Entity\MenuLinkContent; use Drupal\settings_tray_test\Plugin\Block\SettingsTrayFormAnnotationIsClassBlock; use Drupal\settings_tray_test\Plugin\Block\SettingsTrayFormAnnotationNoneBlock; use Drupal\Tests\contextual\FunctionalJavascript\ContextualLinkClickTrait; @@ -43,6 +44,9 @@ class SettingsTrayBlockFormTest extends OffCanvasTestBase { // cause test failures. 'settings_tray_test_css', 'settings_tray_test', + 'settings_tray_override_test', + 'menu_ui', + 'menu_link_content', ]; /** @@ -75,7 +79,7 @@ public function testBlocks($theme, $block_plugin, $new_page_text, $element_selec $page = $this->getSession()->getPage(); $this->enableTheme($theme); $block = $this->placeBlock($block_plugin); - $block_selector = str_replace('_', '-', $this->getBlockSelector($block)); + $block_selector = $this->getBlockSelector($block); $block_id = $block->id(); $this->drupalGet('user'); @@ -267,8 +271,10 @@ protected function assertOffCanvasBlockFormIsValid() { * @param string $contextual_link_container * The element that contains the contextual links. If none provide the * $block_selector will be used. + * @param bool $has_confirm_form + * Determines if the block form should be confirmed. */ - protected function openBlockForm($block_selector, $contextual_link_container = '') { + protected function openBlockForm($block_selector, $contextual_link_container = '', $has_confirm_form = TRUE) { if (!$contextual_link_container) { $contextual_link_container = $block_selector; } @@ -283,7 +289,9 @@ protected function openBlockForm($block_selector, $contextual_link_container = ' $this->assertSession()->assertWaitOnAjaxRequest(); $this->click($block_selector); $this->waitForOffCanvasToOpen(); - $this->assertOffCanvasBlockFormIsValid(); + if ($has_confirm_form) { + $this->assertOffCanvasBlockFormIsValid(); + } } /** @@ -321,7 +329,7 @@ public function testQuickEditLinks() { $this->enableTheme($theme); $block = $this->placeBlock($block_plugin); - $block_selector = str_replace('_', '-', $this->getBlockSelector($block)); + $block_selector = $this->getBlockSelector($block); // Load the same page twice. foreach ([1, 2] as $page_load_times) { $this->drupalGet('node/' . $node->id()); @@ -531,7 +539,7 @@ public function testCustomBlockLinks() { * The CSS selector. */ public function getBlockSelector(Block $block) { - return '#block-' . $block->id(); + return '#block-' . str_replace('_', '-', $block->id()); } /** @@ -577,4 +585,107 @@ protected function getTestThemes() { }); } + /** + * Tests that blocks with configuration overrides are disabled. + */ + public function testOverriddenConfigRemoved() { + $web_assert = $this->assertSession(); + $page = $this->getSession()->getPage(); + $overridden_block = $this->placeBlock('system_powered_by_block', [ + 'id' => 'overridden_block', + 'label_display' => 1, + 'label' => 'This will be overridden.', + ]); + $this->drupalGet('user'); + $this->assertOverriddenBlockDisabled($overridden_block, 'Now this will be the label.'); + + // Test a non-overridden block does show the form in the off-canvas dialog. + $block = $this->placeBlock('system_powered_by_block', [ + 'label_display' => 1, + 'label' => 'Labely label', + ]); + $this->drupalGet('user'); + $block_selector = $this->getBlockSelector($block); + // Confirm the block is marked as Settings Tray editable. + $this->assertEquals('editable', $page->find('css', $block_selector)->getAttribute('data-drupal-settingstray')); + // Confirm the label is not overridden. + $web_assert->elementContains('css', $block_selector, 'Labely label'); + $this->enableEditMode(); + $this->openBlockForm($this->getBlockSelector($block)); + + // Confirm the branding block does include 'site_information' section when + // the site name is not overridden. + $branding_block = $this->placeBlock('system_branding_block'); + $this->drupalGet('user'); + $this->openBlockForm($this->getBlockSelector($branding_block)); + $web_assert->fieldExists('settings[site_information][site_name]'); + // Confirm the branding block does not include 'site_information' section + // when the site name is overridden. + $this->container->get('state')->set('settings_tray_override_test.site_name', TRUE); + $this->drupalGet('user'); + $this->openBlockForm($this->getBlockSelector($branding_block)); + $web_assert->fieldNotExists('settings[site_information][site_name]'); + $page->pressButton('Save Site branding'); + $this->assertElementVisibleAfterWait('css', 'div:contains(The block configuration has been saved)'); + $web_assert->assertWaitOnAjaxRequest(); + // Confirm we did not save changes to the configuration. + $this->assertEquals('Llama Fan Club', \Drupal::configFactory()->get('system.site')->get('name')); + $this->assertEquals('Drupal', \Drupal::configFactory()->getEditable('system.site')->get('name')); + + // Add a link or the menu will not render. + $menu_link_content = MenuLinkContent::create([ + 'title' => 'This is on the menu', + 'menu_name' => 'main', + 'link' => ['uri' => 'route:'], + ]); + $menu_link_content->save(); + // Confirm the menu block does include menu section when the menu is not + // overridden. + $menu_block = $this->placeBlock('system_menu_block:main'); + $web_assert->assertWaitOnAjaxRequest(); + $this->drupalGet('user'); + $this->openBlockForm($this->getBlockSelector($menu_block)); + $web_assert->elementExists('css', '#menu-overview'); + + // Confirm the menu block does not include menu section when the menu is + // overridden. + $this->container->get('state')->set('settings_tray_override_test.menu', TRUE); + $this->drupalGet('user'); + $this->openBlockForm($this->getBlockSelector($menu_block)); + $web_assert->elementNotExists('css', '#menu-overview'); + $page->pressButton('Save Main navigation'); + $this->assertElementVisibleAfterWait('css', 'div:contains(The block configuration has been saved)'); + $web_assert->assertWaitOnAjaxRequest(); + // Confirm we did not save changes to the configuration. + $this->assertEquals('Labely label', \Drupal::configFactory()->get('system.menu.main')->get('label')); + $this->assertEquals('Main navigation', \Drupal::configFactory()->getEditable('system.menu.main')->get('label')); + } + + /** + * Asserts that an overridden block has Settings Tray disabled. + * + * @param \Drupal\block\Entity\Block $overridden_block + * The overridden block. + * @param string $override_text + * The override text that should appear in the block. + */ + protected function assertOverriddenBlockDisabled(Block $overridden_block, $override_text) { + $web_assert = $this->assertSession(); + $page = $this->getSession()->getPage(); + $block_selector = $this->getBlockSelector($overridden_block); + $block_id = $overridden_block->id(); + // Confirm the block does not have a quick edit link. + $contextual_links = $page->findAll('css', "$block_selector .contextual-links li a"); + $this->assertNotEmpty($contextual_links); + foreach ($contextual_links as $link) { + $this->assertNotContains("/admin/structure/block/manage/$block_id/off-canvas", $link->getAttribute('href')); + } + // Confirm the block is not marked as Settings Tray editable. + $this->assertFalse($page->find('css', $block_selector) + ->hasAttribute('data-drupal-settingstray')); + + // Confirm the text is actually overridden. + $web_assert->elementContains('css', $this->getBlockSelector($overridden_block), $override_text); + } + }