diff --git a/core/modules/block_content/block_content.install b/core/modules/block_content/block_content.install index 75180fc985..a02a106ed3 100644 --- a/core/modules/block_content/block_content.install +++ b/core/modules/block_content/block_content.install @@ -146,6 +146,8 @@ function block_content_update_8600() { $reusable = BaseFieldDefinition::create('boolean') ->setLabel(t('Reusable')) ->setDescription(t('A boolean indicating whether this block is reusable.')) + ->setTranslatable(FALSE) + ->setRevisionable(FALSE) ->setDefaultValue(TRUE) ->setInitialValue(TRUE); @@ -161,7 +163,7 @@ function block_content_update_8601() { $base_table = 'block_content_field_data'; // Make sure the view's 'base_table' is correct and the View is not new. The // default view may have been replaced by a completely different view with the - //same ID. + // same ID. if ($view->get('base_table') !== $base_table || $view->isNew()) { return; } diff --git a/core/modules/block_content/src/Entity/BlockContent.php b/core/modules/block_content/src/Entity/BlockContent.php index 2ca5bd1f7d..3b30cd8c32 100644 --- a/core/modules/block_content/src/Entity/BlockContent.php +++ b/core/modules/block_content/src/Entity/BlockContent.php @@ -216,6 +216,8 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) { $fields['reusable'] = BaseFieldDefinition::create('boolean') ->setLabel(t('Reusable')) ->setDescription(t('A boolean indicating whether this block is reusable.')) + ->setTranslatable(FALSE) + ->setRevisionable(FALSE) ->setDefaultValue(TRUE) ->setInitialValue(TRUE); diff --git a/core/modules/block_content/tests/modules/block_content_view_override/block_content_view_override.info.yml b/core/modules/block_content/tests/modules/block_content_view_override/block_content_view_override.info.yml new file mode 100644 index 0000000000..3ca2d1bc37 --- /dev/null +++ b/core/modules/block_content/tests/modules/block_content_view_override/block_content_view_override.info.yml @@ -0,0 +1,9 @@ +name: "Custom Block module reusable tests" +type: module +description: "Support module for custom block reusable testing." +package: Testing +version: VERSION +core: 8.x +dependencies: + - drupal:block_content + - drupal:views diff --git a/core/modules/block_content/tests/modules/block_content_view_override/config/install/views.view.block_content.yml b/core/modules/block_content/tests/modules/block_content_view_override/config/install/views.view.block_content.yml new file mode 100644 index 0000000000..a3cc406077 --- /dev/null +++ b/core/modules/block_content/tests/modules/block_content_view_override/config/install/views.view.block_content.yml @@ -0,0 +1,604 @@ +langcode: en +status: true +dependencies: + module: + - block_content + - user +_core: + default_config_hash: gkRJCqHr3uSO8ALHLatX-7YKfX0lWEgkC5qMBtCf_Sg +id: block_content +label: 'Custom block library' +module: views +description: 'Find and manage custom blocks.' +tag: default +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: tag + 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 + entity_type: block_content + entity_field: changed + type: timestamp + settings: + date_format: short + custom_date_format: '' + timezone: '' + plugin_id: field + 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_data + 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 + - url.query_args + - user.permissions + max-age: -1 + tags: { } + 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 + - url.query_args + - user.permissions + max-age: -1 + tags: { } + page_2: + display_plugin: page + id: page_2 + display_title: 'Page 2' + position: 2 + display_options: + display_extenders: { } + path: extra-view-display + filters: + type: + id: type + table: block_content_field_data + 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 + info: + id: info + table: block_content_field_data + field: info + relationship: none + group_type: group + admin_label: '' + operator: 'contains' + value: block2 + group: 1 + exposed: false + expose: + operator_id: '' + label: '' + description: '' + use_operator: false + operator: '' + identifier: '' + required: false + remember: false + multiple: false + remember_roles: + authenticated: authenticated + placeholder: '' + 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 + defaults: + filters: false + filter_groups: false + filter_groups: + operator: AND + groups: + 1: AND + cache_metadata: + max-age: -1 + contexts: + - 'languages:language_content' + - 'languages:language_interface' + - url + - url.query_args + - user.permissions + tags: { } diff --git a/core/modules/block_content/tests/src/Functional/Update/BlockContentReusableUpdateTest.php b/core/modules/block_content/tests/src/Functional/Update/BlockContentReusableUpdateTest.php new file mode 100644 index 0000000000..82730d3a7d --- /dev/null +++ b/core/modules/block_content/tests/src/Functional/Update/BlockContentReusableUpdateTest.php @@ -0,0 +1,129 @@ +databaseDumpFiles = [ + __DIR__ . '/../../../../../system/tests/fixtures/update/drupal-8.4.0.bare.standard.php.gz', + ]; + } + + /** + * Tests adding a reusable field to the block content entity type. + * + * @see block_content_update_8600 + * @see block_content_update_8601 + */ + public function testReusableFieldAddition() { + $assert_session = $this->assertSession(); + $entity_definition_update_manager = \Drupal::entityDefinitionUpdateManager(); + + // Delete custom block library view. + View::load('block_content')->delete(); + // Install the test module with the 'block_content' view with an extra + // display with overridden filters. This extra display should also have a + // filter added for 'reusable' field so that it does not expose non-reusable + // fields. This display also a filter only show blocks that contain + // 'block2' in the 'info' field. + $this->container->get('module_installer')->install(['block_content_view_override']); + + // Run updates. + $this->runUpdates(); + + // Check that the field exists and is configured correctly. + $reusable_field = $entity_definition_update_manager->getFieldStorageDefinition('reusable', 'block_content'); + $this->assertEquals('Reusable', $reusable_field->getLabel()); + $this->assertEquals('A boolean indicating whether this block is reusable.', $reusable_field->getDescription()); + $this->assertEquals(FALSE, $reusable_field->isRevisionable()); + $this->assertEquals(FALSE, $reusable_field->isTranslatable()); + + $after_block1 = BlockContent::create([ + 'info' => 'After update block1', + 'type' => 'basic_block', + ]); + $after_block1->save(); + // Add second block that will be shown with the 'info' filter on the + // additional view display. + $after_block2 = BlockContent::create([ + 'info' => 'After update block2', + 'type' => 'basic_block', + ]); + $after_block2->save(); + + $this->assertEquals(TRUE, $after_block1->isReusable()); + $this->assertEquals(TRUE, $after_block2->isReusable()); + + $non_reusable_block = BlockContent::create([ + 'info' => 'non-reusable block1', + 'type' => 'basic_block', + 'reusable' => FALSE, + ]); + $non_reusable_block->save(); + // Add second block that will be would shown with the 'info' filter on the + // additional view display if the 'reusable filter was not added. + $non_reusable_block2 = BlockContent::create([ + 'info' => 'non-reusable block2', + 'type' => 'basic_block', + 'reusable' => FALSE, + ]); + $non_reusable_block2->save(); + $this->assertEquals(FALSE, $non_reusable_block->isReusable()); + $this->assertEquals(FALSE, $non_reusable_block2->isReusable()); + + $admin_user = $this->drupalCreateUser(['administer blocks']); + $this->drupalLogin($admin_user); + + // Ensure the Custom Block view shows the reusable blocks but not + // the non-reusable block. + $this->drupalGet('admin/structure/block/block-content'); + $assert_session->statusCodeEquals('200'); + $assert_session->responseContains('view-id-block_content'); + $assert_session->pageTextContains($after_block1->label()); + $assert_session->pageTextContains($after_block2->label()); + $assert_session->pageTextNotContains($non_reusable_block->label()); + $assert_session->pageTextNotContains($non_reusable_block2->label()); + + // Ensure the views other display also filters out non-reusable blocks and + // still filters on the 'info' field. + $this->drupalGet('extra-view-display'); + $assert_session->statusCodeEquals('200'); + $assert_session->responseContains('view-id-block_content'); + $assert_session->pageTextNotContains($after_block1->label()); + $assert_session->pageTextContains($after_block2->label()); + $assert_session->pageTextNotContains($non_reusable_block->label()); + $assert_session->pageTextNotContains($non_reusable_block2->label()); + + $this->drupalGet('block/' . $after_block1->id()); + $assert_session->statusCodeEquals('200'); + + // Ensure that non-reusable blocks edit form edit route is not accessible. + $this->drupalGet('block/' . $non_reusable_block->id()); + $assert_session->statusCodeEquals('403'); + + // Ensure the Custom Block listing without Views installed shows the + // reusable blocks but not the non-reusable blocks. + // the non-reusable block. + $this->drupalGet('admin/structure/block/block-content'); + $this->container->get('module_installer')->uninstall(['views_ui', 'views']); + $this->drupalGet('admin/structure/block/block-content'); + $assert_session->statusCodeEquals('200'); + $assert_session->responseNotContains('view-id-block_content'); + $assert_session->pageTextContains($after_block1->label()); + $assert_session->pageTextContains($after_block2->label()); + $assert_session->pageTextNotContains($non_reusable_block->label()); + $assert_session->pageTextNotContains($non_reusable_block2->label()); + } + +}