diff --git a/core/modules/block_content/config/optional/views.view.block_content.yml b/core/modules/block_content/config/optional/views.view.block_content.yml new file mode 100644 index 0000000000000000000000000000000000000000..7c2bba84e1977a6658e290601f86341e8a91549b --- /dev/null +++ b/core/modules/block_content/config/optional/views.view.block_content.yml @@ -0,0 +1,489 @@ +langcode: en +status: true +dependencies: + module: + - block_content + - user +id: block_content +label: 'Custom block library' +module: views +description: 'Find and manage custom blocks.' +tag: '' +base_table: block_content_field_data +base_field: id +core: 8.x +display: + default: + display_plugin: default + id: default + display_title: Master + position: 0 + display_options: + access: + type: perm + options: + perm: 'administer blocks' + cache: + type: none + options: { } + query: + type: views_query + options: + disable_sql_rewrite: false + distinct: false + replica: false + query_comment: '' + query_tags: { } + exposed_form: + type: basic + options: + submit_button: Apply + reset_button: false + reset_button_label: Reset + exposed_sorts_label: 'Sort by' + expose_sort_order: true + sort_asc_label: Asc + sort_desc_label: Desc + pager: + type: mini + options: + items_per_page: 50 + offset: 0 + id: 0 + total_pages: null + tags: + previous: '‹ previous' + next: 'next ›' + expose: + items_per_page: false + items_per_page_label: 'Items per page' + items_per_page_options: '5, 10, 25, 50' + items_per_page_options_all: false + items_per_page_options_all_label: '- All -' + offset: false + offset_label: Offset + style: + type: table + options: + grouping: { } + row_class: '' + default_row_class: true + override: true + sticky: false + caption: '' + summary: '' + description: '' + columns: + info: info + type: type + changed: changed + operations: operations + info: + info: + sortable: true + default_sort_order: asc + align: '' + separator: '' + empty_column: false + responsive: '' + type: + sortable: true + default_sort_order: asc + align: '' + separator: '' + empty_column: false + responsive: '' + changed: + sortable: true + default_sort_order: desc + align: '' + separator: '' + empty_column: false + responsive: '' + operations: + sortable: false + default_sort_order: asc + align: '' + separator: '' + empty_column: false + responsive: '' + default: changed + empty_table: true + row: + type: fields + fields: + info: + id: info + table: block_content_field_data + field: info + relationship: none + group_type: group + admin_label: '' + label: 'Block description' + exclude: false + alter: + alter_text: false + text: '' + make_link: false + path: '' + absolute: false + external: false + replace_spaces: false + path_case: none + trim_whitespace: false + alt: '' + rel: '' + link_class: '' + prefix: '' + suffix: '' + target: '' + nl2br: false + max_length: 0 + word_boundary: true + ellipsis: true + more_link: false + more_link_text: '' + more_link_path: '' + strip_tags: false + trim: false + preserve_tags: '' + html: false + element_type: '' + element_class: '' + element_label_type: '' + element_label_class: '' + element_label_colon: true + element_wrapper_type: '' + element_wrapper_class: '' + element_default_classes: true + empty: '' + hide_empty: false + empty_zero: false + hide_alter_empty: true + click_sort_column: value + type: string + settings: + link_to_entity: true + group_column: value + group_columns: { } + group_rows: true + delta_limit: 0 + delta_offset: 0 + delta_reversed: false + delta_first_last: false + multi_type: separator + separator: ', ' + field_api_classes: false + entity_type: null + entity_field: info + plugin_id: field + type: + id: type + table: block_content_field_data + field: type + relationship: none + group_type: group + admin_label: '' + label: 'Block type' + exclude: false + alter: + alter_text: false + text: '' + make_link: false + path: '' + absolute: false + external: false + replace_spaces: false + path_case: none + trim_whitespace: false + alt: '' + rel: '' + link_class: '' + prefix: '' + suffix: '' + target: '' + nl2br: false + max_length: 0 + word_boundary: true + ellipsis: true + more_link: false + more_link_text: '' + more_link_path: '' + strip_tags: false + trim: false + preserve_tags: '' + html: false + element_type: '' + element_class: '' + element_label_type: '' + element_label_class: '' + element_label_colon: true + element_wrapper_type: '' + element_wrapper_class: '' + element_default_classes: true + empty: '' + hide_empty: false + empty_zero: false + hide_alter_empty: true + click_sort_column: target_id + type: entity_reference_label + settings: + link: false + group_column: target_id + group_columns: { } + group_rows: true + delta_limit: 0 + delta_offset: 0 + delta_reversed: false + delta_first_last: false + multi_type: separator + separator: ', ' + field_api_classes: false + entity_type: block_content + entity_field: type + plugin_id: field + changed: + id: changed + table: block_content_field_data + field: changed + relationship: none + group_type: group + admin_label: '' + label: Updated + exclude: false + alter: + alter_text: false + text: '' + make_link: false + path: '' + absolute: false + external: false + replace_spaces: false + path_case: none + trim_whitespace: false + alt: '' + rel: '' + link_class: '' + prefix: '' + suffix: '' + target: '' + nl2br: false + max_length: 0 + word_boundary: true + ellipsis: true + more_link: false + more_link_text: '' + more_link_path: '' + strip_tags: false + trim: false + preserve_tags: '' + html: false + element_type: '' + element_class: '' + element_label_type: '' + element_label_class: '' + element_label_colon: true + element_wrapper_type: '' + element_wrapper_class: '' + element_default_classes: true + empty: '' + hide_empty: false + empty_zero: false + hide_alter_empty: true + date_format: short + custom_date_format: '' + timezone: '' + entity_type: block_content + entity_field: changed + plugin_id: date + operations: + id: operations + table: block_content + field: operations + relationship: none + group_type: group + admin_label: '' + label: Operations + exclude: false + alter: + alter_text: false + text: '' + make_link: false + path: '' + absolute: false + external: false + replace_spaces: false + path_case: none + trim_whitespace: false + alt: '' + rel: '' + link_class: '' + prefix: '' + suffix: '' + target: '' + nl2br: false + max_length: 0 + word_boundary: true + ellipsis: true + more_link: false + more_link_text: '' + more_link_path: '' + strip_tags: false + trim: false + preserve_tags: '' + html: false + element_type: '' + element_class: '' + element_label_type: '' + element_label_class: '' + element_label_colon: true + element_wrapper_type: '' + element_wrapper_class: '' + element_default_classes: true + empty: '' + hide_empty: false + empty_zero: false + hide_alter_empty: true + destination: true + entity_type: block_content + plugin_id: entity_operations + filters: + info: + id: info + table: block_content_field_data + field: info + relationship: none + group_type: group + admin_label: '' + operator: contains + value: '' + group: 1 + exposed: true + expose: + operator_id: info_op + label: 'Block description' + description: '' + use_operator: false + operator: info_op + identifier: info + required: false + remember: false + multiple: false + remember_roles: + authenticated: authenticated + anonymous: '0' + administrator: '0' + is_grouped: false + group_info: + label: '' + description: '' + identifier: '' + optional: true + widget: select + multiple: false + remember: false + default_group: All + default_group_multiple: { } + group_items: { } + entity_type: block_content + entity_field: info + plugin_id: string + type: + id: type + table: block_content + field: type + relationship: none + group_type: group + admin_label: '' + operator: in + value: { } + group: 1 + exposed: true + expose: + operator_id: type_op + label: 'Block type' + description: '' + use_operator: false + operator: type_op + identifier: type + required: false + remember: false + multiple: false + remember_roles: + authenticated: authenticated + anonymous: '0' + administrator: '0' + reduce: false + is_grouped: false + group_info: + label: '' + description: '' + identifier: '' + optional: true + widget: select + multiple: false + remember: false + default_group: All + default_group_multiple: { } + group_items: { } + entity_type: block_content + entity_field: type + plugin_id: bundle + sorts: { } + title: 'Custom block library' + header: { } + footer: { } + empty: + area_text_custom: + id: area_text_custom + table: views + field: area_text_custom + relationship: none + group_type: group + admin_label: '' + empty: true + tokenize: false + content: 'There are no custom blocks available. ' + plugin_id: text_custom + block_content_listing_empty: + admin_label: '' + empty: true + field: block_content_listing_empty + group_type: group + id: block_content_listing_empty + label: '' + relationship: none + table: block_content + plugin_id: block_content_listing_empty + entity_type: block_content + relationships: { } + arguments: { } + display_extenders: { } + cache_metadata: + contexts: + - 'languages:language_content' + - 'languages:language_interface' + - url + cacheable: false + page_1: + display_plugin: page + id: page_1 + display_title: Page + position: 1 + display_options: + display_extenders: { } + path: admin/structure/block/block-content + menu: + type: tab + title: 'Custom block library' + description: '' + parent: block.admin_display + weight: 0 + context: '0' + menu_name: admin + cache_metadata: + contexts: + - 'languages:language_content' + - 'languages:language_interface' + - url + cacheable: false diff --git a/core/modules/block_content/src/BlockContentViewsData.php b/core/modules/block_content/src/BlockContentViewsData.php index 09d49b7a83f4c9e86f2a3fc4ed44891866493a6f..abb2cb211be83a3dc5f329c10e9348b7960d7d71 100644 --- a/core/modules/block_content/src/BlockContentViewsData.php +++ b/core/modules/block_content/src/BlockContentViewsData.php @@ -28,6 +28,13 @@ public function getViewsData() { $data['block_content_field_data']['type']['field']['id'] = 'field'; + $data['block_content']['block_content_listing_empty'] = array( + 'title' => t('Empty block library behavior'), + 'help' => t('Provides a link to add a new block.'), + 'area' => array( + 'id' => 'block_content_listing_empty', + ), + ); // Advertise this table as a possible base table. $data['block_content_revision']['table']['base']['help'] = $this->t('Block Content revision is a history of changes to block content.'); $data['block_content_revision']['table']['base']['defaults']['title'] = 'info'; diff --git a/core/modules/block_content/src/Plugin/views/area/ListingEmpty.php b/core/modules/block_content/src/Plugin/views/area/ListingEmpty.php new file mode 100644 index 0000000000000000000000000000000000000000..0e25190ff8757fe7ae5a2b03e0d55a50701eff2b --- /dev/null +++ b/core/modules/block_content/src/Plugin/views/area/ListingEmpty.php @@ -0,0 +1,94 @@ +accessManager = $access_manager; + $this->currentUser = $current_user; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { + return new static( + $configuration, + $plugin_id, + $plugin_definition, + $container->get('access_manager'), + $container->get('current_user') + ); + } + + /** + * {@inheritdoc} + */ + public function render($empty = FALSE) { + if (!$empty || !empty($this->options['empty'])) { + /** @var \Drupal\Core\Access\AccessResultInterface|\Drupal\Core\Cache\CacheableDependencyInterface $access_result */ + $access_result = $this->accessManager->checkNamedRoute('block_content.add_page', array(), $this->currentUser, TRUE); + $element = array( + '#markup' => $this->t('Add a custom block.', array('!url' => Url::fromRoute('block_content.add_page')->toString())), + '#access' => $access_result->isAllowed(), + '#cache' => [ + 'contexts' => $access_result->getCacheContexts(), + 'tags' => $access_result->getCacheTags(), + 'max-age' => $access_result->getCacheMaxAge(), + ], + ); + return $element; + } + return array(); + } + +} diff --git a/core/modules/block_content/src/Tests/BlockContentListTest.php b/core/modules/block_content/src/Tests/BlockContentListTest.php index 03b60e37056bd905b636e3c6c1db4ad0145287e1..aeed4dc5bc6b01004f0c4e014fe5beeff0e31314 100644 --- a/core/modules/block_content/src/Tests/BlockContentListTest.php +++ b/core/modules/block_content/src/Tests/BlockContentListTest.php @@ -7,13 +7,14 @@ namespace Drupal\block_content\Tests; -use Drupal\simpletest\WebTestBase; - /** * Tests the listing of custom blocks. * + * Tests the fallback block content list when Views is disabled. + * * @group block_content * @see \Drupal\block\BlockContentListBuilder + * @see \Drupal\block_content\Tests\BlockContentListViewsTest */ class BlockContentListTest extends BlockContentTestBase { diff --git a/core/modules/block_content/src/Tests/BlockContentListViewsTest.php b/core/modules/block_content/src/Tests/BlockContentListViewsTest.php new file mode 100644 index 0000000000000000000000000000000000000000..ba84f2b28b2433554ca43816afa6821b14d36e5c --- /dev/null +++ b/core/modules/block_content/src/Tests/BlockContentListViewsTest.php @@ -0,0 +1,118 @@ +drupalLogin($this->drupalCreateUser(array('administer blocks', 'translate configuration'))); + $this->drupalGet('admin/structure/block/block-content'); + + // Test for the page title. + $this->assertTitle(t('Custom block library') . ' | Drupal'); + + // Test for the table. + $element = $this->xpath('//div[@class="layout-content"]//table'); + $this->assertTrue($element, 'Views table found.'); + + // Test the table header. + $elements = $this->xpath('//div[@class="layout-content"]//table/thead/tr/th'); + $this->assertEqual(count($elements), 4, 'Correct number of table header cells found.'); + + // Test the contents of each th cell. + $expected_items = [t('Block description'), t('Block type'), t('Updated'), t('Operations')]; + foreach ($elements as $key => $element) { + if ($element->xpath('a')) { + $this->assertIdentical(trim((string) $element->xpath('a')[0]), $expected_items[$key]); + } + else { + $this->assertIdentical(trim((string) $element[0]), $expected_items[$key]); + } + } + + $label = 'Antelope'; + $new_label = 'Albatross'; + // Add a new entity using the operations link. + $link_text = t('Add custom block'); + $this->assertLink($link_text); + $this->clickLink($link_text); + $this->assertResponse(200); + $edit = array(); + $edit['info[0][value]'] = $label; + $edit['body[0][value]'] = $this->randomMachineName(16); + $this->drupalPostForm(NULL, $edit, t('Save')); + + // Confirm that once the user returns to the listing, the text of the label + // (versus elsewhere on the page). + $this->assertFieldByXpath('//td/a', $label, 'Label found for added block.'); + + // Check the number of table row cells. + $elements = $this->xpath('//div[@class="layout-content"]//table/tbody/tr/td'); + $this->assertEqual(count($elements), 4, 'Correct number of table row cells found.'); + // Check the contents of each row cell. The first cell contains the label, + // the second contains the machine name, and the third contains the + // operations list. + $this->assertIdentical((string) $elements[0]->xpath('a')[0], $label); + + // Edit the entity using the operations link. + $blocks = $this->container + ->get('entity.manager') + ->getStorage('block_content') + ->loadByProperties(array('info' => $label)); + $block = reset($blocks); + if (!empty($block)) { + $this->assertLinkByHref('block/' . $block->id()); + $this->clickLink(t('Edit')); + $this->assertResponse(200); + $this->assertTitle(strip_tags(t('Edit custom block %label', array('%label' => $label)) . ' | Drupal')); + $edit = array('info[0][value]' => $new_label); + $this->drupalPostForm(NULL, $edit, t('Save')); + } + else { + $this->fail('Did not find Albatross block in the database.'); + } + + // Confirm that once the user returns to the listing, the text of the label + // (versus elsewhere on the page). + $this->assertFieldByXpath('//td/a', $new_label, 'Label found for updated custom block.'); + + // Delete the added entity using the operations link. + $this->assertLinkByHref('block/' . $block->id() . '/delete'); + $delete_text = t('Delete'); + $this->clickLink($delete_text); + $this->assertResponse(200); + $this->assertTitle(strip_tags(t('Are you sure you want to delete the custom block %label?', array('%label' => $new_label)) . ' | Drupal')); + $this->drupalPostForm(NULL, array(), $delete_text); + + // Verify that the text of the label and machine name does not appear in + // the list (though it may appear elsewhere on the page). + $this->assertNoFieldByXpath('//td', $new_label, 'No label found for deleted custom block.'); + + // Confirm that the empty text is displayed. + $this->assertText('There are no custom blocks available.'); + $this->assertLink('custom block'); + } + +}