diff --git a/core/modules/block/block.module b/core/modules/block/block.module index fa39d87..9ce0ac2 100644 --- a/core/modules/block/block.module +++ b/core/modules/block/block.module @@ -58,9 +58,6 @@ function block_theme() { 'block' => array( 'render element' => 'elements', ), - 'block_list' => array( - 'render element' => 'form', - ), ); } diff --git a/core/modules/block/block.routing.yml b/core/modules/block/block.routing.yml index 2f26a38..396bf0d 100644 --- a/core/modules/block/block.routing.yml +++ b/core/modules/block/block.routing.yml @@ -42,6 +42,15 @@ block.admin_display_theme: _access_theme: 'TRUE' _permission: 'administer blocks' +block.admin_library: + path: 'admin/structure/block/library/{theme}' + defaults: + _controller: '\Drupal\block\Controller\BlockLibraryController::listBlocks' + _title: 'Place block' + requirements: + _access_theme: 'TRUE' + _permission: 'administer blocks' + block.admin_add: path: '/admin/structure/block/add/{plugin_id}/{theme}' defaults: diff --git a/core/modules/block/css/block.admin.css b/core/modules/block/css/block.admin.css index d9f32a2..2b9eb45 100644 --- a/core/modules/block/css/block.admin.css +++ b/core/modules/block/css/block.admin.css @@ -1,3 +1,13 @@ +/* Block listing page */ +.region-title .button { + margin-left: 1em; /* LTR */ +} +[dir="rtl"] .region-title .button { + margin-left: 0; + margin-right: 1em; +} + +/* Block demo mode */ .block-region { background-color: #ff6; margin-top: 4px; @@ -22,87 +32,10 @@ a.block-demo-backlink:hover { text-decoration: underline; } -.layout-region { - box-sizing: border-box; -} -.block-list-secondary { - border: 1px solid #bfbfbf; - border-bottom-width: 0; -} -.block-list { - padding: 0 0.75em; - margin: 0; -} -.block-list li { - list-style: none; - padding: 0.1em 0; -} -.block-list a:before { - content: '+ '; -} -.block-list-secondary .form-type-search { - padding: 0 1em; -} +/* Configure block form - Block description */ .block-form .form-item-settings-admin-label label { display: inline; } .block-form .form-item-settings-admin-label label:after { content: ':'; } - -/* Wide screens */ -@media -screen and (min-width: 780px), -(orientation: landscape) and (min-device-height: 780px) { - - .block-list-primary { - float: left; /* LTR */ - width: 75%; - padding-right: 2em; - } - [dir="rtl"] .block-list-primary { - float: right; - padding-left: 2em; - padding-right: 0; - } - - .block-list-secondary { - float: right; /* LTR */ - width: 25%; - } - [dir="rtl"] .block-list-secondary { - float: left; - } - - /* @todo File an issue to add a standard class to all text-like inputs */ - .block-list-secondary .form-autocomplete, - .block-list-secondary .form-text, - .block-list-secondary .form-tel, - .block-list-secondary .form-email, - .block-list-secondary .form-url, - .block-list-secondary .form-search, - .block-list-secondary .form-number, - .block-list-secondary .form-color, - .block-list-secondary textarea { - box-sizing: border-box; - width: 100%; - max-width: 100%; - } -} - -/** - * The vertical toolbar mode gets triggered for narrow screens, which throws off - * the intent of media queries written for the viewport width. When the vertical - * toolbar is on, we need to suppress layout for the original media width + the - * toolbar width (240px). In this case, 240px + 780px. - */ -@media -screen and (max-width: 1020px) { - - .toolbar-vertical.toolbar-tray-open .block-list-primary, - .toolbar-vertical.toolbar-tray-open .block-list-secondary { - float: none; - width: auto; - padding-right: 0; - } -} diff --git a/core/modules/block/js/block.admin.js b/core/modules/block/js/block.admin.js index 23109f4..0485063 100644 --- a/core/modules/block/js/block.admin.js +++ b/core/modules/block/js/block.admin.js @@ -19,66 +19,38 @@ Drupal.behaviors.blockFilterByText = { attach: function (context, settings) { var $input = $('input.block-filter-text').once('block-filter-text'); - var $element = $($input.attr('data-element')); - var $blocks; - var $details; + var $table = $($input.attr('data-element')); + var $filter_rows; - /** - * Hides the `
` element for a category if it has no visible blocks. - * - * @param {number} index - * @param {HTMLElement} element - */ - function hideCategoryDetails(index, element) { - var $catDetails = $(element); - $catDetails.toggle($catDetails.find('li:visible').length > 0); - } - - /** - * Filters the block list. - * - * @param {jQuery.Event} e - */ function filterBlockList(e) { var query = $(e.target).val().toLowerCase(); /** * Shows or hides the block entry based on the query. * - * @param {number} index - * @param {HTMLElement} block + * @param {number} index The index of the block. + * @param {HTMLElement} label The label of the block. */ - function showBlockEntry(index, block) { - var $block = $(block); - var $sources = $block.find('.block-filter-text-source'); - var textMatch = $sources.text().toLowerCase().indexOf(query) !== -1; - $block.toggle(textMatch); + function toggleBlockEntry(index, label) { + var $label = $(label); + var $row = $label.parent().parent(); + var textMatch = $label.text().toLowerCase().indexOf(query) !== -1; + $row.toggle(textMatch); } // Filter if the length of the query is at least 2 characters. if (query.length >= 2) { - $blocks.each(showBlockEntry); - - // Note that we first open all
to be able to use ':visible'. - // Mark the
elements that were closed before filtering, so - // they can be reclosed when filtering is removed. - $details.not('[open]').attr('data-drupal-block-state', 'forced-open'); - // Hide the category
if they don't have any visible rows. - $details.attr('open', 'open').each(hideCategoryDetails); + $filter_rows.each(toggleBlockEntry); } else { - $blocks.show(); - $details.show(); - // Return
elements that had been closed before filtering - // to a closed state. - $details.filter('[data-drupal-block-state="forced-open"]').removeAttr('open data-drupal-block-state'); + $filter_rows.each(function (index) { + $(this).parent().parent().show(); + }); } } - if ($element.length) { - $details = $element.find('details'); - $blocks = $details.find('li'); - + if ($table.length) { + $filter_rows = $table.find('div.block-filter-text-source'); $input.on('keyup', filterBlockList); } } diff --git a/core/modules/block/src/BlockForm.php b/core/modules/block/src/BlockForm.php index eac4d51..8c13bbb 100644 --- a/core/modules/block/src/BlockForm.php +++ b/core/modules/block/src/BlockForm.php @@ -170,11 +170,13 @@ public function form(array $form, FormStateInterface $form_state) { } // Region settings. + $entity_region = $entity->getRegion(); + $region = $entity->isNew() ? $this->getRequest()->query->get('region', $entity_region) : $entity_region; $form['region'] = array( '#type' => 'select', '#title' => $this->t('Region'), '#description' => $this->t('Select the region where this block should be displayed.'), - '#default_value' => $entity->getRegion(), + '#default_value' => $region, '#empty_value' => BlockInterface::BLOCK_REGION_NONE, '#options' => system_region_list($theme, REGIONS_VISIBLE), '#prefix' => '
', diff --git a/core/modules/block/src/BlockListBuilder.php b/core/modules/block/src/BlockListBuilder.php index 32d5581..43d7a1c 100644 --- a/core/modules/block/src/BlockListBuilder.php +++ b/core/modules/block/src/BlockListBuilder.php @@ -8,7 +8,6 @@ namespace Drupal\block; use Drupal\Component\Utility\Html; -use Drupal\Core\Block\BlockManagerInterface; use Drupal\Component\Serialization\Json; use Drupal\Component\Utility\SafeMarkup; use Drupal\Core\Config\Entity\ConfigEntityListBuilder; @@ -45,13 +44,6 @@ class BlockListBuilder extends ConfigEntityListBuilder implements FormInterface protected $request; /** - * The block manager. - * - * @var \Drupal\Core\Block\BlockManagerInterface - */ - protected $blockManager; - - /** * The theme manager. * * @var \Drupal\Core\Theme\ThemeManagerInterface @@ -77,17 +69,14 @@ class BlockListBuilder extends ConfigEntityListBuilder implements FormInterface * The entity type definition. * @param \Drupal\Core\Entity\EntityStorageInterface $storage * The entity storage class. - * @param \Drupal\Core\Block\BlockManagerInterface $block_manager - * The block manager. * @param \Drupal\Core\Theme\ThemeManagerInterface $theme_manager * The theme manager. * @param \Drupal\Core\Form\FormBuilderInterface $form_builder * The form builder. */ - public function __construct(EntityTypeInterface $entity_type, EntityStorageInterface $storage, BlockManagerInterface $block_manager, ThemeManagerInterface $theme_manager, FormBuilderInterface $form_builder) { + public function __construct(EntityTypeInterface $entity_type, EntityStorageInterface $storage, ThemeManagerInterface $theme_manager, FormBuilderInterface $form_builder) { parent::__construct($entity_type, $storage); - $this->blockManager = $block_manager; $this->themeManager = $theme_manager; $this->formBuilder = $form_builder; } @@ -99,7 +88,6 @@ public static function createInstance(ContainerInterface $container, EntityTypeI return new static( $entity_type, $container->get('entity.manager')->getStorage($entity_type->id()), - $container->get('plugin.manager.block'), $container->get('theme.manager'), $container->get('form_builder') ); @@ -135,7 +123,6 @@ public function getFormId() { * {@inheritdoc} */ public function buildForm(array $form, FormStateInterface $form_state) { - $form['#theme'] = array('block_list'); $form['#attached']['library'][] = 'core/drupal.tableheader'; $form['#attached']['library'][] = 'block/drupal.block'; $form['#attached']['library'][] = 'block/drupal.block.admin'; @@ -143,7 +130,6 @@ public function buildForm(array $form, FormStateInterface $form_state) { // Build the form tree. $form['blocks'] = $this->buildBlocksForm(); - $form['place_blocks'] = $this->buildPlaceBlocksForm(); $form['actions'] = array( '#tree' => FALSE, @@ -206,7 +192,7 @@ protected function buildBlocksForm() { // Loop over each region and build blocks. $regions = $this->systemRegionList($this->getThemeName(), REGIONS_VISIBLE); - $block_regions_with_disabled = $regions + array(BlockInterface::BLOCK_REGION_NONE => BlockInterface::BLOCK_REGION_NONE); + $block_regions_with_disabled = $regions + array(BlockInterface::BLOCK_REGION_NONE => $this->t('Disabled', array(), array('context' => 'Plural'))); foreach ($block_regions_with_disabled as $region => $title) { $form['#tabledrag'][] = array( 'action' => 'match', @@ -229,10 +215,20 @@ protected function buildBlocksForm() { ), ); $form['region-' . $region]['title'] = array( - '#markup' => $region != BlockInterface::BLOCK_REGION_NONE ? $title : $this->t('Disabled', array(), array('context' => 'Plural')), + '#prefix' => $region != BlockInterface::BLOCK_REGION_NONE ? $title : $block_regions_with_disabled[$region], + '#type' => 'link', + '#title' => $this->t('Place block in the %region region', ['%region' => $block_regions_with_disabled[$region]]), + '#url' => Url::fromRoute('block.admin_library', ['theme' => $this->getThemeName()], ['query' => ['region' => $region]]), '#wrapper_attributes' => array( 'colspan' => 5, ), + '#attributes' => [ + 'class' => ['use-ajax', 'button', 'button--small'], + 'data-dialog-type' => 'modal', + 'data-dialog-options' => Json::encode([ + 'width' => 700, + ]), + ], ); $form['region-' . $region . '-message'] = array( @@ -313,78 +309,6 @@ protected function buildBlocksForm() { } /** - * Builds the "Place Blocks" portion of the form. - * - * @return array - */ - protected function buildPlaceBlocksForm() { - $form['title'] = array( - '#type' => 'container', - '#markup' => '

' . $this->t('Place blocks') . '

', - '#attributes' => array( - 'class' => array( - 'entity-meta__header', - ), - ), - ); - - $form['filter'] = array( - '#type' => 'search', - '#title' => $this->t('Filter'), - '#title_display' => 'invisible', - '#size' => 30, - '#placeholder' => $this->t('Filter by block name'), - '#attributes' => array( - 'class' => array('block-filter-text'), - 'data-element' => '.entity-meta', - 'title' => $this->t('Enter a part of the block name to filter by.'), - ), - ); - - $form['list']['#type'] = 'container'; - $form['list']['#attributes']['class'][] = 'entity-meta'; - - // Only add blocks which work without any available context. - $definitions = $this->blockManager->getDefinitionsForContexts(); - $sorted_definitions = $this->blockManager->getSortedDefinitions($definitions); - foreach ($sorted_definitions as $plugin_id => $plugin_definition) { - $category = SafeMarkup::checkPlain($plugin_definition['category']); - $category_key = 'category-' . $category; - if (!isset($form['list'][$category_key])) { - $form['list'][$category_key] = array( - '#type' => 'details', - '#title' => $category, - '#open' => TRUE, - 'content' => array( - '#theme' => 'links', - '#links' => array(), - '#attributes' => array( - 'class' => array( - 'block-list', - ), - ), - ), - ); - } - $form['list'][$category_key]['content']['#links'][$plugin_id] = array( - 'title' => $plugin_definition['admin_label'], - 'url' => Url::fromRoute('block.admin_add', [ - 'plugin_id' => $plugin_id, - 'theme' => $this->theme - ]), - 'attributes' => array( - 'class' => array('use-ajax', 'block-filter-text-source'), - 'data-dialog-type' => 'modal', - 'data-dialog-options' => Json::encode(array( - 'width' => 700, - )), - ), - ); - } - return $form; - } - - /** * Gets the name of the theme used for this block listing. * * @return string diff --git a/core/modules/block/src/Controller/BlockLibraryController.php b/core/modules/block/src/Controller/BlockLibraryController.php new file mode 100644 index 0000000..2a0148f --- /dev/null +++ b/core/modules/block/src/Controller/BlockLibraryController.php @@ -0,0 +1,179 @@ +blockManager = $block_manager; + $this->routeMatch = $route_match; + $this->localActionManager = $local_action_manager; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container) { + return new static( + $container->get('plugin.manager.block'), + $container->get('current_route_match'), + $container->get('plugin.manager.menu.local_action') + ); + } + + /** + * Shows a list of blocks that can be added to a theme's layout. + * + * @param \Symfony\Component\HttpFoundation\Request $request + * The current request. + * @param string $theme + * Theme key of the block list. + * + * @return array + * A render array as expected by the renderer. + */ + public function listBlocks(Request $request, $theme) { + // Since modals do not render any other part of the page, we need to render + // them manually as part of this listing. + if ($request->query->get(MainContentViewSubscriber::WRAPPER_FORMAT) === 'drupal_modal') { + $build['local_actions'] = $this->buildLocalActions(); + } + + $headers = [ + ['data' => $this->t('Block')], + ['data' => $this->t('Category')], + ['data' => $this->t('Operations')], + ]; + + // Only add blocks which work without any available context. + $definitions = $this->blockManager->getDefinitionsForContexts(); + // Order by category, and then by admin label. + $definitions = $this->blockManager->getSortedDefinitions($definitions); + + $region = $request->query->get('region'); + $rows = []; + foreach ($definitions as $plugin_id => $plugin_definition) { + $row = []; + $row['title']['data'] = [ + '#markup' => $plugin_definition['admin_label'], + '#prefix' => '
', + '#suffix' => '
', + ]; + $row['category']['data'] = SafeMarkup::checkPlain($plugin_definition['category']); + $links['add'] = [ + 'title' => $this->t('Place block'), + 'url' => Url::fromRoute('block.admin_add', ['plugin_id' => $plugin_id, 'theme' => $theme]), + 'attributes' => [ + 'class' => ['use-ajax'], + 'data-dialog-type' => 'modal', + 'data-dialog-options' => Json::encode([ + 'width' => 700, + ]), + ], + ]; + if ($region) { + $links['add']['query']['region'] = $region; + } + $row['operations']['data'] = [ + '#type' => 'operations', + '#links' => $links, + ]; + $rows[] = $row; + } + + $build['#attached']['library'][] = 'block/drupal.block.admin'; + + $build['filter'] = [ + '#type' => 'search', + '#title' => $this->t('Filter'), + '#title_display' => 'invisible', + '#size' => 30, + '#placeholder' => $this->t('Filter by block name'), + '#attributes' => [ + 'class' => ['block-filter-text'], + 'data-element' => '.block-add-table', + 'title' => $this->t('Enter a part of the block name to filter by.'), + ], + ]; + + $build['blocks'] = [ + '#type' => 'table', + '#header' => $headers, + '#rows' => $rows, + '#empty' => $this->t('No blocks available.'), + '#attributes' => [ + 'class' => ['block-add-table'], + ], + ]; + + return $build; + } + + /** + * Builds the local actions for this listing. + * + * @return array + * An array of local actions for this listing. + */ + protected function buildLocalActions() { + $build = $this->localActionManager->getActionsForRoute($this->routeMatch->getRouteName()); + // Without this workaround, the action links will be rendered as
  • with + // no wrapping
      element. + if (!empty($build)) { + $build['#prefix'] = ''; + } + return $build; + } + +} diff --git a/core/modules/block/src/Plugin/Derivative/ThemeLocalTask.php b/core/modules/block/src/Plugin/Derivative/ThemeLocalTask.php index cbc0a73..c91bc7c 100644 --- a/core/modules/block/src/Plugin/Derivative/ThemeLocalTask.php +++ b/core/modules/block/src/Plugin/Derivative/ThemeLocalTask.php @@ -69,7 +69,7 @@ public function getDerivativeDefinitions($base_plugin_definition) { } // Default task! if ($default_theme == $theme_name) { - $this->derivatives[$theme_name]['route_name'] = 'block.admin_display'; + $this->derivatives[$theme_name]['route_name'] = $base_plugin_definition['parent_id']; // Emulate default logic because without the base plugin id we can't // change the base_route. $this->derivatives[$theme_name]['weight'] = -10; diff --git a/core/modules/block/src/Tests/BlockLanguageCacheTest.php b/core/modules/block/src/Tests/BlockLanguageCacheTest.php index 45f6803..543e65e 100644 --- a/core/modules/block/src/Tests/BlockLanguageCacheTest.php +++ b/core/modules/block/src/Tests/BlockLanguageCacheTest.php @@ -62,6 +62,7 @@ public function testBlockLinks() { // Create the block cache for all languages. foreach ($this->langcodes as $langcode) { $this->drupalGet('admin/structure/block', array('language' => $langcode)); + $this->clickLink('Place block'); } // Create a menu in the default language. @@ -73,6 +74,7 @@ public function testBlockLinks() { // Check that the block is listed for all languages. foreach ($this->langcodes as $langcode) { $this->drupalGet('admin/structure/block', array('language' => $langcode)); + $this->clickLink('Place block'); $this->assertText($edit['label']); } } diff --git a/core/modules/block/src/Tests/BlockTitleXSSTest.php b/core/modules/block/src/Tests/BlockTitleXSSTest.php deleted file mode 100644 index 3790d56..0000000 --- a/core/modules/block/src/Tests/BlockTitleXSSTest.php +++ /dev/null @@ -1,46 +0,0 @@ -drupalPlaceBlock('test_xss_title', array('label' => '')); - } - - /** - * Test XSS in title. - */ - function testXSSInTitle() { - \Drupal::state()->set('block_test.content', $this->randomMachineName()); - $this->drupalGet(''); - $this->assertNoRaw('', 'The block title was properly sanitized when rendered.'); - - $this->drupalLogin($this->drupalCreateUser(array('administer blocks', 'access administration pages'))); - $default_theme = $this->config('system.theme')->get('default'); - $this->drupalGet('admin/structure/block/list/' . $default_theme); - $this->assertNoRaw("", 'The block title was properly sanitized in Block Plugin UI Admin page.'); - } - -} diff --git a/core/modules/block/src/Tests/BlockUiTest.php b/core/modules/block/src/Tests/BlockUiTest.php index 4dc388f..1f26739 100644 --- a/core/modules/block/src/Tests/BlockUiTest.php +++ b/core/modules/block/src/Tests/BlockUiTest.php @@ -139,14 +139,15 @@ function testBlockAdminUiPage() { */ public function testCandidateBlockList() { $arguments = array( - ':ul_class' => 'block-list', - ':li_class' => 'test-block-instantiation', + ':title' => 'Display message', + ':category' => 'Block test', ':href' => 'admin/structure/block/add/test_block_instantiation/classy', - ':text' => 'Display message', ); + $pattern = '//tr[.//td/div[text()=:title] and .//td[text()=:category] and .//td//a[contains(@href, :href)]]'; $this->drupalGet('admin/structure/block'); - $elements = $this->xpath('//details[@id="edit-category-block-test"]//ul[contains(@class, :ul_class)]/li[contains(@class, :li_class)]/a[contains(@href, :href) and text()=:text]', $arguments); + $this->clickLink('Place block'); + $elements = $this->xpath($pattern, $arguments); $this->assertTrue(!empty($elements), 'The test block appears in the category for its module.'); // Trigger the custom category addition in block_test_block_alter(). @@ -154,7 +155,9 @@ public function testCandidateBlockList() { $this->container->get('plugin.manager.block')->clearCachedDefinitions(); $this->drupalGet('admin/structure/block'); - $elements = $this->xpath('//details[@id="edit-category-custom-category"]//ul[contains(@class, :ul_class)]/li[contains(@class, :li_class)]/a[contains(@href, :href) and text()=:text]', $arguments); + $this->clickLink('Place block'); + $arguments[':category'] = 'Custom category'; + $elements = $this->xpath($pattern, $arguments); $this->assertTrue(!empty($elements), 'The test block appears in a custom category controlled by block_test_block_alter().'); } diff --git a/core/modules/block/src/Tests/BlockXssTest.php b/core/modules/block/src/Tests/BlockXssTest.php index f24811a..0baa67f 100644 --- a/core/modules/block/src/Tests/BlockXssTest.php +++ b/core/modules/block/src/Tests/BlockXssTest.php @@ -26,7 +26,34 @@ class BlockXssTest extends WebTestBase { * * @var array */ - public static $modules = ['block', 'block_content', 'menu_ui', 'views']; + public static $modules = ['block', 'block_content', 'block_test', 'menu_ui', 'views']; + + /** + * Test XSS in title. + */ + public function testXssInTitle() { + $this->drupalPlaceBlock('test_xss_title', ['label' => '']); + + \Drupal::state()->set('block_test.content', $this->randomMachineName()); + $this->drupalGet(''); + $this->assertNoRaw('', 'The block title was properly sanitized when rendered.'); + + $this->drupalLogin($this->drupalCreateUser(['administer blocks', 'access administration pages'])); + $default_theme = $this->config('system.theme')->get('default'); + $this->drupalGet('admin/structure/block/list/' . $default_theme); + $this->assertNoRaw("", 'The block title was properly sanitized in Block Plugin UI Admin page.'); + } + + /** + * Tests XSS in category. + */ + public function testXssInCategory() { + $this->drupalPlaceBlock('test_xss_title'); + $this->drupalLogin($this->drupalCreateUser(['administer blocks', 'access administration pages'])); + $this->drupalGet(Url::fromRoute('block.admin_display')); + $this->clickLink('Place block'); + $this->assertNoRaw(""); + } /** * Tests various modules that provide blocks for XSS. @@ -51,8 +78,9 @@ protected function doViewTest() { $view->save(); $this->drupalGet(Url::fromRoute('block.admin_display')); - $this->clickLink(''); - $this->assertRaw('<script>alert("view");</script>'); + $this->clickLink('Place block'); + // The block admin label is automatically XSS admin filtered. + $this->assertRaw('alert("view");'); $this->assertNoRaw(''); } @@ -66,8 +94,9 @@ protected function doMenuTest() { ])->save(); $this->drupalGet(Url::fromRoute('block.admin_display')); - $this->clickLink(''); - $this->assertRaw('<script>alert("menu");</script>'); + $this->clickLink('Place block'); + // The block admin label is automatically XSS admin filtered. + $this->assertRaw('alert("menu");'); $this->assertNoRaw(''); } @@ -86,8 +115,9 @@ protected function doBlockContentTest() { ])->save(); $this->drupalGet(Url::fromRoute('block.admin_display')); - $this->clickLink(''); - $this->assertRaw('<script>alert("block_content");</script>'); + $this->clickLink('Place block'); + // The block admin label is automatically XSS admin filtered. + $this->assertRaw('alert("block_content");'); $this->assertNoRaw(''); } diff --git a/core/modules/block/src/Tests/Views/DisplayBlockTest.php b/core/modules/block/src/Tests/Views/DisplayBlockTest.php index 3f96ab1..d9f9196 100644 --- a/core/modules/block/src/Tests/Views/DisplayBlockTest.php +++ b/core/modules/block/src/Tests/Views/DisplayBlockTest.php @@ -8,8 +8,6 @@ namespace Drupal\block\Tests\Views; use Drupal\Component\Serialization\Json; -use Drupal\Component\Utility\Html; -use Drupal\Component\Utility\SafeMarkup; use Drupal\views\Views; use Drupal\views\Tests\ViewTestBase; use Drupal\views\Tests\ViewTestData; @@ -60,19 +58,20 @@ public function testBlockCategory() { $edit['block[style][row_plugin]'] = 'fields'; $this->drupalPostForm('admin/structure/views/add', $edit, t('Save and edit')); + $pattern = '//tr[.//td[text()=:category] and .//td//a[contains(@href, :href)]]'; + // Test that the block was given a default category corresponding to its // base table. $arguments = array( - ':id' => 'edit-category-lists-views', - ':li_class' => 'views-block' . Html::getClass($edit['id']) . '-block-1', ':href' => \Drupal::Url('block.admin_add', array( 'plugin_id' => 'views_block:' . $edit['id'] . '-block_1', 'theme' => 'classy', )), - ':text' => $edit['label'], + ':category' => t('Lists (Views)'), ); $this->drupalGet('admin/structure/block'); - $elements = $this->xpath('//details[@id=:id]//li[contains(@class, :li_class)]/a[contains(@href, :href) and text()=:text]', $arguments); + $this->clickLink('Place block'); + $elements = $this->xpath($pattern, $arguments); $this->assertTrue(!empty($elements), 'The test block appears in the category for its base table.'); // Duplicate the block before changing the category. @@ -81,10 +80,9 @@ public function testBlockCategory() { // Change the block category to a random string. $this->drupalGet('admin/structure/views/view/' . $edit['id'] . '/edit/block_1'); - $label = t('Lists (Views)'); - $link = $this->xpath('//a[@id="views-block-1-block-category" and normalize-space(text())=:label]', array(':label' => $label)); + $link = $this->xpath('//a[@id="views-block-1-block-category" and normalize-space(text())=:category]', $arguments); $this->assertTrue(!empty($link)); - $this->clickLink($label); + $this->clickLink(t('Lists (Views)')); $category = $this->randomString(); $this->drupalPostForm(NULL, array('block_category' => $category), t('Apply')); @@ -95,34 +93,30 @@ public function testBlockCategory() { $this->drupalPostForm(NULL, array(), t('Save')); // Test that the blocks are listed under the correct categories. - $category_id = Html::getUniqueId('edit-category-' . SafeMarkup::checkPlain($category)); - $arguments[':id'] = $category_id; + $arguments[':category'] = $category; $this->drupalGet('admin/structure/block'); - $elements = $this->xpath('//details[@id=:id]//li[contains(@class, :li_class)]/a[contains(@href, :href) and text()=:text]', $arguments); + $this->clickLink('Place block'); + $elements = $this->xpath($pattern, $arguments); $this->assertTrue(!empty($elements), 'The test block appears in the custom category.'); $arguments = array( - ':id' => 'edit-category-lists-views', - ':li_class' => 'views-block' . Html::getClass($edit['id']) . '-block-2', ':href' => \Drupal::Url('block.admin_add', array( 'plugin_id' => 'views_block:' . $edit['id'] . '-block_2', 'theme' => 'classy', )), - ':text' => $edit['label'], + ':category' => t('Lists (Views)'), ); - $elements = $this->xpath('//details[@id=:id]//li[contains(@class, :li_class)]/a[contains(@href, :href) and text()=:text]', $arguments); + $elements = $this->xpath($pattern, $arguments); $this->assertTrue(!empty($elements), 'The first duplicated test block remains in the original category.'); $arguments = array( - ':id' => $category_id, - ':li_class' => 'views-block' . Html::getClass($edit['id']) . '-block-3', ':href' => \Drupal::Url('block.admin_add', array( 'plugin_id' => 'views_block:' . $edit['id'] . '-block_3', 'theme' => 'classy', )), - ':text' => $edit['label'], + ':category' => $category, ); - $elements = $this->xpath('//details[@id=:id]//li[contains(@class, :li_class)]/a[contains(@href, :href) and text()=:text]', $arguments); + $elements = $this->xpath($pattern, $arguments); $this->assertTrue(!empty($elements), 'The second duplicated test block appears in the custom category.'); } diff --git a/core/modules/block_content/block_content.links.action.yml b/core/modules/block_content/block_content.links.action.yml index d94ca3f..4772a6f 100644 --- a/core/modules/block_content/block_content.links.action.yml +++ b/core/modules/block_content/block_content.links.action.yml @@ -8,7 +8,6 @@ block_content_add_action: route_name: block_content.add_page title: 'Add custom block' appears_on: - - block.admin_display - - block.admin_display_theme + - block.admin_library - entity.block_content.collection class: \Drupal\block_content\Plugin\Menu\LocalAction\BlockContentAddLocalAction diff --git a/core/modules/block_content/src/Tests/BlockContentTypeTest.php b/core/modules/block_content/src/Tests/BlockContentTypeTest.php index 4270538..681c6ce 100644 --- a/core/modules/block_content/src/Tests/BlockContentTypeTest.php +++ b/core/modules/block_content/src/Tests/BlockContentTypeTest.php @@ -189,6 +189,7 @@ public function testsBlockContentAddTypes() { // block configure form. $path = $theme == $default_theme ? 'admin/structure/block' : "admin/structure/block/list/$theme"; $this->drupalGet($path); + $this->clickLink('Place block'); $this->clickLink(t('Add custom block')); // The seven theme has markup inside the link, we cannot use clickLink(). if ($default_theme == 'seven') { diff --git a/core/modules/menu_ui/src/Tests/MenuTest.php b/core/modules/menu_ui/src/Tests/MenuTest.php index 3a1bac5..3c00e92 100644 --- a/core/modules/menu_ui/src/Tests/MenuTest.php +++ b/core/modules/menu_ui/src/Tests/MenuTest.php @@ -219,6 +219,7 @@ function addCustomMenu() { // Confirm that the custom menu block is available. $this->drupalGet('admin/structure/block/list/' . $this->config('system.theme')->get('default')); + $this->clickLink('Place block'); $this->assertText($label); // Enable the block. @@ -532,6 +533,7 @@ function testSystemMenuRename() { // Make sure menu shows up with new name in block addition. $default_theme = $this->config('system.theme')->get('default'); $this->drupalget('admin/structure/block/list/' . $default_theme); + $this->clickLink('Place block'); $this->assertText($edit['label']); } diff --git a/core/modules/search/src/Tests/SearchBlockTest.php b/core/modules/search/src/Tests/SearchBlockTest.php index bf18056..2348ae0 100644 --- a/core/modules/search/src/Tests/SearchBlockTest.php +++ b/core/modules/search/src/Tests/SearchBlockTest.php @@ -36,6 +36,7 @@ public function testSearchFormBlock() { // Test availability of the search block in the admin "Place blocks" list. $this->drupalGet('admin/structure/block'); + $this->clickLink('Place block'); $this->assertLinkByHref('/admin/structure/block/add/search_form_block/classy', 0, 'Did not find the search block in block candidate list.'); diff --git a/core/modules/views/src/Tests/Wizard/BasicTest.php b/core/modules/views/src/Tests/Wizard/BasicTest.php index d55b941..2ba0dac 100644 --- a/core/modules/views/src/Tests/Wizard/BasicTest.php +++ b/core/modules/views/src/Tests/Wizard/BasicTest.php @@ -133,6 +133,7 @@ function testViewsWizardAndListing() { // Confirm that the block is available in the block administration UI. $this->drupalGet('admin/structure/block/list/' . $this->config('system.theme')->get('default')); + $this->clickLink('Place block'); $this->assertText($view3['label']); // Place the block. diff --git a/core/modules/views/src/Tests/Wizard/ItemsPerPageTest.php b/core/modules/views/src/Tests/Wizard/ItemsPerPageTest.php index 8e8edfc..7fd06d0 100644 --- a/core/modules/views/src/Tests/Wizard/ItemsPerPageTest.php +++ b/core/modules/views/src/Tests/Wizard/ItemsPerPageTest.php @@ -71,6 +71,7 @@ function testItemsPerPage() { // Confirm that the block is listed in the block administration UI. $this->drupalGet('admin/structure/block/list/' . $this->config('system.theme')->get('default')); + $this->clickLink('Place block'); $this->assertText($view['label']); // Place the block, visit a page that displays the block, and check that the diff --git a/core/modules/views_ui/src/Tests/OverrideDisplaysTest.php b/core/modules/views_ui/src/Tests/OverrideDisplaysTest.php index f1815a8..15b6c88 100644 --- a/core/modules/views_ui/src/Tests/OverrideDisplaysTest.php +++ b/core/modules/views_ui/src/Tests/OverrideDisplaysTest.php @@ -50,6 +50,7 @@ function testOverrideDisplays() { // Confirm that the view block is available in the block administration UI. $this->drupalGet('admin/structure/block/list/' . $this->config('system.theme')->get('default')); + $this->clickLink('Place block'); $this->assertText($view['label']); // Place the block. @@ -109,6 +110,7 @@ function testWizardMixedDefaultOverriddenDisplays() { // Confirm that the block is available in the block administration UI. $this->drupalGet('admin/structure/block/list/' . $this->config('system.theme')->get('default')); + $this->clickLink('Place block'); $this->assertText($view['label']); // Put the block into the first sidebar region, and make sure it will not