diff --git a/core/modules/content_translation/tests/modules/content_translation_test_views/test_views/views.view.test_entity_translations_link.yml b/core/modules/content_translation/tests/modules/content_translation_test_views/test_views/views.view.test_entity_translations_link.yml index ffa73e8edef87028ce82217c8e094ff0d269e045..cb5f73d89ffcdc3ac198d9c538124833d503ec0f 100644 --- a/core/modules/content_translation/tests/modules/content_translation_test_views/test_views/views.view.test_entity_translations_link.yml +++ b/core/modules/content_translation/tests/modules/content_translation_test_views/test_views/views.view.test_entity_translations_link.yml @@ -1,6 +1,8 @@ langcode: en status: true dependencies: + config: + - filter.format.plain_text module: - content_translation - user diff --git a/core/modules/views/src/Plugin/views/area/Text.php b/core/modules/views/src/Plugin/views/area/Text.php index f37c7eb8b3aa4b6a5750adc1a364e491d49e9aba..9d1a354c5f15d0f6d426b26c82d21a8f77ec8b38 100644 --- a/core/modules/views/src/Plugin/views/area/Text.php +++ b/core/modules/views/src/Plugin/views/area/Text.php @@ -2,8 +2,10 @@ namespace Drupal\views\Plugin\views\area; +use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\Form\FormStateInterface; use Drupal\views\Attribute\ViewsArea; +use Symfony\Component\DependencyInjection\ContainerInterface; /** * Views area text handler. @@ -13,6 +15,22 @@ #[ViewsArea("text")] class Text extends TokenizeAreaPluginBase { + public function __construct(array $configuration, $plugin_id, $plugin_definition, protected EntityTypeManagerInterface $entityTypeManager) { + parent::__construct($configuration, $plugin_id, $plugin_definition); + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { + return new static( + $configuration, + $plugin_id, + $plugin_definition, + $container->get('entity_type.manager') + ); + } + /** * {@inheritdoc} */ @@ -38,7 +56,7 @@ public function buildOptionsForm(&$form, FormStateInterface $form_state) { '#type' => 'text_format', '#default_value' => $this->options['content']['value'], '#rows' => 6, - '#format' => $this->options['content']['format'] ?? filter_default_format(), + '#format' => $this->getFormatId(), '#editor' => FALSE, ]; } @@ -58,7 +76,7 @@ public function preQuery() { * {@inheritdoc} */ public function render($empty = FALSE) { - $format = $this->options['content']['format'] ?? filter_default_format(); + $format = $this->getFormatId(); if (!$empty || !empty($this->options['empty'])) { return [ '#type' => 'processed_text', @@ -70,4 +88,25 @@ public function render($empty = FALSE) { return []; } + /** + * {@inheritdoc} + */ + public function calculateDependencies() { + $format = $this->entityTypeManager->getStorage('filter_format') + ->load($this->getFormatId()); + return [ + $format->getConfigDependencyKey() => [$format->getConfigDependencyName()], + ]; + } + + /** + * Returns the ID of the text format used for this area. + * + * @return string + * The text format ID. + */ + protected function getFormatId() { + return $this->options['content']['format'] ?? filter_default_format(); + } + } diff --git a/core/modules/views/tests/fixtures/update/test_filter_format_dependencies.php b/core/modules/views/tests/fixtures/update/test_filter_format_dependencies.php new file mode 100644 index 0000000000000000000000000000000000000000..15db7f011a2a90be6810f4ceb2c985d7a04c5ef4 --- /dev/null +++ b/core/modules/views/tests/fixtures/update/test_filter_format_dependencies.php @@ -0,0 +1,19 @@ +<?php + +/** + * @file + * Test fixture. + */ + +use Drupal\Core\Database\Database; +use Drupal\Core\Serialization\Yaml; + +$connection = Database::getConnection(); + +$connection->insert('config') + ->fields([ + 'collection' => '', + 'name' => 'views.view.test_filter_format_dependencies', + 'data' => serialize(Yaml::decode(file_get_contents('core/modules/views/tests/fixtures/update/views.view.test_filter_format_dependencies.yml'))), + ]) + ->execute(); diff --git a/core/modules/views/tests/fixtures/update/views.view.test_entity_id_argument_update.yml b/core/modules/views/tests/fixtures/update/views.view.test_entity_id_argument_update.yml index 04081a72149a623efac6b4d723a98ff649937662..696b72c0ccc36f11734845848c6afea4fde7a0e6 100644 --- a/core/modules/views/tests/fixtures/update/views.view.test_entity_id_argument_update.yml +++ b/core/modules/views/tests/fixtures/update/views.view.test_entity_id_argument_update.yml @@ -4,6 +4,7 @@ status: true dependencies: config: - core.entity_view_mode.node.teaser + - filter.format.basic_html - node.type.article - taxonomy.vocabulary.tags module: diff --git a/core/modules/views/tests/fixtures/update/views.view.test_filter_format_dependencies.yml b/core/modules/views/tests/fixtures/update/views.view.test_filter_format_dependencies.yml new file mode 100644 index 0000000000000000000000000000000000000000..ff5d011800ab44ab268e51ca89276707f2431824 --- /dev/null +++ b/core/modules/views/tests/fixtures/update/views.view.test_filter_format_dependencies.yml @@ -0,0 +1,197 @@ +uuid: d5e0758d-7f56-4a6a-ab24-86b7d73d5801 +langcode: en +status: true +dependencies: + module: + - node + - user +id: test_filter_format_dependencies +label: test_filter_format_dependencies +module: views +description: '' +tag: '' +base_table: node_field_data +base_field: nid +display: + default: + id: default + display_title: Default + display_plugin: default + position: 0 + display_options: + title: test_filter_format_dependencies + fields: + title: + id: title + table: node_field_data + field: title + relationship: none + group_type: group + admin_label: '' + entity_type: node + entity_field: title + plugin_id: field + label: '' + exclude: false + alter: + alter_text: false + make_link: false + absolute: false + word_boundary: false + ellipsis: false + strip_tags: false + trim: false + html: false + element_type: '' + element_class: '' + element_label_type: '' + element_label_class: '' + element_label_colon: false + 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 + pager: + type: mini + options: + offset: 0 + pagination_heading_level: h4 + items_per_page: 10 + total_pages: null + id: 0 + tags: + next: ›› + previous: ‹‹ + 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 + 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 + access: + type: perm + options: + perm: 'access content' + cache: + type: tag + options: { } + empty: { } + sorts: + created: + id: created + table: node_field_data + field: created + relationship: none + group_type: group + admin_label: '' + entity_type: node + entity_field: created + plugin_id: date + order: DESC + expose: + label: '' + field_identifier: '' + exposed: false + granularity: second + arguments: { } + filters: + status: + id: status + table: node_field_data + field: status + entity_type: node + entity_field: status + plugin_id: boolean + value: '1' + group: 1 + expose: + operator: '' + style: + type: default + row: + type: fields + options: + default_field_elements: true + inline: { } + separator: '' + hide_empty: false + query: + type: views_query + options: + query_comment: '' + disable_sql_rewrite: false + distinct: false + replica: false + query_tags: { } + relationships: { } + header: + area: + id: area + table: views + field: area + relationship: none + group_type: group + admin_label: '' + plugin_id: text + empty: false + content: + value: 'Header content body' + format: basic_html + tokenize: false + footer: { } + display_extenders: { } + cache_metadata: + max-age: -1 + contexts: + - 'languages:language_content' + - 'languages:language_interface' + - url.query_args + - 'user.node_grants:view' + - user.permissions + tags: { } + page_1: + id: page_1 + display_title: Page + display_plugin: page + position: 1 + display_options: + display_extenders: { } + path: test-filter-format-dependencies + cache_metadata: + max-age: -1 + contexts: + - 'languages:language_content' + - 'languages:language_interface' + - url.query_args + - 'user.node_grants:view' + - user.permissions + tags: { } diff --git a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_entity_id_argument.yml b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_entity_id_argument.yml index c74d28db5ba698b5ef95528e2e4bc2f24a12d8af..5fde58622b3b2459aa90b7b2c1aa412218908f7c 100644 --- a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_entity_id_argument.yml +++ b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_entity_id_argument.yml @@ -3,6 +3,7 @@ status: true dependencies: config: - core.entity_view_mode.node.teaser + - filter.format.plain_text - node.type.article - taxonomy.vocabulary.tags module: @@ -156,7 +157,7 @@ display: tokenize: true content: value: 'title {{ arguments.field_views_testing_tags_target_id }}, input {{ raw_arguments.field_views_testing_tags_target_id }}' - format: basic_html + format: plain_text plugin_id: text footer: { } empty: { } diff --git a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_token_view.yml b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_token_view.yml index 7c21d5c01ce11435c3bc329407d565ad3f1a00a7..097336eafee71fbefdbe8d4a9155a08b9a670669 100644 --- a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_token_view.yml +++ b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_token_view.yml @@ -1,6 +1,8 @@ langcode: en status: true dependencies: + config: + - filter.format.basic_html module: - node - user diff --git a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_tokens.yml b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_tokens.yml index 7dc33c342b0f891ef75bd167abf4e02a85cfe6a0..19cbb51e475786afcdc1d5f9256b0dbae9c3ac54 100644 --- a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_tokens.yml +++ b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_tokens.yml @@ -1,6 +1,8 @@ langcode: en status: true -dependencies: { } +dependencies: + config: + - filter.format.plain_text id: test_tokens label: 'Test tokens' module: views @@ -105,7 +107,7 @@ display: tokenize: false content: value: "Total rows: [view:total-rows] - Page count: [view:page-count]" - format: basic_html + format: plain_text plugin_id: text page_4: display_plugin: page @@ -137,5 +139,5 @@ display: tokenize: false content: value: "Total rows: [view:total-rows]" - format: basic_html + format: plain_text plugin_id: text diff --git a/core/modules/views/tests/src/Functional/Update/FilterFormatDependencyUpdateTest.php b/core/modules/views/tests/src/Functional/Update/FilterFormatDependencyUpdateTest.php new file mode 100644 index 0000000000000000000000000000000000000000..e0bda47df7bcc569d29482768def41d069ffea29 --- /dev/null +++ b/core/modules/views/tests/src/Functional/Update/FilterFormatDependencyUpdateTest.php @@ -0,0 +1,42 @@ +<?php + +declare(strict_types=1); + +namespace Drupal\Tests\views\Functional\Update; + +use Drupal\FunctionalTests\Update\UpdatePathTestBase; +use Drupal\views\Entity\View; + +/** + * Tests the upgrade path for fixing dependencies on filter formats. + * + * @group Update + */ +class FilterFormatDependencyUpdateTest extends UpdatePathTestBase { + + /** + * {@inheritdoc} + */ + protected function setDatabaseDumpFiles(): void { + $this->databaseDumpFiles = [ + __DIR__ . '/../../../../../system/tests/fixtures/update/drupal-10.3.0.filled.standard.php.gz', + __DIR__ . '/../../../fixtures/update/test_filter_format_dependencies.php', + ]; + } + + /** + * @covers views_post_update_views_filter_format_dependencies + */ + public function testViewsFieldPluginConversion(): void { + $view = View::load('test_filter_format_dependencies'); + $data = $view->toArray(); + $this->assertArrayNotHasKey('config', $data['dependencies']); + + $this->runUpdates(); + + $view = View::load('test_filter_format_dependencies'); + $data = $view->toArray(); + $this->assertEquals(['filter.format.basic_html'], $data['dependencies']['config']); + } + +} diff --git a/core/modules/views/tests/src/Kernel/TokenReplaceTest.php b/core/modules/views/tests/src/Kernel/TokenReplaceTest.php index b90e98f3ad89440f18dbe546c07f3ea7322d137c..e55ca736bb3244b6da2f52a67789874d84de593e 100644 --- a/core/modules/views/tests/src/Kernel/TokenReplaceTest.php +++ b/core/modules/views/tests/src/Kernel/TokenReplaceTest.php @@ -18,7 +18,7 @@ class TokenReplaceTest extends ViewsKernelTestBase { /** * {@inheritdoc} */ - protected static $modules = ['system']; + protected static $modules = ['filter', 'system']; /** * Views used by this test. @@ -27,6 +27,14 @@ class TokenReplaceTest extends ViewsKernelTestBase { */ public static $testViews = ['test_tokens', 'test_invalid_tokens']; + /** + * {@inheritdoc} + */ + protected function setUpFixtures(): void { + $this->installConfig('filter'); + parent::setUpFixtures(); + } + /** * Tests core token replacements generated from a view. */ diff --git a/core/modules/views/tests/src/Unit/Plugin/area/TextTest.php b/core/modules/views/tests/src/Unit/Plugin/area/TextTest.php new file mode 100644 index 0000000000000000000000000000000000000000..3d5f3dd485dfa561ff4b1c2e634bdc12f75c5dfe --- /dev/null +++ b/core/modules/views/tests/src/Unit/Plugin/area/TextTest.php @@ -0,0 +1,50 @@ +<?php + +declare(strict_types=1); + +namespace Drupal\Tests\views\Unit\Plugin\area; + +use Drupal\Core\Entity\EntityStorageInterface; +use Drupal\Core\Entity\EntityTypeManagerInterface; +use Drupal\filter\FilterFormatInterface; +use Drupal\Tests\UnitTestCase; +use Drupal\views\Plugin\views\area\Text; + +/** + * @coversDefaultClass \Drupal\views\Plugin\views\area\Text + * @group views + */ +class TextTest extends UnitTestCase { + + /** + * @covers ::calculateDependencies + */ + public function testCalculateDependencies(): void { + $dependency_key = 'config'; + $format_id = 'test_format'; + + $format = $this->createMock(FilterFormatInterface::class); + $format->expects($this->once()) + ->method('getConfigDependencyKey') + ->willReturn($dependency_key); + $format->expects($this->once()) + ->method('getConfigDependencyName') + ->willReturn('filter.format.' . $format_id); + $format_storage = $this->createMock(EntityStorageInterface::class); + $format_storage->expects($this->once()) + ->method('load') + ->with($format_id) + ->willReturn($format); + $entity_type_manager = $this->createMock(EntityTypeManagerInterface::class); + $entity_type_manager->expects($this->once()) + ->method('getStorage') + ->with('filter_format') + ->willReturn($format_storage); + + $plugin = new Text([], 'text', [], $entity_type_manager); + $plugin->options['content']['format'] = $format_id; + $expected = [$dependency_key => ['filter.format.' . $format_id]]; + $this->assertEquals($expected, $plugin->calculateDependencies()); + } + +} diff --git a/core/modules/views/views.post_update.php b/core/modules/views/views.post_update.php index c3f352e99a4203c026860c66a84ef43f5d535d4b..10bdb2d7309e868b9eecd28d5fcbbaf35d3cf090 100644 --- a/core/modules/views/views.post_update.php +++ b/core/modules/views/views.post_update.php @@ -87,3 +87,10 @@ function views_post_update_table_css_class(?array &$sandbox = NULL): void { return $view_config_updater->needsTableCssClassUpdate($view); }); } + +/** + * Fix views with filter_format dependencies. + */ +function views_post_update_views_filter_format_dependencies(?array &$sandbox = NULL): void { + \Drupal::classResolver(ConfigEntityUpdater::class)->update($sandbox, 'view'); +} diff --git a/core/profiles/demo_umami/config/install/views.view.frontpage.yml b/core/profiles/demo_umami/config/install/views.view.frontpage.yml index b8dc220f4c8f9aaa2ef9143cfbce13edaf86e8a4..6eef474c9df918790f722467bb90a3373385e450 100644 --- a/core/profiles/demo_umami/config/install/views.view.frontpage.yml +++ b/core/profiles/demo_umami/config/install/views.view.frontpage.yml @@ -4,6 +4,7 @@ dependencies: config: - core.entity_view_mode.node.card_common - core.entity_view_mode.node.rss + - filter.format.full_html - node.type.recipe module: - node