diff --git a/composer.lock b/composer.lock index 06555cc03665309c910c79454b71aff187fe9f05..2e4118d494a5620e6f190fdca6e12d06fffbe3aa 100644 --- a/composer.lock +++ b/composer.lock @@ -2958,16 +2958,16 @@ }, { "name": "drupal/core", - "version": "9.2.7", + "version": "9.2.8", "source": { "type": "git", "url": "https://github.com/drupal/core.git", - "reference": "ce3220458c7a744bb00e9436e48d8e644e134576" + "reference": "b40ae04a0a8a8a0952e77da7545de5ee8fa40757" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/drupal/core/zipball/ce3220458c7a744bb00e9436e48d8e644e134576", - "reference": "ce3220458c7a744bb00e9436e48d8e644e134576", + "url": "https://api.github.com/repos/drupal/core/zipball/b40ae04a0a8a8a0952e77da7545de5ee8fa40757", + "reference": "b40ae04a0a8a8a0952e77da7545de5ee8fa40757", "shasum": "" }, "require": { @@ -3206,9 +3206,9 @@ ], "description": "Drupal is an open source content management platform powering millions of websites and applications.", "support": { - "source": "https://github.com/drupal/core/tree/9.2.7" + "source": "https://github.com/drupal/core/tree/9.2.8" }, - "time": "2021-10-06T10:34:39+00:00" + "time": "2021-11-03T17:25:16+00:00" }, { "name": "drupal/core-composer-scaffold", @@ -3262,16 +3262,16 @@ }, { "name": "drupal/core-recommended", - "version": "9.2.7", + "version": "9.2.8", "source": { "type": "git", "url": "https://github.com/drupal/core-recommended.git", - "reference": "87c998e8d60d6b2452b21827fb7b16f77d02a38a" + "reference": "c77ac58a8ef159065a7d28853e41f758a9cb057d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/drupal/core-recommended/zipball/87c998e8d60d6b2452b21827fb7b16f77d02a38a", - "reference": "87c998e8d60d6b2452b21827fb7b16f77d02a38a", + "url": "https://api.github.com/repos/drupal/core-recommended/zipball/c77ac58a8ef159065a7d28853e41f758a9cb057d", + "reference": "c77ac58a8ef159065a7d28853e41f758a9cb057d", "shasum": "" }, "require": { @@ -3280,7 +3280,7 @@ "doctrine/annotations": "1.13.1", "doctrine/lexer": "1.2.1", "doctrine/reflection": "1.2.2", - "drupal/core": "9.2.7", + "drupal/core": "9.2.8", "egulias/email-validator": "2.1.25", "guzzlehttp/guzzle": "6.5.5", "guzzlehttp/promises": "1.4.1", @@ -3343,9 +3343,9 @@ ], "description": "Locked core dependencies; require this project INSTEAD OF drupal/core.", "support": { - "source": "https://github.com/drupal/core-recommended/tree/9.2.7" + "source": "https://github.com/drupal/core-recommended/tree/9.2.8" }, - "time": "2021-10-06T10:34:39+00:00" + "time": "2021-11-03T17:25:16+00:00" }, { "name": "drupal/crop", diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json index f2c3cba795c6794dae580de3c484c271636a68e1..c3d3fbae53f5db3b3205afd9251efd7cf7131e8f 100644 --- a/vendor/composer/installed.json +++ b/vendor/composer/installed.json @@ -3039,17 +3039,17 @@ }, { "name": "drupal/core", - "version": "9.2.7", - "version_normalized": "9.2.7.0", + "version": "9.2.8", + "version_normalized": "9.2.8.0", "source": { "type": "git", "url": "https://github.com/drupal/core.git", - "reference": "ce3220458c7a744bb00e9436e48d8e644e134576" + "reference": "b40ae04a0a8a8a0952e77da7545de5ee8fa40757" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/drupal/core/zipball/ce3220458c7a744bb00e9436e48d8e644e134576", - "reference": "ce3220458c7a744bb00e9436e48d8e644e134576", + "url": "https://api.github.com/repos/drupal/core/zipball/b40ae04a0a8a8a0952e77da7545de5ee8fa40757", + "reference": "b40ae04a0a8a8a0952e77da7545de5ee8fa40757", "shasum": "" }, "require": { @@ -3214,7 +3214,7 @@ "drupal/workflows": "self.version", "drupal/workspaces": "self.version" }, - "time": "2021-10-06T10:34:39+00:00", + "time": "2021-11-03T17:25:16+00:00", "type": "drupal-core", "extra": { "drupal-scaffold": { @@ -3294,7 +3294,7 @@ ], "description": "Drupal is an open source content management platform powering millions of websites and applications.", "support": { - "source": "https://github.com/drupal/core/tree/9.2.7" + "source": "https://github.com/drupal/core/tree/9.2.8" }, "install-path": "../../web/core" }, @@ -3350,17 +3350,17 @@ }, { "name": "drupal/core-recommended", - "version": "9.2.7", - "version_normalized": "9.2.7.0", + "version": "9.2.8", + "version_normalized": "9.2.8.0", "source": { "type": "git", "url": "https://github.com/drupal/core-recommended.git", - "reference": "87c998e8d60d6b2452b21827fb7b16f77d02a38a" + "reference": "c77ac58a8ef159065a7d28853e41f758a9cb057d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/drupal/core-recommended/zipball/87c998e8d60d6b2452b21827fb7b16f77d02a38a", - "reference": "87c998e8d60d6b2452b21827fb7b16f77d02a38a", + "url": "https://api.github.com/repos/drupal/core-recommended/zipball/c77ac58a8ef159065a7d28853e41f758a9cb057d", + "reference": "c77ac58a8ef159065a7d28853e41f758a9cb057d", "shasum": "" }, "require": { @@ -3369,7 +3369,7 @@ "doctrine/annotations": "1.13.1", "doctrine/lexer": "1.2.1", "doctrine/reflection": "1.2.2", - "drupal/core": "9.2.7", + "drupal/core": "9.2.8", "egulias/email-validator": "2.1.25", "guzzlehttp/guzzle": "6.5.5", "guzzlehttp/promises": "1.4.1", @@ -3425,7 +3425,7 @@ "conflict": { "webflo/drupal-core-strict": "*" }, - "time": "2021-10-06T10:34:39+00:00", + "time": "2021-11-03T17:25:16+00:00", "type": "metapackage", "notification-url": "https://packagist.org/downloads/", "license": [ @@ -3433,7 +3433,7 @@ ], "description": "Locked core dependencies; require this project INSTEAD OF drupal/core.", "support": { - "source": "https://github.com/drupal/core-recommended/tree/9.2.7" + "source": "https://github.com/drupal/core-recommended/tree/9.2.8" }, "install-path": null }, diff --git a/vendor/composer/installed.php b/vendor/composer/installed.php index 1e63755e084feeb5a42af11b20d70a011fb5cf3b..ddc65fc6a3bbd8b9aa0b61b9f468a5381a77d3f8 100644 --- a/vendor/composer/installed.php +++ b/vendor/composer/installed.php @@ -5,7 +5,7 @@ 'type' => 'project', 'install_path' => __DIR__ . '/../../', 'aliases' => array(), - 'reference' => '5baa0f240fa9db4ac35dde696ec3fe32e6e3eec2', + 'reference' => 'df61ac4c80357a6ff9e6e56957f55630f42d3212', 'name' => 'osu-asc-webservices/d8-upstream', 'dev' => true, ), @@ -271,7 +271,7 @@ 'drupal/action' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '9.2.7', + 0 => '9.2.8', ), ), 'drupal/addtocalendar' => array( @@ -304,7 +304,7 @@ 'drupal/aggregator' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '9.2.7', + 0 => '9.2.8', ), ), 'drupal/allowed_formats' => array( @@ -328,25 +328,25 @@ 'drupal/automated_cron' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '9.2.7', + 0 => '9.2.8', ), ), 'drupal/ban' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '9.2.7', + 0 => '9.2.8', ), ), 'drupal/bartik' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '9.2.7', + 0 => '9.2.8', ), ), 'drupal/basic_auth' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '9.2.7', + 0 => '9.2.8', ), ), 'drupal/better_exposed_filters' => array( @@ -361,19 +361,19 @@ 'drupal/big_pipe' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '9.2.7', + 0 => '9.2.8', ), ), 'drupal/block' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '9.2.7', + 0 => '9.2.8', ), ), 'drupal/block_content' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '9.2.7', + 0 => '9.2.8', ), ), 'drupal/block_field' => array( @@ -406,7 +406,7 @@ 'drupal/book' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '9.2.7', + 0 => '9.2.8', ), ), 'drupal/bootstrap' => array( @@ -421,7 +421,7 @@ 'drupal/breakpoint' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '9.2.7', + 0 => '9.2.8', ), ), 'drupal/cache_control_override' => array( @@ -445,7 +445,7 @@ 'drupal/ckeditor' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '9.2.7', + 0 => '9.2.8', ), ), 'drupal/ckeditor_indentblock' => array( @@ -460,31 +460,31 @@ 'drupal/claro' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '9.2.7', + 0 => '9.2.8', ), ), 'drupal/classy' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '9.2.7', + 0 => '9.2.8', ), ), 'drupal/color' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '9.2.7', + 0 => '9.2.8', ), ), 'drupal/comment' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '9.2.7', + 0 => '9.2.8', ), ), 'drupal/config' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '9.2.7', + 0 => '9.2.8', ), ), 'drupal/config_direct_save' => array( @@ -517,7 +517,7 @@ 'drupal/config_translation' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '9.2.7', + 0 => '9.2.8', ), ), 'drupal/config_update' => array( @@ -568,7 +568,7 @@ 'drupal/contact' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '9.2.7', + 0 => '9.2.8', ), ), 'drupal/content_access' => array( @@ -583,52 +583,52 @@ 'drupal/content_moderation' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '9.2.7', + 0 => '9.2.8', ), ), 'drupal/content_translation' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '9.2.7', + 0 => '9.2.8', ), ), 'drupal/contextual' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '9.2.7', + 0 => '9.2.8', ), ), 'drupal/core' => array( - 'pretty_version' => '9.2.7', - 'version' => '9.2.7.0', + 'pretty_version' => '9.2.8', + 'version' => '9.2.8.0', 'type' => 'drupal-core', 'install_path' => __DIR__ . '/../../web/core', 'aliases' => array(), - 'reference' => 'ce3220458c7a744bb00e9436e48d8e644e134576', + 'reference' => 'b40ae04a0a8a8a0952e77da7545de5ee8fa40757', 'dev_requirement' => false, ), 'drupal/core-annotation' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '9.2.7', + 0 => '9.2.8', ), ), 'drupal/core-assertion' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '9.2.7', + 0 => '9.2.8', ), ), 'drupal/core-bridge' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '9.2.7', + 0 => '9.2.8', ), ), 'drupal/core-class-finder' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '9.2.7', + 0 => '9.2.8', ), ), 'drupal/core-composer-scaffold' => array( @@ -643,136 +643,136 @@ 'drupal/core-datetime' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '9.2.7', + 0 => '9.2.8', ), ), 'drupal/core-dependency-injection' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '9.2.7', + 0 => '9.2.8', ), ), 'drupal/core-diff' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '9.2.7', + 0 => '9.2.8', ), ), 'drupal/core-discovery' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '9.2.7', + 0 => '9.2.8', ), ), 'drupal/core-event-dispatcher' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '9.2.7', + 0 => '9.2.8', ), ), 'drupal/core-file-cache' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '9.2.7', + 0 => '9.2.8', ), ), 'drupal/core-file-security' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '9.2.7', + 0 => '9.2.8', ), ), 'drupal/core-filesystem' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '9.2.7', + 0 => '9.2.8', ), ), 'drupal/core-front-matter' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '9.2.7', + 0 => '9.2.8', ), ), 'drupal/core-gettext' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '9.2.7', + 0 => '9.2.8', ), ), 'drupal/core-graph' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '9.2.7', + 0 => '9.2.8', ), ), 'drupal/core-http-foundation' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '9.2.7', + 0 => '9.2.8', ), ), 'drupal/core-php-storage' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '9.2.7', + 0 => '9.2.8', ), ), 'drupal/core-plugin' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '9.2.7', + 0 => '9.2.8', ), ), 'drupal/core-proxy-builder' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '9.2.7', + 0 => '9.2.8', ), ), 'drupal/core-recommended' => array( - 'pretty_version' => '9.2.7', - 'version' => '9.2.7.0', + 'pretty_version' => '9.2.8', + 'version' => '9.2.8.0', 'type' => 'metapackage', 'install_path' => NULL, 'aliases' => array(), - 'reference' => '87c998e8d60d6b2452b21827fb7b16f77d02a38a', + 'reference' => 'c77ac58a8ef159065a7d28853e41f758a9cb057d', 'dev_requirement' => false, ), 'drupal/core-render' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '9.2.7', + 0 => '9.2.8', ), ), 'drupal/core-serialization' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '9.2.7', + 0 => '9.2.8', ), ), 'drupal/core-transliteration' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '9.2.7', + 0 => '9.2.8', ), ), 'drupal/core-utility' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '9.2.7', + 0 => '9.2.8', ), ), 'drupal/core-uuid' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '9.2.7', + 0 => '9.2.8', ), ), 'drupal/core-version' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '9.2.7', + 0 => '9.2.8', ), ), 'drupal/crop' => array( @@ -796,19 +796,19 @@ 'drupal/datetime' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '9.2.7', + 0 => '9.2.8', ), ), 'drupal/datetime_range' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '9.2.7', + 0 => '9.2.8', ), ), 'drupal/dblog' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '9.2.7', + 0 => '9.2.8', ), ), 'drupal/dropzonejs' => array( @@ -832,13 +832,13 @@ 'drupal/dynamic_page_cache' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '9.2.7', + 0 => '9.2.8', ), ), 'drupal/editor' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '9.2.7', + 0 => '9.2.8', ), ), 'drupal/editor_advanced_link' => array( @@ -907,7 +907,7 @@ 'drupal/entity_reference' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '9.2.7', + 0 => '9.2.8', ), ), 'drupal/entity_reference_revisions' => array( @@ -931,7 +931,7 @@ 'drupal/field' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '9.2.7', + 0 => '9.2.8', ), ), 'drupal/field_group' => array( @@ -946,7 +946,7 @@ 'drupal/field_layout' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '9.2.7', + 0 => '9.2.8', ), ), 'drupal/field_permissions' => array( @@ -961,13 +961,13 @@ 'drupal/field_ui' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '9.2.7', + 0 => '9.2.8', ), ), 'drupal/file' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '9.2.7', + 0 => '9.2.8', ), ), 'drupal/file_browser' => array( @@ -982,7 +982,7 @@ 'drupal/filter' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '9.2.7', + 0 => '9.2.8', ), ), 'drupal/focal_point' => array( @@ -997,7 +997,7 @@ 'drupal/forum' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '9.2.7', + 0 => '9.2.8', ), ), 'drupal/google_analytics' => array( @@ -1021,25 +1021,25 @@ 'drupal/hal' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '9.2.7', + 0 => '9.2.8', ), ), 'drupal/help' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '9.2.7', + 0 => '9.2.8', ), ), 'drupal/help_topics' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '9.2.7', + 0 => '9.2.8', ), ), 'drupal/history' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '9.2.7', + 0 => '9.2.8', ), ), 'drupal/honeypot' => array( @@ -1054,7 +1054,7 @@ 'drupal/image' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '9.2.7', + 0 => '9.2.8', ), ), 'drupal/inline_entity_form' => array( @@ -1069,7 +1069,7 @@ 'drupal/inline_form_errors' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '9.2.7', + 0 => '9.2.8', ), ), 'drupal/jquery_ui' => array( @@ -1111,25 +1111,25 @@ 'drupal/jsonapi' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '9.2.7', + 0 => '9.2.8', ), ), 'drupal/language' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '9.2.7', + 0 => '9.2.8', ), ), 'drupal/layout_builder' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '9.2.7', + 0 => '9.2.8', ), ), 'drupal/layout_discovery' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '9.2.7', + 0 => '9.2.8', ), ), 'drupal/libraries' => array( @@ -1144,7 +1144,7 @@ 'drupal/link' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '9.2.7', + 0 => '9.2.8', ), ), 'drupal/link_attributes' => array( @@ -1168,7 +1168,7 @@ 'drupal/locale' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '9.2.7', + 0 => '9.2.8', ), ), 'drupal/mathjax' => array( @@ -1183,7 +1183,7 @@ 'drupal/media' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '9.2.7', + 0 => '9.2.8', ), ), 'drupal/media_entity_browser' => array( @@ -1207,7 +1207,7 @@ 'drupal/media_library' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '9.2.7', + 0 => '9.2.8', ), ), 'drupal/menu_block' => array( @@ -1240,13 +1240,13 @@ 'drupal/menu_link_content' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '9.2.7', + 0 => '9.2.8', ), ), 'drupal/menu_ui' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '9.2.7', + 0 => '9.2.8', ), ), 'drupal/metatag' => array( @@ -1261,7 +1261,7 @@ 'drupal/migrate' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '9.2.7', + 0 => '9.2.8', ), ), 'drupal/migrate_devel' => array( @@ -1276,19 +1276,19 @@ 'drupal/migrate_drupal' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '9.2.7', + 0 => '9.2.8', ), ), 'drupal/migrate_drupal_multilingual' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '9.2.7', + 0 => '9.2.8', ), ), 'drupal/migrate_drupal_ui' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '9.2.7', + 0 => '9.2.8', ), ), 'drupal/migrate_plus' => array( @@ -1312,7 +1312,7 @@ 'drupal/minimal' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '9.2.7', + 0 => '9.2.8', ), ), 'drupal/mobile_detect' => array( @@ -1354,25 +1354,25 @@ 'drupal/node' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '9.2.7', + 0 => '9.2.8', ), ), 'drupal/olivero' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '9.2.7', + 0 => '9.2.8', ), ), 'drupal/options' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '9.2.7', + 0 => '9.2.8', ), ), 'drupal/page_cache' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '9.2.7', + 0 => '9.2.8', ), ), 'drupal/pantheon_advanced_page_cache' => array( @@ -1396,13 +1396,13 @@ 'drupal/path' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '9.2.7', + 0 => '9.2.8', ), ), 'drupal/path_alias' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '9.2.7', + 0 => '9.2.8', ), ), 'drupal/pathauto' => array( @@ -1426,13 +1426,13 @@ 'drupal/quickedit' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '9.2.7', + 0 => '9.2.8', ), ), 'drupal/rdf' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '9.2.7', + 0 => '9.2.8', ), ), 'drupal/rebuild_cache_access' => array( @@ -1474,13 +1474,13 @@ 'drupal/responsive_image' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '9.2.7', + 0 => '9.2.8', ), ), 'drupal/rest' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '9.2.7', + 0 => '9.2.8', ), ), 'drupal/roleassign' => array( @@ -1504,7 +1504,7 @@ 'drupal/search' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '9.2.7', + 0 => '9.2.8', ), ), 'drupal/search_api' => array( @@ -1528,25 +1528,25 @@ 'drupal/serialization' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '9.2.7', + 0 => '9.2.8', ), ), 'drupal/settings_tray' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '9.2.7', + 0 => '9.2.8', ), ), 'drupal/seven' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '9.2.7', + 0 => '9.2.8', ), ), 'drupal/shortcut' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '9.2.7', + 0 => '9.2.8', ), ), 'drupal/simple_gmap' => array( @@ -1615,19 +1615,19 @@ 'drupal/standard' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '9.2.7', + 0 => '9.2.8', ), ), 'drupal/stark' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '9.2.7', + 0 => '9.2.8', ), ), 'drupal/statistics' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '9.2.7', + 0 => '9.2.8', ), ), 'drupal/superfish' => array( @@ -1651,31 +1651,31 @@ 'drupal/syslog' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '9.2.7', + 0 => '9.2.8', ), ), 'drupal/system' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '9.2.7', + 0 => '9.2.8', ), ), 'drupal/taxonomy' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '9.2.7', + 0 => '9.2.8', ), ), 'drupal/telephone' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '9.2.7', + 0 => '9.2.8', ), ), 'drupal/text' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '9.2.7', + 0 => '9.2.8', ), ), 'drupal/token' => array( @@ -1690,19 +1690,19 @@ 'drupal/toolbar' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '9.2.7', + 0 => '9.2.8', ), ), 'drupal/tour' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '9.2.7', + 0 => '9.2.8', ), ), 'drupal/tracker' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '9.2.7', + 0 => '9.2.8', ), ), 'drupal/twig_tweak' => array( @@ -1735,13 +1735,13 @@ 'drupal/update' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '9.2.7', + 0 => '9.2.8', ), ), 'drupal/user' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '9.2.7', + 0 => '9.2.8', ), ), 'drupal/userprotect' => array( @@ -1774,7 +1774,7 @@ 'drupal/views' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '9.2.7', + 0 => '9.2.8', ), ), 'drupal/views_ajax_history' => array( @@ -1834,7 +1834,7 @@ 'drupal/views_ui' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '9.2.7', + 0 => '9.2.8', ), ), 'drupal/webform' => array( @@ -1849,13 +1849,13 @@ 'drupal/workflows' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '9.2.7', + 0 => '9.2.8', ), ), 'drupal/workspaces' => array( 'dev_requirement' => false, 'replaced' => array( - 0 => '9.2.7', + 0 => '9.2.8', ), ), 'drush-ops/behat-drush-endpoint' => array( @@ -2104,7 +2104,7 @@ 'type' => 'project', 'install_path' => __DIR__ . '/../../', 'aliases' => array(), - 'reference' => '5baa0f240fa9db4ac35dde696ec3fe32e6e3eec2', + 'reference' => 'df61ac4c80357a6ff9e6e56957f55630f42d3212', 'dev_requirement' => false, ), 'pantheon-systems/quicksilver-pushback' => array( diff --git a/web/core/lib/Drupal.php b/web/core/lib/Drupal.php index 286a13668d9ba9179ed253c2404502cdf6778375..b4b1be906e0db56ac269cc5e4895f1010f05437b 100644 --- a/web/core/lib/Drupal.php +++ b/web/core/lib/Drupal.php @@ -75,7 +75,7 @@ class Drupal { /** * The current system version. */ - const VERSION = '9.2.7'; + const VERSION = '9.2.8'; /** * Core API compatibility. diff --git a/web/core/lib/Drupal/Core/Asset/CssCollectionOptimizer.php b/web/core/lib/Drupal/Core/Asset/CssCollectionOptimizer.php index 62dff2c7f92aec3f2a1faab1a563175677547404..7d47dad86e6ce688188f5f9a24647c06f9733524 100644 --- a/web/core/lib/Drupal/Core/Asset/CssCollectionOptimizer.php +++ b/web/core/lib/Drupal/Core/Asset/CssCollectionOptimizer.php @@ -123,8 +123,12 @@ public function optimize(array $css_assets) { // Per the W3C specification at // http://www.w3.org/TR/REC-CSS2/cascade.html#at-import, @import // rules must precede any other style, so we move those to the - // top. - $regexp = '/@import[^;]+;/i'; + // top. The regular expression is expressed in NOWDOC since it is + // detecting backslashes as well as single and double quotes. It + // is difficult to read when represented as a quoted string. + $regexp = <<<'REGEXP' +/@import\s*(?:'(?:\\'|.)*'|"(?:\\"|.)*"|url\(\s*(?:\\[\)\'\"]|[^'")])*\s*\)|url\(\s*'(?:\'|.)*'\s*\)|url\(\s*"(?:\"|.)*"\s*\)).*;/iU +REGEXP; preg_match_all($regexp, $data, $matches); $data = preg_replace($regexp, '', $data); $data = implode('', $matches[0]) . $data; diff --git a/web/core/lib/Drupal/Core/Asset/CssOptimizer.php b/web/core/lib/Drupal/Core/Asset/CssOptimizer.php index e939b3d0542f16fcb700dfc36b0b24fed1a3d45c..0aec443f987e0703d11f3678fb1d08b8466c8291 100644 --- a/web/core/lib/Drupal/Core/Asset/CssOptimizer.php +++ b/web/core/lib/Drupal/Core/Asset/CssOptimizer.php @@ -166,7 +166,7 @@ protected function loadNestedFile($matches) { // the url() path. $directory = $directory == '.' ? '' : $directory . '/'; - // Alter all internal url() paths. Leave external paths alone. We don't need + // Alter all internal asset paths. Leave external paths alone. We don't need // to normalize absolute paths here because that will be done later. return preg_replace('/url\(\s*([\'"]?)(?![a-z]+:|\/+)([^\'")]+)([\'"]?)\s*\)/i', 'url(\1' . $directory . '\2\3)', $file); } @@ -230,7 +230,9 @@ protected function processCss($contents, $optimize = FALSE) { } // Replaces @import commands with the actual stylesheet content. - // This happens recursively but omits external files. + // This happens recursively but omits external files and local files + // with supports- or media-query qualifiers, as those are conditionally + // loaded depending on the user agent. $contents = preg_replace_callback('/@import\s*(?:url\(\s*)?[\'"]?(?![a-z]+:)(?!\/\/)([^\'"\()]+)[\'"]?\s*\)?\s*;/', [$this, 'loadNestedFile'], $contents); return $contents; diff --git a/web/core/lib/Drupal/Core/Logger/LogMessageParser.php b/web/core/lib/Drupal/Core/Logger/LogMessageParser.php index 690847e7d5556ad66225059109d60f174085b188..dc0ba4fdc5ffeace1bd4c32b39d90e5162ab5c82 100644 --- a/web/core/lib/Drupal/Core/Logger/LogMessageParser.php +++ b/web/core/lib/Drupal/Core/Logger/LogMessageParser.php @@ -17,7 +17,7 @@ public function parseMessagePlaceholders(&$message, array &$context) { $has_psr3 = TRUE; // Transform PSR3 style messages containing placeholders to // \Drupal\Component\Render\FormattableMarkup style. - $message = preg_replace('/\{(.*)\}/U', '@$1', $message); + $message = preg_replace('/\{([^\{}]*)\}/U', '@$1', $message); } foreach ($context as $key => $variable) { // PSR3 style placeholders. diff --git a/web/core/lib/Drupal/Core/Render/Element/MachineName.php b/web/core/lib/Drupal/Core/Render/Element/MachineName.php index 9e0532646861ca54931f8095ff5ef41760f10075..555a044c0e29f56cfd4db0d896141e179a44d954 100644 --- a/web/core/lib/Drupal/Core/Render/Element/MachineName.php +++ b/web/core/lib/Drupal/Core/Render/Element/MachineName.php @@ -44,7 +44,8 @@ * - error: (optional) A custom form error message string to show, if the * machine name contains disallowed characters. * - standalone: (optional) Whether the live preview should stay in its own - * form element rather than in the suffix of the source element. Defaults + * form element rather than in the suffix of the source element. The source + * element must appear in the form structure before this element. Defaults * to FALSE. * - #maxlength: (optional) Maximum allowed length of the machine name. Defaults * to 64. @@ -183,6 +184,13 @@ public static function processMachineName(&$element, FormStateInterface $form_st return $element; } + // The source element must be defined before the machine name element. + if (!isset($source['#id'])) { + $element_parents = implode('][', $element['#array_parents']); + $source_parents = implode('][', $element['#machine_name']['source']); + throw new \LogicException(sprintf('The machine name element "%s" is defined before the source element "%s", it must be defined after or the source element must specify an id.', $element_parents, $source_parents)); + } + $suffix_id = $source['#id'] . '-machine-name-suffix'; $element['#machine_name']['suffix'] = '#' . $suffix_id; diff --git a/web/core/lib/Drupal/Core/Render/HtmlResponseAttachmentsProcessor.php b/web/core/lib/Drupal/Core/Render/HtmlResponseAttachmentsProcessor.php index d698604b15bf9bfc6716b3fabb7ee6259a5a7e39..2dc416f4bd7b19a8a9b55c0b35efc58c851ac539 100644 --- a/web/core/lib/Drupal/Core/Render/HtmlResponseAttachmentsProcessor.php +++ b/web/core/lib/Drupal/Core/Render/HtmlResponseAttachmentsProcessor.php @@ -436,7 +436,15 @@ protected function processHtmlHeadLink(array $html_head_link) { '#attributes' => $attributes, ]; $href = $attributes['href']; - $attached['html_head'][] = [$element, 'html_head_link:' . $attributes['rel'] . ':' . $href]; + $rel = $attributes['rel']; + + // Allow multiple hreflang tags to use the same href. + if (isset($attributes['hreflang'])) { + $attached['html_head'][] = [$element, 'html_head_link:' . $rel . ':' . $attributes['hreflang'] . ':' . $href]; + } + else { + $attached['html_head'][] = [$element, 'html_head_link:' . $rel . ':' . $href]; + } if ($should_add_header) { // Also add a HTTP header "Link:". diff --git a/web/core/lib/Drupal/Core/Test/TestRunnerKernel.php b/web/core/lib/Drupal/Core/Test/TestRunnerKernel.php index 85d84b75a7bd0f2ee72a94e5bd979bdf906c37a1..2315693eacf442ce5d7c5d44a8f13a73a688d7d3 100644 --- a/web/core/lib/Drupal/Core/Test/TestRunnerKernel.php +++ b/web/core/lib/Drupal/Core/Test/TestRunnerKernel.php @@ -87,8 +87,8 @@ public function boot() { // Create the build/artifacts directory if necessary. include_once $this->getAppRoot() . '/core/includes/file.inc'; - if (!is_dir('public://simpletest')) { - mkdir('public://simpletest', 0777, TRUE); + if (!is_dir('public://simpletest') && !@mkdir('public://simpletest', 0777, TRUE) && !is_dir('public://simpletest')) { + throw new \RuntimeException('Unable to create directory: public://simpletest'); } } diff --git a/web/core/misc/cspell/dictionary.txt b/web/core/misc/cspell/dictionary.txt index 80d6cb2fc6795b4bea1a56410f88a70899180164..9a23b6260e4df9ddd922147c7b5e5df354c0c800 100644 --- a/web/core/misc/cspell/dictionary.txt +++ b/web/core/misc/cspell/dictionary.txt @@ -1045,6 +1045,7 @@ nothere notnull notsimpletest nourriture +nowdoc nplurals nresponse ntfs diff --git a/web/core/modules/block/src/Plugin/migrate/source/Block.php b/web/core/modules/block/src/Plugin/migrate/source/Block.php index 20b128c3a06170653a9dded4862cdb3fab214c2f..16fd6cd9aa251e493c393231971dc4b71de7b07a 100644 --- a/web/core/modules/block/src/Plugin/migrate/source/Block.php +++ b/web/core/modules/block/src/Plugin/migrate/source/Block.php @@ -6,9 +6,10 @@ use Drupal\migrate_drupal\Plugin\migrate\source\DrupalSqlBase; /** - * Drupal block source from database. + * Drupal 6/7 block source from database. + * + * For available configuration keys, refer to the parent classes. * - * For available configuration keys, refer to the parent classes: * @see \Drupal\migrate\Plugin\migrate\source\SqlBase * @see \Drupal\migrate\Plugin\migrate\source\SourcePluginBase * diff --git a/web/core/modules/block/src/Plugin/migrate/source/d6/BlockTranslation.php b/web/core/modules/block/src/Plugin/migrate/source/d6/BlockTranslation.php index 007c25d28946778fc0d06e9e0019920d320e3bc4..6cfa1d6ffcd6041fc67d9159f3dbd5587717344f 100644 --- a/web/core/modules/block/src/Plugin/migrate/source/d6/BlockTranslation.php +++ b/web/core/modules/block/src/Plugin/migrate/source/d6/BlockTranslation.php @@ -7,9 +7,10 @@ use Drupal\migrate\Row; /** - * Gets i18n block data from source database. + * Drupal 6 i18n block data from database. + * + * For available configuration keys, refer to the parent classes. * - * For available configuration keys, refer to the parent classes: * @see \Drupal\migrate\Plugin\migrate\source\SqlBase * @see \Drupal\migrate\Plugin\migrate\source\SourcePluginBase * diff --git a/web/core/modules/block/src/Plugin/migrate/source/d7/BlockTranslation.php b/web/core/modules/block/src/Plugin/migrate/source/d7/BlockTranslation.php index 132599357b6d6ef90611713e62d9e2e38b6a6cf8..8eb01c8015ccb515dd5cbfe3dec9cab95ae8072e 100644 --- a/web/core/modules/block/src/Plugin/migrate/source/d7/BlockTranslation.php +++ b/web/core/modules/block/src/Plugin/migrate/source/d7/BlockTranslation.php @@ -5,9 +5,10 @@ use Drupal\block\Plugin\migrate\source\Block; /** - * Gets i18n block data from source database. + * Drupal 7 i18n block data from database. + * + * For available configuration keys, refer to the parent classes. * - * For available configuration keys, refer to the parent classes: * @see \Drupal\migrate\Plugin\migrate\source\SqlBase * @see \Drupal\migrate\Plugin\migrate\source\SourcePluginBase * diff --git a/web/core/modules/block_content/src/Plugin/migrate/source/d6/Box.php b/web/core/modules/block_content/src/Plugin/migrate/source/d6/Box.php index 1e8a88d471ee89d83f0b2914effb19a4467d57a0..f264f20892277b66172fd4f6ae204091e67680be 100644 --- a/web/core/modules/block_content/src/Plugin/migrate/source/d6/Box.php +++ b/web/core/modules/block_content/src/Plugin/migrate/source/d6/Box.php @@ -7,7 +7,8 @@ /** * Drupal 6 block source from database. * - * For available configuration keys, refer to the parent classes: + * For available configuration keys, refer to the parent classes. + * * @see \Drupal\migrate\Plugin\migrate\source\SqlBase * @see \Drupal\migrate\Plugin\migrate\source\SourcePluginBase * diff --git a/web/core/modules/block_content/src/Plugin/migrate/source/d6/BoxTranslation.php b/web/core/modules/block_content/src/Plugin/migrate/source/d6/BoxTranslation.php index 50685e3b3dad4dfb5211562f5f33d5abeaace9fb..234af073c01bd900fe3a3c86a2bb10fd499e80c3 100644 --- a/web/core/modules/block_content/src/Plugin/migrate/source/d6/BoxTranslation.php +++ b/web/core/modules/block_content/src/Plugin/migrate/source/d6/BoxTranslation.php @@ -5,9 +5,10 @@ use Drupal\block_content\Plugin\migrate\source\d7\BlockCustomTranslation as D7BlockCustomTranslation; /** - * Gets Drupal 6 i18n custom block translations from database. + * Drupal 6 i18n custom block translations source from database. + * + * For available configuration keys, refer to the parent classes. * - * For available configuration keys, refer to the parent classes: * @see \Drupal\migrate\Plugin\migrate\source\SqlBase * @see \Drupal\migrate\Plugin\migrate\source\SourcePluginBase * diff --git a/web/core/modules/block_content/src/Plugin/migrate/source/d7/BlockCustom.php b/web/core/modules/block_content/src/Plugin/migrate/source/d7/BlockCustom.php index 4b9c5e89cca8345ffa856491c6c979b6d59d56fe..70a0711f4030c95d67660f15831019b175d17155 100644 --- a/web/core/modules/block_content/src/Plugin/migrate/source/d7/BlockCustom.php +++ b/web/core/modules/block_content/src/Plugin/migrate/source/d7/BlockCustom.php @@ -7,7 +7,8 @@ /** * Drupal 7 custom block source from database. * - * For available configuration keys, refer to the parent classes: + * For available configuration keys, refer to the parent classes. + * * @see \Drupal\migrate\Plugin\migrate\source\SqlBase * @see \Drupal\migrate\Plugin\migrate\source\SourcePluginBase * diff --git a/web/core/modules/block_content/src/Plugin/migrate/source/d7/BlockCustomTranslation.php b/web/core/modules/block_content/src/Plugin/migrate/source/d7/BlockCustomTranslation.php index a297ff3cea8c662c090767cef1beac9a26d613c6..8298a0ee939efde1c3340667d1b3635e0cbf8a31 100644 --- a/web/core/modules/block_content/src/Plugin/migrate/source/d7/BlockCustomTranslation.php +++ b/web/core/modules/block_content/src/Plugin/migrate/source/d7/BlockCustomTranslation.php @@ -7,9 +7,10 @@ use Drupal\content_translation\Plugin\migrate\source\I18nQueryTrait; /** - * Gets Drupal 7 i18n custom block translations from database. + * Drupal 7 i18n custom block translations source from database. + * + * For available configuration keys, refer to the parent classes. * - * For available configuration keys, refer to the parent classes: * @see \Drupal\migrate\Plugin\migrate\source\SqlBase * @see \Drupal\migrate\Plugin\migrate\source\SourcePluginBase * diff --git a/web/core/modules/ckeditor/src/Plugin/CKEditorPlugin/Internal.php b/web/core/modules/ckeditor/src/Plugin/CKEditorPlugin/Internal.php index 8b4dbe098013ad70fd6fb172b2dc92229016236d..135cdee2902f0b9dd4c1a90c2354f55f4f86b80a 100644 --- a/web/core/modules/ckeditor/src/Plugin/CKEditorPlugin/Internal.php +++ b/web/core/modules/ckeditor/src/Plugin/CKEditorPlugin/Internal.php @@ -116,8 +116,8 @@ public function getConfig(Editor $editor) { // Add the format_tags setting, if its button is enabled. $toolbar_buttons = CKEditorPluginManager::getEnabledButtons($editor); - if (in_array('Format', $toolbar_buttons)) { - $config['format_tags'] = $this->generateFormatTagsSetting($editor); + if (in_array('Format', $toolbar_buttons) && $format_string = $this->generateFormatTagsSetting($editor)) { + $config['format_tags'] = $format_string; } return $config; @@ -344,14 +344,15 @@ public function getButtons() { * @param \Drupal\editor\Entity\Editor $editor * A configured text editor object. * - * @return array - * An array containing the "format_tags" configuration. + * @return string|false + * A string containing the "format_tags" configuration or FALSE if the + * editor has not an associated filter format. */ protected function generateFormatTagsSetting(Editor $editor) { // When no text format is associated yet, assume no tag is allowed. // @see \Drupal\editor\EditorInterface::hasAssociatedFilterFormat() if (!$editor->hasAssociatedFilterFormat()) { - return []; + return FALSE; } $format = $editor->getFilterFormat(); diff --git a/web/core/modules/config_translation/src/Plugin/migrate/source/d6/ProfileFieldTranslation.php b/web/core/modules/config_translation/src/Plugin/migrate/source/d6/ProfileFieldTranslation.php index 2ccb43cb6106dbf6a8d9ad08f5006c9057c588fe..11215b4403eeed58fe0715a4347515e9ae1ece54 100644 --- a/web/core/modules/config_translation/src/Plugin/migrate/source/d6/ProfileFieldTranslation.php +++ b/web/core/modules/config_translation/src/Plugin/migrate/source/d6/ProfileFieldTranslation.php @@ -7,7 +7,8 @@ /** * Drupal 6 i18n strings profile field source from database. * - * For available configuration keys, refer to the parent classes: + * For available configuration keys, refer to the parent classes. + * * @see \Drupal\migrate\Plugin\migrate\source\SqlBase * @see \Drupal\migrate\Plugin\migrate\source\SourcePluginBase * diff --git a/web/core/modules/content_translation/migrations/d7_language_content_comment_settings.yml b/web/core/modules/content_translation/migrations/d7_language_content_comment_settings.yml index c40173d4fca9f38676cb7c60827e07bca50cd7f5..4515535d13242b6d80cffc1b72fff1c7af2287d2 100644 --- a/web/core/modules/content_translation/migrations/d7_language_content_comment_settings.yml +++ b/web/core/modules/content_translation/migrations/d7_language_content_comment_settings.yml @@ -8,22 +8,17 @@ source: plugin: d7_language_content_settings constants: target_type: comment - id_prefix: comment_node_ - label_suffix: comment process: target_bundle: - - plugin: concat + plugin: migration_lookup + migration: d7_comment_type + no_stub: true source: - - constants/id_prefix - type - - plugin: static_map - bypass: true - # The Forum module provides its own comment type (comment_forum), which we - # want to reuse if it exists. - map: - comment_node_forum: comment_forum + plugin: skip_on_empty + method: row target_entity_type_id: constants/target_type default_langcode: - diff --git a/web/core/modules/content_translation/migrations/d7_taxonomy_term_translation.yml b/web/core/modules/content_translation/migrations/d7_taxonomy_term_translation.yml index a964a26f3117d4f82abb45fdaf0e61c8c3258aa5..10091a8e7607744fc7a3d6169afafd94974f212b 100644 --- a/web/core/modules/content_translation/migrations/d7_taxonomy_term_translation.yml +++ b/web/core/modules/content_translation/migrations/d7_taxonomy_term_translation.yml @@ -8,18 +8,6 @@ source: plugin: d7_taxonomy_term_translation translations: true process: - skip: - - - plugin: static_map - source: i18n_mode - default_value: 0 - map: - 1: 0 - 2: 2 - 4: 4 - - - plugin: skip_on_empty - method: row # If you are using this file to build a custom migration consider removing # the tid field to allow incremental migrations. tid: tid diff --git a/web/core/modules/dblog/tests/src/Functional/DbLogTest.php b/web/core/modules/dblog/tests/src/Functional/DbLogTest.php index e3b7931519ae0760f2ece83724b6591dcb4470c8..1e57cde952b69f3ab99ca101ea1ade89d7e0f898 100644 --- a/web/core/modules/dblog/tests/src/Functional/DbLogTest.php +++ b/web/core/modules/dblog/tests/src/Functional/DbLogTest.php @@ -245,6 +245,23 @@ public function testLogEventPageWithMissingInfo() { $this->assertSession()->linkNotExists($request_uri); } + /** + * Test that twig errors are displayed correctly. + */ + public function testMessageParsing() { + $this->drupalLogin($this->adminUser); + // Log a common twig error with {{ }} and { } variables. + \Drupal::service('logger.factory')->get("php") + ->error('Incorrect parameter {{foo}} in path {path}: {value}', + ['foo' => 'bar', 'path' => '/baz', 'value' => 'horse'] + ); + // View the log page to verify it's correct. + $wid = \Drupal::database()->query('SELECT MAX(wid) FROM {watchdog}')->fetchField(); + $this->drupalGet('admin/reports/dblog/event/' . $wid); + $this->assertSession() + ->responseContains('Incorrect parameter {bar} in path /baz: horse'); + } + /** * Verifies setting of the database log row limit. * diff --git a/web/core/modules/editor/editor.module b/web/core/modules/editor/editor.module index 2dd2ed976028818b9cbad43bee47677eb059e8e8..f9dbec18049dbb404a983d87999952ed2be7b9e1 100644 --- a/web/core/modules/editor/editor.module +++ b/web/core/modules/editor/editor.module @@ -396,13 +396,19 @@ function editor_entity_update(EntityInterface $entity) { // File references that existed both in the previous version of the revision // and in the new one don't need their usage to be updated. else { - $original_uuids_by_field = _editor_get_file_uuids_by_field($entity->original); + $original_uuids_by_field = empty($entity->original) ? [] : + _editor_get_file_uuids_by_field($entity->original); + $uuids_by_field = _editor_get_file_uuids_by_field($entity); // Detect file usages that should be incremented. foreach ($uuids_by_field as $field => $uuids) { - $added_files = array_diff($uuids_by_field[$field], $original_uuids_by_field[$field]); - _editor_record_file_usage($added_files, $entity); + $original_uuids = isset($original_uuids_by_field[$field]) ? + $original_uuids_by_field[$field] : []; + + if ($added_files = array_diff($uuids_by_field[$field], $original_uuids)) { + _editor_record_file_usage($added_files, $entity); + } } // Detect file usages that should be decremented. diff --git a/web/core/modules/editor/tests/modules/editor_test.module b/web/core/modules/editor/tests/modules/editor_test.module index 70fe59fa4151a984bc6f688e712cd3ddd4d79547..bdb7292d62a83ac1801728de8c8fc14121ba2a93 100644 --- a/web/core/modules/editor/tests/modules/editor_test.module +++ b/web/core/modules/editor/tests/modules/editor_test.module @@ -5,9 +5,45 @@ * Helper module for the Text Editor tests. */ +use Drupal\Core\Entity\EntityInterface; +use Drupal\node\NodeInterface; use Drupal\filter\FilterFormatInterface; use Drupal\file\FileInterface; +/** + * Implements hook_entity_update(). + * + * @see \Drupal\Tests\editor\Kernel\EntityUpdateTest + */ +function editor_test_entity_update(EntityInterface $entity) { + // Only act on nodes. + if (!$entity instanceof NodeInterface) { + return; + } + + // Avoid infinite loop by only going through our post save logic once. + if (!empty($entity->editor_test_updating)) { + return; + } + + // Set flag for whether or not the entity needs to be resaved. + $needs_update = FALSE; + + // Perform our post save logic. + if ($entity->title->value == 'test updated') { + // Change the node title. + $entity->title->value = 'test updated 2'; + $needs_update = TRUE; + } + + if ($needs_update) { + // Set flag on entity that our logic was already executed. + $entity->editor_test_updating = TRUE; + // And resave entity. + $entity->save(); + } +} + /** * Implements hook_editor_js_settings_alter(). */ diff --git a/web/core/modules/editor/tests/src/FunctionalJavascript/EditorAdminTest.php b/web/core/modules/editor/tests/src/FunctionalJavascript/EditorAdminTest.php index 51aee88140011fbbb18408ab848cf6dce8c3b5ce..3d19d3e59961f2d92649e9fdd6b5c0ec5e024e17 100644 --- a/web/core/modules/editor/tests/src/FunctionalJavascript/EditorAdminTest.php +++ b/web/core/modules/editor/tests/src/FunctionalJavascript/EditorAdminTest.php @@ -54,6 +54,7 @@ public function testEditorSelection() { $this->assertNotEmpty($assert_session->waitForText('sulaco')); $page->selectFieldOption('editor[editor]', 'ckeditor'); $this->assertNotEmpty($this->assertSession()->waitForElementVisible('css', 'ul.ckeditor-toolbar-group-buttons')); + $this->assertNotEmpty($this->assertSession()->waitForElementVisible('css', '#ckeditor-plugin-settings')); $page->pressButton('Save configuration'); // Test that toggling the editor selection off and back on works. diff --git a/web/core/modules/editor/tests/src/Kernel/EntityUpdateTest.php b/web/core/modules/editor/tests/src/Kernel/EntityUpdateTest.php new file mode 100644 index 0000000000000000000000000000000000000000..1a454ea349e432cdb9d07d27fca0ca6e0770a8e6 --- /dev/null +++ b/web/core/modules/editor/tests/src/Kernel/EntityUpdateTest.php @@ -0,0 +1,68 @@ +<?php + +namespace Drupal\Tests\editor\Kernel; + +use Drupal\KernelTests\Core\Entity\EntityKernelTestBase; +use Drupal\node\Entity\NodeType; +use Drupal\node\Entity\Node; + +/** + * Tests updating an entity. + * + * @group editor + */ +class EntityUpdateTest extends EntityKernelTestBase { + + /** + * Modules to enable. + * + * @var array + */ + protected static $modules = ['editor', 'editor_test', 'node']; + + /** + * {@inheritdoc} + */ + protected function setUp(): void { + parent::setUp(); + $this->installSchema('node', ['node_access']); + $this->installConfig(['node']); + + // Create a node type for testing. + $type = NodeType::create(['type' => 'page', 'name' => 'page']); + $type->save(); + + // Set editor_test module weight to be lower than editor module's weight so + // that editor_test_entity_update() is called before editor_entity_update(). + $extension_config = \Drupal::configFactory()->get('core.extension'); + $editor_module_weight = $extension_config->get('module.editor'); + module_set_weight('editor_test', $editor_module_weight - 1); + } + + /** + * Tests updating an existing entity. + * + * @see editor_test_entity_update() + */ + public function testEntityUpdate() { + // Create a node. + $node = Node::create([ + 'type' => 'page', + 'title' => 'test', + ]); + $node->save(); + + // Update the node. + // What happens is the following: + // 1. \Drupal\Core\Entity\EntityStorageBase::doPostSave() gets called. + // 2. editor_test_entity_update() gets called. + // 3. A resave of the updated entity gets triggered (second save call). + // 4. \Drupal\Core\Entity\EntityStorageBase::doPostSave() gets called. + // 5. editor_test_entity_update() gets called. + // 6. editor_entity_update() gets called (caused by the second save call). + // 7. editor_entity_update() gets called (caused by the first save call). + $node->title->value = 'test updated'; + $node->save(); + } + +} diff --git a/web/core/modules/field/src/Plugin/migrate/source/d6/Field.php b/web/core/modules/field/src/Plugin/migrate/source/d6/Field.php index d2c6392ae241a933d41fb01bf2c0433438b4a651..c3fba78ecccddf019bb894c6d64c96e3350518c4 100644 --- a/web/core/modules/field/src/Plugin/migrate/source/d6/Field.php +++ b/web/core/modules/field/src/Plugin/migrate/source/d6/Field.php @@ -8,7 +8,8 @@ /** * Drupal 6 field source from database. * - * For available configuration keys, refer to the parent classes: + * For available configuration keys, refer to the parent classes. + * * @see \Drupal\migrate\Plugin\migrate\source\SqlBase * @see \Drupal\migrate\Plugin\migrate\source\SourcePluginBase * diff --git a/web/core/modules/field/src/Plugin/migrate/source/d6/FieldInstance.php b/web/core/modules/field/src/Plugin/migrate/source/d6/FieldInstance.php index ed302ea6dbbacc5de4fc62eb30af359a16b52905..66f24fd2237fd49ae523ba8251cf09d9b322d325 100644 --- a/web/core/modules/field/src/Plugin/migrate/source/d6/FieldInstance.php +++ b/web/core/modules/field/src/Plugin/migrate/source/d6/FieldInstance.php @@ -26,7 +26,8 @@ * In this example field instances of type page are retrieved from the source * database. * - * For additional configuration keys, refer to the parent classes: + * For additional configuration keys, refer to the parent classes. + * * @see \Drupal\migrate\Plugin\migrate\source\SqlBase * @see \Drupal\migrate\Plugin\migrate\source\SourcePluginBase * diff --git a/web/core/modules/field/src/Plugin/migrate/source/d6/FieldInstanceOptionTranslation.php b/web/core/modules/field/src/Plugin/migrate/source/d6/FieldInstanceOptionTranslation.php index 505aa1c9aafb73f2986063527ce6b4538ad06eca..c0f58b78e5007959ed226a7624540e794fac3f6f 100644 --- a/web/core/modules/field/src/Plugin/migrate/source/d6/FieldInstanceOptionTranslation.php +++ b/web/core/modules/field/src/Plugin/migrate/source/d6/FieldInstanceOptionTranslation.php @@ -5,7 +5,8 @@ /** * Drupal 6 i18n field instance option labels source from database. * - * For available configuration keys, refer to the parent classes: + * For available configuration keys, refer to the parent classes. + * * @see \Drupal\migrate\Plugin\migrate\source\SqlBase * @see \Drupal\migrate\Plugin\migrate\source\SourcePluginBase * diff --git a/web/core/modules/field/src/Plugin/migrate/source/d6/FieldInstancePerFormDisplay.php b/web/core/modules/field/src/Plugin/migrate/source/d6/FieldInstancePerFormDisplay.php index 438b2198e613c8d580c20026936c8da302dc7444..9112e9a63ee39dc64ebd040b964adcab71309ec9 100644 --- a/web/core/modules/field/src/Plugin/migrate/source/d6/FieldInstancePerFormDisplay.php +++ b/web/core/modules/field/src/Plugin/migrate/source/d6/FieldInstancePerFormDisplay.php @@ -7,7 +7,8 @@ /** * Drupal 6 field instance per form display source from database. * - * For available configuration keys, refer to the parent classes: + * For available configuration keys, refer to the parent classes. + * * @see \Drupal\migrate\Plugin\migrate\source\SqlBase * @see \Drupal\migrate\Plugin\migrate\source\SourcePluginBase * diff --git a/web/core/modules/field/src/Plugin/migrate/source/d6/FieldInstancePerViewMode.php b/web/core/modules/field/src/Plugin/migrate/source/d6/FieldInstancePerViewMode.php index b571991084bcf0d1a6b1f37ffd720eefafc0ce41..62b4e3d36854c55461c3e725c97a6e3ae6149df7 100644 --- a/web/core/modules/field/src/Plugin/migrate/source/d6/FieldInstancePerViewMode.php +++ b/web/core/modules/field/src/Plugin/migrate/source/d6/FieldInstancePerViewMode.php @@ -7,7 +7,8 @@ /** * Drupal 6 field instance per view mode source from database. * - * For available configuration keys, refer to the parent classes: + * For available configuration keys, refer to the parent classes. + * * @see \Drupal\migrate\Plugin\migrate\source\SqlBase * @see \Drupal\migrate\Plugin\migrate\source\SourcePluginBase * diff --git a/web/core/modules/field/src/Plugin/migrate/source/d6/FieldLabelDescriptionTranslation.php b/web/core/modules/field/src/Plugin/migrate/source/d6/FieldLabelDescriptionTranslation.php index 81edff92090ee78e52408797f5608122406df486..51382e6cc6fcd9cda5fad15bf4459b43df4880f2 100644 --- a/web/core/modules/field/src/Plugin/migrate/source/d6/FieldLabelDescriptionTranslation.php +++ b/web/core/modules/field/src/Plugin/migrate/source/d6/FieldLabelDescriptionTranslation.php @@ -7,7 +7,8 @@ /** * Drupal 6 i18n field label and description source from database. * - * For available configuration keys, refer to the parent classes: + * For available configuration keys, refer to the parent classes. + * * @see \Drupal\migrate\Plugin\migrate\source\SqlBase * @see \Drupal\migrate\Plugin\migrate\source\SourcePluginBase * diff --git a/web/core/modules/field/src/Plugin/migrate/source/d6/FieldOptionTranslation.php b/web/core/modules/field/src/Plugin/migrate/source/d6/FieldOptionTranslation.php index 94aaeb43682c5c0f9d35bb7758d9b82c7045dadb..325a27e8baa3e7c9671f5c03c7aa9f4d839fb27c 100644 --- a/web/core/modules/field/src/Plugin/migrate/source/d6/FieldOptionTranslation.php +++ b/web/core/modules/field/src/Plugin/migrate/source/d6/FieldOptionTranslation.php @@ -5,7 +5,8 @@ /** * Drupal 6 i18n field option labels source from database. * - * For available configuration keys, refer to the parent classes: + * For available configuration keys, refer to the parent classes. + * * @see \Drupal\migrate\Plugin\migrate\source\SqlBase * @see \Drupal\migrate\Plugin\migrate\source\SourcePluginBase * diff --git a/web/core/modules/field/src/Plugin/migrate/source/d7/Field.php b/web/core/modules/field/src/Plugin/migrate/source/d7/Field.php index ae71d7dde0ff9a13fd1d5f91b714acb2d201bade..f978146afba9e8b87c7af34882995a9c7ce4d2b6 100644 --- a/web/core/modules/field/src/Plugin/migrate/source/d7/Field.php +++ b/web/core/modules/field/src/Plugin/migrate/source/d7/Field.php @@ -12,7 +12,8 @@ * migrated. The values of those fields will be migrated to the base fields they * were replacing. * - * For available configuration keys, refer to the parent classes: + * For available configuration keys, refer to the parent classes. + * * @see \Drupal\migrate\Plugin\migrate\source\SqlBase * @see \Drupal\migrate\Plugin\migrate\source\SourcePluginBase * diff --git a/web/core/modules/field/src/Plugin/migrate/source/d7/FieldInstance.php b/web/core/modules/field/src/Plugin/migrate/source/d7/FieldInstance.php index d23c1b52c4fc559345f94706decf4a09f79d60e3..079139b688dba3dfb6967ee93e2c9a015757b601 100644 --- a/web/core/modules/field/src/Plugin/migrate/source/d7/FieldInstance.php +++ b/web/core/modules/field/src/Plugin/migrate/source/d7/FieldInstance.php @@ -41,7 +41,8 @@ * In this example field instances of page content type are retrieved from the * source database. * - * For additional configuration keys, refer to the parent classes: + * For additional configuration keys, refer to the parent classes. + * * @see \Drupal\migrate\Plugin\migrate\source\SqlBase * @see \Drupal\migrate\Plugin\migrate\source\SourcePluginBase * diff --git a/web/core/modules/field/src/Plugin/migrate/source/d7/FieldInstancePerFormDisplay.php b/web/core/modules/field/src/Plugin/migrate/source/d7/FieldInstancePerFormDisplay.php index 0ea30440a3545514a5a26e5d58c695368483cb09..e1f999718f8887a101cd988bbf63cbc172e76874 100644 --- a/web/core/modules/field/src/Plugin/migrate/source/d7/FieldInstancePerFormDisplay.php +++ b/web/core/modules/field/src/Plugin/migrate/source/d7/FieldInstancePerFormDisplay.php @@ -5,7 +5,8 @@ /** * Drupal 7 field instance per form display source from database. * - * For available configuration keys, refer to the parent classes: + * For available configuration keys, refer to the parent classes. + * * @see \Drupal\field\Plugin\migrate\source\d7\FieldInstance * @see \Drupal\migrate\Plugin\migrate\source\SqlBase * @see \Drupal\migrate\Plugin\migrate\source\SourcePluginBase diff --git a/web/core/modules/field/src/Plugin/migrate/source/d7/FieldInstancePerViewMode.php b/web/core/modules/field/src/Plugin/migrate/source/d7/FieldInstancePerViewMode.php index 9e38bb247d7aa3aaf007e500fb6acda3b2b0d733..44a0b42ec41b298e4f032b0464466cda6767904a 100644 --- a/web/core/modules/field/src/Plugin/migrate/source/d7/FieldInstancePerViewMode.php +++ b/web/core/modules/field/src/Plugin/migrate/source/d7/FieldInstancePerViewMode.php @@ -5,7 +5,8 @@ /** * Drupal 7 field instance per view mode source class. * - * For available configuration keys, refer to the parent classes: + * For available configuration keys, refer to the parent classes. + * * @see \Drupal\field\Plugin\migrate\source\d7\FieldInstance * @see \Drupal\migrate\Plugin\migrate\source\SqlBase * @see \Drupal\migrate\Plugin\migrate\source\SourcePluginBase diff --git a/web/core/modules/field/src/Plugin/migrate/source/d7/FieldLabelDescriptionTranslation.php b/web/core/modules/field/src/Plugin/migrate/source/d7/FieldLabelDescriptionTranslation.php index 1ea718af50a2803b698e2db52f75a79dfa9024e3..bec150fe78e3b323a4e41886be806224bc487533 100644 --- a/web/core/modules/field/src/Plugin/migrate/source/d7/FieldLabelDescriptionTranslation.php +++ b/web/core/modules/field/src/Plugin/migrate/source/d7/FieldLabelDescriptionTranslation.php @@ -7,7 +7,8 @@ /** * Drupal 7 i18n field label and description source from database. * - * For available configuration keys, refer to the parent classes: + * For available configuration keys, refer to the parent classes. + * * @see \Drupal\migrate\Plugin\migrate\source\SqlBase * @see \Drupal\migrate\Plugin\migrate\source\SourcePluginBase * diff --git a/web/core/modules/field/src/Plugin/migrate/source/d7/FieldOptionTranslation.php b/web/core/modules/field/src/Plugin/migrate/source/d7/FieldOptionTranslation.php index 5569541d2d43f5590500595a2bcf65526aa4a9ab..b7159b31d4fea107d3119905155415404324b95e 100644 --- a/web/core/modules/field/src/Plugin/migrate/source/d7/FieldOptionTranslation.php +++ b/web/core/modules/field/src/Plugin/migrate/source/d7/FieldOptionTranslation.php @@ -5,7 +5,8 @@ /** * Drupal 7 i18n field option label source from database. * - * For available configuration keys, refer to the parent classes: + * For available configuration keys, refer to the parent classes. + * * @see \Drupal\migrate\Plugin\migrate\source\SqlBase * @see \Drupal\migrate\Plugin\migrate\source\SourcePluginBase * diff --git a/web/core/modules/jsonapi/src/Controller/EntityResource.php b/web/core/modules/jsonapi/src/Controller/EntityResource.php index 47bc9c056dedd941c3efba054de0750c39506c71..ca588d12f7a2e180d0a5d3bcdc0b651ebf15d759 100644 --- a/web/core/modules/jsonapi/src/Controller/EntityResource.php +++ b/web/core/modules/jsonapi/src/Controller/EntityResource.php @@ -369,7 +369,25 @@ public function patchIndividual(ResourceType $resource_type, EntityInterface $en * The response. */ public function deleteIndividual(EntityInterface $entity) { - $entity->delete(); + // @todo Replace with entity handlers in: https://www.drupal.org/project/drupal/issues/3230434 + if ($entity->getEntityTypeId() === 'user') { + $cancel_method = \Drupal::service('config.factory')->get('user.settings')->get('cancel_method'); + + // Allow other modules to act. + + user_cancel([], $entity->id(), $cancel_method); + // Since user_cancel() is not invoked via Form API, batch processing + // needs to be invoked manually. + $batch =& batch_get(); + // Mark this batch as non-progressive to bypass the progress bar and + // redirect. + $batch['progressive'] = FALSE; + batch_process(); + } + else { + $entity->delete(); + } + return new ResourceResponse(NULL, 204); } diff --git a/web/core/modules/jsonapi/tests/src/Functional/JsonApiFunctionalDateFieldTest.php b/web/core/modules/jsonapi/tests/src/Functional/JsonApiFunctionalDateFieldTest.php new file mode 100644 index 0000000000000000000000000000000000000000..21b2a7b9a39b6a526050ff94731388a589086660 --- /dev/null +++ b/web/core/modules/jsonapi/tests/src/Functional/JsonApiFunctionalDateFieldTest.php @@ -0,0 +1,182 @@ +<?php + +namespace Drupal\Tests\jsonapi\Functional; + +use Drupal\Component\Serialization\Json; +use Drupal\datetime\Plugin\Field\FieldType\DateTimeItemInterface; +use Drupal\field\Entity\FieldConfig; +use Drupal\field\Entity\FieldStorageConfig; +use Drupal\node\NodeInterface; + +/** + * JSON:API integration test for the "Date" field. + * + * @group jsonapi + */ +class JsonApiFunctionalDateFieldTest extends JsonApiFunctionalTestBase { + + /** + * {@inheritdoc} + */ + protected static $modules = [ + 'basic_auth', + 'datetime', + ]; + + /** + * {@inheritdoc} + */ + protected function setUp(): void { + parent::setUp(); + + FieldStorageConfig::create([ + 'field_name' => 'field_datetime', + 'entity_type' => 'node', + 'type' => 'datetime', + 'settings' => [ + 'datetime_type' => 'datetime', + ], + 'cardinality' => 1, + ])->save(); + + FieldConfig::create([ + 'field_name' => 'field_datetime', + 'label' => 'Date and time', + 'entity_type' => 'node', + 'bundle' => 'article', + 'required' => FALSE, + 'settings' => [], + 'description' => '', + ])->save(); + } + + /** + * Tests the GET method. + */ + public function testRead() { + /** @var \Drupal\Core\Datetime\DateFormatterInterface $date_formatter */ + $date_formatter = $this->container->get('date.formatter'); + + $timestamp_1 = '5000000'; + $timestamp_2 = '6000000'; + $timestamp_3 = '7000000'; + // Expected: node 1. + $timestamp_smaller_than_value = $timestamp_2; + // Expected: node 1 and node 2. + $timestamp_smaller_than_or_equal_value = $timestamp_2; + // Expected: node 3. + $timestamp_greater_than_value = $timestamp_2; + // Expected: node 2 and node 3. + $timestamp_greater_than_or_equal_value = $timestamp_2; + + $node_1 = $this->createNode([ + 'type' => 'article', + 'uuid' => 'es_test_1', + 'status' => NodeInterface::PUBLISHED, + 'field_datetime' => $date_formatter->format($timestamp_1, 'custom', DateTimeItemInterface::DATETIME_STORAGE_FORMAT), + ]); + $node_2 = $this->createNode([ + 'type' => 'article', + 'uuid' => 'es_test_2', + 'status' => NodeInterface::PUBLISHED, + 'field_datetime' => $date_formatter->format($timestamp_2, 'custom', DateTimeItemInterface::DATETIME_STORAGE_FORMAT), + ]); + $node_3 = $this->createNode([ + 'type' => 'article', + 'uuid' => 'es_test_3', + 'status' => NodeInterface::PUBLISHED, + 'field_datetime' => $date_formatter->format($timestamp_3, 'custom', DateTimeItemInterface::DATETIME_STORAGE_FORMAT), + ]); + + // Checks whether the date is greater than the given timestamp. + $filter = [ + 'filter_datetime' => [ + 'condition' => [ + 'path' => 'field_datetime', + 'operator' => '>', + 'value' => date(DateTimeItemInterface::DATETIME_STORAGE_FORMAT, $timestamp_greater_than_value), + ], + ], + ]; + $output = Json::decode($this->drupalGet('/jsonapi/node/article', [ + 'query' => ['filter' => $filter], + ])); + $this->assertSession()->statusCodeEquals(200); + $output_uuids = array_map(function ($result) { + return $result['id']; + }, $output['data']); + $this->assertCount(1, $output_uuids); + $this->assertSame([ + $node_3->uuid(), + ], $output_uuids); + + // Checks whether the date is greater than or equal to the given timestamp. + $filter = [ + 'filter_datetime' => [ + 'condition' => [ + 'path' => 'field_datetime', + 'operator' => '>=', + 'value' => date(DateTimeItemInterface::DATETIME_STORAGE_FORMAT, $timestamp_greater_than_or_equal_value), + ], + ], + ]; + $output = Json::decode($this->drupalGet('/jsonapi/node/article', [ + 'query' => ['filter' => $filter], + ])); + $this->assertSession()->statusCodeEquals(200); + $output_uuids = array_map(function ($result) { + return $result['id']; + }, $output['data']); + $this->assertCount(2, $output_uuids); + $this->assertSame([ + $node_2->uuid(), + $node_3->uuid(), + ], $output_uuids); + + // Checks whether the date is less than the given timestamp. + $filter = [ + 'filter_datetime' => [ + 'condition' => [ + 'path' => 'field_datetime', + 'operator' => '<', + 'value' => date(DateTimeItemInterface::DATETIME_STORAGE_FORMAT, $timestamp_smaller_than_value), + ], + ], + ]; + $output = Json::decode($this->drupalGet('/jsonapi/node/article', [ + 'query' => ['filter' => $filter], + ])); + $this->assertSession()->statusCodeEquals(200); + $output_uuids = array_map(function ($result) { + return $result['id']; + }, $output['data']); + $this->assertCount(1, $output_uuids); + $this->assertSame([ + $node_1->uuid(), + ], $output_uuids); + + // Checks whether the date is less than or equal to the given timestamp. + $filter = [ + 'filter_datetime' => [ + 'condition' => [ + 'path' => 'field_datetime', + 'operator' => '<=', + 'value' => date(DateTimeItemInterface::DATETIME_STORAGE_FORMAT, $timestamp_smaller_than_or_equal_value), + ], + ], + ]; + $output = Json::decode($this->drupalGet('/jsonapi/node/article', [ + 'query' => ['filter' => $filter], + ])); + $this->assertSession()->statusCodeEquals(200); + $output_uuids = array_map(function ($result) { + return $result['id']; + }, $output['data']); + $this->assertCount(2, $output_uuids); + $this->assertSame([ + $node_1->uuid(), + $node_2->uuid(), + ], $output_uuids); + } + +} diff --git a/web/core/modules/jsonapi/tests/src/Functional/UserTest.php b/web/core/modules/jsonapi/tests/src/Functional/UserTest.php index bc6b453b4317533e4f57b300d48531b18a944030..2184c3bcb4b8e0a46e99e5d75b4cc10d20e1c2ff 100644 --- a/web/core/modules/jsonapi/tests/src/Functional/UserTest.php +++ b/web/core/modules/jsonapi/tests/src/Functional/UserTest.php @@ -10,6 +10,7 @@ use Drupal\field\Entity\FieldStorageConfig; use Drupal\node\Entity\Node; use Drupal\user\Entity\User; +use Drupal\user\UserInterface; use GuzzleHttp\RequestOptions; /** @@ -19,10 +20,12 @@ */ class UserTest extends ResourceTestBase { + const BATCH_TEST_NODE_COUNT = 15; + /** * {@inheritdoc} */ - protected static $modules = ['user', 'jsonapi_test_user']; + protected static $modules = ['user', 'jsonapi_test_user', 'node']; /** * {@inheritdoc} @@ -119,6 +122,15 @@ protected function createAnotherEntity($key) { return $user; } + /** + * {@inheritdoc} + */ + public function testDeleteIndividual() { + $this->config('user.settings')->set('cancel_method', 'user_cancel_delete')->save(TRUE); + + parent::testDeleteIndividual(); + } + /** * {@inheritdoc} */ @@ -597,6 +609,175 @@ public function testResaveAccountName() { $this->assertEquals($original_name, $updated_user->get('name')->value); } + /** + * Tests if JSON:API respects user.settings.cancel_method: user_cancel_block. + */ + public function testDeleteRespectsUserCancelBlock() { + $cancel_method = 'user_cancel_block'; + $this->config('jsonapi.settings')->set('read_only', FALSE)->save(TRUE); + $this->config('user.settings')->set('cancel_method', $cancel_method)->save(TRUE); + + $account = $this->createAnotherEntity($cancel_method); + $node = $this->drupalCreateNode(['uid' => $account->id()]); + + $this->sendDeleteRequestForUser($account, $cancel_method); + + $user_storage = $this->container->get('entity_type.manager') + ->getStorage('user'); + $user_storage->resetCache([$account->id()]); + $account = $user_storage->load($account->id()); + + $this->assertNotNull($account, 'User is not deleted after JSON:API DELETE operation with user.settings.cancel_method: ' . $cancel_method); + $this->assertTrue($account->isBlocked(), 'User is blocked after JSON:API DELETE operation with user.settings.cancel_method: ' . $cancel_method); + + $node_storage = $this->container->get('entity_type.manager')->getStorage('node'); + $node_storage->resetCache([$node->id()]); + $test_node = $node_storage->load($node->id()); + $this->assertNotNull($test_node, 'Node of the user is not deleted.'); + $this->assertTrue($test_node->isPublished(), 'Node of the user is published.'); + $test_node = node_revision_load($node->getRevisionId()); + $this->assertTrue($test_node->isPublished(), 'Node revision of the user is published.'); + } + + /** + * Tests if JSON:API respects user.settings.cancel_method: user_cancel_block_unpublish. + */ + public function testDeleteRespectsUserCancelBlockUnpublish() { + $cancel_method = 'user_cancel_block_unpublish'; + $this->config('jsonapi.settings')->set('read_only', FALSE)->save(TRUE); + $this->config('user.settings')->set('cancel_method', $cancel_method)->save(TRUE); + + $account = $this->createAnotherEntity($cancel_method); + $node = $this->drupalCreateNode(['uid' => $account->id()]); + + $this->sendDeleteRequestForUser($account, $cancel_method); + + $user_storage = $this->container->get('entity_type.manager') + ->getStorage('user'); + $user_storage->resetCache([$account->id()]); + $account = $user_storage->load($account->id()); + + $this->assertNotNull($account, 'User is not deleted after JSON:API DELETE operation with user.settings.cancel_method: ' . $cancel_method); + $this->assertTrue($account->isBlocked(), 'User is blocked after JSON:API DELETE operation with user.settings.cancel_method: ' . $cancel_method); + + $node_storage = $this->container->get('entity_type.manager')->getStorage('node'); + $node_storage->resetCache([$node->id()]); + $test_node = $node_storage->load($node->id()); + $this->assertNotNull($test_node, 'Node of the user is not deleted.'); + $this->assertFalse($test_node->isPublished(), 'Node of the user is no longer published.'); + $test_node = node_revision_load($node->getRevisionId()); + $this->assertFalse($test_node->isPublished(), 'Node revision of the user is no longer published.'); + } + + /** + * Tests if JSON:API respects user.settings.cancel_method: user_cancel_block_unpublish. + * @group jsonapi + */ + public function testDeleteRespectsUserCancelBlockUnpublishAndProcessesBatches() { + $cancel_method = 'user_cancel_block_unpublish'; + $this->config('jsonapi.settings')->set('read_only', FALSE)->save(TRUE); + $this->config('user.settings')->set('cancel_method', $cancel_method)->save(TRUE); + + $account = $this->createAnotherEntity($cancel_method); + + $nodeCount = self::BATCH_TEST_NODE_COUNT; + $node_ids = []; + $nodes = []; + while ($nodeCount-- > 0) { + $node = $this->drupalCreateNode(['uid' => $account->id()]); + $nodes[] = $node; + $node_ids[] = $node->id(); + } + + $this->sendDeleteRequestForUser($account, $cancel_method); + + $user_storage = $this->container->get('entity_type.manager') + ->getStorage('user'); + $user_storage->resetCache([$account->id()]); + $account = $user_storage->load($account->id()); + + $this->assertNotNull($account, 'User is not deleted after JSON:API DELETE operation with user.settings.cancel_method: ' . $cancel_method); + $this->assertTrue($account->isBlocked(), 'User is blocked after JSON:API DELETE operation with user.settings.cancel_method: ' . $cancel_method); + + $node_storage = $this->container->get('entity_type.manager')->getStorage('node'); + $node_storage->resetCache($node_ids); + + $test_nodes = $node_storage->loadMultiple($node_ids); + + $this->assertCount(self::BATCH_TEST_NODE_COUNT, $test_nodes, 'Nodes of the user are not deleted.'); + + foreach ($test_nodes as $test_node) { + $this->assertFalse($test_node->isPublished(), 'Node of the user is no longer published.'); + } + + foreach ($nodes as $node) { + $test_node = node_revision_load($node->getRevisionId()); + $this->assertFalse($test_node->isPublished(), 'Node revision of the user is no longer published.'); + } + } + + /** + * Tests if JSON:API respects user.settings.cancel_method: user_cancel_reassign. + */ + public function testDeleteRespectsUserCancelReassign() { + $cancel_method = 'user_cancel_reassign'; + $this->config('jsonapi.settings')->set('read_only', FALSE)->save(TRUE); + $this->config('user.settings')->set('cancel_method', $cancel_method)->save(TRUE); + + $account = $this->createAnotherEntity($cancel_method); + $node = $this->drupalCreateNode(['uid' => $account->id()]); + + $this->sendDeleteRequestForUser($account, $cancel_method); + + $user_storage = $this->container->get('entity_type.manager') + ->getStorage('user'); + $user_storage->resetCache([$account->id()]); + $account = $user_storage->load($account->id()); + + $this->assertNull($account, 'User is deleted after JSON:API DELETE operation with user.settings.cancel_method: ' . $cancel_method); + + $node_storage = $this->container->get('entity_type.manager')->getStorage('node'); + $node_storage->resetCache([$node->id()]); + $test_node = $node_storage->load($node->id()); + $this->assertNotNull($test_node, 'Node of the user is not deleted.'); + $this->assertTrue($test_node->isPublished(), 'Node of the user is still published.'); + $this->assertEquals(0, $test_node->getOwnerId(), 'Node of the user has been attributed to anonymous user.'); + $test_node = node_revision_load($node->getRevisionId()); + $this->assertTrue($test_node->isPublished(), 'Node revision of the user is still published.'); + $this->assertEquals(0, $test_node->getRevisionUser()->id(), 'Node revision of the user has been attributed to anonymous user.'); + } + + /** + * Tests if JSON:API respects user.settings.cancel_method: user_cancel_delete. + */ + public function testDeleteRespectsUserCancelDelete() { + $cancel_method = 'user_cancel_delete'; + $this->config('jsonapi.settings')->set('read_only', FALSE)->save(TRUE); + $this->config('user.settings')->set('cancel_method', $cancel_method)->save(TRUE); + + $account = $this->createAnotherEntity($cancel_method); + $node = $this->drupalCreateNode(['uid' => $account->id()]); + + $url = Url::fromRoute(sprintf('jsonapi.%s.individual', static::$resourceTypeName), ['entity' => $account->uuid()]); + $request_options = []; + $request_options[RequestOptions::HEADERS]['Accept'] = 'application/vnd.api+json'; + $request_options = NestedArray::mergeDeep($request_options, $this->getAuthenticationRequestOptions()); + $this->setUpAuthorization('DELETE'); + $response = $this->request('DELETE', $url, $request_options); + $this->assertResourceResponse(204, NULL, $response); + + $node_storage = $this->container->get('entity_type.manager')->getStorage('node'); + $user_storage = $this->container->get('entity_type.manager')->getStorage('user'); + + $user_storage->resetCache([$account->id()]); + $account = $user_storage->load($account->id()); + $this->assertNull($account, 'User is deleted after JSON:API DELETE operation with user.settings.cancel_method: ' . $cancel_method); + + $node_storage->resetCache([$node->id()]); + $test_node = $node_storage->load($node->id()); + $this->assertNull($test_node, 'Node of the user is deleted.'); + } + /** * {@inheritdoc} */ @@ -620,4 +801,18 @@ protected function makeNormalizationInvalid(array $document, $entity_key) { return parent::makeNormalizationInvalid($document, $entity_key); } + /** + * @param \Drupal\user\UserInterface $account + * @param string $cancel_method + */ + private function sendDeleteRequestForUser(UserInterface $account, string $cancel_method) { + $url = Url::fromRoute(sprintf('jsonapi.%s.individual', static::$resourceTypeName), ['entity' => $account->uuid()]); + $request_options = []; + $request_options[RequestOptions::HEADERS]['Accept'] = 'application/vnd.api+json'; + $request_options = NestedArray::mergeDeep($request_options, $this->getAuthenticationRequestOptions()); + $this->setUpAuthorization('DELETE'); + $response = $this->request('DELETE', $url, $request_options); + $this->assertResourceResponse(204, NULL, $response); + } + } diff --git a/web/core/modules/language/src/Plugin/migrate/source/Language.php b/web/core/modules/language/src/Plugin/migrate/source/Language.php index 9576cf7a79db939aff90e9fc5868cbd77d20e32b..870f9790e6591efcdd54bb1a434c558041130a20 100644 --- a/web/core/modules/language/src/Plugin/migrate/source/Language.php +++ b/web/core/modules/language/src/Plugin/migrate/source/Language.php @@ -6,7 +6,7 @@ use Drupal\migrate_drupal\Plugin\migrate\source\DrupalSqlBase; /** - * Drupal language source from database. + * Drupal 6/7 language source from database. * * Available configuration keys: * - fetch_all: (optional) If not empty, all source languages are retrieved and @@ -28,7 +28,8 @@ * Given that fetch_all and domain_negotiation are specified, each row also * contains all languages and the domain negotiation status, if enabled. * - * For additional configuration keys, refer to the parent classes: + * For additional configuration keys, refer to the parent classes. + * * @see \Drupal\migrate\Plugin\migrate\source\SqlBase * @see \Drupal\migrate\Plugin\migrate\source\SourcePluginBase * diff --git a/web/core/modules/language/src/Plugin/migrate/source/d6/LanguageContentSettings.php b/web/core/modules/language/src/Plugin/migrate/source/d6/LanguageContentSettings.php index 9fbe85c831f3117e9f86cef7c8fe22d081b32770..d2f4c8b342b1cfc2b6064a68d57dd2ca2f4767af 100644 --- a/web/core/modules/language/src/Plugin/migrate/source/d6/LanguageContentSettings.php +++ b/web/core/modules/language/src/Plugin/migrate/source/d6/LanguageContentSettings.php @@ -8,7 +8,8 @@ /** * Drupal 6 i18n node settings from database. * - * For available configuration keys, refer to the parent classes: + * For available configuration keys, refer to the parent classes. + * * @see \Drupal\migrate\Plugin\migrate\source\SqlBase * @see \Drupal\migrate\Plugin\migrate\source\SourcePluginBas * diff --git a/web/core/modules/language/src/Plugin/migrate/source/d6/LanguageContentSettingsTaxonomyVocabulary.php b/web/core/modules/language/src/Plugin/migrate/source/d6/LanguageContentSettingsTaxonomyVocabulary.php index 246e635ddf2b16fcde459fb35f87def7567dce91..4eddde832ec42d497eef48f76360151a26b94024 100644 --- a/web/core/modules/language/src/Plugin/migrate/source/d6/LanguageContentSettingsTaxonomyVocabulary.php +++ b/web/core/modules/language/src/Plugin/migrate/source/d6/LanguageContentSettingsTaxonomyVocabulary.php @@ -8,7 +8,8 @@ /** * Drupal 6 i18n vocabularies source from database. * - * For available configuration keys, refer to the parent classes: + * For available configuration keys, refer to the parent classes. + * * @see \Drupal\migrate\Plugin\migrate\source\SqlBase * @see \Drupal\migrate\Plugin\migrate\source\SourcePluginBas * diff --git a/web/core/modules/language/src/Plugin/migrate/source/d7/LanguageContentSettings.php b/web/core/modules/language/src/Plugin/migrate/source/d7/LanguageContentSettings.php index 6ce9283c5186980e4a53248cf341be7c36897e9b..ae0b11409d3e075a597162d522ac979993e7148f 100644 --- a/web/core/modules/language/src/Plugin/migrate/source/d7/LanguageContentSettings.php +++ b/web/core/modules/language/src/Plugin/migrate/source/d7/LanguageContentSettings.php @@ -8,7 +8,8 @@ /** * Drupal 7 i18n node settings from database. * - * For available configuration keys, refer to the parent classes: + * For available configuration keys, refer to the parent classes. + * * @see \Drupal\migrate\Plugin\migrate\source\SqlBase * @see \Drupal\migrate\Plugin\migrate\source\SourcePluginBas * diff --git a/web/core/modules/language/src/Plugin/migrate/source/d7/LanguageContentSettingsTaxonomyVocabulary.php b/web/core/modules/language/src/Plugin/migrate/source/d7/LanguageContentSettingsTaxonomyVocabulary.php index 9017a9e461d5b5d4ca253b67894ef7ad8359fa39..89622ab5b0100f7078a7e2a6db995ad6f75060b5 100644 --- a/web/core/modules/language/src/Plugin/migrate/source/d7/LanguageContentSettingsTaxonomyVocabulary.php +++ b/web/core/modules/language/src/Plugin/migrate/source/d7/LanguageContentSettingsTaxonomyVocabulary.php @@ -7,7 +7,8 @@ /** * Drupal 7 i18n vocabularies source from database. * - * For available configuration keys, refer to the parent classes: + * For available configuration keys, refer to the parent classes. + * * @see \Drupal\migrate\Plugin\migrate\source\SqlBase * @see \Drupal\migrate\Plugin\migrate\source\SourcePluginBas * diff --git a/web/core/modules/language/tests/src/Kernel/Migrate/d7/MigrateLanguageContentCommentSettingsNoEntityTranslationTest.php b/web/core/modules/language/tests/src/Kernel/Migrate/d7/MigrateLanguageContentCommentSettingsNoEntityTranslationTest.php index 287a98e2fdfcb6fa4beae989a798dadd296a1973..29cb946832e8da9bce759dc4d6a85996999498c5 100644 --- a/web/core/modules/language/tests/src/Kernel/Migrate/d7/MigrateLanguageContentCommentSettingsNoEntityTranslationTest.php +++ b/web/core/modules/language/tests/src/Kernel/Migrate/d7/MigrateLanguageContentCommentSettingsNoEntityTranslationTest.php @@ -31,6 +31,7 @@ class MigrateLanguageContentCommentSettingsNoEntityTranslationTest extends Migra */ protected function setUp(): void { parent::setUp(); + $this->startCollectingMessages(); $this->migrateCommentTypes(); $this->executeMigrations([ 'language', @@ -57,6 +58,9 @@ public static function migrateDumpAlter(KernelTestBase $test) { * Tests migration of content language settings. */ public function testLanguageCommentSettings() { + // Confirm there is no message about a missing bundle. + $this->assertEmpty($this->migrateMessages, $this->migrateMessages['error'][0] ?? ''); + // Article and Blog content type have multilingual settings of 'Enabled, // with Translation'. Assert that comments are translatable and the default // language is 'current_interface'. @@ -88,6 +92,15 @@ public function testLanguageCommentSettings() { $this->assertTrue($config->isLanguageAlterable()); $this->assertSame($third_party_settings, $config->get('third_party_settings')); + // The content type with a long name has multilingual settings of 'Enabled'. + $config = ContentLanguageSettings::loadByEntityTypeBundle('comment', 'comment_node_a_thirty_two_char'); + $this->assertFalse($config->isNew()); + $this->assertSame('comment', $config->getTargetEntityTypeId()); + $this->assertSame('comment_node_a_thirty_two_char', $config->getTargetBundle()); + $this->assertSame('current_interface', $config->getDefaultLangcode()); + $this->assertTrue($config->isLanguageAlterable()); + $this->assertSame($third_party_settings, $config->get('third_party_settings')); + // Test content type has multilingual settings of 'Enabled, with field // translation'. $config = ContentLanguageSettings::loadByEntityTypeBundle('comment', 'comment_node_test_content_type'); diff --git a/web/core/modules/language/tests/src/Kernel/Migrate/d7/MigrateLanguageContentCommentSettingsTest.php b/web/core/modules/language/tests/src/Kernel/Migrate/d7/MigrateLanguageContentCommentSettingsTest.php index 4032c9dd05ba95684d21c1c8f9570be7574ff905..db157140ced568544005c68e6b4c1d8ff10c4fb0 100644 --- a/web/core/modules/language/tests/src/Kernel/Migrate/d7/MigrateLanguageContentCommentSettingsTest.php +++ b/web/core/modules/language/tests/src/Kernel/Migrate/d7/MigrateLanguageContentCommentSettingsTest.php @@ -28,6 +28,7 @@ class MigrateLanguageContentCommentSettingsTest extends MigrateDrupal7TestBase { */ protected function setUp(): void { parent::setUp(); + $this->startCollectingMessages(); $this->migrateCommentTypes(); $this->executeMigrations([ 'language', @@ -39,6 +40,9 @@ protected function setUp(): void { * Tests migration of content language settings. */ public function testLanguageCommentSettings() { + // Confirm there is no message about a missing bundle. + $this->assertEmpty($this->migrateMessages, $this->migrateMessages['error'][0] ?? ''); + // Article and Blog content type have multilingual settings of 'Enabled, // with Translation'. Assert that comments are translatable and the default // language is 'current_interface'. @@ -70,6 +74,15 @@ public function testLanguageCommentSettings() { $this->assertTrue($config->isLanguageAlterable()); $this->assertSame($third_party_settings, $config->get('third_party_settings')); + // The content type with a long name has multilingual settings of 'Enabled'. + $config = ContentLanguageSettings::loadByEntityTypeBundle('comment', 'comment_node_a_thirty_two_char'); + $this->assertFalse($config->isNew()); + $this->assertSame('comment', $config->getTargetEntityTypeId()); + $this->assertSame('comment_node_a_thirty_two_char', $config->getTargetBundle()); + $this->assertSame('current_interface', $config->getDefaultLangcode()); + $this->assertTrue($config->isLanguageAlterable()); + $this->assertSame($third_party_settings, $config->get('third_party_settings')); + // Test content type has multilingual settings of 'Enabled, with field // translation'. $config = ContentLanguageSettings::loadByEntityTypeBundle('comment', 'comment_node_test_content_type'); diff --git a/web/core/modules/layout_builder/src/EventSubscriber/BlockComponentRenderArray.php b/web/core/modules/layout_builder/src/EventSubscriber/BlockComponentRenderArray.php index d9218b538776654f766fdc1bbba2c1e78012e5c6..8ed8137f253f5ad1dfb0d9f3e029ecf3dbb89995 100644 --- a/web/core/modules/layout_builder/src/EventSubscriber/BlockComponentRenderArray.php +++ b/web/core/modules/layout_builder/src/EventSubscriber/BlockComponentRenderArray.php @@ -12,6 +12,7 @@ use Drupal\Core\StringTranslation\StringTranslationTrait; use Drupal\layout_builder\Access\LayoutPreviewAccessAllowed; use Drupal\layout_builder\Event\SectionComponentBuildRenderArrayEvent; +use Drupal\layout_builder\Plugin\Block\InlineBlock; use Drupal\layout_builder\LayoutBuilderEvents; use Drupal\views\Plugin\Block\ViewsBlock; use Symfony\Component\EventDispatcher\EventSubscriberInterface; @@ -124,9 +125,31 @@ public function onBuildRender(SectionComponentBuildRenderArrayEvent $event) { '#base_plugin_id' => $block->getBaseId(), '#derivative_plugin_id' => $block->getDerivativeId(), '#weight' => $event->getComponent()->getWeight(), - 'content' => $content, ]; + // Place the $content returned by the block plugin into a 'content' child + // element, as a way to allow the plugin to have complete control of its + // properties and rendering (for instance, its own #theme) without + // conflicting with the properties used above, or alternate ones used by + // alternate block rendering approaches in contributed modules. However, + // the use of a child element is an implementation detail of this + // particular block rendering approach. Semantically, the content returned + // by the block plugin, and in particular, attributes and contextual links + // are information that belong to the entire block. Therefore, we must + // move these properties from $content and merge them into the top-level + // element. + if (isset($content['#attributes'])) { + $build['#attributes'] = $content['#attributes']; + unset($content['#attributes']); + } + // Hide contextual links for inline blocks until the UX issues surrounding + // editing them directly are resolved. + // @see https://www.drupal.org/project/drupal/issues/3075308 + if (!$block instanceof InlineBlock && !empty($content['#contextual_links'])) { + $build['#contextual_links'] = $content['#contextual_links']; + } + $build['content'] = $content; + if ($event->inPreview()) { if ($block instanceof PreviewFallbackInterface) { $preview_fallback_string = $block->getPreviewFallbackString(); diff --git a/web/core/modules/layout_builder/tests/modules/layout_builder_test/layout_builder_test.links.contextual.yml b/web/core/modules/layout_builder/tests/modules/layout_builder_test/layout_builder_test.links.contextual.yml new file mode 100644 index 0000000000000000000000000000000000000000..62a4a509dd3c334855e06abe701221af98a00c81 --- /dev/null +++ b/web/core/modules/layout_builder/tests/modules/layout_builder_test/layout_builder_test.links.contextual.yml @@ -0,0 +1,4 @@ +layout_builder_test: + title: 'Test contextual link' + route_name: '<front>' + group: 'layout_builder_test' diff --git a/web/core/modules/layout_builder/tests/modules/layout_builder_test/src/Plugin/Block/TestAttributesBlock.php b/web/core/modules/layout_builder/tests/modules/layout_builder_test/src/Plugin/Block/TestAttributesBlock.php new file mode 100644 index 0000000000000000000000000000000000000000..877fc3bfe7fa51a421893a05bc37a5246dad02ce --- /dev/null +++ b/web/core/modules/layout_builder/tests/modules/layout_builder_test/src/Plugin/Block/TestAttributesBlock.php @@ -0,0 +1,43 @@ +<?php + +namespace Drupal\layout_builder_test\Plugin\Block; + +use Drupal\Core\Block\BlockBase; +use Drupal\Core\Form\FormStateInterface; + +/** + * Provides a 'TestAttributes' block. + * + * @Block( + * id = "layout_builder_test_test_attributes", + * admin_label = @Translation("Test Attributes"), + * category = @Translation("Test") + * ) + */ +class TestAttributesBlock extends BlockBase { + + /** + * {@inheritdoc} + */ + public function blockForm($form, FormStateInterface $form_state) { + return $form; + } + + /** + * {@inheritdoc} + */ + public function build() { + $build = [ + '#attributes' => [ + 'class' => ['attribute-test-class'], + 'custom-attribute' => 'test', + ], + '#markup' => $this->t('Example block providing its own attributes.'), + '#contextual_links' => [ + 'layout_builder_test' => ['route_parameters' => []], + ], + ]; + return $build; + } + +} diff --git a/web/core/modules/layout_builder/tests/src/Functional/LayoutBuilderTest.php b/web/core/modules/layout_builder/tests/src/Functional/LayoutBuilderTest.php index ba6f5bc41c48e369a36c5ef39f5a6511b9a0eece..8f121c75b4738bc525de07e3267ea1c4d943739b 100644 --- a/web/core/modules/layout_builder/tests/src/Functional/LayoutBuilderTest.php +++ b/web/core/modules/layout_builder/tests/src/Functional/LayoutBuilderTest.php @@ -25,6 +25,7 @@ class LayoutBuilderTest extends BrowserTestBase { 'layout_test', 'block', 'block_test', + 'contextual', 'node', 'layout_builder_test', ]; @@ -639,6 +640,37 @@ public function testPluginDependencies() { $assert_session->elementNotExists('css', '.block.menu--my-menu'); } + /** + * Tests that block plugins can define custom attributes and contextual links. + */ + public function testPluginsProvidingCustomAttributesAndContextualLinks() { + $assert_session = $this->assertSession(); + $page = $this->getSession()->getPage(); + + $this->drupalLogin($this->drupalCreateUser([ + 'access contextual links', + 'configure any layout', + 'administer node display', + ])); + + $this->drupalGet('admin/structure/types/manage/bundle_with_section_field/display/default'); + $this->submitForm(['layout[enabled]' => TRUE], 'Save'); + $page->clickLink('Manage layout'); + $page->clickLink('Add section'); + $page->clickLink('Layout Builder Test Plugin'); + $page->pressButton('Add section'); + $page->clickLink('Add block'); + $page->clickLink('Test Attributes'); + $page->pressButton('Add block'); + $page->pressButton('Save layout'); + + $this->drupalGet('node/1'); + + $assert_session->elementExists('css', '.attribute-test-class'); + $assert_session->elementExists('css', '[custom-attribute=test]'); + $assert_session->elementExists('css', 'div[data-contextual-id*="layout_builder_test"]'); + } + /** * Tests the interaction between full and default view modes. * diff --git a/web/core/modules/link/src/Plugin/migrate/process/FieldLink.php b/web/core/modules/link/src/Plugin/migrate/process/FieldLink.php index 232ed903f9b582194b067e4c91979c7edaad8965..b118f449b1a80c51d4301b5add42241b4f1ab0ec 100644 --- a/web/core/modules/link/src/Plugin/migrate/process/FieldLink.php +++ b/web/core/modules/link/src/Plugin/migrate/process/FieldLink.php @@ -59,6 +59,13 @@ public function __construct(array $configuration, $plugin_id, $plugin_definition * @see \Drupal\link\Plugin\Field\FieldWidget\LinkWidget::getUserEnteredStringAsUri() */ protected function canonicalizeUri($uri) { + // If the path starts with 2 slashes then it is always considered an + // external URL without an explicit protocol part. + // @todo Remove this when https://www.drupal.org/node/2744729 lands. + if (strpos($uri, '//') === 0) { + return $this->configuration['uri_scheme'] . ltrim($uri, '/'); + } + // If we already have a scheme, we're fine. if (parse_url($uri, PHP_URL_SCHEME)) { return $uri; diff --git a/web/core/modules/link/tests/src/Unit/Plugin/migrate/process/FieldLinkTest.php b/web/core/modules/link/tests/src/Unit/Plugin/migrate/process/FieldLinkTest.php index d6721e51f2e5f952493f9618244a72e62fa5517d..1142ec0c2005193b90ea6ecb852b2cbda6e7dff6 100644 --- a/web/core/modules/link/tests/src/Unit/Plugin/migrate/process/FieldLinkTest.php +++ b/web/core/modules/link/tests/src/Unit/Plugin/migrate/process/FieldLinkTest.php @@ -66,6 +66,10 @@ public function canonicalizeUriDataProvider() { 'https://yahoo.com', ['uri_scheme' => 'https://'], ], + 'Absolute URL without explicit protocol (protocol-relative)' => [ + '//example.com', + 'http://example.com', + ], 'Absolute URL with non-standard characters' => [ 'http://www.ßÀÑÐ¥ƒå¢ë.com', 'http://www.ßÀÑÐ¥ƒå¢ë.com', diff --git a/web/core/modules/media/media.install b/web/core/modules/media/media.install index f9fddb2511c9d5754159dd7a34029156876f1c93..a2c5eed95d1e758b6b1b9a424f7820184664bf77 100644 --- a/web/core/modules/media/media.install +++ b/web/core/modules/media/media.install @@ -120,22 +120,37 @@ function media_requirements($phase) { } } - // When a new media type with an image source is created we're configuring - // the default entity view display using the 'large' image style. - // Unfortunately, if a site builder has deleted the 'large' image style, - // we need some other image style to use, but at this point, we can't - // really know the site builder's intentions. So rather than do something - // surprising, we're leaving the embedded media without an image style and - // adding a warning that the site builder might want to add an image style. - // @see Drupal\media\Plugin\media\Source\Image::prepareViewDisplay $module_handler = \Drupal::service('module_handler'); foreach (MediaType::loadMultiple() as $type) { // Load the default display. $display = \Drupal::service('entity_display.repository') ->getViewDisplay('media', $type->id()); + // Check for missing source field definition. $source_field_definition = $type->getSource()->getSourceFieldDefinition($type); - if (empty($source_field_definition) || !is_a($source_field_definition->getItemDefinition()->getClass(), ImageItem::class, TRUE)) { + if (empty($source_field_definition)) { + $requirements['media_missing_source_field_' . $type->id()] = [ + 'title' => t('Media'), + 'description' => t('The source field definition for the %type media type is missing.', + [ + '%type' => $type->label(), + ] + ), + 'severity' => REQUIREMENT_ERROR, + ]; + continue; + } + + // When a new media type with an image source is created we're + // configuring the default entity view display using the 'large' image + // style. Unfortunately, if a site builder has deleted the 'large' image + // style, we need some other image style to use, but at this point, we + // can't really know the site builder's intentions. So rather than do + // something surprising, we're leaving the embedded media without an + // image style and adding a warning that the site builder might want to + // add an image style. + // @see Drupal\media\Plugin\media\Source\Image::prepareViewDisplay + if (!is_a($source_field_definition->getItemDefinition()->getClass(), ImageItem::class, TRUE)) { continue; } diff --git a/web/core/modules/media/src/Plugin/Field/FieldWidget/OEmbedWidget.php b/web/core/modules/media/src/Plugin/Field/FieldWidget/OEmbedWidget.php index 1252265e45b89261f7bff3f5e0dfc8a16b49576a..9ca270c6a17b9e2b593a1a4bb9d4666ed446fb65 100644 --- a/web/core/modules/media/src/Plugin/Field/FieldWidget/OEmbedWidget.php +++ b/web/core/modules/media/src/Plugin/Field/FieldWidget/OEmbedWidget.php @@ -36,7 +36,7 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen $source = $items->getEntity()->getSource(); $message = $this->t('You can link to media from the following services: @providers', ['@providers' => implode(', ', $source->getProviders())]); - if (!empty($element['#value']['#description'])) { + if (!empty($element['value']['#description'])) { $element['value']['#description'] = [ '#theme' => 'item_list', '#items' => [$element['value']['#description'], $message], diff --git a/web/core/modules/media/tests/src/Functional/FieldWidget/OEmbedFieldWidgetTest.php b/web/core/modules/media/tests/src/Functional/FieldWidget/OEmbedFieldWidgetTest.php new file mode 100644 index 0000000000000000000000000000000000000000..5d1caf11ae77667cdc728910d894ba5ec1597714 --- /dev/null +++ b/web/core/modules/media/tests/src/Functional/FieldWidget/OEmbedFieldWidgetTest.php @@ -0,0 +1,43 @@ +<?php + +namespace Drupal\Tests\media\Functional\FieldWidget; + +use Drupal\field\Entity\FieldConfig; +use Drupal\Tests\media\Functional\MediaFunctionalTestBase; + +/** + * @covers \Drupal\media\Plugin\Field\FieldWidget\OEmbedWidget + * + * @group media + */ +class OEmbedFieldWidgetTest extends MediaFunctionalTestBase { + + /** + * {@inheritdoc} + */ + protected $defaultTheme = 'stark'; + + /** + * Tests that the oEmbed field widget shows the configured help text. + */ + public function testFieldWidgetHelpText() { + $account = $this->drupalCreateUser(['create media']); + $this->drupalLogin($account); + + $media_type = $this->createMediaType('oembed:video'); + $source_field = $media_type->getSource() + ->getSourceFieldDefinition($media_type) + ->getName(); + + /** @var \Drupal\field\Entity\FieldConfig $field */ + $field = FieldConfig::loadByName('media', $media_type->id(), $source_field); + $field->setDescription('This is help text for oEmbed field.') + ->save(); + + $this->drupalGet('media/add/' . $media_type->id()); + $assert_session = $this->assertSession(); + $assert_session->pageTextContains('This is help text for oEmbed field.'); + $assert_session->pageTextContains('You can link to media from the following services: YouTube, Vimeo'); + } + +} diff --git a/web/core/modules/media/tests/src/Functional/MediaRequirementsTest.php b/web/core/modules/media/tests/src/Functional/MediaRequirementsTest.php index e8ff550cbcb2ccde00f33b5e0b68477c955d0779..db12de615073f0cf1d1960c3822bfaf8b48a23cb 100644 --- a/web/core/modules/media/tests/src/Functional/MediaRequirementsTest.php +++ b/web/core/modules/media/tests/src/Functional/MediaRequirementsTest.php @@ -26,10 +26,13 @@ public function testMissingSourceFieldDefinition() { $field_storage_definition = $field_definition->getFieldStorageDefinition(); $field_definition->delete(); $field_storage_definition->delete(); + $valid_media_type = $this->createMediaType('test'); $this->drupalLogin($this->rootUser); $this->drupalGet('/admin/reports/status'); $this->assertSession()->statusCodeEquals(200); + $this->assertSession()->pageTextContains("The source field definition for the {$media_type->label()} media type is missing."); + $this->assertSession()->pageTextNotContains("The source field definition for the {$valid_media_type->label()} media type is missing."); } } diff --git a/web/core/modules/media_library/tests/src/FunctionalJavascript/CKEditorIntegrationTest.php b/web/core/modules/media_library/tests/src/FunctionalJavascript/CKEditorIntegrationTest.php index ca95070bc775dee456d166fb0a8aec5890534052..40fe5f7f537c4bdd40518e94a7b86dad982301d8 100644 --- a/web/core/modules/media_library/tests/src/FunctionalJavascript/CKEditorIntegrationTest.php +++ b/web/core/modules/media_library/tests/src/FunctionalJavascript/CKEditorIntegrationTest.php @@ -236,8 +236,6 @@ public function testButton() { $this->assignNameToCkeditorIframe(); $this->getSession()->switchToIFrame('ckeditor'); $this->assertNotEmpty($assert_session->waitForElementVisible('css', '.cke_widget_drupalmedia drupal-media .media')); - // @todo Inserting media embed should enable undo. - // @see https://www.drupal.org/project/drupal/issues/3073294 $this->pressEditorButton('source'); $value = $assert_session->elementExists('css', 'textarea.cke_source')->getValue(); $dom = Html::load($value); diff --git a/web/core/modules/menu_link_content/src/Plugin/migrate/source/MenuLink.php b/web/core/modules/menu_link_content/src/Plugin/migrate/source/MenuLink.php index 1b186a6e04a18749d3ccb7e09194364c5e485c44..ff413abf8a60d339feee76f32cc67b253b4d1cd7 100644 --- a/web/core/modules/menu_link_content/src/Plugin/migrate/source/MenuLink.php +++ b/web/core/modules/menu_link_content/src/Plugin/migrate/source/MenuLink.php @@ -7,7 +7,7 @@ use Drupal\migrate\Row; /** - * Drupal menu link source from database. + * Drupal 6/7 menu link source from database. * * @MigrateSource( * id = "menu_link", diff --git a/web/core/modules/menu_link_content/src/Plugin/migrate/source/d6/MenuLinkTranslation.php b/web/core/modules/menu_link_content/src/Plugin/migrate/source/d6/MenuLinkTranslation.php index 54b20687342bdbec786c73dc7d9f4bdf685adc90..863e5bee494ad35d7d455a17d9df7caf05484a2e 100644 --- a/web/core/modules/menu_link_content/src/Plugin/migrate/source/d6/MenuLinkTranslation.php +++ b/web/core/modules/menu_link_content/src/Plugin/migrate/source/d6/MenuLinkTranslation.php @@ -7,7 +7,7 @@ use Drupal\menu_link_content\Plugin\migrate\source\MenuLink; /** - * Gets Menu link translations from source database. + * Drupal 6 i18n menu link translations source from database. * * @MigrateSource( * id = "d6_menu_link_translation", diff --git a/web/core/modules/menu_link_content/src/Plugin/migrate/source/d7/MenuLinkLocalized.php b/web/core/modules/menu_link_content/src/Plugin/migrate/source/d7/MenuLinkLocalized.php index b683186cea1b9cd5a4c8f25e3df13cce355f03eb..0b9e5ce30f1c27349a0a8858b3ed4089378a885e 100644 --- a/web/core/modules/menu_link_content/src/Plugin/migrate/source/d7/MenuLinkLocalized.php +++ b/web/core/modules/menu_link_content/src/Plugin/migrate/source/d7/MenuLinkLocalized.php @@ -6,7 +6,7 @@ use Drupal\migrate\Row; /** - * Gets localized menu link translations from source database. + * Drupal 7 localized menu link translations source from database. * * @MigrateSource( * id = "d7_menu_link_localized", diff --git a/web/core/modules/menu_link_content/src/Plugin/migrate/source/d7/MenuLinkTranslation.php b/web/core/modules/menu_link_content/src/Plugin/migrate/source/d7/MenuLinkTranslation.php index b55a1a218f57ed6e7f08fa12527ee880be38c455..267e896b9bcad6637ea9f42186eb117b1233a2b9 100644 --- a/web/core/modules/menu_link_content/src/Plugin/migrate/source/d7/MenuLinkTranslation.php +++ b/web/core/modules/menu_link_content/src/Plugin/migrate/source/d7/MenuLinkTranslation.php @@ -7,7 +7,7 @@ use Drupal\menu_link_content\Plugin\migrate\source\MenuLink; /** - * Gets Menu link translations from source database. + * Drupal 7 i18n menu link translations source from database. * * @MigrateSource( * id = "d7_menu_link_translation", diff --git a/web/core/modules/menu_ui/src/MenuListBuilder.php b/web/core/modules/menu_ui/src/MenuListBuilder.php index c6efe07222775d9d52e283316f12cc23aa1d3ab5..59af3090bd47d0d914496301367e6215e48dc7e6 100644 --- a/web/core/modules/menu_ui/src/MenuListBuilder.php +++ b/web/core/modules/menu_ui/src/MenuListBuilder.php @@ -4,6 +4,7 @@ use Drupal\Core\Config\Entity\ConfigEntityListBuilder; use Drupal\Core\Entity\EntityInterface; +use Drupal\Core\Url; /** * Defines a class to build a listing of menu entities. @@ -49,6 +50,9 @@ public function getDefaultOperations(EntityInterface $entity) { 'title' => t('Add link'), 'weight' => 20, 'url' => $entity->toUrl('add-link-form'), + 'query' => [ + 'destination' => $entity->toUrl('edit-form')->toString(), + ], ]; } if (isset($operations['delete'])) { @@ -57,6 +61,16 @@ public function getDefaultOperations(EntityInterface $entity) { return $operations; } + /** + * {@inheritdoc} + */ + protected function ensureDestination(Url $url) { + // We don't want to add the destination URL here, as it means we get + // redirected back to the list-builder after adding/deleting menu links from + // a menu. + return $url; + } + /** * {@inheritdoc} */ diff --git a/web/core/modules/menu_ui/tests/src/Functional/MenuUiTest.php b/web/core/modules/menu_ui/tests/src/Functional/MenuUiTest.php index 2ded666bff89cd3ab9e338c53e098107f2c2ce64..d565e532036783f43e131f4e799a6cf675f6e8d6 100644 --- a/web/core/modules/menu_ui/tests/src/Functional/MenuUiTest.php +++ b/web/core/modules/menu_ui/tests/src/Functional/MenuUiTest.php @@ -85,6 +85,10 @@ protected function setUp(): void { $this->drupalPlaceBlock('page_title_block'); $this->drupalPlaceBlock('system_menu_block:main'); + $this->drupalPlaceBlock('local_actions_block', [ + 'region' => 'content', + 'weight' => -100, + ]); $this->drupalCreateContentType(['type' => 'article', 'name' => 'Article']); @@ -101,7 +105,7 @@ protected function setUp(): void { /** * Tests menu functionality using the admin and user interfaces. */ - public function testMenu() { + public function testMenuAdministration() { // Log in the user. $this->drupalLogin($this->adminUser); $this->items = []; @@ -195,6 +199,16 @@ public function addCustomMenuCRUD() { $menu->save(); $this->drupalGet('admin/structure/menu/manage/' . $menu_name); $this->assertSession()->pageTextContains($new_label); + + // Delete the custom menu via the UI to testing destination handling. + $this->drupalGet('admin/structure/menu'); + $this->assertSession()->pageTextContains($new_label); + // Click the "Delete menu" operation in the Tools row. + $links = $this->xpath('//*/td[contains(text(),:menu_label)]/following::a[normalize-space()=:link_label]', [':menu_label' => $new_label, ':link_label' => 'Delete menu']); + $links[0]->click(); + $this->submitForm([], 'Delete'); + $this->assertSession()->addressEquals('admin/structure/menu'); + $this->assertSession()->responseContains("The menu <em class=\"placeholder\">$new_label</em> has been deleted."); } /** @@ -259,6 +273,7 @@ public function deleteCustomMenu() { $this->drupalGet("admin/structure/menu/manage/{$menu_name}/delete"); $this->submitForm([], 'Delete'); $this->assertSession()->statusCodeEquals(200); + $this->assertSession()->addressEquals("admin/structure/menu"); $this->assertSession()->pageTextContains("The menu $label has been deleted."); $this->assertNull(Menu::load($menu_name), 'Custom menu was deleted'); // Test if all menu links associated with the menu were removed from @@ -279,11 +294,37 @@ public function deleteCustomMenu() { * Tests menu functionality. */ public function doMenuTests() { + // Add a link to the tools menu first, to test cacheability metadata of the + // destination query string. + $this->drupalGet('admin/structure/menu/manage/tools'); + $this->clickLink('Add link'); + $link_title = $this->randomString(); + $this->submitForm(['link[0][uri]' => '/', 'title[0][value]' => $link_title], 'Save'); + $this->assertSession()->linkExists($link_title); + $this->assertSession()->addressEquals('admin/structure/menu/manage/tools'); + + // Test adding a menu link direct from the menus listing page. + $this->drupalGet('admin/structure/menu'); + // Click the "Add link" operation in the Tools row. + $links = $this->xpath('//*/td[contains(text(),:menu_label)]/following::a[normalize-space()=:link_label]', [':menu_label' => 'Tools', ':link_label' => 'Add link']); + $links[0]->click(); + $this->assertMatchesRegularExpression('#admin/structure/menu/manage/tools/add\?destination=(/[^/]*)*/admin/structure/menu/manage/tools$#', $this->getSession()->getCurrentUrl()); + $link_title = $this->randomString(); + $this->submitForm(['link[0][uri]' => '/', 'title[0][value]' => $link_title], 'Save'); + $this->assertSession()->linkExists($link_title); + $this->assertSession()->addressEquals('admin/structure/menu/manage/tools'); + $menu_name = $this->menu->id(); - // Test the 'Add link' local action. - $this->drupalGet(Url::fromRoute('entity.menu.edit_form', ['menu' => $menu_name])); + // Access the menu via the overview form to ensure it does not add a + // destination that breaks the user interface. + $this->drupalGet('admin/structure/menu'); + // Select the edit menu link for our menu. + $links = $this->xpath('//*/td[contains(text(),:menu_label)]/following::a[normalize-space()=:link_label]', [':menu_label' => (string) $this->menu->label(), ':link_label' => 'Edit menu']); + $links[0]->click(); + + // Test the 'Add link' local action. $this->clickLink('Add link'); $link_title = $this->randomString(); $this->submitForm(['link[0][uri]' => '/', 'title[0][value]' => $link_title], 'Save'); @@ -300,6 +341,22 @@ public function doMenuTests() { $this->submitForm([], 'Delete'); $this->assertSession()->addressEquals(Url::fromRoute('entity.menu.edit_form', ['menu' => $menu_name])); + // Clear the cache to ensure that recent caches aren't preventing us from + // seeing a broken add link. + $this->resetAll(); + $this->drupalGet('admin/structure/menu'); + + // Select the edit menu link for our menu. + $links = $this->xpath('//*/td[contains(text(),:menu_label)]/following::a[normalize-space()=:link_label]', [':menu_label' => (string) $this->menu->label(), ':link_label' => 'Edit menu']); + $links[0]->click(); + + // Test the 'Add link' local action. + $this->clickLink('Add link'); + $link_title = $this->randomString(); + $this->submitForm(['link[0][uri]' => '/', 'title[0][value]' => $link_title], 'Save'); + $this->assertSession()->linkExists($link_title); + $this->assertSession()->addressEquals(Url::fromRoute('entity.menu.edit_form', ['menu' => $menu_name])); + // Add nodes to use as links for menu links. $node1 = $this->drupalCreateNode(['type' => 'article']); $node2 = $this->drupalCreateNode(['type' => 'article']); diff --git a/web/core/modules/migrate_drupal/src/Plugin/migrate/EntityReferenceTranslationDeriver.php b/web/core/modules/migrate_drupal/src/Plugin/migrate/EntityReferenceTranslationDeriver.php index 603cbbe70a5611dacdb2c5f3f60b899adbd99529..e66c8a170b0f896ed6904f088a86008ca073c584 100644 --- a/web/core/modules/migrate_drupal/src/Plugin/migrate/EntityReferenceTranslationDeriver.php +++ b/web/core/modules/migrate_drupal/src/Plugin/migrate/EntityReferenceTranslationDeriver.php @@ -153,7 +153,7 @@ public function getDerivativeDefinitions($base_plugin_definition) { 'plugin' => 'sub_process', 'source' => $field_name, 'process' => [ - 'target_id' => [ + 'translation_target_id' => [ [ 'plugin' => 'migration_lookup', 'source' => 'target_id', @@ -162,13 +162,22 @@ public function getDerivativeDefinitions($base_plugin_definition) { ], [ 'plugin' => 'skip_on_empty', - 'method' => 'row', + 'method' => 'process', ], [ 'plugin' => 'extract', 'index' => [0], ], ], + 'target_id' => [ + [ + 'plugin' => 'null_coalesce', + 'source' => [ + '@translation_target_id', + 'target_id', + ], + ], + ], ], ]; diff --git a/web/core/modules/migrate_drupal/src/Plugin/migrate/source/DrupalSqlBase.php b/web/core/modules/migrate_drupal/src/Plugin/migrate/source/DrupalSqlBase.php index 54cce94105510b77a65e34627af9892a5cafadf6..428bfd2b42cdbb37ff76b0cf6e99df7dc0617ed5 100644 --- a/web/core/modules/migrate_drupal/src/Plugin/migrate/source/DrupalSqlBase.php +++ b/web/core/modules/migrate_drupal/src/Plugin/migrate/source/DrupalSqlBase.php @@ -22,7 +22,8 @@ * * For a full list, refer to the methods of this class. * - * For available configuration keys, refer to the parent classes: + * For available configuration keys, refer to the parent classes. + * * @see \Drupal\migrate\Plugin\migrate\source\SqlBase * @see \Drupal\migrate\Plugin\migrate\source\SourcePluginBase */ diff --git a/web/core/modules/migrate_drupal/src/Plugin/migrate/source/Variable.php b/web/core/modules/migrate_drupal/src/Plugin/migrate/source/Variable.php index 1b7679ea0dbc00f919799f0fd791137a269c4133..aed5245d0a4a08dda5fc18592681d7f2c0a4788e 100644 --- a/web/core/modules/migrate_drupal/src/Plugin/migrate/source/Variable.php +++ b/web/core/modules/migrate_drupal/src/Plugin/migrate/source/Variable.php @@ -7,7 +7,7 @@ use Drupal\migrate\Plugin\MigrationInterface; /** - * Drupal variable source from database. + * Drupal 6/7 variable source from database. * * This source class fetches variables from the source Drupal database. * Depending on the configuration, this returns zero or a single row and as such @@ -70,7 +70,8 @@ * - book_allowed_types * @endcode * - * For additional configuration keys, refer to the parent classes: + * For additional configuration keys, refer to the parent classes. + * * @see \Drupal\migrate\Plugin\migrate\source\SqlBase * @see \Drupal\migrate\Plugin\migrate\source\SourcePluginBase * diff --git a/web/core/modules/migrate_drupal/src/Plugin/migrate/source/VariableMultiRow.php b/web/core/modules/migrate_drupal/src/Plugin/migrate/source/VariableMultiRow.php index 27d820fb9eb2482162ab1e2b8cee4544bd63b744..98f22edbda6787ac7a1f0f2d7bcb76950c3f75b0 100644 --- a/web/core/modules/migrate_drupal/src/Plugin/migrate/source/VariableMultiRow.php +++ b/web/core/modules/migrate_drupal/src/Plugin/migrate/source/VariableMultiRow.php @@ -5,7 +5,7 @@ use Drupal\migrate\Row; /** - * Multiple variables source from database. + * Drupal 6/7 multiple variables source from database. * * Unlike the variable source plugin, this one returns one row per * variable. @@ -27,7 +27,8 @@ * In this example the specified variables are retrieved from the source * database one row per variable. * - * For additional configuration keys, refer to the parent classes: + * For additional configuration keys, refer to the parent classes. + * * @see \Drupal\migrate\Plugin\migrate\source\SqlBase * @see \Drupal\migrate\Plugin\migrate\source\SourcePluginBase * diff --git a/web/core/modules/migrate_drupal/src/Plugin/migrate/source/d6/VariableTranslation.php b/web/core/modules/migrate_drupal/src/Plugin/migrate/source/d6/VariableTranslation.php index eb897ee385802a0d111193ab6546cd08847c6bb3..c41bc5d2e225720656945c4a0cfee462a8c8072e 100644 --- a/web/core/modules/migrate_drupal/src/Plugin/migrate/source/d6/VariableTranslation.php +++ b/web/core/modules/migrate_drupal/src/Plugin/migrate/source/d6/VariableTranslation.php @@ -9,7 +9,7 @@ use Drupal\migrate_drupal\Plugin\migrate\source\DrupalSqlBase; /** - * Drupal i18n_variable source from database. + * Drupal 6 i18n_variable source from database. * * Available configuration keys: * - variables: (required) The list of variable translations to retrieve from @@ -25,7 +25,8 @@ * In this example the translations for site_offline_message variable are * retrieved from the source database. * - * For additional configuration keys, refer to the parent classes: + * For additional configuration keys, refer to the parent classes. + * * @see \Drupal\migrate\Plugin\migrate\source\SqlBase * @see \Drupal\migrate\Plugin\migrate\source\SourcePluginBase * diff --git a/web/core/modules/migrate_drupal/src/Plugin/migrate/source/d7/FieldableEntity.php b/web/core/modules/migrate_drupal/src/Plugin/migrate/source/d7/FieldableEntity.php index dbb5ed67d6c38418c3bbaf6c1aac5541dc45a079..176749480b5478fe9ab3f35947af739c1d0eedd2 100644 --- a/web/core/modules/migrate_drupal/src/Plugin/migrate/source/d7/FieldableEntity.php +++ b/web/core/modules/migrate_drupal/src/Plugin/migrate/source/d7/FieldableEntity.php @@ -12,7 +12,8 @@ * @see \Drupal\node\Plugin\migrate\source\d7\Node * @see \Drupal\user\Plugin\migrate\source\d7\User * - * For available configuration keys, refer to the parent classes: + * For available configuration keys, refer to the parent classes. + * * @see \Drupal\migrate\Plugin\migrate\source\SqlBase * @see \Drupal\migrate\Plugin\migrate\source\SourcePluginBase */ @@ -21,6 +22,12 @@ abstract class FieldableEntity extends DrupalSqlBase { /** * Returns all non-deleted field instances attached to a specific entity type. * + * Typically, getFields() is used in the prepareRow method of a source plugin + * to get a list of all the field instances of the entity. A source plugin can + * then loop through the list of fields to do any other preparation before + * processing the row. Typically, a source plugin will use getFieldValues() + * to get the values of each field. + * * @param string $entity_type * The entity type ID. * @param string|null $bundle @@ -47,6 +54,9 @@ protected function getFields($entity_type, $bundle = NULL) { /** * Retrieves field values for a single field of a single entity. * + * Typically, getFieldValues() is used in the prepareRow method of a source + * plugin where the return values are placed on the row source. + * * @param string $entity_type * The entity type. * @param string $field diff --git a/web/core/modules/migrate_drupal/src/Plugin/migrate/source/d7/VariableTranslation.php b/web/core/modules/migrate_drupal/src/Plugin/migrate/source/d7/VariableTranslation.php index eb71189a442a2f3c616981ce28b7cee9aefe29df..32bdfb2eb9b92f3ad1c86382cffd1387ea359c1f 100644 --- a/web/core/modules/migrate_drupal/src/Plugin/migrate/source/d7/VariableTranslation.php +++ b/web/core/modules/migrate_drupal/src/Plugin/migrate/source/d7/VariableTranslation.php @@ -8,7 +8,7 @@ use Drupal\migrate_drupal\Plugin\migrate\source\DrupalSqlBase; /** - * Gets Drupal variable_store source from database. + * Drupal 7 variable_store source from database. * * Available configuration keys: * - variables: (required) The list of variable translations to retrieve from @@ -25,7 +25,8 @@ * In this example the translations for site_name and site_slogan variables are * retrieved from the source database. * - * For additional configuration keys, refer to the parent classes: + * For additional configuration keys, refer to the parent classes. + * * @see \Drupal\migrate\Plugin\migrate\source\SqlBase * @see \Drupal\migrate\Plugin\migrate\source\SourcePluginBase * diff --git a/web/core/modules/migrate_drupal/src/Plugin/migrate/source/d8/Config.php b/web/core/modules/migrate_drupal/src/Plugin/migrate/source/d8/Config.php index 5aba90f2acdf14f996f06d035f036b9e2f9a0a0b..16b545c0fc8bcb099c9257a9ec1e28f5341b3377 100644 --- a/web/core/modules/migrate_drupal/src/Plugin/migrate/source/d8/Config.php +++ b/web/core/modules/migrate_drupal/src/Plugin/migrate/source/d8/Config.php @@ -42,7 +42,8 @@ * As a result, French versions of specified configuration objects are retrieved * from the source database. * - * For additional configuration keys, refer to the parent classes: + * For additional configuration keys, refer to the parent classes. + * * @see \Drupal\migrate\Plugin\migrate\source\SqlBase * @see \Drupal\migrate\Plugin\migrate\source\SourcePluginBase * diff --git a/web/core/modules/migrate_drupal/tests/fixtures/drupal7.php b/web/core/modules/migrate_drupal/tests/fixtures/drupal7.php index 2412555aaf42fcc9a2014afa0390d763ce78c139..0662f876dc5be277476145638bf45eef6d62e204 100644 --- a/web/core/modules/migrate_drupal/tests/fixtures/drupal7.php +++ b/web/core/modules/migrate_drupal/tests/fixtures/drupal7.php @@ -4,7 +4,7 @@ * @file * A database agnostic dump for testing purposes. * - * This file was generated by the Drupal 8.0 db-tools.php script. + * This file was generated by the Drupal 9.3.0-dev db-tools.php script. */ use Drupal\Core\Database\Database; @@ -4312,7 +4312,7 @@ 'storage_module' => 'field_sql_storage', 'storage_active' => '1', 'locked' => '0', - 'data' => 'a:7:{s:12:"translatable";s:1:"0";s:12:"entity_types";a:0:{}s:8:"settings";a:3:{s:11:"target_type";s:4:"node";s:7:"handler";s:4:"base";s:16:"handler_settings";a:2:{s:14:"target_bundles";a:1:{s:7:"article";s:7:"article";}s:4:"sort";a:1:{s:4:"type";s:4:"none";}}}s:7:"storage";a:5:{s:4:"type";s:17:"field_sql_storage";s:8:"settings";a:0:{}s:6:"module";s:17:"field_sql_storage";s:6:"active";s:1:"1";s:7:"details";a:1:{s:3:"sql";a:2:{s:18:"FIELD_LOAD_CURRENT";a:1:{s:28:"field_data_field_reference_2";a:1:{s:9:"target_id";s:27:"field_reference_2_target_id";}}s:19:"FIELD_LOAD_REVISION";a:1:{s:32:"field_revision_field_reference_2";a:1:{s:9:"target_id";s:27:"field_reference_2_target_id";}}}}}s:12:"foreign keys";a:1:{s:4:"node";a:2:{s:5:"table";s:4:"node";s:7:"columns";a:1:{s:9:"target_id";s:3:"nid";}}}s:7:"indexes";a:1:{s:9:"target_id";a:1:{i:0;s:9:"target_id";}}s:2:"id";s:2:"39";}', + 'data' => 'a:7:{s:12:"translatable";s:1:"0";s:12:"entity_types";a:0:{}s:8:"settings";a:3:{s:11:"target_type";s:4:"node";s:7:"handler";s:4:"base";s:16:"handler_settings";a:2:{s:14:"target_bundles";a:2:{s:7:"article";s:7:"article";s:5:"forum";s:5:"forum";}s:4:"sort";a:1:{s:4:"type";s:4:"none";}}}s:7:"storage";a:5:{s:4:"type";s:17:"field_sql_storage";s:8:"settings";a:0:{}s:6:"module";s:17:"field_sql_storage";s:6:"active";s:1:"1";s:7:"details";a:1:{s:3:"sql";a:2:{s:18:"FIELD_LOAD_CURRENT";a:1:{s:28:"field_data_field_reference_2";a:1:{s:9:"target_id";s:27:"field_reference_2_target_id";}}s:19:"FIELD_LOAD_REVISION";a:1:{s:32:"field_revision_field_reference_2";a:1:{s:9:"target_id";s:27:"field_reference_2_target_id";}}}}}s:12:"foreign keys";a:1:{s:4:"node";a:2:{s:5:"table";s:4:"node";s:7:"columns";a:1:{s:9:"target_id";s:3:"nid";}}}s:7:"indexes";a:1:{s:9:"target_id";a:1:{i:0;s:9:"target_id";}}s:2:"id";s:2:"39";}', 'cardinality' => '1', 'translatable' => '0', 'deleted' => '0', @@ -8181,7 +8181,7 @@ 'bundle' => 'article', 'deleted' => '0', 'entity_id' => '2', - 'revision_id' => '2', + 'revision_id' => '11', 'language' => 'und', 'delta' => '0', 'field_node_reference_nid' => '5', @@ -8768,7 +8768,7 @@ 'revision_id' => '11', 'language' => 'und', 'delta' => '0', - 'field_reference_2_target_id' => '5', + 'field_reference_2_target_id' => '6', )) ->values(array( 'entity_type' => 'node', @@ -14731,6 +14731,16 @@ 'delta' => '0', 'field_node_reference_nid' => '5', )) +->values(array( + 'entity_type' => 'node', + 'bundle' => 'article', + 'deleted' => '0', + 'entity_id' => '2', + 'revision_id' => '11', + 'language' => 'und', + 'delta' => '0', + 'field_node_reference_nid' => '5', +)) ->execute(); $connection->schema()->createTable('field_revision_field_phone', array( 'fields' => array( @@ -15378,7 +15388,7 @@ 'revision_id' => '11', 'language' => 'und', 'delta' => '0', - 'field_reference_2_target_id' => '5', + 'field_reference_2_target_id' => '6', )) ->values(array( 'entity_type' => 'node', @@ -59872,6 +59882,10 @@ 'name' => 'language_content_type_article', 'value' => 's:1:"2";', )) +->values(array( + 'name' => 'language_content_type_a_thirty_two_character_type_name', + 'value' => 's:1:"1";', +)) ->values(array( 'name' => 'language_content_type_blog', 'value' => 's:1:"2";', diff --git a/web/core/modules/migrate_drupal/tests/src/Kernel/d7/FollowUpMigrationsTest.php b/web/core/modules/migrate_drupal/tests/src/Kernel/d7/FollowUpMigrationsTest.php index 94ef1d68f715b2252929090d62f1da4bb7a92a75..b67b3f335819ee5a37026167bcfab9a519364798 100644 --- a/web/core/modules/migrate_drupal/tests/src/Kernel/d7/FollowUpMigrationsTest.php +++ b/web/core/modules/migrate_drupal/tests/src/Kernel/d7/FollowUpMigrationsTest.php @@ -51,8 +51,6 @@ protected function setUp(): void { 'language', 'd7_language_content_settings', 'd7_taxonomy_vocabulary', - 'd7_node', - 'd7_node_translation', ]); } @@ -70,12 +68,16 @@ protected function getFileMigrationInfo() { /** * Tests entity reference translations. + * + * @dataProvider providerTestEntityReferenceTranslations */ - public function testEntityReferenceTranslations() { + public function testEntityReferenceTranslations($node_migrations) { + $this->executeMigrations($node_migrations); + // Test the entity reference field before the follow-up migrations. $node = Node::load(2); $this->assertSame('5', $node->get('field_reference')->target_id); - $this->assertSame('5', $node->get('field_reference_2')->target_id); + $this->assertSame('6', $node->get('field_reference_2')->target_id); $translation = $node->getTranslation('is'); $this->assertSame('4', $translation->get('field_reference')->target_id); $this->assertSame('4', $translation->get('field_reference_2')->target_id); @@ -99,7 +101,7 @@ public function testEntityReferenceTranslations() { // Test the entity reference field after the follow-up migrations. $node = Node::load(2); $this->assertSame('4', $node->get('field_reference')->target_id); - $this->assertSame('4', $node->get('field_reference_2')->target_id); + $this->assertSame('6', $node->get('field_reference_2')->target_id); $translation = $node->getTranslation('is'); $this->assertSame('4', $translation->get('field_reference')->target_id); $this->assertSame('4', $translation->get('field_reference_2')->target_id); @@ -115,4 +117,18 @@ public function testEntityReferenceTranslations() { $this->assertSame('2', $user->get('field_reference')->target_id); } + /** + * Data provider for testEntityReferenceTranslations(). + */ + public function providerTestEntityReferenceTranslations() { + return [ + [ + ['d7_node', 'd7_node_translation'], + ], + [ + ['d7_node_complete'], + ], + ]; + } + } diff --git a/web/core/modules/migrate_drupal_ui/tests/src/Functional/d7/Upgrade7Test.php b/web/core/modules/migrate_drupal_ui/tests/src/Functional/d7/Upgrade7Test.php index 2652e6db235a4761b0a29532397689427ebb2e81..b29adb07a8548382529d3ede841d4057b85bd97d 100644 --- a/web/core/modules/migrate_drupal_ui/tests/src/Functional/d7/Upgrade7Test.php +++ b/web/core/modules/migrate_drupal_ui/tests/src/Functional/d7/Upgrade7Test.php @@ -89,7 +89,7 @@ protected function getEntityCounts() { 'file' => 3, 'filter_format' => 7, 'image_style' => 7, - 'language_content_settings' => 22, + 'language_content_settings' => 24, 'node' => 7, 'node_type' => 8, 'rdf_mapping' => 8, @@ -237,7 +237,7 @@ public function testUpgradeAndIncremental() { protected function assertFollowUpMigrationResults() { $node = Node::load(2); $this->assertSame('4', $node->get('field_reference')->target_id); - $this->assertSame('4', $node->get('field_reference_2')->target_id); + $this->assertSame('6', $node->get('field_reference_2')->target_id); $translation = $node->getTranslation('is'); $this->assertSame('4', $translation->get('field_reference')->target_id); $this->assertSame('4', $translation->get('field_reference_2')->target_id); diff --git a/web/core/modules/node/src/Plugin/migrate/source/d6/Node.php b/web/core/modules/node/src/Plugin/migrate/source/d6/Node.php index 4fe426d890d7776bbc5a0fd6e05a192034b273ab..e120ef9cc671aa9e950a38624f6244dc76d2eabd 100644 --- a/web/core/modules/node/src/Plugin/migrate/source/d6/Node.php +++ b/web/core/modules/node/src/Plugin/migrate/source/d6/Node.php @@ -297,6 +297,10 @@ protected function getFieldData(array $field, Row $node) { if (isset($query)) { $columns = array_keys($field['db_columns']); + // If there are no columns then there are no values to return. + if (empty($columns)) { + return []; + } // Add every column in the field's schema. foreach ($columns as $column) { diff --git a/web/core/modules/node/src/Plugin/migrate/source/d7/NodeEntityTranslation.php b/web/core/modules/node/src/Plugin/migrate/source/d7/NodeEntityTranslation.php index ca61c6c563a05013346544e064c217629c391953..d9ea90c3739d07da0d15eb9f5e33e3031a4a6a9f 100644 --- a/web/core/modules/node/src/Plugin/migrate/source/d7/NodeEntityTranslation.php +++ b/web/core/modules/node/src/Plugin/migrate/source/d7/NodeEntityTranslation.php @@ -6,7 +6,7 @@ use Drupal\migrate_drupal\Plugin\migrate\source\d7\FieldableEntity; /** - * Provides Drupal 7 node entity translations source plugin. + * Drupal 7 node entity translations source from database. * * Available configuration keys: * - node_type: The node_types to get from the source - can be a string or diff --git a/web/core/modules/node/tests/src/Kernel/Migrate/d7/MigrateNodeCompleteTest.php b/web/core/modules/node/tests/src/Kernel/Migrate/d7/MigrateNodeCompleteTest.php index 1d366d34144180b08ecdcfa9f3db93c9cbf36dc0..2c3acd8cf24c32d506e3fe2f75c02fea8c847b7b 100644 --- a/web/core/modules/node/tests/src/Kernel/Migrate/d7/MigrateNodeCompleteTest.php +++ b/web/core/modules/node/tests/src/Kernel/Migrate/d7/MigrateNodeCompleteTest.php @@ -4,6 +4,7 @@ use Drupal\migrate\MigrateExecutable; use Drupal\migrate_drupal\NodeMigrateType; +use Drupal\node\Entity\Node; use Drupal\node\NodeInterface; use Drupal\Tests\file\Kernel\Migrate\d7\FileMigrationSetupTrait; use Drupal\Tests\migrate_drupal\Kernel\d7\MigrateDrupal7TestBase; @@ -136,6 +137,15 @@ public function testNodeCompleteMigration() { $this->assertSame('Bob', $revision->field_user_reference[0]->entity->getAccountName()); } + // Test the translated node reference in the latest revision of node 2. This + // references the legacy site node 4 instead of node 2. The reference is + // fixed by the followup migrations, 'd7_entity_reference_translation' and + // tested in \Drupal\Tests\migrate_drupal\Kernel\d7\FollowUpMigrationsTest. + $node = Node::load(2); + $this->assertSame('6', $node->get('field_reference_2')->target_id); + $translation = $node->getTranslation('is'); + $this->assertSame('4', $translation->get('field_reference_2')->target_id); + // Test the order in multi-value fields. $revision = $this->nodeStorage->loadRevision(1); $this->assertSame([ diff --git a/web/core/modules/node/tests/src/Kernel/Plugin/migrate/source/d6/NodeTest.php b/web/core/modules/node/tests/src/Kernel/Plugin/migrate/source/d6/NodeTest.php index 9a78d61da3ff602041afaa898289197114886687..c6f72ee3eccc5eec6b7858c3e4e23cf3f676ed6f 100644 --- a/web/core/modules/node/tests/src/Kernel/Plugin/migrate/source/d6/NodeTest.php +++ b/web/core/modules/node/tests/src/Kernel/Plugin/migrate/source/d6/NodeTest.php @@ -38,6 +38,18 @@ public function providerSource() { 'active' => '1', 'locked' => '0', ], + [ + 'field_name' => 'field_test_empty_db_columns', + 'type' => 'empty_db_columns', + 'global_settings' => 'a:0:{}', + 'required' => '0', + 'multiple' => '0', + 'db_storage' => '1', + 'module' => 'empty_db_columns', + 'db_columns' => 'a:0:{}', + 'active' => '1', + 'locked' => '0', + ], ]; $tests[0]['source_data']['content_node_field_instance'] = [ [ @@ -52,6 +64,18 @@ public function providerSource() { 'widget_module' => 'number', 'widget_active' => '1', ], + [ + 'field_name' => 'field_test_empty_db_columns', + 'type_name' => 'story', + 'weight' => '33', + 'label' => 'Empty db_columns Field', + 'widget_type' => 'empty_db_columns', + 'widget_settings' => 'a:0:{}', + 'display_settings' => 'a:0:{}', + 'description' => 'An example field with empty db_columns.', + 'widget_module' => 'empty_db_columns', + 'widget_active' => '1', + ], ]; $tests[0]['source_data']['content_type_story'] = [ [ diff --git a/web/core/modules/system/tests/modules/render_attached_test/src/Controller/RenderAttachedTestController.php b/web/core/modules/system/tests/modules/render_attached_test/src/Controller/RenderAttachedTestController.php index 1bbf8ebe65db07d8b133cdd3e0f268d702b60a86..955e3daa221de4d3d6c948c67325d115125ad094 100644 --- a/web/core/modules/system/tests/modules/render_attached_test/src/Controller/RenderAttachedTestController.php +++ b/web/core/modules/system/tests/modules/render_attached_test/src/Controller/RenderAttachedTestController.php @@ -80,6 +80,7 @@ public function htmlHeaderLink() { $render['#attached']['html_head_link'][] = [['href' => '/foo?bar=<baz>&baz=false', 'rel' => 'alternate'], TRUE]; $render['#attached']['html_head_link'][] = [['href' => '/not-added-to-http-headers', 'rel' => 'alternate'], FALSE]; $render['#attached']['html_head_link'][] = [['href' => '/foo/bar', 'hreflang' => 'nl', 'rel' => 'alternate'], TRUE]; + $render['#attached']['html_head_link'][] = [['href' => '/foo/bar', 'hreflang' => 'de', 'rel' => 'alternate'], TRUE]; return $render; } diff --git a/web/core/modules/system/tests/src/Functional/Render/HtmlResponseAttachmentsTest.php b/web/core/modules/system/tests/src/Functional/Render/HtmlResponseAttachmentsTest.php index f07424b455e31f0410a28c61bc95cd9452c5c9b4..9921fe04a9001f34fc563a55c56a0fad39514630 100644 --- a/web/core/modules/system/tests/src/Functional/Render/HtmlResponseAttachmentsTest.php +++ b/web/core/modules/system/tests/src/Functional/Render/HtmlResponseAttachmentsTest.php @@ -65,8 +65,13 @@ public function testAttachments() { $expected_link_headers = [ '</foo?bar=<baz>&baz=false>; rel="alternate"', '</foo/bar>; hreflang="nl"; rel="alternate"', + '</foo/bar>; hreflang="de"; rel="alternate"', ]; $this->assertEquals($expected_link_headers, $this->getSession()->getResponseHeaders()['Link']); + + // Check that duplicate alternate URLs with different hreflangs are allowed. + $test_link = $this->xpath('//head/link[@rel="alternate"][@href="/foo/bar"]'); + $this->assertEquals(2, count($test_link), 'Duplicate alternate URLs are allowed.'); } /** diff --git a/web/core/modules/taxonomy/src/Plugin/migrate/source/d6/TermLocalizedTranslation.php b/web/core/modules/taxonomy/src/Plugin/migrate/source/d6/TermLocalizedTranslation.php index 6fd6a1ce4e057a8dbfa4e802c7a86fee1b8caeb7..57397c62bca9ec69c85dc5cb70167acd0a9f2168 100644 --- a/web/core/modules/taxonomy/src/Plugin/migrate/source/d6/TermLocalizedTranslation.php +++ b/web/core/modules/taxonomy/src/Plugin/migrate/source/d6/TermLocalizedTranslation.php @@ -5,7 +5,7 @@ use Drupal\migrate\Row; /** - * Drupal 6 i18n taxonomy terms from source database. + * Drupal 6 i18n taxonomy terms source from database. * * For available configuration keys, refer to the parent classes. * diff --git a/web/core/modules/taxonomy/src/Plugin/migrate/source/d6/VocabularyTranslation.php b/web/core/modules/taxonomy/src/Plugin/migrate/source/d6/VocabularyTranslation.php index 294da00e31874c1e4a27aae686b89109d26bdfcd..617daf7e4730dbf363e707a4bf76778a1ebda510 100644 --- a/web/core/modules/taxonomy/src/Plugin/migrate/source/d6/VocabularyTranslation.php +++ b/web/core/modules/taxonomy/src/Plugin/migrate/source/d6/VocabularyTranslation.php @@ -6,7 +6,7 @@ use Drupal\migrate_drupal\Plugin\migrate\source\DrupalSqlBase; /** - * Drupal 6 i18n vocabulary translations from source database. + * Drupal 6 i18n vocabulary translations source from database. * * For available configuration keys, refer to the parent classes. * diff --git a/web/core/modules/taxonomy/src/Plugin/migrate/source/d7/TermLocalizedTranslation.php b/web/core/modules/taxonomy/src/Plugin/migrate/source/d7/TermLocalizedTranslation.php index a06ec0d138c60446e4f25f1476121c33e86aa801..43d5cfaaf4a22a5d4e930bad9d548f1ccc1e62ec 100644 --- a/web/core/modules/taxonomy/src/Plugin/migrate/source/d7/TermLocalizedTranslation.php +++ b/web/core/modules/taxonomy/src/Plugin/migrate/source/d7/TermLocalizedTranslation.php @@ -6,7 +6,7 @@ use Drupal\migrate\Row; /** - * Drupal 7 i18n taxonomy terms from source database. + * Drupal 7 i18n taxonomy terms source from database. * * For available configuration keys, refer to the parent classes. * diff --git a/web/core/modules/taxonomy/src/Plugin/migrate/source/d7/TermTranslation.php b/web/core/modules/taxonomy/src/Plugin/migrate/source/d7/TermTranslation.php index 50fb58161ab1b2f198881dbfe38e7e32105433ac..c58dba0731ee6ee51cd5d5e27e96de8927596911 100644 --- a/web/core/modules/taxonomy/src/Plugin/migrate/source/d7/TermTranslation.php +++ b/web/core/modules/taxonomy/src/Plugin/migrate/source/d7/TermTranslation.php @@ -5,7 +5,7 @@ use Drupal\migrate\Row; /** - * Drupal 7 i18n taxonomy terms from source database. + * Drupal 7 i18n taxonomy terms source from database. * * For available configuration keys, refer to the parent classes. * @@ -28,9 +28,17 @@ public function query() { if ($this->database->schema()->fieldExists('taxonomy_term_data', 'language')) { $query->addField('td', 'language', 'td_language'); } + // Get data when the i18n_mode column exists and it is not the Drupal 7 + // value I18N_MODE_NONE or I18N_MODE_LOCALIZE. Otherwise, return no data. + // @see https://git.drupalcode.org/project/i18n/-/blob/7.x-1.x/i18n.module#L26 if ($this->database->schema()->fieldExists('taxonomy_vocabulary', 'i18n_mode')) { $query->addField('tv', 'i18n_mode'); + $query->condition('tv.i18n_mode', ['0', '1'], 'NOT IN'); } + else { + $query->alwaysFalse(); + } + return $query; } diff --git a/web/core/modules/taxonomy/tests/src/Kernel/Plugin/migrate/source/d7/TermTranslationTest.php b/web/core/modules/taxonomy/tests/src/Kernel/Plugin/migrate/source/d7/TermTranslationTest.php new file mode 100644 index 0000000000000000000000000000000000000000..a218b3ec3ee783ea418ba742a91a846672e73b93 --- /dev/null +++ b/web/core/modules/taxonomy/tests/src/Kernel/Plugin/migrate/source/d7/TermTranslationTest.php @@ -0,0 +1,361 @@ +<?php + +namespace Drupal\Tests\taxonomy\Kernel\Plugin\migrate\source\d7; + +/** + * Tests D7 i18n term localized source plugin. + * + * @covers \Drupal\taxonomy\Plugin\migrate\source\d7\TermTranslation + * @group taxonomy + */ +class TermTranslationTest extends TermTest { + + /** + * {@inheritdoc} + */ + protected static $modules = ['taxonomy', 'migrate_drupal']; + + /** + * {@inheritdoc} + */ + public function providerSource() { + $tests = []; + + // Ignore i18_modes 0 and 1, get i18n_mode 4. + $tests[0]['source_data']['taxonomy_term_data'] = [ + [ + 'tid' => 1, + 'vid' => 5, + 'name' => 'fr - name 1', + 'description' => 'desc 1', + 'weight' => 0, + 'is_container' => FALSE, + 'language' => 'fr', + 'i18n_tsid' => '1', + ], + [ + 'tid' => 2, + 'vid' => 5, + 'name' => 'name 2', + 'description' => 'desc 2', + 'weight' => 0, + 'is_container' => TRUE, + 'language' => 'en', + 'i18n_tsid' => '1', + ], + [ + 'tid' => 3, + 'vid' => 6, + 'name' => 'name 3', + 'description' => 'desc 3', + 'weight' => 0, + 'is_container' => FALSE, + 'language' => '', + 'i18n_tsid' => '', + ], + [ + 'tid' => 4, + 'vid' => 5, + 'name' => 'is - name 4', + 'description' => 'desc 4', + 'weight' => 1, + 'is_container' => FALSE, + 'language' => 'is', + 'i18n_tsid' => '1', + ], + [ + 'tid' => 5, + 'vid' => 6, + 'name' => 'name 5', + 'description' => 'desc 5', + 'weight' => 1, + 'is_container' => FALSE, + 'language' => '', + 'i18n_tsid' => '', + ], + [ + 'tid' => 6, + 'vid' => 6, + 'name' => 'name 6', + 'description' => 'desc 6', + 'weight' => 0, + 'is_container' => TRUE, + 'language' => '', + 'i18n_tsid' => '', + ], + [ + 'tid' => 7, + 'vid' => 7, + 'name' => 'is - captains', + 'description' => 'desc 7', + 'weight' => 0, + 'is_container' => TRUE, + 'language' => 'is', + 'i18n_tsid' => '', + ], + ]; + $tests[0]['source_data']['taxonomy_term_hierarchy'] = [ + [ + 'tid' => 1, + 'parent' => 0, + ], + [ + 'tid' => 2, + 'parent' => 0, + ], + [ + 'tid' => 3, + 'parent' => 0, + ], + [ + 'tid' => 4, + 'parent' => 1, + ], + [ + 'tid' => 5, + 'parent' => 2, + ], + [ + 'tid' => 6, + 'parent' => 3, + ], + [ + 'tid' => 6, + 'parent' => 2, + ], + [ + 'tid' => 7, + 'parent' => 0, + ], + ]; + + $tests[0]['source_data']['taxonomy_vocabulary'] = [ + [ + 'vid' => 3, + 'machine_name' => 'foo', + 'language' => 'und', + 'i18n_mode' => '0', + ], + [ + 'vid' => 5, + 'machine_name' => 'tags', + 'language' => 'und', + 'i18n_mode' => '4', + ], + [ + 'vid' => 6, + 'machine_name' => 'categories', + 'language' => 'is', + 'i18n_mode' => '1', + ], + ]; + + $tests[0]['source_data']['field_config'] = [ + [ + 'id' => '3', + 'translatable' => '0', + ], + [ + 'id' => '4', + 'translatable' => '1', + ], + [ + 'id' => '5', + 'translatable' => '1', + ], + ]; + $tests[0]['source_data']['field_config_instance'] = [ + [ + 'id' => '2', + 'field_id' => 3, + 'field_name' => 'field_term_field', + 'entity_type' => 'taxonomy_term', + 'bundle' => 'tags', + 'data' => 'a:0:{}', + 'deleted' => 0, + ], + [ + 'id' => '3', + 'field_id' => 3, + 'field_name' => 'field_term_field', + 'entity_type' => 'taxonomy_term', + 'bundle' => 'categories', + 'data' => 'a:0:{}', + 'deleted' => 0, + ], + [ + 'id' => '4', + 'field_id' => '4', + 'field_name' => 'name_field', + 'entity_type' => 'taxonomy_term', + 'bundle' => 'tags', + 'data' => 'a:0:{}', + 'deleted' => '0', + ], + [ + 'id' => '5', + 'field_id' => '5', + 'field_name' => 'description_field', + 'entity_type' => 'taxonomy_term', + 'bundle' => 'tags', + 'data' => 'a:0:{}', + 'deleted' => '0', + ], + ]; + $tests[0]['source_data']['field_data_field_term_field'] = [ + [ + 'entity_type' => 'taxonomy_term', + 'bundle' => 'tags', + 'deleted' => 0, + 'entity_id' => 1, + 'delta' => 0, + ], + [ + 'entity_type' => 'taxonomy_term', + 'bundle' => 'categories', + 'deleted' => 0, + 'entity_id' => 1, + 'delta' => 0, + ], + ]; + $tests[0]['source_data']['field_data_name_field'] = [ + [ + 'entity_type' => 'taxonomy_term', + 'bundle' => 'tags', + 'deleted' => '0', + 'entity_id' => '1', + 'revision_id' => '1', + 'language' => 'und', + 'delta' => '0', + 'name_field_value' => 'fr - name 1', + 'name_field_format' => NULL, + ], + [ + 'entity_type' => 'taxonomy_term', + 'bundle' => 'tags', + 'deleted' => '0', + 'entity_id' => '4', + 'revision_id' => '4', + 'language' => 'und', + 'delta' => '0', + 'name_field_value' => 'is - name 4', + 'name_field_format' => NULL, + ], + ]; + $tests[0]['source_data']['field_data_description_field'] = [ + [ + 'entity_type' => 'taxonomy_term', + 'bundle' => 'tags', + 'deleted' => '0', + 'entity_id' => '1', + 'revision_id' => '1', + 'language' => 'und', + 'delta' => '0', + 'description_field_value' => 'desc 1', + 'description_field_format' => NULL, + ], + [ + 'entity_type' => 'taxonomy_term', + 'bundle' => 'tags', + 'deleted' => '0', + 'entity_id' => '4', + 'revision_id' => '4', + 'language' => 'und', + 'delta' => '0', + 'description_field_value' => 'desc 4', + 'description_field_format' => NULL, + ], + ]; + + // The expected results. + $tests[0]['expected_data'] = [ + [ + 'tid' => 1, + 'vid' => 5, + 'name' => 'fr - name 1', + 'description' => 'desc 1', + 'weight' => 0, + 'is_container' => '', + 'language' => 'fr', + 'i18n_tsid' => '1', + 'machine_name' => 'tags', + 'i18n_mode' => '4', + 'td_language' => 'fr', + 'tv_i18n_mode' => '4', + ], + [ + 'tid' => 2, + 'vid' => 5, + 'name' => 'name 2', + 'description' => 'desc 2', + 'weight' => 0, + 'is_container' => '', + 'language' => 'en', + 'i18n_tsid' => '1', + 'machine_name' => 'tags', + 'i18n_mode' => '4', + 'td_language' => 'en', + 'tv_i18n_mode' => '4', + ], + [ + 'tid' => 4, + 'vid' => 5, + 'name' => 'is - name 4', + 'description' => 'desc 4', + 'weight' => 1, + 'is_container' => '', + 'language' => 'is', + 'i18n_tsid' => '1', + 'machine_name' => 'tags', + 'i18n_mode' => '4', + 'td_language' => 'is', + 'tv_i18n_mode' => '4', + ], + ]; + + $tests[0]['expected_count'] = NULL; + // Get translations for the tags bundle. + $tests[0]['configuration']['bundle'] = ['tags']; + + // Ignore i18_modes 0. get i18n_mode 2 and 4. + $tests[1] = $tests[0]; + // Change a vocabulary to using fixed translation. + $tests[1]['source_data']['taxonomy_vocabulary'][2] = [ + 'vid' => 7, + 'machine_name' => 'categories', + 'language' => 'is', + 'i18n_mode' => '2', + ]; + + // Add the term with fixed translation. + $tests[1]['expected_data'][] = [ + 'tid' => 7, + 'vid' => 7, + 'name' => 'is - captains', + 'description' => 'desc 7', + 'weight' => 0, + 'is_container' => '', + 'language' => 'is', + 'i18n_tsid' => '', + 'machine_name' => 'categories', + 'i18n_mode' => '2', + 'td_language' => 'is', + 'tv_i18n_mode' => '2', + ]; + + $tests[1]['expected_count'] = NULL; + $tests[1]['configuration']['bundle'] = NULL; + + // No data returned when there is no i18n_mode column. + $tests[2] = []; + $tests[2]['source_data'] = $tests[0]['source_data']; + foreach ($tests[2]['source_data']['taxonomy_vocabulary'] as &$table) { + unset($table['i18n_mode']); + } + $tests[2]['expected_data'] = [0]; + $tests[2]['expected_count'] = 0; + + return $tests; + } + +} diff --git a/web/core/modules/text/src/Plugin/migrate/field/d7/TextField.php b/web/core/modules/text/src/Plugin/migrate/field/d7/TextField.php index 2d2396ed87070ff921d494ff089a8e63d7cc0b54..21bcd74d8710eb7af5dcd8650bd39f753252a182 100644 --- a/web/core/modules/text/src/Plugin/migrate/field/d7/TextField.php +++ b/web/core/modules/text/src/Plugin/migrate/field/d7/TextField.php @@ -34,7 +34,7 @@ public function getFieldFormatterType(Row $row) { break; case 'string_long': - $formatter_type = str_replace('text_default', 'basic_string', $formatter_type); + $formatter_type = str_replace(['text_default', 'text_plain'], 'basic_string', $formatter_type); break; } diff --git a/web/core/modules/text/tests/src/Unit/Plugin/migrate/field/d7/TextFieldTest.php b/web/core/modules/text/tests/src/Unit/Plugin/migrate/field/d7/TextFieldTest.php index 6923a2ec5e960f9d1e51ff6620139da42c6db319..71adb4e59d12b39a842b7a595445e3f54c1462b6 100644 --- a/web/core/modules/text/tests/src/Unit/Plugin/migrate/field/d7/TextFieldTest.php +++ b/web/core/modules/text/tests/src/Unit/Plugin/migrate/field/d7/TextFieldTest.php @@ -25,12 +25,13 @@ protected function setUp(): void { } /** - * Data provider for getFieldFormatterType(). + * Data provider for testGetFieldFormatterType(). */ public function getFieldFormatterTypeProvider() { return [ ['text', 'text_plain', 'string'], ['text_long', 'text_default', 'basic_string'], + ['text_long', 'text_plain', 'basic_string'], ]; } @@ -39,7 +40,7 @@ public function getFieldFormatterTypeProvider() { * @covers ::getFieldType * @dataProvider getFieldFormatterTypeProvider */ - public function testGetFieldType($type, $formatter_type, $expected) { + public function testGetFieldFormatterType($type, $formatter_type, $expected) { $row = new Row(); $row->setSourceProperty('type', $type); $row->setSourceProperty('formatter/type', $formatter_type); diff --git a/web/core/modules/toolbar/tests/modules/toolbar_test/toolbar_test.module b/web/core/modules/toolbar/tests/modules/toolbar_test/toolbar_test.module index 1f919a1e0b3f5d30493378ad097b9b572138fd01..cbc014054d3c5075601ec2ad65ae63d15fda7403 100644 --- a/web/core/modules/toolbar/tests/modules/toolbar_test/toolbar_test.module +++ b/web/core/modules/toolbar/tests/modules/toolbar_test/toolbar_test.module @@ -48,3 +48,14 @@ function toolbar_test_toolbar() { return $items; } + +/** + * Implements hook_preprocess_HOOK(). + */ +function toolbar_test_preprocess_menu(&$variables) { + // All the standard hook_theme variables should be populated when the + // Toolbar module is rendering a menu. + foreach (['menu_name', 'items', 'attributes'] as $variable) { + $variables[$variable]; + } +} diff --git a/web/core/modules/toolbar/toolbar.module b/web/core/modules/toolbar/toolbar.module index 483a30b76ae22f3a9441f40741ac550bb0091be3..5451dcb751567d27c56b1cfda9f7937ce9f9d144 100644 --- a/web/core/modules/toolbar/toolbar.module +++ b/web/core/modules/toolbar/toolbar.module @@ -41,7 +41,7 @@ function toolbar_theme($existing, $type, $theme, $path) { ]; $items['menu__toolbar'] = [ 'base hook' => 'menu', - 'variables' => ['items' => [], 'attributes' => []], + 'variables' => ['menu_name' => NULL, 'items' => [], 'attributes' => []], ]; return $items; diff --git a/web/core/modules/user/src/Plugin/migrate/source/ProfileField.php b/web/core/modules/user/src/Plugin/migrate/source/ProfileField.php index ce40c399afb1d80e4fc0b9d6d5c536a5f6b2a493..db87a26fc15e816ad529480b78cbff6b30bceb89 100644 --- a/web/core/modules/user/src/Plugin/migrate/source/ProfileField.php +++ b/web/core/modules/user/src/Plugin/migrate/source/ProfileField.php @@ -7,9 +7,10 @@ use Drupal\migrate\Row; /** - * Profile field source from database. + * Drupal 6/7 profile field source from database. + * + * For available configuration keys, refer to the parent classes. * - * For available configuration keys, refer to the parent classes: * @see \Drupal\migrate\Plugin\migrate\source\SqlBase * @see \Drupal\migrate\Plugin\migrate\source\SourcePluginBase * diff --git a/web/core/modules/user/src/Plugin/migrate/source/UserPictureInstance.php b/web/core/modules/user/src/Plugin/migrate/source/UserPictureInstance.php index 591cc441e5a30836203444a4c8cc0358b03d9331..cd4f0b00ac1c125e7c153bbb1b20a8a9424d8612 100644 --- a/web/core/modules/user/src/Plugin/migrate/source/UserPictureInstance.php +++ b/web/core/modules/user/src/Plugin/migrate/source/UserPictureInstance.php @@ -6,9 +6,10 @@ use Drupal\migrate\Plugin\migrate\source\DummyQueryTrait; /** - * User picture field instance source from database. + * Drupal 6/7 user picture field instance source from database. + * + * For available configuration keys, refer to the parent classes. * - * For available configuration keys, refer to the parent classes: * @see \Drupal\migrate\Plugin\migrate\source\SqlBase * @see \Drupal\migrate\Plugin\migrate\source\SourcePluginBase * diff --git a/web/core/modules/user/src/Plugin/migrate/source/d6/ProfileFieldOptionTranslation.php b/web/core/modules/user/src/Plugin/migrate/source/d6/ProfileFieldOptionTranslation.php index b74fb5e260e1fac637f703d6bb1f5b2288957cf3..41c384876521aa548f0649b43882f198d8455e52 100644 --- a/web/core/modules/user/src/Plugin/migrate/source/d6/ProfileFieldOptionTranslation.php +++ b/web/core/modules/user/src/Plugin/migrate/source/d6/ProfileFieldOptionTranslation.php @@ -7,7 +7,8 @@ /** * Drupal 6 i18n profile field option labels source from database. * - * For available configuration keys, refer to the parent classes: + * For available configuration keys, refer to the parent classes. + * * @see \Drupal\migrate\Plugin\migrate\source\SqlBase * @see \Drupal\migrate\Plugin\migrate\source\SourcePluginBase * diff --git a/web/core/modules/user/src/Plugin/migrate/source/d6/ProfileFieldValues.php b/web/core/modules/user/src/Plugin/migrate/source/d6/ProfileFieldValues.php index f73aa059d44a0fa5e48a27a823166ba5e616ddf5..8445be5cc3f8ab898335790aefad3a857c345618 100644 --- a/web/core/modules/user/src/Plugin/migrate/source/d6/ProfileFieldValues.php +++ b/web/core/modules/user/src/Plugin/migrate/source/d6/ProfileFieldValues.php @@ -8,7 +8,8 @@ /** * Drupal 6 profile fields values source from database. * - * For available configuration keys, refer to the parent classes: + * For available configuration keys, refer to the parent classes. + * * @see \Drupal\migrate\Plugin\migrate\source\SqlBase * @see \Drupal\migrate\Plugin\migrate\source\SourcePluginBase * diff --git a/web/core/modules/user/src/Plugin/migrate/source/d6/Role.php b/web/core/modules/user/src/Plugin/migrate/source/d6/Role.php index 6be90ea8e75205e4422cefc2e60b9bcd2e65beaa..26cdc5408215a14c11c61bcba93be2690ed6ba98 100644 --- a/web/core/modules/user/src/Plugin/migrate/source/d6/Role.php +++ b/web/core/modules/user/src/Plugin/migrate/source/d6/Role.php @@ -8,7 +8,8 @@ /** * Drupal 6 role source from database. * - * For available configuration keys, refer to the parent classes: + * For available configuration keys, refer to the parent classes. + * * @see \Drupal\migrate\Plugin\migrate\source\SqlBase * @see \Drupal\migrate\Plugin\migrate\source\SourcePluginBase * diff --git a/web/core/modules/user/src/Plugin/migrate/source/d6/User.php b/web/core/modules/user/src/Plugin/migrate/source/d6/User.php index 99cdcdebdf425bac2b94e5c1b86bf4e446c4ba7c..6c2349f21081853332627062e4eea91dc8823a44 100644 --- a/web/core/modules/user/src/Plugin/migrate/source/d6/User.php +++ b/web/core/modules/user/src/Plugin/migrate/source/d6/User.php @@ -8,7 +8,8 @@ /** * Drupal 6 user source from database. * - * For available configuration keys, refer to the parent classes: + * For available configuration keys, refer to the parent classes. + * * @see \Drupal\migrate\Plugin\migrate\source\SqlBase * @see \Drupal\migrate\Plugin\migrate\source\SourcePluginBase * diff --git a/web/core/modules/user/src/Plugin/migrate/source/d6/UserPicture.php b/web/core/modules/user/src/Plugin/migrate/source/d6/UserPicture.php index c32a77385da647b6148963945f644046afe55d8d..cd52c7bd20c87ce40c70ad5020f1b85203784c16 100644 --- a/web/core/modules/user/src/Plugin/migrate/source/d6/UserPicture.php +++ b/web/core/modules/user/src/Plugin/migrate/source/d6/UserPicture.php @@ -7,7 +7,8 @@ /** * Drupal 6 user picture source from database. * - * For available configuration keys, refer to the parent classes: + * For available configuration keys, refer to the parent classes. + * * @see \Drupal\migrate\Plugin\migrate\source\SqlBase * @see \Drupal\migrate\Plugin\migrate\source\SourcePluginBase * diff --git a/web/core/modules/user/src/Plugin/migrate/source/d6/UserPictureFile.php b/web/core/modules/user/src/Plugin/migrate/source/d6/UserPictureFile.php index 60e38e8e4dde1090d9690e0adf6a873f6935403d..02ba6163f6bcdd10811e1b85e840009f1e99276d 100644 --- a/web/core/modules/user/src/Plugin/migrate/source/d6/UserPictureFile.php +++ b/web/core/modules/user/src/Plugin/migrate/source/d6/UserPictureFile.php @@ -12,7 +12,8 @@ * - site_path: (optional) The path to the site directory relative to Drupal * root. Defaults to 'sites/default'. * - * For additional configuration keys, refer to the parent classes: + * For additional configuration keys, refer to the parent classes. + * * @see \Drupal\migrate\Plugin\migrate\source\SqlBase * @see \Drupal\migrate\Plugin\migrate\source\SourcePluginBase * diff --git a/web/core/modules/user/src/Plugin/migrate/source/d7/Role.php b/web/core/modules/user/src/Plugin/migrate/source/d7/Role.php index 2939cd9549861464edc17999dd1450d6d6865f7d..d07842058a60e1040735053baf876ad3318b583c 100644 --- a/web/core/modules/user/src/Plugin/migrate/source/d7/Role.php +++ b/web/core/modules/user/src/Plugin/migrate/source/d7/Role.php @@ -8,7 +8,8 @@ /** * Drupal 7 role source from database. * - * For available configuration keys, refer to the parent classes: + * For available configuration keys, refer to the parent classes. + * * @see \Drupal\migrate\Plugin\migrate\source\SqlBase * @see \Drupal\migrate\Plugin\migrate\source\SourcePluginBase * diff --git a/web/core/modules/user/src/Plugin/migrate/source/d7/User.php b/web/core/modules/user/src/Plugin/migrate/source/d7/User.php index e92ae14e5fac73ebc5926a8090455b8313f0384b..286857a6b6b8a78c77f5ef8fa11c31ecc77488d5 100644 --- a/web/core/modules/user/src/Plugin/migrate/source/d7/User.php +++ b/web/core/modules/user/src/Plugin/migrate/source/d7/User.php @@ -8,7 +8,8 @@ /** * Drupal 7 user source from database. * - * For available configuration keys, refer to the parent classes: + * For available configuration keys, refer to the parent classes. + * * @see \Drupal\migrate\Plugin\migrate\source\SqlBase * @see \Drupal\migrate\Plugin\migrate\source\SourcePluginBase * diff --git a/web/core/modules/user/src/Plugin/migrate/source/d7/UserEntityTranslation.php b/web/core/modules/user/src/Plugin/migrate/source/d7/UserEntityTranslation.php index 067f86b6950edc179b511e4b49fdf79fff94bd34..04c6cead822e36f6bdad3c20111b89c7b1395e81 100644 --- a/web/core/modules/user/src/Plugin/migrate/source/d7/UserEntityTranslation.php +++ b/web/core/modules/user/src/Plugin/migrate/source/d7/UserEntityTranslation.php @@ -6,9 +6,10 @@ use Drupal\migrate_drupal\Plugin\migrate\source\d7\FieldableEntity; /** - * Provides Drupal 7 user entity translations source plugin. + * Drupal 7 user entity translations source from database. + * + * For available configuration keys, refer to the parent classes. * - * For available configuration keys, refer to the parent classes: * @see \Drupal\migrate\Plugin\migrate\source\SqlBase * @see \Drupal\migrate\Plugin\migrate\source\SourcePluginBase * diff --git a/web/core/modules/views/src/Plugin/views/query/Sql.php b/web/core/modules/views/src/Plugin/views/query/Sql.php index c6200bc16a9671cf9e0e1e1bfff6d4694c858bb1..53a9ac037c68abaa1c53098583104c13be5a2b2c 100644 --- a/web/core/modules/views/src/Plugin/views/query/Sql.php +++ b/web/core/modules/views/src/Plugin/views/query/Sql.php @@ -326,8 +326,14 @@ public function buildOptionsForm(&$form, FormStateInterface $form_state) { */ public function submitOptionsForm(&$form, FormStateInterface $form_state) { $element = ['#parents' => ['query', 'options', 'query_tags']]; - $value = explode(',', NestedArray::getValue($form_state->getValues(), $element['#parents'])); - $value = array_filter(array_map('trim', $value)); + $value = NestedArray::getValue($form_state->getValues(), $element['#parents']); + // When toggling a display to override defaults or vice-versa the submit + // handler gets invoked twice, and we don't want to bash the values from the + // original call. + if (is_array($value)) { + return; + } + $value = array_filter(array_map('trim', explode(',', $value))); $form_state->setValueForElement($element, $value); } diff --git a/web/core/modules/views/tests/src/Functional/Plugin/QueryOptionsTest.php b/web/core/modules/views/tests/src/Functional/Plugin/QueryOptionsTest.php new file mode 100644 index 0000000000000000000000000000000000000000..cb027c06e12c5f2e5bbb1aaaabc38be33b9bdb45 --- /dev/null +++ b/web/core/modules/views/tests/src/Functional/Plugin/QueryOptionsTest.php @@ -0,0 +1,70 @@ +<?php + +namespace Drupal\Tests\views\Functional\Plugin; + +use Drupal\Tests\views\Functional\ViewTestBase; + +/** + * Tests setting the query options. + * + * @group views + */ +class QueryOptionsTest extends ViewTestBase { + + /** + * Views used by this test. + * + * @var array + */ + public static $testViews = ['test_view']; + + /** + * Modules to enable. + * + * @var array + */ + protected static $modules = ['node', 'views_ui']; + + /** + * {@inheritdoc} + */ + protected $defaultTheme = 'stark'; + + /** + * Test that query overrides are stored. + */ + public function testStoreQuerySettingsOverride() { + // Show the default display so the override selection is shown. + \Drupal::configFactory()->getEditable('views.settings')->set('ui.show.default_display', TRUE)->save(); + + $admin_user = $this->drupalCreateUser([ + 'administer views', + 'administer site configuration', + ]); + $this->drupalLogin($admin_user); + + $edit = []; + $this->drupalGet('admin/structure/views/view/test_view/edit'); + $this->submitForm($edit, 'Add Page'); + + $this->drupalGet('admin/structure/views/nojs/display/test_view/page_1/query'); + $this->assertSession()->checkboxNotChecked('query[options][distinct]'); + $edit = [ + 'override[dropdown]' => 'page_1', + 'query[options][distinct]' => 1, + ]; + $this->submitForm($edit, 'Apply'); + $this->drupalGet('admin/structure/views/nojs/display/test_view/page_1/query'); + $this->assertSession()->checkboxChecked('query[options][distinct]'); + $edit = [ + 'query[options][query_comment]' => 'comment', + 'query[options][query_tags]' => 'query_tag, another_tag', + ]; + $this->submitForm($edit, 'Apply'); + $this->drupalGet('admin/structure/views/nojs/display/test_view/page_1/query'); + $this->assertSession()->checkboxChecked('query[options][distinct]'); + $this->assertSession()->fieldValueEquals('query[options][query_comment]', 'comment'); + $this->assertSession()->fieldValueEquals('query[options][query_tags]', 'query_tag, another_tag'); + } + +} diff --git a/web/core/modules/views/tests/src/Kernel/Entity/EntityViewsDataTest.php b/web/core/modules/views/tests/src/Kernel/Entity/EntityViewsDataTest.php new file mode 100644 index 0000000000000000000000000000000000000000..6bab4cfdbdec1756c16a56aa2bf30df6f42f098a --- /dev/null +++ b/web/core/modules/views/tests/src/Kernel/Entity/EntityViewsDataTest.php @@ -0,0 +1,821 @@ +<?php + +namespace Drupal\Tests\views\Kernel\Entity; + +use Drupal\Core\Cache\CacheBackendInterface; +use Drupal\Core\Config\Entity\ConfigEntityBase; +use Drupal\Core\Config\Entity\ConfigEntityType; +use Drupal\Core\Entity\ContentEntityBase; +use Drupal\Core\Entity\ContentEntityType; +use Drupal\Core\Entity\EntityTypeInterface; +use Drupal\Core\Field\BaseFieldDefinition; +use Drupal\KernelTests\KernelTestBase; +use Drupal\views\EntityViewsData; + +/** + * Tests entity views data. + * + * @coversDefaultClass \Drupal\views\EntityViewsData + * @group views + */ +class EntityViewsDataTest extends KernelTestBase { + + /** + * The entity type manager. + * + * @var \Drupal\Core\Entity\EntityTypeManagerInterface + */ + protected $entityTypeManager; + + /** + * The base entity type definition, which some tests modify. + * + * Uses a custom class which allows changing entity keys. + * + * @var \Drupal\Tests\views\Kernel\Entity\TestEntityType + */ + protected $baseEntityType; + + /** + * The common base fields for test entity types. + * + * @var \Drupal\Core\Field\BaseFieldDefinition[] + */ + protected $commonBaseFields; + + /** + * Modules to enable. + * + * @var array + */ + protected static $modules = [ + 'user', + 'system', + 'field', + 'text', + 'filter', + ]; + + /** + * {@inheritdoc} + */ + protected function setUp(): void { + parent::setUp(); + + $this->entityTypeManager = $this->container->get('entity_type.manager'); + + // A common entity type definition. Tests may change this prior to passing + // it to setUpEntityType(). + $this->baseEntityType = new TestEntityType([ + // A normal entity type would have its class picked up during discovery, + // but as we're mocking this without an annotation we have to specify it. + 'class' => ViewsTestEntity::class, + 'base_table' => 'entity_test', + 'id' => 'entity_test', + 'label' => 'Entity test', + 'entity_keys' => [ + 'uuid' => 'uuid', + 'id' => 'id', + 'langcode' => 'langcode', + 'bundle' => 'type', + 'revision' => 'revision_id', + ], + 'handlers' => [ + 'views_data' => EntityViewsData::class, + ], + 'provider' => 'entity_test', + 'list_cache_contexts' => ['entity_test_list_cache_context'], + ]); + + // Base fields for the test entity types. + $this->commonBaseFields['name'] = BaseFieldDefinition::create('string') + ->setLabel(t('Name')) + ->setDescription(t('The name of the test entity.')) + ->setTranslatable(TRUE) + ->setSetting('max_length', 32); + + $this->commonBaseFields['created'] = BaseFieldDefinition::create('created') + ->setLabel(t('Authored on')) + ->setDescription(t('Time the entity was created')) + ->setTranslatable(TRUE); + + $this->commonBaseFields['user_id'] = BaseFieldDefinition::create('entity_reference') + ->setLabel(t('User ID')) + ->setDescription(t('The ID of the associated user.')) + ->setSetting('target_type', 'user') + ->setSetting('handler', 'default') + // Default EntityTest entities to have the root user as the owner, to + // simplify testing. + ->setDefaultValue([0 => ['target_id' => 1]]) + ->setTranslatable(TRUE); + + // Add a description field. This example comes from the taxonomy Term + // entity. + $this->commonBaseFields['description'] = BaseFieldDefinition::create('text_long') + ->setLabel('Description') + ->setDescription('A description of the term.') + ->setTranslatable(TRUE); + + // Add a URL field; this example is from the Comment entity. + $this->commonBaseFields['homepage'] = BaseFieldDefinition::create('uri') + ->setLabel('Homepage') + ->setDescription("The comment author's home page address.") + ->setTranslatable(TRUE) + ->setSetting('max_length', 255); + + // A base field with cardinality > 1 + $this->commonBaseFields['string'] = BaseFieldDefinition::create('string') + ->setLabel('Strong') + ->setTranslatable(TRUE) + ->setCardinality(2); + + // Set up the basic 'entity_test' entity type. This is used by several + // tests; others customize it and the base fields. + $this->setUpEntityType($this->baseEntityType, $this->commonBaseFields); + } + + /** + * Mocks an entity type and its base fields. + * + * This works by: + * - inserting the entity type definition into the entity type manager's cache + * - setting the base fields on the ViewsTestEntity class as a static property + * for its baseFieldsDefinitions() method to use. + * + * @param \Drupal\Core\Entity\EntityTypeInterface $definition + * An entity type definition to add to the entity type manager. + * @param \Drupal\Core\Field\BaseFieldDefinition[] $base_fields + * An array of base field definitions + */ + protected function setUpEntityType(EntityTypeInterface $definition, array $base_fields = []) { + // Replace the cache backend in the entity type manager so it returns + // our test entity type in addition to the existing ones. + $definitions = $this->entityTypeManager->getDefinitions(); + $definitions[$definition->id()] = $definition; + + $cache_backend = $this->prophesize(CacheBackendInterface::class); + $cache_data = new \StdClass(); + $cache_data->data = $definitions; + $cache_backend->get('entity_type')->willReturn($cache_data); + $this->entityTypeManager->setCacheBackend($cache_backend->reveal(), 'entity_type', ['entity_types']); + $this->entityTypeManager->clearCachedDefinitions(); + + if ($base_fields) { + ViewsTestEntity::setMockedBaseFieldDefinitions($definition->id(), $base_fields); + } + } + + /** + * Tests base tables. + */ + public function testBaseTables() { + $data = $this->entityTypeManager->getHandler('entity_test', 'views_data')->getViewsData(); + + $this->assertEquals('entity_test', $data['entity_test']['table']['entity type']); + $this->assertEquals(FALSE, $data['entity_test']['table']['entity revision']); + $this->assertEquals('Entity test', $data['entity_test']['table']['group']); + $this->assertEquals('entity_test', $data['entity_test']['table']['provider']); + + $this->assertEquals('id', $data['entity_test']['table']['base']['field']); + $this->assertEquals(['entity_test_list_cache_context'], $data['entity_test']['table']['base']['cache_contexts']); + $this->assertEquals('Entity test', $data['entity_test']['table']['base']['title']); + + // TODO: change these to assertArrayNotHasKey(). + $this->assertFalse(isset($data['entity_test']['table']['defaults'])); + + $this->assertFalse(isset($data['entity_test_mul_property_data'])); + $this->assertFalse(isset($data['revision_table'])); + $this->assertFalse(isset($data['revision_data_table'])); + } + + /** + * Tests data_table support. + */ + public function testDataTable() { + $entity_type = $this->baseEntityType + ->set('data_table', 'entity_test_mul_property_data') + ->set('id', 'entity_test_mul') + ->set('translatable', TRUE) + ->setKey('label', 'label'); + + $this->setUpEntityType($entity_type); + + // Tests the join definition between the base and the data table. + $data = $this->entityTypeManager->getHandler('entity_test_mul', 'views_data')->getViewsData(); + // TODO: change the base table in the entity type definition to match the + // changed entity ID. + $base_views_data = $data['entity_test']; + + // Ensure that the base table is set to the data table. + $this->assertEquals('id', $data['entity_test_mul_property_data']['table']['base']['field']); + $this->assertEquals('Entity test', $data['entity_test_mul_property_data']['table']['base']['title']); + $this->assertFalse(isset($data['entity_test']['table']['base'])); + + $this->assertEquals('entity_test_mul', $data['entity_test_mul_property_data']['table']['entity type']); + $this->assertEquals(FALSE, $data['entity_test_mul_property_data']['table']['entity revision']); + $this->assertEquals('Entity test', $data['entity_test_mul_property_data']['table']['group']); + $this->assertEquals('entity_test', $data['entity_test']['table']['provider']); + $this->assertEquals(['field' => 'label', 'table' => 'entity_test_mul_property_data'], $data['entity_test_mul_property_data']['table']['base']['defaults']); + + // Ensure the join information is set up properly. + $this->assertCount(1, $base_views_data['table']['join']); + $this->assertEquals(['entity_test_mul_property_data' => ['left_field' => 'id', 'field' => 'id', 'type' => 'INNER']], $base_views_data['table']['join']); + $this->assertFalse(isset($data['revision_table'])); + $this->assertFalse(isset($data['revision_data_table'])); + } + + /** + * Tests revision table without data table support. + */ + public function testRevisionTableWithoutDataTable() { + $entity_type = $this->baseEntityType + ->set('revision_table', 'entity_test_mulrev_revision') + ->set('revision_data_table', NULL) + ->set('id', 'entity_test_mulrev') + ->setKey('revision', 'revision_id'); + + $this->setUpEntityType($entity_type); + + $data = $this->entityTypeManager->getHandler('entity_test_mulrev', 'views_data')->getViewsData(); + + $this->assertEquals('Entity test revisions', $data['entity_test_mulrev_revision']['table']['base']['title']); + $this->assertEquals('revision_id', $data['entity_test_mulrev_revision']['table']['base']['field']); + + $this->assertEquals(FALSE, $data['entity_test']['table']['entity revision']); + $this->assertEquals('entity_test_mulrev', $data['entity_test_mulrev_revision']['table']['entity type']); + $this->assertEquals(TRUE, $data['entity_test_mulrev_revision']['table']['entity revision']); + $this->assertEquals('entity_test_mulrev', $data['entity_test_mulrev_revision']['table']['entity type']); + $this->assertEquals(TRUE, $data['entity_test_mulrev_revision']['table']['entity revision']); + + $this->assertEquals('Entity test revision', $data['entity_test_mulrev_revision']['table']['group']); + $this->assertEquals('entity_test', $data['entity_test']['table']['provider']); + + // Ensure the join information is set up properly. + // Tests the join definition between the base and the revision table. + $revision_data = $data['entity_test_mulrev_revision']; + $this->assertCount(1, $revision_data['table']['join']); + $this->assertEquals(['entity_test' => ['left_field' => 'revision_id', 'field' => 'revision_id', 'type' => 'INNER']], $revision_data['table']['join']); + $this->assertFalse(isset($data['data_table'])); + } + + /** + * Tests revision table with data table support. + */ + public function testRevisionTableWithRevisionDataTableAndDataTable() { + $entity_type = $this->baseEntityType + ->set('data_table', 'entity_test_mul_property_data') + ->set('revision_table', 'entity_test_mulrev_revision') + ->set('revision_data_table', 'entity_test_mulrev_property_revision') + ->set('id', 'entity_test_mulrev') + ->set('translatable', TRUE) + ->setKey('revision', 'revision_id'); + $this->setUpEntityType($entity_type); + + $data = $this->entityTypeManager->getHandler('entity_test_mulrev', 'views_data')->getViewsData(); + + $this->assertEquals('Entity test revisions', $data['entity_test_mulrev_property_revision']['table']['base']['title']); + $this->assertEquals('revision_id', $data['entity_test_mulrev_property_revision']['table']['base']['field']); + $this->assertFalse(isset($data['entity_test_mulrev_revision']['table']['base'])); + + $this->assertEquals('entity_test_mulrev', $data['entity_test_mulrev_property_revision']['table']['entity type']); + $this->assertEquals('Entity test revision', $data['entity_test_mulrev_revision']['table']['group']); + $this->assertEquals('entity_test', $data['entity_test']['table']['provider']); + + // Ensure the join information is set up properly. + // Tests the join definition between the base and the revision table. + $revision_field_data = $data['entity_test_mulrev_property_revision']; + $this->assertCount(1, $revision_field_data['table']['join']); + $this->assertEquals([ + 'entity_test_mul_property_data' => [ + 'left_field' => 'revision_id', + 'field' => 'revision_id', + 'type' => 'INNER', + ], + ], $revision_field_data['table']['join']); + + $revision_base_data = $data['entity_test_mulrev_revision']; + $this->assertCount(1, $revision_base_data['table']['join']); + $this->assertEquals([ + 'entity_test_mulrev_property_revision' => [ + 'left_field' => 'revision_id', + 'field' => 'revision_id', + 'type' => 'INNER', + ], + ], $revision_base_data['table']['join']); + + $this->assertFalse(isset($data['data_table'])); + } + + /** + * Tests revision table with data table support. + */ + public function testRevisionTableWithRevisionDataTable() { + $entity_type = $this->baseEntityType + ->set('revision_table', 'entity_test_mulrev_revision') + ->set('revision_data_table', 'entity_test_mulrev_property_revision') + ->set('id', 'entity_test_mulrev') + ->set('translatable', TRUE) + ->setKey('revision', 'revision_id'); + $this->setUpEntityType($entity_type); + + $data = $this->entityTypeManager->getHandler('entity_test_mulrev', 'views_data')->getViewsData(); + + $this->assertEquals('Entity test revisions', $data['entity_test_mulrev_property_revision']['table']['base']['title']); + $this->assertEquals('revision_id', $data['entity_test_mulrev_property_revision']['table']['base']['field']); + $this->assertFalse(isset($data['entity_test_mulrev_revision']['table']['base'])); + + $this->assertEquals('entity_test_mulrev', $data['entity_test_mulrev_property_revision']['table']['entity type']); + $this->assertEquals('Entity test revision', $data['entity_test_mulrev_revision']['table']['group']); + $this->assertEquals('entity_test', $data['entity_test']['table']['provider']); + + // Ensure the join information is set up properly. + // Tests the join definition between the base and the revision table. + $revision_field_data = $data['entity_test_mulrev_property_revision']; + $this->assertCount(1, $revision_field_data['table']['join']); + $this->assertEquals([ + 'entity_test_mulrev_field_data' => [ + 'left_field' => 'revision_id', + 'field' => 'revision_id', + 'type' => 'INNER', + ], + ], $revision_field_data['table']['join']); + + $revision_base_data = $data['entity_test_mulrev_revision']; + $this->assertCount(1, $revision_base_data['table']['join']); + $this->assertEquals([ + 'entity_test_mulrev_property_revision' => [ + 'left_field' => 'revision_id', + 'field' => 'revision_id', + 'type' => 'INNER', + ], + ], $revision_base_data['table']['join']); + $this->assertFalse(isset($data['data_table'])); + } + + /** + * Tests fields on the base table. + */ + public function testBaseTableFields() { + $data = $this->entityTypeManager->getHandler('entity_test', 'views_data')->getViewsData(); + + $this->assertNumericField($data['entity_test']['id']); + $this->assertViewsDataField($data['entity_test']['id'], 'id'); + $this->assertUuidField($data['entity_test']['uuid']); + $this->assertViewsDataField($data['entity_test']['uuid'], 'uuid'); + $this->assertStringField($data['entity_test']['type']); + $this->assertEquals('type', $data['entity_test']['type']['entity field']); + + $this->assertLanguageField($data['entity_test']['langcode']); + $this->assertViewsDataField($data['entity_test']['langcode'], 'langcode'); + $this->assertEquals('Original language', $data['entity_test']['langcode']['title']); + + $this->assertStringField($data['entity_test']['name']); + $this->assertViewsDataField($data['entity_test']['name'], 'name'); + + $this->assertLongTextField($data['entity_test'], 'description'); + $this->assertViewsDataField($data['entity_test']['description__value'], 'description'); + $this->assertViewsDataField($data['entity_test']['description__format'], 'description'); + + $this->assertUriField($data['entity_test']['homepage']); + $this->assertViewsDataField($data['entity_test']['homepage'], 'homepage'); + + $this->assertEntityReferenceField($data['entity_test']['user_id']); + $this->assertViewsDataField($data['entity_test']['user_id'], 'user_id'); + + $relationship = $data['entity_test']['user_id']['relationship']; + $this->assertEquals('users_field_data', $relationship['base']); + $this->assertEquals('uid', $relationship['base field']); + + // The string field name should be used as the 'entity field' but the actual + // field should reflect what the column mapping is using for multi-value + // base fields NOT just the field name. The actual column name returned from + // mappings in the test mocks is 'value'. + $this->assertStringField($data['entity_test__string']['string_value']); + $this->assertViewsDataField($data['entity_test__string']['string_value'], 'string'); + $this->assertEquals([ + 'left_field' => 'id', + 'field' => 'entity_id', + 'extra' => [[ + 'field' => 'deleted', + 'value' => 0, + 'numeric' => TRUE, + ], + ], + ], $data['entity_test__string']['table']['join']['entity_test']); + } + + /** + * Tests fields on the data table. + */ + public function testDataTableFields() { + $entity_test_type = new ConfigEntityType([ + 'class' => ConfigEntityBase::class, + 'id' => 'entity_test_bundle', + 'entity_keys' => [ + 'id' => 'type', + 'label' => 'name', + ], + ]); + $this->setUpEntityType($entity_test_type); + + $entity_type = $this->baseEntityType + ->set('data_table', 'entity_test_mul_property_data') + ->set('base_table', 'entity_test_mul') + ->set('translatable', TRUE) + ->set('id', 'entity_test_mul') + ->set('bundle_entity_type', 'entity_test_bundle') + ->setKey('bundle', 'type'); + + $base_field_definitions = $this->commonBaseFields; + $base_field_definitions['type'] = BaseFieldDefinition::create('entity_reference') + ->setLabel('entity test type') + ->setSetting('target_type', 'entity_test_bundle'); + + $this->setUpEntityType($entity_type, $base_field_definitions); + + $data = $this->entityTypeManager->getHandler('entity_test_mul', 'views_data')->getViewsData(); + + // Check the base fields. + $this->assertFalse(isset($data['entity_test_mul']['id'])); + $this->assertFalse(isset($data['entity_test_mul']['type'])); + $this->assertUuidField($data['entity_test_mul']['uuid']); + $this->assertViewsDataField($data['entity_test_mul']['uuid'], 'uuid'); + + $this->assertFalse(isset($data['entity_test_mul']['type']['relationship'])); + + // Also ensure that field_data only fields don't appear on the base table. + $this->assertFalse(isset($data['entity_test_mul']['name'])); + $this->assertFalse(isset($data['entity_test_mul']['description'])); + $this->assertFalse(isset($data['entity_test_mul']['description__value'])); + $this->assertFalse(isset($data['entity_test_mul']['description__format'])); + $this->assertFalse(isset($data['entity_test_mul']['user_id'])); + $this->assertFalse(isset($data['entity_test_mul']['homepage'])); + + // Check the data fields. + $this->assertNumericField($data['entity_test_mul_property_data']['id']); + $this->assertViewsDataField($data['entity_test_mul_property_data']['id'], 'id'); + + $this->assertBundleField($data['entity_test_mul_property_data']['type']); + $this->assertViewsDataField($data['entity_test_mul_property_data']['type'], 'type'); + + $this->assertLanguageField($data['entity_test_mul_property_data']['langcode']); + $this->assertViewsDataField($data['entity_test_mul_property_data']['langcode'], 'langcode'); + $this->assertEquals('Translation language', $data['entity_test_mul_property_data']['langcode']['title']); + + $this->assertStringField($data['entity_test_mul_property_data']['name']); + $this->assertViewsDataField($data['entity_test_mul_property_data']['name'], 'name'); + + $this->assertLongTextField($data['entity_test_mul_property_data'], 'description'); + $this->assertViewsDataField($data['entity_test_mul_property_data']['description__value'], 'description'); + $this->assertViewsDataField($data['entity_test_mul_property_data']['description__format'], 'description'); + + $this->assertUriField($data['entity_test_mul_property_data']['homepage']); + $this->assertViewsDataField($data['entity_test_mul_property_data']['homepage'], 'homepage'); + + $this->assertEntityReferenceField($data['entity_test_mul_property_data']['user_id']); + $this->assertViewsDataField($data['entity_test_mul_property_data']['user_id'], 'user_id'); + $relationship = $data['entity_test_mul_property_data']['user_id']['relationship']; + $this->assertEquals('users_field_data', $relationship['base']); + $this->assertEquals('uid', $relationship['base field']); + + $this->assertStringField($data['entity_test_mul__string']['string_value']); + $this->assertViewsDataField($data['entity_test_mul__string']['string_value'], 'string'); + $this->assertEquals([ + 'left_field' => 'id', + 'field' => 'entity_id', + 'extra' => [[ + 'field' => 'deleted', + 'value' => 0, + 'numeric' => TRUE, + ], + ], + ], $data['entity_test_mul__string']['table']['join']['entity_test_mul_property_data']); + } + + /** + * Tests fields on the revision table. + */ + public function testRevisionTableFields() { + $entity_type = $this->baseEntityType + ->set('id', 'entity_test_mulrev') + ->set('base_table', 'entity_test_mulrev') + ->set('revision_table', 'entity_test_mulrev_revision') + ->set('data_table', 'entity_test_mulrev_property_data') + ->set('revision_data_table', 'entity_test_mulrev_property_revision') + ->set('translatable', TRUE); + + $base_field_definitions = $this->commonBaseFields; + + $base_field_definitions['name']->setRevisionable(TRUE); + $base_field_definitions['description']->setRevisionable(TRUE); + $base_field_definitions['homepage']->setRevisionable(TRUE); + $base_field_definitions['user_id']->setRevisionable(TRUE); + + $base_field_definitions['non_rev_field'] = BaseFieldDefinition::create('string') + ->setLabel(t('Non Revisionable Field')) + ->setDescription(t('A non-revisionable test field.')) + ->setRevisionable(FALSE) + ->setTranslatable(TRUE) + ->setCardinality(1) + ->setReadOnly(TRUE); + + $base_field_definitions['non_mul_field'] = BaseFieldDefinition::create('string') + ->setLabel(t('Non translatable')) + ->setDescription(t('A non-translatable string field')) + ->setRevisionable(TRUE); + + $this->setUpEntityType($entity_type, $base_field_definitions); + + $data = $this->entityTypeManager->getHandler('entity_test_mulrev', 'views_data')->getViewsData(); + + // Check the base fields. + $this->assertFalse(isset($data['entity_test_mulrev']['id'])); + $this->assertFalse(isset($data['entity_test_mulrev']['type'])); + $this->assertFalse(isset($data['entity_test_mulrev']['revision_id'])); + $this->assertUuidField($data['entity_test_mulrev']['uuid']); + $this->assertViewsDataField($data['entity_test_mulrev']['uuid'], 'uuid'); + + // Also ensure that field_data only fields don't appear on the base table. + $this->assertFalse(isset($data['entity_test_mulrev']['name'])); + $this->assertFalse(isset($data['entity_test_mul']['description'])); + $this->assertFalse(isset($data['entity_test_mul']['description__value'])); + $this->assertFalse(isset($data['entity_test_mul']['description__format'])); + $this->assertFalse(isset($data['entity_test_mul']['homepage'])); + // $this->assertFalse(isset($data['entity_test_mulrev']['langcode'])); + $this->assertFalse(isset($data['entity_test_mulrev']['user_id'])); + + // Check the revision fields. The revision ID should only appear in the data + // table. + $this->assertFalse(isset($data['entity_test_mulrev_revision']['revision_id'])); + + // Also ensure that field_data only fields don't appear on the revision table. + $this->assertFalse(isset($data['entity_test_mulrev_revision']['id'])); + $this->assertFalse(isset($data['entity_test_mulrev_revision']['name'])); + $this->assertFalse(isset($data['entity_test_mulrev_revision']['description'])); + $this->assertFalse(isset($data['entity_test_mulrev_revision']['description__value'])); + $this->assertFalse(isset($data['entity_test_mulrev_revision']['description__format'])); + $this->assertFalse(isset($data['entity_test_mulrev_revision']['homepage'])); + $this->assertFalse(isset($data['entity_test_mulrev_revision']['user_id'])); + + // Check the data fields. + $this->assertNumericField($data['entity_test_mulrev_property_data']['id']); + $this->assertViewsDataField($data['entity_test_mulrev_property_data']['id'], 'id'); + $this->assertNumericField($data['entity_test_mulrev_property_data']['revision_id']); + $this->assertViewsDataField($data['entity_test_mulrev_property_data']['revision_id'], 'revision_id'); + $this->assertLanguageField($data['entity_test_mulrev_property_data']['langcode']); + $this->assertViewsDataField($data['entity_test_mulrev_property_data']['langcode'], 'langcode'); + $this->assertStringField($data['entity_test_mulrev_property_data']['name']); + $this->assertViewsDataField($data['entity_test_mulrev_property_data']['name'], 'name'); + + $this->assertLongTextField($data['entity_test_mulrev_property_data'], 'description'); + $this->assertViewsDataField($data['entity_test_mulrev_property_data']['description__value'], 'description'); + $this->assertViewsDataField($data['entity_test_mulrev_property_data']['description__format'], 'description'); + $this->assertUriField($data['entity_test_mulrev_property_data']['homepage']); + $this->assertViewsDataField($data['entity_test_mulrev_property_data']['homepage'], 'homepage'); + + $this->assertEntityReferenceField($data['entity_test_mulrev_property_data']['user_id']); + $this->assertViewsDataField($data['entity_test_mulrev_property_data']['user_id'], 'user_id'); + $relationship = $data['entity_test_mulrev_property_data']['user_id']['relationship']; + $this->assertEquals('users_field_data', $relationship['base']); + $this->assertEquals('uid', $relationship['base field']); + + // Check the property data fields. + $this->assertNumericField($data['entity_test_mulrev_property_revision']['id']); + $this->assertViewsDataField($data['entity_test_mulrev_property_revision']['id'], 'id'); + + $this->assertLanguageField($data['entity_test_mulrev_property_revision']['langcode']); + $this->assertViewsDataField($data['entity_test_mulrev_property_revision']['langcode'], 'langcode'); + $this->assertEquals('Translation language', $data['entity_test_mulrev_property_revision']['langcode']['title']); + + $this->assertStringField($data['entity_test_mulrev_property_revision']['name']); + $this->assertViewsDataField($data['entity_test_mulrev_property_revision']['name'], 'name'); + + $this->assertLongTextField($data['entity_test_mulrev_property_revision'], 'description'); + $this->assertViewsDataField($data['entity_test_mulrev_property_revision']['description__value'], 'description'); + $this->assertViewsDataField($data['entity_test_mulrev_property_revision']['description__format'], 'description'); + + $this->assertUriField($data['entity_test_mulrev_property_revision']['homepage']); + $this->assertViewsDataField($data['entity_test_mulrev_property_revision']['homepage'], 'homepage'); + + $this->assertEntityReferenceField($data['entity_test_mulrev_property_revision']['user_id']); + $this->assertViewsDataField($data['entity_test_mulrev_property_revision']['user_id'], 'user_id'); + $relationship = $data['entity_test_mulrev_property_revision']['user_id']['relationship']; + $this->assertEquals('users_field_data', $relationship['base']); + $this->assertEquals('uid', $relationship['base field']); + + $this->assertStringField($data['entity_test_mulrev__string']['string_value']); + $this->assertViewsDataField($data['entity_test_mulrev__string']['string_value'], 'string'); + $this->assertEquals([ + 'left_field' => 'id', + 'field' => 'entity_id', + 'extra' => [[ + 'field' => 'deleted', + 'value' => 0, + 'numeric' => TRUE, + ], + ], + ], $data['entity_test_mulrev__string']['table']['join']['entity_test_mulrev_property_data']); + + $this->assertStringField($data['entity_test_mulrev_revision__string']['string_value']); + $this->assertViewsDataField($data['entity_test_mulrev_revision__string']['string_value'], 'string'); + $this->assertEquals([ + 'left_field' => 'revision_id', + 'field' => 'entity_id', + 'extra' => [[ + 'field' => 'deleted', + 'value' => 0, + 'numeric' => TRUE, + ], + ], + ], $data['entity_test_mulrev_revision__string']['table']['join']['entity_test_mulrev_property_revision']); + } + + /** + * Tests generic stuff per field. + * + * @param array $data + * The views data to check. + * @param string $field_name + * The entity field name. + */ + protected function assertViewsDataField($data, $field_name) { + $this->assertEquals($field_name, $data['entity field']); + } + + /** + * Tests views data for a string field. + * + * @param $data + * The views data to check. + */ + protected function assertStringField($data) { + $this->assertEquals('field', $data['field']['id']); + $this->assertEquals('string', $data['filter']['id']); + $this->assertEquals('string', $data['argument']['id']); + $this->assertEquals('standard', $data['sort']['id']); + } + + /** + * Tests views data for a URI field. + * + * @param $data + * The views data to check. + */ + protected function assertUriField($data) { + $this->assertEquals('field', $data['field']['id']); + $this->assertEquals('string', $data['field']['default_formatter']); + $this->assertEquals('string', $data['filter']['id']); + $this->assertEquals('string', $data['argument']['id']); + $this->assertEquals('standard', $data['sort']['id']); + } + + /** + * Tests views data for a long text field. + * + * @param $data + * The views data for the table this field is in. + * @param $field_name + * The name of the field being checked. + */ + protected function assertLongTextField($data, $field_name) { + $value_field = $data[$field_name . '__value']; + $this->assertEquals('field', $value_field['field']['id']); + $this->assertEquals($field_name . '__format', $value_field['field']['format']); + $this->assertEquals('string', $value_field['filter']['id']); + $this->assertEquals('string', $value_field['argument']['id']); + $this->assertEquals('standard', $value_field['sort']['id']); + + $this->assertStringField($data[$field_name . '__format']); + } + + /** + * Tests views data for a UUID field. + * + * @param array $data + * The views data to check. + */ + protected function assertUuidField($data) { + // @todo Can we provide additional support for UUIDs in views? + $this->assertEquals('field', $data['field']['id']); + $this->assertFalse($data['field']['click sortable']); + $this->assertEquals('string', $data['filter']['id']); + $this->assertEquals('string', $data['argument']['id']); + $this->assertEquals('standard', $data['sort']['id']); + } + + /** + * Tests views data for a numeric field. + * + * @param array $data + * The views data to check. + */ + protected function assertNumericField($data) { + $this->assertEquals('field', $data['field']['id']); + $this->assertEquals('numeric', $data['filter']['id']); + $this->assertEquals('numeric', $data['argument']['id']); + $this->assertEquals('standard', $data['sort']['id']); + } + + /** + * Tests views data for a language field. + * + * @param array $data + * The views data to check. + */ + protected function assertLanguageField($data) { + $this->assertEquals('field', $data['field']['id']); + $this->assertEquals('language', $data['filter']['id']); + $this->assertEquals('language', $data['argument']['id']); + $this->assertEquals('standard', $data['sort']['id']); + } + + /** + * Tests views data for an entity reference field. + */ + protected function assertEntityReferenceField($data) { + $this->assertEquals('field', $data['field']['id']); + $this->assertEquals('numeric', $data['filter']['id']); + $this->assertEquals('numeric', $data['argument']['id']); + $this->assertEquals('standard', $data['sort']['id']); + } + + /** + * Tests views data for a bundle field. + */ + protected function assertBundleField($data) { + $this->assertEquals('field', $data['field']['id']); + $this->assertEquals('bundle', $data['filter']['id']); + $this->assertEquals('string', $data['argument']['id']); + $this->assertEquals('standard', $data['sort']['id']); + } + +} + +/** + * Entity type class which allows changing the entity keys. + */ +class TestEntityType extends ContentEntityType { + + /** + * Sets a specific entity key. + * + * @param string $key + * The name of the entity key. + * @param string $value + * The new value of the key. + * + * @return $this + */ + public function setKey($key, $value) { + $this->entity_keys[$key] = $value; + return $this; + } + +} + +/** + * Generic entity class for our test entity types. + * + * Allows mocked base field definitions. + */ +class ViewsTestEntity extends ContentEntityBase { + + /** + * The mocked base fields for test entity types. + * + * An array keyed by entity type ID, whose values are arrays of base field + * definitions. + * + * @var array + */ + protected static $mockedBaseFieldDefinitions = []; + + /** + * Sets up the mocked base field definitions. + * + * @param string $entity_type_id + * The entity type ID. + * @param array $definitions + * The array of base field definitions to mock. These are added to the + * defaults ones from the parent class. + */ + public static function setMockedBaseFieldDefinitions(string $entity_type_id, array $definitions) { + static::$mockedBaseFieldDefinitions[$entity_type_id] = $definitions; + } + + /** + * {@inheritdoc} + */ + public static function baseFieldDefinitions(EntityTypeInterface $entity_type) { + $fields = parent::baseFieldDefinitions($entity_type); + + if (isset(static::$mockedBaseFieldDefinitions[$entity_type->id()])) { + $mocked_fields = static::$mockedBaseFieldDefinitions[$entity_type->id()]; + // Mocked fields take priority over ones from the base class. + $fields = $mocked_fields + $fields; + } + + return $fields; + } + +} diff --git a/web/core/modules/views/tests/src/Unit/EntityViewsDataTest.php b/web/core/modules/views/tests/src/Unit/EntityViewsDataTest.php deleted file mode 100644 index 6e0621e40fde5b6d0b9b87e9c4343ce4360b0576..0000000000000000000000000000000000000000 --- a/web/core/modules/views/tests/src/Unit/EntityViewsDataTest.php +++ /dev/null @@ -1,1197 +0,0 @@ -<?php - -/** - * @file - * Contains \Drupal\Tests\views\Unit\EntityViewsDataTest. - */ - -namespace Drupal\Tests\views\Unit; - -use Drupal\Core\Config\Entity\ConfigEntityType; -use Drupal\Core\Entity\ContentEntityType; -use Drupal\Core\Entity\EntityFieldManagerInterface; -use Drupal\Core\Entity\EntityTypeInterface; -use Drupal\Core\Entity\EntityTypeManagerInterface; -use Drupal\Core\Entity\Sql\DefaultTableMapping; -use Drupal\Core\Field\BaseFieldDefinition; -use Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem; -use Drupal\Core\Field\Plugin\Field\FieldType\IntegerItem; -use Drupal\Core\Field\Plugin\Field\FieldType\LanguageItem; -use Drupal\Core\Field\Plugin\Field\FieldType\StringItem; -use Drupal\Core\Field\Plugin\Field\FieldType\UriItem; -use Drupal\Core\Field\Plugin\Field\FieldType\UuidItem; -use Drupal\Core\State\StateInterface; -use Drupal\Core\TypedData\TypedDataManagerInterface; -use Drupal\text\Plugin\Field\FieldType\TextLongItem; -use Drupal\entity_test\Entity\EntityTest; -use Drupal\entity_test\Entity\EntityTestMul; -use Drupal\entity_test\Entity\EntityTestMulRev; -use Drupal\Tests\UnitTestCase; -use Drupal\views\EntityViewsData; -use Prophecy\Argument; -use Symfony\Component\DependencyInjection\ContainerBuilder; - -/** - * @coversDefaultClass \Drupal\views\EntityViewsData - * @group Views - */ -class EntityViewsDataTest extends UnitTestCase { - - /** - * Entity info to use in this test. - * - * @var \Drupal\Core\Entity\EntityTypeInterface|\Drupal\Tests\views\Unit\TestEntityType - */ - protected $baseEntityType; - - /** - * The mocked entity storage. - * - * @var \Drupal\Core\Entity\Sql\SqlContentEntityStorage|\PHPUnit\Framework\MockObject\MockObject - */ - protected $entityStorage; - - /** - * The mocked entity field manager. - * - * @var \Drupal\Core\Entity\EntityFieldManagerInterface|\PHPUnit\Framework\MockObject\MockObject - */ - protected $entityFieldManager; - - - /** - * The mocked entity type manager. - * - * @var \Drupal\Core\Entity\EntityTypeManagerInterface|\PHPUnit\Framework\MockObject\MockObject - */ - protected $entityTypeManager; - - /** - * The mocked module handler. - * - * @var \Drupal\Core\Extension\ModuleHandlerInterface|\PHPUnit\Framework\MockObject\MockObject - */ - protected $moduleHandler; - - /** - * The mocked translation manager. - * - * @var \Drupal\Core\StringTranslation\TranslationInterface|\PHPUnit\Framework\MockObject\MockObject - */ - protected $translationManager; - - /** - * The tested entity views controller. - * - * @var \Drupal\Tests\views\Unit\TestEntityViewsData - */ - protected $viewsData; - - /** - * {@inheritdoc} - */ - protected function setUp(): void { - $this->entityStorage = $this->getMockBuilder('Drupal\Core\Entity\Sql\SqlContentEntityStorage') - ->disableOriginalConstructor() - ->getMock(); - $this->entityTypeManager = $this->createMock(EntityTypeManagerInterface::class); - $this->entityFieldManager = $this->createMock(EntityFieldManagerInterface::class); - - $typed_data_manager = $this->createMock(TypedDataManagerInterface::class); - $typed_data_manager->expects($this->any()) - ->method('createDataDefinition') - ->willReturn($this->createMock('Drupal\Core\TypedData\DataDefinitionInterface')); - - $typed_data_manager->expects($this->any()) - ->method('getDefinition') - ->will($this->returnValueMap([ - 'entity:user' => ['class' => '\Drupal\Core\TypedData\DataDefinitionInterface'], - 'field_item:string_long' => ['class' => '\Drupal\Core\Field\Plugin\Field\FieldType\StringLongItem'], - ])); - - $this->baseEntityType = new TestEntityType([ - 'base_table' => 'entity_test', - 'id' => 'entity_test', - 'label' => 'Entity test', - 'entity_keys' => [ - 'uuid' => 'uuid', - 'id' => 'id', - 'langcode' => 'langcode', - 'bundle' => 'type', - 'revision' => 'revision_id', - ], - 'provider' => 'entity_test', - 'list_cache_contexts' => ['entity_test_list_cache_context'], - ]); - - $this->translationManager = $this->getStringTranslationStub(); - $this->baseEntityType->setStringTranslation($this->translationManager); - $this->moduleHandler = $this->createMock('Drupal\Core\Extension\ModuleHandlerInterface'); - - $this->viewsData = new TestEntityViewsData($this->baseEntityType, $this->entityStorage, $this->entityTypeManager, $this->moduleHandler, $this->translationManager, $this->entityFieldManager); - - $field_type_manager = $this->getMockBuilder('Drupal\Core\Field\FieldTypePluginManager') - ->disableOriginalConstructor() - ->getMock(); - $field_type_manager->expects($this->any()) - ->method('getDefaultStorageSettings') - ->willReturn([]); - $field_type_manager->expects($this->any()) - ->method('getDefaultFieldSettings') - ->willReturn([]); - - $state = $this->prophesize(StateInterface::class); - $state->get(Argument::any(), [])->willReturn([]); - - $container = new ContainerBuilder(); - $container->set('plugin.manager.field.field_type', $field_type_manager); - $container->set('entity_type.manager', $this->entityTypeManager); - $container->set('entity_field.manager', $this->entityFieldManager); - $container->set('typed_data_manager', $typed_data_manager); - $container->set('state', $state->reveal()); - \Drupal::setContainer($container); - } - - /** - * Helper method to setup base fields. - * - * @param \Drupal\Core\Field\BaseFieldDefinition[] $base_fields - * The base fields which are adapted. - * - * @return \Drupal\Core\Field\BaseFieldDefinition[] - * The setup base fields. - */ - protected function setupBaseFields(array $base_fields) { - // Add a description field to the fields supplied by the EntityTest - // classes. This example comes from the taxonomy Term entity. - $base_fields['description'] = BaseFieldDefinition::create('text_long') - ->setLabel('Description') - ->setDescription('A description of the term.') - ->setTranslatable(TRUE) - ->setDisplayOptions('view', [ - 'label' => 'hidden', - 'type' => 'text_default', - 'weight' => 0, - ]) - ->setDisplayConfigurable('view', TRUE) - ->setDisplayOptions('form', [ - 'type' => 'text_textfield', - 'weight' => 0, - ]) - ->setDisplayConfigurable('form', TRUE); - - // Add a URL field; this example is from the Comment entity. - $base_fields['homepage'] = BaseFieldDefinition::create('uri') - ->setLabel('Homepage') - ->setDescription("The comment author's home page address.") - ->setTranslatable(TRUE) - ->setSetting('max_length', 255); - - // A base field with cardinality > 1 - $base_fields['string'] = BaseFieldDefinition::create('string') - ->setLabel('Strong') - ->setTranslatable(TRUE) - ->setCardinality(2); - - foreach ($base_fields as $name => $base_field) { - $base_field->setName($name); - } - return $base_fields; - } - - /** - * Tests base tables. - */ - public function testBaseTables() { - $data = $this->viewsData->getViewsData(); - - $this->assertEquals('entity_test', $data['entity_test']['table']['entity type']); - $this->assertEquals(FALSE, $data['entity_test']['table']['entity revision']); - $this->assertEquals('Entity test', $data['entity_test']['table']['group']); - $this->assertEquals('entity_test', $data['entity_test']['table']['provider']); - - $this->assertEquals('id', $data['entity_test']['table']['base']['field']); - $this->assertEquals(['entity_test_list_cache_context'], $data['entity_test']['table']['base']['cache_contexts']); - $this->assertEquals('Entity test', $data['entity_test']['table']['base']['title']); - - $this->assertFalse(isset($data['entity_test']['table']['defaults'])); - - $this->assertFalse(isset($data['entity_test_mul_property_data'])); - $this->assertFalse(isset($data['revision_table'])); - $this->assertFalse(isset($data['revision_data_table'])); - } - - /** - * Tests data_table support. - */ - public function testDataTable() { - $entity_type = $this->baseEntityType - ->set('data_table', 'entity_test_mul_property_data') - ->set('id', 'entity_test_mul') - ->set('translatable', TRUE) - ->setKey('label', 'label'); - - $this->viewsData->setEntityType($entity_type); - - // Tests the join definition between the base and the data table. - $data = $this->viewsData->getViewsData(); - $base_views_data = $data['entity_test']; - - // Ensure that the base table is set to the data table. - $this->assertEquals('id', $data['entity_test_mul_property_data']['table']['base']['field']); - $this->assertEquals('Entity test', $data['entity_test_mul_property_data']['table']['base']['title']); - $this->assertFalse(isset($data['entity_test']['table']['base'])); - - $this->assertEquals('entity_test_mul', $data['entity_test_mul_property_data']['table']['entity type']); - $this->assertEquals(FALSE, $data['entity_test_mul_property_data']['table']['entity revision']); - $this->assertEquals('Entity test', $data['entity_test_mul_property_data']['table']['group']); - $this->assertEquals('entity_test', $data['entity_test']['table']['provider']); - $this->assertEquals(['field' => 'label', 'table' => 'entity_test_mul_property_data'], $data['entity_test_mul_property_data']['table']['base']['defaults']); - - // Ensure the join information is set up properly. - $this->assertCount(1, $base_views_data['table']['join']); - $this->assertEquals(['entity_test_mul_property_data' => ['left_field' => 'id', 'field' => 'id', 'type' => 'INNER']], $base_views_data['table']['join']); - $this->assertFalse(isset($data['revision_table'])); - $this->assertFalse(isset($data['revision_data_table'])); - } - - /** - * Tests revision table without data table support. - */ - public function testRevisionTableWithoutDataTable() { - $entity_type = $this->baseEntityType - ->set('revision_table', 'entity_test_mulrev_revision') - ->set('revision_data_table', NULL) - ->set('id', 'entity_test_mulrev') - ->setKey('revision', 'revision_id'); - $this->viewsData->setEntityType($entity_type); - - $data = $this->viewsData->getViewsData(); - - $this->assertEquals('Entity test revisions', $data['entity_test_mulrev_revision']['table']['base']['title']); - $this->assertEquals('revision_id', $data['entity_test_mulrev_revision']['table']['base']['field']); - - $this->assertEquals(FALSE, $data['entity_test']['table']['entity revision']); - $this->assertEquals('entity_test_mulrev', $data['entity_test_mulrev_revision']['table']['entity type']); - $this->assertEquals(TRUE, $data['entity_test_mulrev_revision']['table']['entity revision']); - $this->assertEquals('entity_test_mulrev', $data['entity_test_mulrev_revision']['table']['entity type']); - $this->assertEquals(TRUE, $data['entity_test_mulrev_revision']['table']['entity revision']); - - $this->assertEquals('Entity test revision', $data['entity_test_mulrev_revision']['table']['group']); - $this->assertEquals('entity_test', $data['entity_test']['table']['provider']); - - // Ensure the join information is set up properly. - // Tests the join definition between the base and the revision table. - $revision_data = $data['entity_test_mulrev_revision']; - $this->assertCount(1, $revision_data['table']['join']); - $this->assertEquals(['entity_test' => ['left_field' => 'revision_id', 'field' => 'revision_id', 'type' => 'INNER']], $revision_data['table']['join']); - $this->assertFalse(isset($data['data_table'])); - } - - /** - * Tests revision table with data table support. - */ - public function testRevisionTableWithRevisionDataTableAndDataTable() { - $entity_type = $this->baseEntityType - ->set('data_table', 'entity_test_mul_property_data') - ->set('revision_table', 'entity_test_mulrev_revision') - ->set('revision_data_table', 'entity_test_mulrev_property_revision') - ->set('id', 'entity_test_mulrev') - ->set('translatable', TRUE) - ->setKey('revision', 'revision_id'); - $this->viewsData->setEntityType($entity_type); - - $data = $this->viewsData->getViewsData(); - - $this->assertEquals('Entity test revisions', $data['entity_test_mulrev_property_revision']['table']['base']['title']); - $this->assertEquals('revision_id', $data['entity_test_mulrev_property_revision']['table']['base']['field']); - $this->assertFalse(isset($data['entity_test_mulrev_revision']['table']['base'])); - - $this->assertEquals('entity_test_mulrev', $data['entity_test_mulrev_property_revision']['table']['entity type']); - $this->assertEquals('Entity test revision', $data['entity_test_mulrev_revision']['table']['group']); - $this->assertEquals('entity_test', $data['entity_test']['table']['provider']); - - // Ensure the join information is set up properly. - // Tests the join definition between the base and the revision table. - $revision_field_data = $data['entity_test_mulrev_property_revision']; - $this->assertCount(1, $revision_field_data['table']['join']); - $this->assertEquals([ - 'entity_test_mul_property_data' => [ - 'left_field' => 'revision_id', - 'field' => 'revision_id', - 'type' => 'INNER', - ], - ], $revision_field_data['table']['join']); - - $revision_base_data = $data['entity_test_mulrev_revision']; - $this->assertCount(1, $revision_base_data['table']['join']); - $this->assertEquals([ - 'entity_test_mulrev_property_revision' => [ - 'left_field' => 'revision_id', - 'field' => 'revision_id', - 'type' => 'INNER', - ], - ], $revision_base_data['table']['join']); - - $this->assertFalse(isset($data['data_table'])); - } - - /** - * Tests revision table with data table support. - */ - public function testRevisionTableWithRevisionDataTable() { - $entity_type = $this->baseEntityType - ->set('revision_table', 'entity_test_mulrev_revision') - ->set('revision_data_table', 'entity_test_mulrev_property_revision') - ->set('id', 'entity_test_mulrev') - ->set('translatable', TRUE) - ->setKey('revision', 'revision_id'); - $this->viewsData->setEntityType($entity_type); - - $data = $this->viewsData->getViewsData(); - - $this->assertEquals('Entity test revisions', $data['entity_test_mulrev_property_revision']['table']['base']['title']); - $this->assertEquals('revision_id', $data['entity_test_mulrev_property_revision']['table']['base']['field']); - $this->assertFalse(isset($data['entity_test_mulrev_revision']['table']['base'])); - - $this->assertEquals('entity_test_mulrev', $data['entity_test_mulrev_property_revision']['table']['entity type']); - $this->assertEquals('Entity test revision', $data['entity_test_mulrev_revision']['table']['group']); - $this->assertEquals('entity_test', $data['entity_test']['table']['provider']); - - // Ensure the join information is set up properly. - // Tests the join definition between the base and the revision table. - $revision_field_data = $data['entity_test_mulrev_property_revision']; - $this->assertCount(1, $revision_field_data['table']['join']); - $this->assertEquals([ - 'entity_test_mulrev_field_data' => [ - 'left_field' => 'revision_id', - 'field' => 'revision_id', - 'type' => 'INNER', - ], - ], $revision_field_data['table']['join']); - - $revision_base_data = $data['entity_test_mulrev_revision']; - $this->assertCount(1, $revision_base_data['table']['join']); - $this->assertEquals([ - 'entity_test_mulrev_property_revision' => [ - 'left_field' => 'revision_id', - 'field' => 'revision_id', - 'type' => 'INNER', - ], - ], $revision_base_data['table']['join']); - $this->assertFalse(isset($data['data_table'])); - } - - /** - * Helper method to mock all store definitions. - */ - protected function setupFieldStorageDefinition() { - $id_field_storage_definition = $this->createMock('Drupal\Core\Field\FieldStorageDefinitionInterface'); - $id_field_storage_definition->expects($this->any()) - ->method('getSchema') - ->willReturn(IntegerItem::schema($id_field_storage_definition)); - $uuid_field_storage_definition = $this->createMock('Drupal\Core\Field\FieldStorageDefinitionInterface'); - $uuid_field_storage_definition->expects($this->any()) - ->method('getSchema') - ->willReturn(UuidItem::schema($uuid_field_storage_definition)); - $type_field_storage_definition = $this->createMock('Drupal\Core\Field\FieldStorageDefinitionInterface'); - $type_field_storage_definition->expects($this->any()) - ->method('getSchema') - ->willReturn(StringItem::schema($type_field_storage_definition)); - $langcode_field_storage_definition = $this->createMock('Drupal\Core\Field\FieldStorageDefinitionInterface'); - $langcode_field_storage_definition->expects($this->any()) - ->method('getSchema') - ->willReturn(LanguageItem::schema($langcode_field_storage_definition)); - $name_field_storage_definition = $this->createMock('Drupal\Core\Field\FieldStorageDefinitionInterface'); - $name_field_storage_definition->expects($this->any()) - ->method('getSchema') - ->willReturn(StringItem::schema($name_field_storage_definition)); - $description_field_storage_definition = $this->createMock('Drupal\Core\Field\FieldStorageDefinitionInterface'); - $description_field_storage_definition->expects($this->any()) - ->method('getSchema') - ->willReturn(TextLongItem::schema($description_field_storage_definition)); - $homepage_field_storage_definition = $this->createMock('Drupal\Core\Field\FieldStorageDefinitionInterface'); - $homepage_field_storage_definition->expects($this->any()) - ->method('getSchema') - ->willReturn(UriItem::schema($homepage_field_storage_definition)); - $string_field_storage_definition = $this->createMock('Drupal\Core\Field\FieldStorageDefinitionInterface'); - $string_field_storage_definition->expects($this->any()) - ->method('getSchema') - ->willReturn(StringItem::schema($string_field_storage_definition)); - - // Setup the user_id entity reference field. - $this->entityTypeManager->expects($this->any()) - ->method('getDefinition') - ->willReturnMap([ - ['user', TRUE, static::userEntityInfo()], - ] - ); - $this->entityTypeManager->expects($this->any()) - ->method('getDefinition') - ->willReturnMap([ - ['user', TRUE, static::userEntityInfo()], - ] - ); - $user_id_field_storage_definition = $this->createMock('Drupal\Core\Field\FieldStorageDefinitionInterface'); - $user_id_field_storage_definition->expects($this->any()) - ->method('getSetting') - ->with('target_type') - ->willReturn('user'); - $user_id_field_storage_definition->expects($this->any()) - ->method('getSettings') - ->willReturn(['target_type' => 'user']); - $user_id_field_storage_definition->expects($this->any()) - ->method('getSchema') - ->willReturn(EntityReferenceItem::schema($user_id_field_storage_definition)); - - $revision_id_field_storage_definition = $this->createMock('Drupal\Core\Field\FieldStorageDefinitionInterface'); - $revision_id_field_storage_definition->expects($this->any()) - ->method('getSchema') - ->willReturn(IntegerItem::schema($revision_id_field_storage_definition)); - - $this->entityFieldManager->expects($this->any()) - ->method('getFieldStorageDefinitions') - ->willReturn([ - 'id' => $id_field_storage_definition, - 'uuid' => $uuid_field_storage_definition, - 'type' => $type_field_storage_definition, - 'langcode' => $langcode_field_storage_definition, - 'name' => $name_field_storage_definition, - 'description' => $description_field_storage_definition, - 'homepage' => $homepage_field_storage_definition, - 'string' => $string_field_storage_definition, - 'user_id' => $user_id_field_storage_definition, - 'revision_id' => $revision_id_field_storage_definition, - ]); - } - - /** - * Tests fields on the base table. - */ - public function testBaseTableFields() { - $base_field_definitions = $this->setupBaseFields(EntityTest::baseFieldDefinitions($this->baseEntityType)); - $user_base_field_definitions = [ - 'uid' => BaseFieldDefinition::create('integer') - ->setLabel('ID') - ->setDescription('The ID of the user entity.') - ->setReadOnly(TRUE) - ->setSetting('unsigned', TRUE), - ]; - $this->entityFieldManager->expects($this->any()) - ->method('getBaseFieldDefinitions') - ->will($this->returnValueMap([ - ['user', $user_base_field_definitions], - ['entity_test', $base_field_definitions], - ])); - $this->entityFieldManager->expects($this->any()) - ->method('getBaseFieldDefinitions') - ->will($this->returnValueMap([ - ['user', $user_base_field_definitions], - ['entity_test', $base_field_definitions], - ])); - // Setup the table mapping. - $table_mapping = $this->getMockBuilder(DefaultTableMapping::class) - ->disableOriginalConstructor() - ->getMock(); - $table_mapping->expects($this->any()) - ->method('getTableNames') - ->willReturn(['entity_test', 'entity_test__string']); - $table_mapping->expects($this->any()) - ->method('getColumnNames') - ->willReturnMap([ - ['id', ['value' => 'id']], - ['uuid', ['value' => 'uuid']], - ['type', ['value' => 'type']], - ['langcode', ['value' => 'langcode']], - ['name', ['value' => 'name']], - ['description', ['value' => 'description__value', 'format' => 'description__format']], - ['homepage', ['value' => 'homepage']], - ['user_id', ['target_id' => 'user_id']], - ['string', ['value' => 'string_value']], - ]); - $table_mapping->expects($this->any()) - ->method('getFieldNames') - ->willReturnMap([ - ['entity_test', ['id', 'uuid', 'type', 'langcode', 'name', 'description', 'homepage', 'user_id']], - ['entity_test__string', ['string']], - ]); - $table_mapping->expects($this->any()) - ->method('requiresDedicatedTableStorage') - ->willReturnCallback(function (BaseFieldDefinition $base_field) { - return $base_field->getName() === 'string'; - }); - $table_mapping->expects($this->any()) - ->method('getDedicatedDataTableName') - ->willReturnCallback(function (BaseFieldDefinition $base_field) { - if ($base_field->getName() === 'string') { - return 'entity_test__string'; - } - }); - - $this->entityStorage->expects($this->once()) - ->method('getTableMapping') - ->willReturn($table_mapping); - - $this->setupFieldStorageDefinition(); - - $data = $this->viewsData->getViewsData(); - - $this->assertNumericField($data['entity_test']['id']); - $this->assertField($data['entity_test']['id'], 'id'); - $this->assertUuidField($data['entity_test']['uuid']); - $this->assertField($data['entity_test']['uuid'], 'uuid'); - $this->assertStringField($data['entity_test']['type']); - $this->assertEquals('type', $data['entity_test']['type']['entity field']); - - $this->assertLanguageField($data['entity_test']['langcode']); - $this->assertField($data['entity_test']['langcode'], 'langcode'); - $this->assertEquals('Original language', $data['entity_test']['langcode']['title']); - - $this->assertStringField($data['entity_test']['name']); - $this->assertField($data['entity_test']['name'], 'name'); - - $this->assertLongTextField($data['entity_test'], 'description'); - $this->assertField($data['entity_test']['description__value'], 'description'); - $this->assertField($data['entity_test']['description__format'], 'description'); - - $this->assertUriField($data['entity_test']['homepage']); - $this->assertField($data['entity_test']['homepage'], 'homepage'); - - $this->assertEntityReferenceField($data['entity_test']['user_id']); - $this->assertField($data['entity_test']['user_id'], 'user_id'); - - $relationship = $data['entity_test']['user_id']['relationship']; - $this->assertEquals('users_field_data', $relationship['base']); - $this->assertEquals('uid', $relationship['base field']); - - // The string field name should be used as the 'entity field' but the actual - // field should reflect what the column mapping is using for multi-value - // base fields NOT just the field name. The actual column name returned from - // mappings in the test mocks is 'value'. - $this->assertStringField($data['entity_test__string']['string_value']); - $this->assertField($data['entity_test__string']['string_value'], 'string'); - $this->assertEquals([ - 'left_field' => 'id', - 'field' => 'entity_id', - 'extra' => [[ - 'field' => 'deleted', - 'value' => 0, - 'numeric' => TRUE, - ], - ], - ], $data['entity_test__string']['table']['join']['entity_test']); - } - - /** - * Tests fields on the data table. - */ - public function testDataTableFields() { - $entity_type = $this->baseEntityType - ->set('data_table', 'entity_test_mul_property_data') - ->set('base_table', 'entity_test_mul') - ->set('id', 'entity_test_mul') - ->setKey('bundle', 'type'); - $base_field_definitions = $this->setupBaseFields(EntityTestMul::baseFieldDefinitions($this->baseEntityType)); - $base_field_definitions['type'] = BaseFieldDefinition::create('entity_reference') - ->setLabel('entity test type') - ->setSetting('target_type', 'entity_test_bundle') - ->setTranslatable(TRUE); - $base_field_definitions = $this->setupBaseFields($base_field_definitions); - $user_base_field_definitions = [ - 'uid' => BaseFieldDefinition::create('integer') - ->setLabel('ID') - ->setDescription('The ID of the user entity.') - ->setReadOnly(TRUE) - ->setSetting('unsigned', TRUE), - ]; - $entity_test_type = new ConfigEntityType(['id' => 'entity_test_bundle']); - - $this->entityFieldManager->expects($this->any()) - ->method('getBaseFieldDefinitions') - ->will($this->returnValueMap([ - ['user', $user_base_field_definitions], - ['entity_test_mul', $base_field_definitions], - ])); - $this->entityFieldManager->expects($this->any()) - ->method('getBaseFieldDefinitions') - ->will($this->returnValueMap([ - ['user', $user_base_field_definitions], - ['entity_test_mul', $base_field_definitions], - ])); - - $this->viewsData->setEntityType($entity_type); - - // Setup the table mapping. - $table_mapping = $this->getMockBuilder(DefaultTableMapping::class) - ->disableOriginalConstructor() - ->getMock(); - $table_mapping->expects($this->any()) - ->method('getTableNames') - ->willReturn(['entity_test_mul', 'entity_test_mul_property_data', 'entity_test_mul__string']); - $table_mapping->expects($this->any()) - ->method('getColumnNames') - ->willReturnMap([ - ['id', ['value' => 'id']], - ['uuid', ['value' => 'uuid']], - ['type', ['value' => 'type']], - ['langcode', ['value' => 'langcode']], - ['name', ['value' => 'name']], - ['description', ['value' => 'description__value', 'format' => 'description__format']], - ['homepage', ['value' => 'homepage']], - ['user_id', ['target_id' => 'user_id']], - ['string', ['value' => 'string_value']], - ]); - $table_mapping->expects($this->any()) - ->method('getFieldNames') - ->willReturnMap([ - ['entity_test_mul', ['uuid']], - ['entity_test_mul_property_data', ['id', 'type', 'langcode', 'name', 'description', 'homepage', 'user_id']], - ['entity_test_mul__string', ['string']], - ]); - - $table_mapping->expects($this->any()) - ->method('getFieldTableName') - ->willReturnCallback(function ($field) { - if ($field == 'uuid') { - return 'entity_test_mul'; - } - return 'entity_test_mul_property_data'; - }); - $table_mapping->expects($this->any()) - ->method('requiresDedicatedTableStorage') - ->willReturnCallback(function (BaseFieldDefinition $base_field) { - return $base_field->getName() === 'string'; - }); - $table_mapping->expects($this->any()) - ->method('getDedicatedDataTableName') - ->willReturnCallback(function (BaseFieldDefinition $base_field) { - if ($base_field->getName() === 'string') { - return 'entity_test_mul__string'; - } - }); - - $this->entityStorage->expects($this->once()) - ->method('getTableMapping') - ->willReturn($table_mapping); - - $this->setupFieldStorageDefinition(); - - $user_entity_type = static::userEntityInfo(); - $this->entityTypeManager->expects($this->any()) - ->method('getDefinition') - ->will($this->returnValueMap([ - ['user', TRUE, $user_entity_type], - ['entity_test_bundle', TRUE, $entity_test_type], - ])); - - $data = $this->viewsData->getViewsData(); - - // Check the base fields. - $this->assertFalse(isset($data['entity_test_mul']['id'])); - $this->assertFalse(isset($data['entity_test_mul']['type'])); - $this->assertUuidField($data['entity_test_mul']['uuid']); - $this->assertField($data['entity_test_mul']['uuid'], 'uuid'); - - $this->assertFalse(isset($data['entity_test_mul']['type']['relationship'])); - - // Also ensure that field_data only fields don't appear on the base table. - $this->assertFalse(isset($data['entity_test_mul']['name'])); - $this->assertFalse(isset($data['entity_test_mul']['description'])); - $this->assertFalse(isset($data['entity_test_mul']['description__value'])); - $this->assertFalse(isset($data['entity_test_mul']['description__format'])); - $this->assertFalse(isset($data['entity_test_mul']['user_id'])); - $this->assertFalse(isset($data['entity_test_mul']['homepage'])); - - // Check the data fields. - $this->assertNumericField($data['entity_test_mul_property_data']['id']); - $this->assertField($data['entity_test_mul_property_data']['id'], 'id'); - - $this->assertBundleField($data['entity_test_mul_property_data']['type']); - $this->assertField($data['entity_test_mul_property_data']['type'], 'type'); - - $this->assertLanguageField($data['entity_test_mul_property_data']['langcode']); - $this->assertField($data['entity_test_mul_property_data']['langcode'], 'langcode'); - $this->assertEquals('Translation language', $data['entity_test_mul_property_data']['langcode']['title']); - - $this->assertStringField($data['entity_test_mul_property_data']['name']); - $this->assertField($data['entity_test_mul_property_data']['name'], 'name'); - - $this->assertLongTextField($data['entity_test_mul_property_data'], 'description'); - $this->assertField($data['entity_test_mul_property_data']['description__value'], 'description'); - $this->assertField($data['entity_test_mul_property_data']['description__format'], 'description'); - - $this->assertUriField($data['entity_test_mul_property_data']['homepage']); - $this->assertField($data['entity_test_mul_property_data']['homepage'], 'homepage'); - - $this->assertEntityReferenceField($data['entity_test_mul_property_data']['user_id']); - $this->assertField($data['entity_test_mul_property_data']['user_id'], 'user_id'); - $relationship = $data['entity_test_mul_property_data']['user_id']['relationship']; - $this->assertEquals('users_field_data', $relationship['base']); - $this->assertEquals('uid', $relationship['base field']); - - $this->assertStringField($data['entity_test_mul__string']['string_value']); - $this->assertField($data['entity_test_mul__string']['string_value'], 'string'); - $this->assertEquals([ - 'left_field' => 'id', - 'field' => 'entity_id', - 'extra' => [[ - 'field' => 'deleted', - 'value' => 0, - 'numeric' => TRUE, - ], - ], - ], $data['entity_test_mul__string']['table']['join']['entity_test_mul']); - } - - /** - * Tests fields on the revision table. - */ - public function testRevisionTableFields() { - $entity_type = $this->baseEntityType - ->set('base_table', 'entity_test_mulrev') - ->set('revision_table', 'entity_test_mulrev_revision') - ->set('data_table', 'entity_test_mulrev_property_data') - ->set('revision_data_table', 'entity_test_mulrev_property_revision') - ->set('id', 'entity_test_mulrev') - ->set('translatable', TRUE); - $base_field_definitions = $this->setupBaseFields(EntityTestMulRev::baseFieldDefinitions($this->baseEntityType)); - $user_base_field_definitions = [ - 'uid' => BaseFieldDefinition::create('integer') - ->setLabel('ID') - ->setDescription('The ID of the user entity.') - ->setReadOnly(TRUE) - ->setSetting('unsigned', TRUE), - ]; - $this->entityFieldManager->expects($this->any()) - ->method('getBaseFieldDefinitions') - ->will($this->returnValueMap([ - ['user', $user_base_field_definitions], - ['entity_test_mulrev', $base_field_definitions], - ])); - $this->entityFieldManager->expects($this->any()) - ->method('getBaseFieldDefinitions') - ->will($this->returnValueMap([ - ['user', $user_base_field_definitions], - ['entity_test_mulrev', $base_field_definitions], - ])); - - $this->viewsData->setEntityType($entity_type); - - // Setup the table mapping. - $table_mapping = $this->getMockBuilder(DefaultTableMapping::class) - ->disableOriginalConstructor() - ->getMock(); - $table_mapping->expects($this->any()) - ->method('getTableNames') - ->willReturn(['entity_test_mulrev', 'entity_test_mulrev_revision', 'entity_test_mulrev_property_data', 'entity_test_mulrev_property_revision', 'entity_test_mulrev__string', 'entity_test_mulrev_revision__string']); - $table_mapping->expects($this->any()) - ->method('getColumnNames') - ->willReturnMap([ - ['id', ['value' => 'id']], - ['uuid', ['value' => 'uuid']], - ['type', ['value' => 'type']], - ['langcode', ['value' => 'langcode']], - ['name', ['value' => 'name']], - ['description', ['value' => 'description__value', 'format' => 'description__format']], - ['homepage', ['value' => 'homepage']], - ['user_id', ['target_id' => 'user_id']], - ['revision_id', ['value' => 'revision_id']], - ['string', ['value' => 'string_value']], - ]); - $table_mapping->expects($this->any()) - ->method('getFieldNames') - ->willReturnMap([ - ['entity_test_mulrev', ['id', 'revision_id', 'uuid', 'type']], - ['entity_test_mulrev_revision', ['id', 'revision_id', 'langcode']], - ['entity_test_mulrev_property_data', ['id', 'revision_id', 'langcode', 'name', 'description', 'homepage', 'user_id']], - ['entity_test_mulrev_property_revision', ['id', 'revision_id', 'langcode', 'name', 'description', 'homepage', 'user_id']], - ['entity_test_mulrev__string', ['string']], - ['entity_test_mulrev_revision__string', ['string']], - ]); - $table_mapping->expects($this->any()) - ->method('requiresDedicatedTableStorage') - ->willReturnCallback(function (BaseFieldDefinition $base_field) { - return $base_field->getName() === 'string'; - }); - $table_mapping->expects($this->any()) - ->method('getDedicatedDataTableName') - ->willReturnCallback(function (BaseFieldDefinition $base_field) { - if ($base_field->getName() === 'string') { - return 'entity_test_mulrev__string'; - } - }); - - $table_mapping->expects($this->any()) - ->method('getDedicatedRevisionTableName') - ->willReturnCallback(function (BaseFieldDefinition $base_field) { - if ($base_field->getName() === 'string') { - return 'entity_test_mulrev_revision__string'; - } - }); - - $table_mapping->expects($this->any()) - ->method('getFieldTableName') - ->willReturnCallback(function ($field) { - if ($field == 'uuid') { - return 'entity_test_mulrev'; - } - return 'entity_test_mulrev_property_data'; - }); - - $this->entityStorage->expects($this->once()) - ->method('getTableMapping') - ->willReturn($table_mapping); - - $this->setupFieldStorageDefinition(); - - $data = $this->viewsData->getViewsData(); - - // Check the base fields. - $this->assertFalse(isset($data['entity_test_mulrev']['id'])); - $this->assertFalse(isset($data['entity_test_mulrev']['type'])); - $this->assertFalse(isset($data['entity_test_mulrev']['revision_id'])); - $this->assertUuidField($data['entity_test_mulrev']['uuid']); - $this->assertField($data['entity_test_mulrev']['uuid'], 'uuid'); - - // Also ensure that field_data only fields don't appear on the base table. - $this->assertFalse(isset($data['entity_test_mulrev']['name'])); - $this->assertFalse(isset($data['entity_test_mul']['description'])); - $this->assertFalse(isset($data['entity_test_mul']['description__value'])); - $this->assertFalse(isset($data['entity_test_mul']['description__format'])); - $this->assertFalse(isset($data['entity_test_mul']['homepage'])); - $this->assertFalse(isset($data['entity_test_mulrev']['langcode'])); - $this->assertFalse(isset($data['entity_test_mulrev']['user_id'])); - - // Check the revision fields. The revision ID should only appear in the data - // table. - $this->assertFalse(isset($data['entity_test_mulrev_revision']['revision_id'])); - - // Also ensure that field_data only fields don't appear on the revision table. - $this->assertFalse(isset($data['entity_test_mulrev_revision']['id'])); - $this->assertFalse(isset($data['entity_test_mulrev_revision']['name'])); - $this->assertFalse(isset($data['entity_test_mulrev_revision']['description'])); - $this->assertFalse(isset($data['entity_test_mulrev_revision']['description__value'])); - $this->assertFalse(isset($data['entity_test_mulrev_revision']['description__format'])); - $this->assertFalse(isset($data['entity_test_mulrev_revision']['homepage'])); - $this->assertFalse(isset($data['entity_test_mulrev_revision']['user_id'])); - - // Check the data fields. - $this->assertNumericField($data['entity_test_mulrev_property_data']['id']); - $this->assertField($data['entity_test_mulrev_property_data']['id'], 'id'); - $this->assertNumericField($data['entity_test_mulrev_property_data']['revision_id']); - $this->assertField($data['entity_test_mulrev_property_data']['revision_id'], 'revision_id'); - $this->assertLanguageField($data['entity_test_mulrev_property_data']['langcode']); - $this->assertField($data['entity_test_mulrev_property_data']['langcode'], 'langcode'); - $this->assertStringField($data['entity_test_mulrev_property_data']['name']); - $this->assertField($data['entity_test_mulrev_property_data']['name'], 'name'); - - $this->assertLongTextField($data['entity_test_mulrev_property_data'], 'description'); - $this->assertField($data['entity_test_mulrev_property_data']['description__value'], 'description'); - $this->assertField($data['entity_test_mulrev_property_data']['description__format'], 'description'); - $this->assertUriField($data['entity_test_mulrev_property_data']['homepage']); - $this->assertField($data['entity_test_mulrev_property_data']['homepage'], 'homepage'); - - $this->assertEntityReferenceField($data['entity_test_mulrev_property_data']['user_id']); - $this->assertField($data['entity_test_mulrev_property_data']['user_id'], 'user_id'); - $relationship = $data['entity_test_mulrev_property_data']['user_id']['relationship']; - $this->assertEquals('users_field_data', $relationship['base']); - $this->assertEquals('uid', $relationship['base field']); - - // Check the property data fields. - $this->assertNumericField($data['entity_test_mulrev_property_revision']['id']); - $this->assertField($data['entity_test_mulrev_property_revision']['id'], 'id'); - - $this->assertLanguageField($data['entity_test_mulrev_property_revision']['langcode']); - $this->assertField($data['entity_test_mulrev_property_revision']['langcode'], 'langcode'); - $this->assertEquals('Translation language', $data['entity_test_mulrev_property_revision']['langcode']['title']); - - $this->assertStringField($data['entity_test_mulrev_property_revision']['name']); - $this->assertField($data['entity_test_mulrev_property_revision']['name'], 'name'); - - $this->assertLongTextField($data['entity_test_mulrev_property_revision'], 'description'); - $this->assertField($data['entity_test_mulrev_property_revision']['description__value'], 'description'); - $this->assertField($data['entity_test_mulrev_property_revision']['description__format'], 'description'); - - $this->assertUriField($data['entity_test_mulrev_property_revision']['homepage']); - $this->assertField($data['entity_test_mulrev_property_revision']['homepage'], 'homepage'); - - $this->assertEntityReferenceField($data['entity_test_mulrev_property_revision']['user_id']); - $this->assertField($data['entity_test_mulrev_property_revision']['user_id'], 'user_id'); - $relationship = $data['entity_test_mulrev_property_revision']['user_id']['relationship']; - $this->assertEquals('users_field_data', $relationship['base']); - $this->assertEquals('uid', $relationship['base field']); - - $this->assertStringField($data['entity_test_mulrev__string']['string_value']); - $this->assertField($data['entity_test_mulrev__string']['string_value'], 'string'); - $this->assertEquals([ - 'left_field' => 'id', - 'field' => 'entity_id', - 'extra' => [[ - 'field' => 'deleted', - 'value' => 0, - 'numeric' => TRUE, - ], - ], - ], $data['entity_test_mulrev__string']['table']['join']['entity_test_mulrev_property_data']); - - $this->assertStringField($data['entity_test_mulrev_revision__string']['string_value']); - $this->assertField($data['entity_test_mulrev_revision__string']['string_value'], 'string'); - $this->assertEquals([ - 'left_field' => 'revision_id', - 'field' => 'entity_id', - 'extra' => [[ - 'field' => 'deleted', - 'value' => 0, - 'numeric' => TRUE, - ], - ], - ], $data['entity_test_mulrev_revision__string']['table']['join']['entity_test_mulrev_property_revision']); - } - - /** - * Tests generic stuff per field. - * - * @param array $data - * The views data to check. - * @param string $field_name - * The entity field name. - */ - protected function assertField($data, $field_name) { - $this->assertEquals($field_name, $data['entity field']); - } - - /** - * Tests add link types. - */ - public function testEntityLinks() { - $this->baseEntityType->setLinkTemplate('canonical', '/entity_test/{entity_test}'); - $this->baseEntityType->setLinkTemplate('edit-form', '/entity_test/{entity_test}/edit'); - $this->baseEntityType->setLinkTemplate('delete-form', '/entity_test/{entity_test}/delete'); - - $data = $this->viewsData->getViewsData(); - foreach (['entity_test', 'entity_test_revision'] as $table_name) { - $this->assertEquals('entity_link', $data[$table_name]['view_entity_test']['field']['id']); - $this->assertEquals('entity_link_edit', $data[$table_name]['edit_entity_test']['field']['id']); - $this->assertEquals('entity_link_delete', $data[$table_name]['delete_entity_test']['field']['id']); - } - } - - /** - * Tests additional edit links. - */ - public function testEntityLinksJustEditForm() { - $this->baseEntityType->setLinkTemplate('edit-form', '/entity_test/{entity_test}/edit'); - - $data = $this->viewsData->getViewsData(); - - foreach (['entity_test', 'entity_test_revision'] as $table_name) { - $this->assertFalse(isset($data[$table_name]['view_entity_test'])); - $this->assertFalse(isset($data[$table_name]['delete_entity_test'])); - - $this->assertEquals('entity_link_edit', $data[$table_name]['edit_entity_test']['field']['id']); - } - } - - /** - * @covers ::getViewsData - */ - public function testGetViewsDataWithoutEntityOperations() { - // Make sure there is no list builder. The API does not document is - // supports resetting entity handlers, so this might break in the future. - $this->baseEntityType->setListBuilderClass(NULL); - $data = $this->viewsData->getViewsData(); - $this->assertArrayNotHasKey('operations', $data[$this->baseEntityType->getBaseTable()]); - } - - /** - * @covers ::getViewsData - */ - public function testGetViewsDataWithEntityOperations() { - $this->baseEntityType->setListBuilderClass('\Drupal\Core\Entity\EntityListBuilder'); - $data = $this->viewsData->getViewsData(); - - $tables = ['entity_test', 'entity_test_revision']; - $this->assertSame($tables, array_keys($data)); - foreach ($tables as $table_name) { - $this->assertSame('entity_operations', $data[$table_name]['operations']['field']['id']); - } - } - - /** - * @covers ::getViewsData - */ - public function testGetViewsDataWithNonRevisionableEntityOperations() { - $this->baseEntityType->setListBuilderClass('\Drupal\Core\Entity\EntityListBuilder'); - - $entity_type_without_revisions = $this->baseEntityType; - $views_data = $this->viewsData; - - $entity_type_keys = $entity_type_without_revisions->getKeys(); - unset($entity_type_keys['revision']); - - $entity_type_without_revisions->set('entity_keys', $entity_type_keys); - $views_data->setEntityType($entity_type_without_revisions); - - $data = $views_data->getViewsData(); - - $tables = ['entity_test']; - $this->assertSame($tables, array_keys($data)); - foreach ($tables as $table_name) { - $this->assertSame('entity_operations', $data[$table_name]['operations']['field']['id']); - } - } - - /** - * Tests views data for a string field. - * - * @param $data - * The views data to check. - */ - protected function assertStringField($data) { - $this->assertEquals('field', $data['field']['id']); - $this->assertEquals('string', $data['filter']['id']); - $this->assertEquals('string', $data['argument']['id']); - $this->assertEquals('standard', $data['sort']['id']); - } - - /** - * Tests views data for a URI field. - * - * @param $data - * The views data to check. - */ - protected function assertUriField($data) { - $this->assertEquals('field', $data['field']['id']); - $this->assertEquals('string', $data['field']['default_formatter']); - $this->assertEquals('string', $data['filter']['id']); - $this->assertEquals('string', $data['argument']['id']); - $this->assertEquals('standard', $data['sort']['id']); - } - - /** - * Tests views data for a long text field. - * - * @param $data - * The views data for the table this field is in. - * @param $field_name - * The name of the field being checked. - */ - protected function assertLongTextField($data, $field_name) { - $value_field = $data[$field_name . '__value']; - $this->assertEquals('field', $value_field['field']['id']); - $this->assertEquals($field_name . '__format', $value_field['field']['format']); - $this->assertEquals('string', $value_field['filter']['id']); - $this->assertEquals('string', $value_field['argument']['id']); - $this->assertEquals('standard', $value_field['sort']['id']); - - $this->assertStringField($data[$field_name . '__format']); - } - - /** - * Tests views data for a UUID field. - * - * @param array $data - * The views data to check. - */ - protected function assertUuidField($data) { - // @todo Can we provide additional support for UUIDs in views? - $this->assertEquals('field', $data['field']['id']); - $this->assertFalse($data['field']['click sortable']); - $this->assertEquals('string', $data['filter']['id']); - $this->assertEquals('string', $data['argument']['id']); - $this->assertEquals('standard', $data['sort']['id']); - } - - /** - * Tests views data for a numeric field. - * - * @param array $data - * The views data to check. - */ - protected function assertNumericField($data) { - $this->assertEquals('field', $data['field']['id']); - $this->assertEquals('numeric', $data['filter']['id']); - $this->assertEquals('numeric', $data['argument']['id']); - $this->assertEquals('standard', $data['sort']['id']); - } - - /** - * Tests views data for a language field. - * - * @param array $data - * The views data to check. - */ - protected function assertLanguageField($data) { - $this->assertEquals('field', $data['field']['id']); - $this->assertEquals('language', $data['filter']['id']); - $this->assertEquals('language', $data['argument']['id']); - $this->assertEquals('standard', $data['sort']['id']); - } - - /** - * Tests views data for an entity reference field. - */ - protected function assertEntityReferenceField($data) { - $this->assertEquals('field', $data['field']['id']); - $this->assertEquals('numeric', $data['filter']['id']); - $this->assertEquals('numeric', $data['argument']['id']); - $this->assertEquals('standard', $data['sort']['id']); - } - - /** - * Tests views data for a bundle field. - */ - protected function assertBundleField($data) { - $this->assertEquals('field', $data['field']['id']); - $this->assertEquals('bundle', $data['filter']['id']); - $this->assertEquals('string', $data['argument']['id']); - $this->assertEquals('standard', $data['sort']['id']); - } - - /** - * Returns entity info for the user entity. - * - * @return array - */ - protected static function userEntityInfo() { - return new ContentEntityType([ - 'id' => 'user', - 'class' => 'Drupal\user\Entity\User', - 'label' => 'User', - 'base_table' => 'users', - 'data_table' => 'users_field_data', - 'entity_keys' => [ - 'id' => 'uid', - 'uuid' => 'uuid', - ], - ]); - } - -} - -class TestEntityViewsData extends EntityViewsData { - - public function setEntityType(EntityTypeInterface $entity_type) { - $this->entityType = $entity_type; - } - -} - -class TestEntityType extends ContentEntityType { - - /** - * Sets a specific entity key. - * - * @param string $key - * The name of the entity key. - * @param string $value - * The new value of the key. - * - * @return $this - */ - public function setKey($key, $value) { - $this->entity_keys[$key] = $value; - return $this; - } - -} diff --git a/web/core/tests/Drupal/KernelTests/Core/Entity/EntityQueryAggregateTest.php b/web/core/tests/Drupal/KernelTests/Core/Entity/EntityQueryAggregateTest.php index b0cfc72804d42f84cf16adb2a1a24351eee0448c..796f6ca1391e61612cabfb54478ad60edd653f7c 100644 --- a/web/core/tests/Drupal/KernelTests/Core/Entity/EntityQueryAggregateTest.php +++ b/web/core/tests/Drupal/KernelTests/Core/Entity/EntityQueryAggregateTest.php @@ -139,7 +139,7 @@ public function testAggregation() { // We need to check that a character exists before and after the table, // column and alias identifiers. These would be the quote characters // specific for each database system. - $this->assertMatchesRegularExpression('/' . $aggregation_function . '\(.entity_test.\..id.\) AS .id_' . $aggregation_function . './', (string) $query, 'The argument to the aggregation function should be a quoted field.'); + $this->assertMatchesRegularExpression('/' . $aggregation_function . '\(.*entity_test.\..id.\).* AS .id_' . $aggregation_function . './', (string) $query, 'The argument to the aggregation function should be a quoted field.'); $this->assertEquals($expected, $this->queryResult); } diff --git a/web/core/tests/Drupal/KernelTests/Core/Render/Element/MachineNameTest.php b/web/core/tests/Drupal/KernelTests/Core/Render/Element/MachineNameTest.php new file mode 100644 index 0000000000000000000000000000000000000000..74c3e95a963b5b4794f16e6c651050deefa62d28 --- /dev/null +++ b/web/core/tests/Drupal/KernelTests/Core/Render/Element/MachineNameTest.php @@ -0,0 +1,74 @@ +<?php + +namespace Drupal\KernelTests\Core\Render\Element; + +use Drupal\Core\Form\FormInterface; +use Drupal\Core\Form\FormStateInterface; +use Drupal\KernelTests\KernelTestBase; + +/** + * @coversDefaultClass \Drupal\Core\Render\Element\MachineName + * @group Render + */ +class MachineNameTest extends KernelTestBase implements FormInterface { + + /** + * {@inheritdoc} + */ + protected static $modules = ['system']; + + /** + * {@inheritdoc} + */ + public function getFormId() { + return __CLASS__; + } + + /** + * {@inheritdoc} + */ + public function buildForm(array $form, FormStateInterface $form_state) { + $element = [ + '#id' => 'test', + '#type' => 'machine_name', + '#machine_name' => [ + 'source' => [ + 'test_source', + ], + ], + '#name' => 'test_machine_name', + '#default_value' => NULL, + ]; + + $complete_form = [ + 'test_machine_name' => $element, + 'test_source' => [ + '#type' => 'textfield', + ], + ]; + return $complete_form; + } + + /** + * {@inheritdoc} + */ + public function validateForm(array &$form, FormStateInterface $form_state) { + } + + /** + * {@inheritdoc} + */ + public function submitForm(array &$form, FormStateInterface $form_state) { + } + + /** + * Tests the order of the machine name field and the source. + */ + public function testMachineNameOrderException() { + $this->expectException(\LogicException::class); + $this->expectErrorMessage('The machine name element "test_machine_name" is defined before the source element "test_source", it must be defined after or the source element must specify an id.'); + $form = \Drupal::formBuilder()->getForm($this); + $this->render($form); + } + +} diff --git a/web/core/tests/Drupal/KernelTests/Core/Theme/ClaroVerticalTabsTest.php b/web/core/tests/Drupal/KernelTests/Core/Theme/ClaroVerticalTabsTest.php index 6ae639149565575d0dd20058335568defda97233..c880c1f3770bdb39930c0cfc6908ba9b18308344 100644 --- a/web/core/tests/Drupal/KernelTests/Core/Theme/ClaroVerticalTabsTest.php +++ b/web/core/tests/Drupal/KernelTests/Core/Theme/ClaroVerticalTabsTest.php @@ -26,9 +26,9 @@ public function testVerticalTabs() { $this->config('system.theme')->set('default', 'claro')->save(); $form = [ - '#parents' => [], + '#parents' => ['parent'], '#array_parents' => [], - '#tree' => FALSE, + '#tree' => TRUE, ]; $form['vertical_tabs'] = [ @@ -38,7 +38,7 @@ public function testVerticalTabs() { $form['vertical_tabs_details'] = [ '#type' => 'details', '#title' => 'Details', - '#group' => 'vertical_tabs', + '#group' => 'parent][vertical_tabs', ]; // Needs to be rendered after the vertical tabs. @@ -61,9 +61,9 @@ public function testVerticalTabs() { // Assert that the vertical tab details has the appropriate class. $this->assertCount(1, $this->cssSelect('.vertical-tabs__items details.vertical-tabs__item')); // Assert that there is a details element. - $this->assertCount(1, $this->cssSelect('#edit-container-details')); + $this->assertCount(1, $this->cssSelect('#edit-parent-container-details')); // Assert that details element doesn't have the vertical tab classes. - $this->assertCount(0, $this->cssSelect('#edit-container-details.vertical-tabs__item')); + $this->assertCount(0, $this->cssSelect('#edit-parent-container-details.vertical-tabs__item')); } } diff --git a/web/core/tests/Drupal/Tests/Composer/Plugin/Scaffold/Functional/ManageGitIgnoreTest.php b/web/core/tests/Drupal/Tests/Composer/Plugin/Scaffold/Functional/ManageGitIgnoreTest.php index dee14dfcf4a304cfb5b1747b6f7e55a03dac3367..e2fe7da2229d6588b091faa723a737ec5104aece 100644 --- a/web/core/tests/Drupal/Tests/Composer/Plugin/Scaffold/Functional/ManageGitIgnoreTest.php +++ b/web/core/tests/Drupal/Tests/Composer/Plugin/Scaffold/Functional/ManageGitIgnoreTest.php @@ -202,7 +202,14 @@ public function testUnmanagedGitIgnoreWhenGitNotAvailable() { // executable script named 'git' that always exits with 127, as if git were // not found. Note that we run our tests using process isolation, so we do // not need to restore the PATH when we are done. - $unavailableGitPath = $this->fixtures->binFixtureDir('disable-git-bin'); + $unavailableGitPath = $sut . '/bin'; + mkdir($unavailableGitPath); + $bash = <<<SH +#!/bin/bash +exit 127 + +SH; + file_put_contents($unavailableGitPath . '/git', $bash); chmod($unavailableGitPath . '/git', 0755); $oldPath = getenv('PATH'); putenv('PATH=' . $unavailableGitPath . ':' . getenv('PATH')); diff --git a/web/core/tests/Drupal/Tests/Composer/Plugin/Scaffold/fixtures/scripts/disable-git-bin/git b/web/core/tests/Drupal/Tests/Composer/Plugin/Scaffold/fixtures/scripts/disable-git-bin/git deleted file mode 100755 index 36fa746230ae1c508ab92b249828c15ba2f23ef9..0000000000000000000000000000000000000000 --- a/web/core/tests/Drupal/Tests/Composer/Plugin/Scaffold/fixtures/scripts/disable-git-bin/git +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/bash -exit 127 diff --git a/web/core/tests/Drupal/Tests/Core/Asset/CssCollectionOptimizerUnitTest.php b/web/core/tests/Drupal/Tests/Core/Asset/CssCollectionOptimizerUnitTest.php new file mode 100644 index 0000000000000000000000000000000000000000..a27ce061978067d4a6ceb8541a321f4b7a6dc43a --- /dev/null +++ b/web/core/tests/Drupal/Tests/Core/Asset/CssCollectionOptimizerUnitTest.php @@ -0,0 +1,82 @@ +<?php + +namespace Drupal\Tests\Core\Asset; + +use Drupal\Core\Asset\AssetCollectionGrouperInterface; +use Drupal\Core\Asset\AssetDumperInterface; +use Drupal\Core\Asset\AssetOptimizerInterface; +use Drupal\Core\Asset\CssCollectionOptimizer; +use Drupal\Core\File\FileSystemInterface; +use Drupal\Core\State\StateInterface; +use Drupal\Tests\UnitTestCase; + +/** + * Tests the CSS asset optimizer. + * + * @group Asset + */ +class CssCollectionOptimizerUnitTest extends UnitTestCase { + + /** + * The data from the dumper. + * + * @var string + */ + protected $dumperData; + + /** + * A CSS Collection optimizer. + * + * @var \Drupal\Core\Asset\AssetCollectionOptimizerInterface + */ + protected $optimizer; + + protected function setUp(): void { + parent::setUp(); + $mock_grouper = $this->createMock(AssetCollectionGrouperInterface::class); + $mock_grouper->method('group') + ->willReturnCallback(function ($assets) { + return [ + [ + 'items' => $assets, + 'type' => 'file', + 'preprocess' => TRUE, + ], + ]; + }); + $mock_optimizer = $this->createMock(AssetOptimizerInterface::class); + $mock_optimizer->method('optimize') + ->willReturn( + file_get_contents(__DIR__ . '/css_test_files/css_input_with_import.css.optimized.css'), + file_get_contents(__DIR__ . '/css_test_files/css_subfolder/css_input_with_import.css.optimized.css') + ); + $mock_dumper = $this->createMock(AssetDumperInterface::class); + $mock_dumper->method('dump') + ->willReturnCallback(function ($css) { + $this->dumperData = $css; + }); + $mock_state = $this->createMock(StateInterface::class); + $mock_file_system = $this->createMock(FileSystemInterface::class); + $this->optimizer = new CssCollectionOptimizer($mock_grouper, $mock_optimizer, $mock_dumper, $mock_state, $mock_file_system); + } + + /** + * Test that css imports with strange letters do not destroy the css output. + */ + public function testCssImport() { + $this->optimizer->optimize([ + 'core/modules/system/tests/modules/common_test/common_test_css_import.css' => [ + 'type' => 'file', + 'data' => 'core/modules/system/tests/modules/common_test/common_test_css_import.css', + 'preprocess' => TRUE, + ], + 'core/modules/system/tests/modules/common_test/common_test_css_import_not_preprocessed.css' => [ + 'type' => 'file', + 'data' => 'core/modules/system/tests/modules/common_test/common_test_css_import.css', + 'preprocess' => TRUE, + ], + ]); + self::assertEquals(file_get_contents(__DIR__ . '/css_test_files/css_input_with_import.css.optimized.aggregated.css'), $this->dumperData); + } + +} diff --git a/web/core/tests/Drupal/Tests/Core/Asset/CssOptimizerUnitTest.php b/web/core/tests/Drupal/Tests/Core/Asset/CssOptimizerUnitTest.php index dba9fc2ad2b94615730fe12b5b5c13e08781c6ad..669e2218917b8fc16f0cf6d41f83634d6aacbcda 100644 --- a/web/core/tests/Drupal/Tests/Core/Asset/CssOptimizerUnitTest.php +++ b/web/core/tests/Drupal/Tests/Core/Asset/CssOptimizerUnitTest.php @@ -61,6 +61,8 @@ public function providerTestOptimize() { // file_create_url(). (https://www.drupal.org/node/1961340) // - Imported files that are external (protocol-relative URL or not) // should not be expanded. (https://www.drupal.org/node/2014851) + // Potential forms of @import might also include media queries. + // (https://developer.mozilla.org/en-US/docs/Web/CSS/@import) [ [ 'group' => -100, @@ -72,7 +74,7 @@ public function providerTestOptimize() { 'browsers' => ['IE' => TRUE, '!IE' => TRUE], 'basename' => 'css_input_with_import.css', ], - str_replace('url(images/icon.png)', 'url(' . file_url_transform_relative(file_create_url($path . 'images/icon.png')) . ')', file_get_contents($absolute_path . 'css_input_with_import.css.optimized.css')), + str_replace("url('import1.css')", 'url(' . file_url_transform_relative(file_create_url($path . 'import1.css')) . ')', str_replace('url(images/icon.png)', 'url(' . file_url_transform_relative(file_create_url($path . 'images/icon.png')) . ')', file_get_contents($absolute_path . 'css_input_with_import.css.optimized.css'))), ], // File. Tests: // - Retain comment hacks. diff --git a/web/core/tests/Drupal/Tests/Core/Asset/css_test_files/css_input_with_import.css b/web/core/tests/Drupal/Tests/Core/Asset/css_test_files/css_input_with_import.css index 34270ee6966444719889593624a4176fea37f762..d71b91b82007e00b466bb83fc42a18ff4b29ca03 100644 --- a/web/core/tests/Drupal/Tests/Core/Asset/css_test_files/css_input_with_import.css +++ b/web/core/tests/Drupal/Tests/Core/Asset/css_test_files/css_input_with_import.css @@ -1,9 +1,25 @@ -@import "import1.css"; +@import 'import1.css'; @import "import2.css"; +@import url('import1.css'); +@import url("https://fonts.fontprovider.com/css2?family=Roboto+Mono:wght@300;400&family=Roboto:ital,wght@0,300;0,400;1,300;1,400&display=swap") print; +@import url(import1.css); +@import url('import1.css') screen; @import url("http://example.com/style.css"); @import url("//example.com/style.css"); +@import url("https://fonts.fontprovider.com/css2?family=Roboto+Mono:wght@300;400&family=Roboto:ital,wght@0,300;0,400;1,300;1,400&display=swap"); +@import url("http://example.com/style.css") screen and (orientation:landscape); +@import "http://example.com/style.css" screen; +@import "http://example.com/style.css" supports(display: table-cell); +@import "http://example.com/style.css" supports(display: table-cell) screen; +@import url("http://example.com/style.css") screen and (orientation:landscape); +@import url("http://example.com/style.css") screen; +@import url("http://user:pass@example.com/style.css") screen and (orientation:landscape); +@import url(http://example.com/cus\(t;om.css); +@import url('http://example.com/cu(st;o)m.css'); +@import url("http://user:pass@example.com/cu(s)t;om.css"); +@import url(http://user:pass@example.com/cu\(s\)t;om.css); body { margin: 0; @@ -29,4 +45,3 @@ textarea, select { font: 1em/160% Verdana, sans-serif; color: #494949; } - diff --git a/web/core/tests/Drupal/Tests/Core/Asset/css_test_files/css_input_with_import.css.optimized.aggregated.css b/web/core/tests/Drupal/Tests/Core/Asset/css_test_files/css_input_with_import.css.optimized.aggregated.css new file mode 100644 index 0000000000000000000000000000000000000000..5ca58da466734c3048ec3ed45e07637c60ebe275 --- /dev/null +++ b/web/core/tests/Drupal/Tests/Core/Asset/css_test_files/css_input_with_import.css.optimized.aggregated.css @@ -0,0 +1,14 @@ +@import url("https://fonts.fontprovider.com/css2?family=Roboto+Mono:wght@300;400&family=Roboto:ital,wght@0,300;0,400;1,300;1,400&display=swap") print;@import url('import1.css') screen;@import url("http://example.com/style.css");@import url("//example.com/style.css");@import url("https://fonts.fontprovider.com/css2?family=Roboto+Mono:wght@300;400&family=Roboto:ital,wght@0,300;0,400;1,300;1,400&display=swap");@import url("http://example.com/style.css") screen and (orientation:landscape);@import "http://example.com/style.css" screen;@import "http://example.com/style.css" supports(display:table-cell);@import "http://example.com/style.css" supports(display:table-cell) screen;@import url("http://example.com/style.css") screen and (orientation:landscape);@import url("http://example.com/style.css") screen;@import url("http://user:pass@example.com/style.css") screen and (orientation:landscape);@import url(http://example.com/cus\(t;om.css);@import url('http://example.com/cu(st;o)m.css');@import url("http://user:pass@example.com/cu(s)t;om.css");@import url(http://user:pass@example.com/cu\(s\)t;om.css);ul,select{font:1em/160% Verdana,sans-serif;color:#494949;}.ui-icon{background-image:url(images/icon.png);}.data .double-quote{background-image:url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7");}.data .single-quote{background-image:url('data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDACAWGBwYFCAcGhwkIiAmMFA0MCwsMGJGSjpQdGZ6eHJmcG6AkLicgIiuim5woNqirr7EztDOfJri8uDI8LjKzsb/2wBDASIkJDAqMF40NF7GhHCExsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsb/wAARCAABAAEDASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAb/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhEDEQA/AKAAH//Z');}.data .no-quote{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACEAAAAEAQAAAAAo/mtHAAAAIElEQVQIHWMRnWHwcRNLN8NZ7QYWwT8PlBlYsgqVBRsAankIMw5MtnoAAAAASUVORK5CYII=);} +p,select{font:1em/160% Verdana,sans-serif;color:#494949;} +ul,select{font:1em/160% Verdana,sans-serif;color:#494949;}.ui-icon{background-image:url(images/icon.png);}.data .double-quote{background-image:url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7");}.data .single-quote{background-image:url('data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDACAWGBwYFCAcGhwkIiAmMFA0MCwsMGJGSjpQdGZ6eHJmcG6AkLicgIiuim5woNqirr7EztDOfJri8uDI8LjKzsb/2wBDASIkJDAqMF40NF7GhHCExsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsb/wAARCAABAAEDASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAb/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhEDEQA/AKAAH//Z');}.data .no-quote{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACEAAAAEAQAAAAAo/mtHAAAAIElEQVQIHWMRnWHwcRNLN8NZ7QYWwT8PlBlYsgqVBRsAankIMw5MtnoAAAAASUVORK5CYII=);} +ul,select{font:1em/160% Verdana,sans-serif;color:#494949;}.ui-icon{background-image:url(images/icon.png);}.data .double-quote{background-image:url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7");}.data .single-quote{background-image:url('data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDACAWGBwYFCAcGhwkIiAmMFA0MCwsMGJGSjpQdGZ6eHJmcG6AkLicgIiuim5woNqirr7EztDOfJri8uDI8LjKzsb/2wBDASIkJDAqMF40NF7GhHCExsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsb/wAARCAABAAEDASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAb/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhEDEQA/AKAAH//Z');}.data .no-quote{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACEAAAAEAQAAAAAo/mtHAAAAIElEQVQIHWMRnWHwcRNLN8NZ7QYWwT8PlBlYsgqVBRsAankIMw5MtnoAAAAASUVORK5CYII=);} +body{margin:0;padding:0;background:#edf5fa;font:76%/170% Verdana,sans-serif;color:#494949;}.this .is .a .test{font:1em/100% Verdana,sans-serif;color:#494949;}.this +.is +.a +.test{font:1em/100% Verdana,sans-serif;color:#494949;}textarea,select{font:1em/160% Verdana,sans-serif;color:#494949;} +ul,select{font:1em/160% Verdana,sans-serif;color:#494949;}.ui-icon{background-image:url(../images/icon.png);}.data .double-quote{background-image:url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7");}.data .single-quote{background-image:url('data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDACAWGBwYFCAcGhwkIiAmMFA0MCwsMGJGSjpQdGZ6eHJmcG6AkLicgIiuim5woNqirr7EztDOfJri8uDI8LjKzsb/2wBDASIkJDAqMF40NF7GhHCExsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsb/wAARCAABAAEDASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAb/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhEDEQA/AKAAH//Z');}.data .no-quote{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACEAAAAEAQAAAAAo/mtHAAAAIElEQVQIHWMRnWHwcRNLN8NZ7QYWwT8PlBlYsgqVBRsAankIMw5MtnoAAAAASUVORK5CYII=);} +p,select{font:1em/160% Verdana,sans-serif;color:#494949;} +body{margin:0;padding:0;background:#edf5fa;font:76%/170% Verdana,sans-serif;color:#494949;}.this .is .a .test{font:1em/100% Verdana,sans-serif;color:#494949;}.this +.is +.a +.test{font:1em/100% Verdana,sans-serif;color:#494949;}textarea,select{font:1em/160% Verdana,sans-serif;color:#494949;} diff --git a/web/core/tests/Drupal/Tests/Core/Asset/css_test_files/css_input_with_import.css.optimized.css b/web/core/tests/Drupal/Tests/Core/Asset/css_test_files/css_input_with_import.css.optimized.css index 16bd93be7ede890e186df3573dc878a81f12f10f..a35ef29adb1dab70c32bbd9533b551a1e272fef1 100644 --- a/web/core/tests/Drupal/Tests/Core/Asset/css_test_files/css_input_with_import.css.optimized.css +++ b/web/core/tests/Drupal/Tests/Core/Asset/css_test_files/css_input_with_import.css.optimized.css @@ -1,6 +1,8 @@ ul,select{font:1em/160% Verdana,sans-serif;color:#494949;}.ui-icon{background-image:url(images/icon.png);}.data .double-quote{background-image:url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7");}.data .single-quote{background-image:url('data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDACAWGBwYFCAcGhwkIiAmMFA0MCwsMGJGSjpQdGZ6eHJmcG6AkLicgIiuim5woNqirr7EztDOfJri8uDI8LjKzsb/2wBDASIkJDAqMF40NF7GhHCExsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsb/wAARCAABAAEDASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAb/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhEDEQA/AKAAH//Z');}.data .no-quote{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACEAAAAEAQAAAAAo/mtHAAAAIElEQVQIHWMRnWHwcRNLN8NZ7QYWwT8PlBlYsgqVBRsAankIMw5MtnoAAAAASUVORK5CYII=);} p,select{font:1em/160% Verdana,sans-serif;color:#494949;} -@import url("http://example.com/style.css");@import url("//example.com/style.css");body{margin:0;padding:0;background:#edf5fa;font:76%/170% Verdana,sans-serif;color:#494949;}.this .is .a .test{font:1em/100% Verdana,sans-serif;color:#494949;}.this +ul,select{font:1em/160% Verdana,sans-serif;color:#494949;}.ui-icon{background-image:url(images/icon.png);}.data .double-quote{background-image:url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7");}.data .single-quote{background-image:url('data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDACAWGBwYFCAcGhwkIiAmMFA0MCwsMGJGSjpQdGZ6eHJmcG6AkLicgIiuim5woNqirr7EztDOfJri8uDI8LjKzsb/2wBDASIkJDAqMF40NF7GhHCExsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsb/wAARCAABAAEDASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAb/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhEDEQA/AKAAH//Z');}.data .no-quote{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACEAAAAEAQAAAAAo/mtHAAAAIElEQVQIHWMRnWHwcRNLN8NZ7QYWwT8PlBlYsgqVBRsAankIMw5MtnoAAAAASUVORK5CYII=);} +@import url("https://fonts.fontprovider.com/css2?family=Roboto+Mono:wght@300;400&family=Roboto:ital,wght@0,300;0,400;1,300;1,400&display=swap") print;ul,select{font:1em/160% Verdana,sans-serif;color:#494949;}.ui-icon{background-image:url(images/icon.png);}.data .double-quote{background-image:url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7");}.data .single-quote{background-image:url('data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDACAWGBwYFCAcGhwkIiAmMFA0MCwsMGJGSjpQdGZ6eHJmcG6AkLicgIiuim5woNqirr7EztDOfJri8uDI8LjKzsb/2wBDASIkJDAqMF40NF7GhHCExsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsb/wAARCAABAAEDASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAb/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhEDEQA/AKAAH//Z');}.data .no-quote{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACEAAAAEAQAAAAAo/mtHAAAAIElEQVQIHWMRnWHwcRNLN8NZ7QYWwT8PlBlYsgqVBRsAankIMw5MtnoAAAAASUVORK5CYII=);} +@import url('import1.css') screen;@import url("http://example.com/style.css");@import url("//example.com/style.css");@import url("https://fonts.fontprovider.com/css2?family=Roboto+Mono:wght@300;400&family=Roboto:ital,wght@0,300;0,400;1,300;1,400&display=swap");@import url("http://example.com/style.css") screen and (orientation:landscape);@import "http://example.com/style.css" screen;@import "http://example.com/style.css" supports(display:table-cell);@import "http://example.com/style.css" supports(display:table-cell) screen;@import url("http://example.com/style.css") screen and (orientation:landscape);@import url("http://example.com/style.css") screen;@import url("http://user:pass@example.com/style.css") screen and (orientation:landscape);@import url(http://example.com/cus\(t;om.css);@import url('http://example.com/cu(st;o)m.css');@import url("http://user:pass@example.com/cu(s)t;om.css");@import url(http://user:pass@example.com/cu\(s\)t;om.css);body{margin:0;padding:0;background:#edf5fa;font:76%/170% Verdana,sans-serif;color:#494949;}.this .is .a .test{font:1em/100% Verdana,sans-serif;color:#494949;}.this .is .a .test{font:1em/100% Verdana,sans-serif;color:#494949;}textarea,select{font:1em/160% Verdana,sans-serif;color:#494949;} diff --git a/web/core/tests/Drupal/Tests/Core/Logger/LogMessageParserTest.php b/web/core/tests/Drupal/Tests/Core/Logger/LogMessageParserTest.php index 996ea54c2fb71f43281cf8231340aa669676dcc2..6e45055b512bd4aeaa74adaf64336b29c2df911a 100644 --- a/web/core/tests/Drupal/Tests/Core/Logger/LogMessageParserTest.php +++ b/web/core/tests/Drupal/Tests/Core/Logger/LogMessageParserTest.php @@ -60,8 +60,8 @@ public function providerTestParseMessagePlaceholders() { ], // Message with double PSR3 style messages. [ - ['message' => 'Test {with} two {encapsuled} strings', 'context' => ['with' => 'together', 'encapsuled' => 'awesome']], - ['message' => 'Test @with two @encapsuled strings', 'context' => ['@with' => 'together', '@encapsuled' => 'awesome']], + ['message' => 'Test {with} two {{encapsuled}} strings', 'context' => ['with' => 'together', 'encapsuled' => 'awesome']], + ['message' => 'Test @with two {@encapsuled} strings', 'context' => ['@with' => 'together', '@encapsuled' => 'awesome']], ], ]; } diff --git a/web/core/themes/claro/claro.theme b/web/core/themes/claro/claro.theme index 5916898c02c8bd99e8308fc6c3e4534625db3633..ba41e6d7c86c95354a5e8442404c7580f2a53074 100644 --- a/web/core/themes/claro/claro.theme +++ b/web/core/themes/claro/claro.theme @@ -747,10 +747,12 @@ function claro_form_system_modules_alter(&$form, FormStateInterface $form_state) foreach (Element::children($form['modules'][$key]) as $module_key) { if (isset($form['modules'][$key][$module_key]['links'])) { foreach ($form['modules'][$key][$module_key]['links'] as $link_key => &$link) { - $action_link_type = $link_key_to_action_link_type[$link_key]; - $link['#options']['attributes']['class'][] = 'action-link'; - $link['#options']['attributes']['class'][] = 'action-link--small'; - $link['#options']['attributes']['class'][] = "action-link--icon-$action_link_type"; + if (array_key_exists($link_key, $link_key_to_action_link_type)) { + $action_link_type = $link_key_to_action_link_type[$link_key]; + $link['#options']['attributes']['class'][] = 'action-link'; + $link['#options']['attributes']['class'][] = 'action-link--small'; + $link['#options']['attributes']['class'][] = "action-link--icon-$action_link_type"; + } } } } diff --git a/web/core/themes/claro/src/ClaroPreRender.php b/web/core/themes/claro/src/ClaroPreRender.php index c31505dc2d9c75251a7f37665f1875ca2d377264..40885064e95b2fb2b61276a6b9b442b5715bce4b 100644 --- a/web/core/themes/claro/src/ClaroPreRender.php +++ b/web/core/themes/claro/src/ClaroPreRender.php @@ -77,13 +77,10 @@ public static function verticalTabs($element) { $last_group_with_child_key = NULL; $last_group_with_child_key_last_child_key = NULL; - // Only iterate through the parents instead of all the group keys. - foreach ($element['#parents'] as $group_key) { - // Check parents against groups because we are only looking for group - // elements. - if (!in_array($group_key, $group_keys)) { - continue; - } + $group_key = implode('][', $element['#parents']); + // Only check siblings against groups because we are only looking for + // group elements. + if (in_array($group_key, $group_keys)) { $children_keys = Element::children($element['group']['#groups'][$group_key], TRUE); foreach ($children_keys as $child_key) { diff --git a/web/core/themes/olivero/css/components/fieldset.css b/web/core/themes/olivero/css/components/fieldset.css index ce2883e86366368950429a44ccbc3bc65f464007..679eb821d574868ba6914191e6b7f51793aec1a0 100644 --- a/web/core/themes/olivero/css/components/fieldset.css +++ b/web/core/themes/olivero/css/components/fieldset.css @@ -49,7 +49,7 @@ padding-top: 0; padding-bottom: 0; color: inherit; - border: solid 2px #7e96a7; + border: solid 2px #5d7585; border-radius: 0.1875rem; background-color: #fff; } @@ -78,7 +78,7 @@ _:-ms-fullscreen, color: inherit; border-top-left-radius: 3px; border-top-right-radius: 3px; - background-color: #7e96a7; + background-color: #5d7585; font-size: 1.125rem; font-weight: 700; line-height: 1.6875rem @@ -130,7 +130,7 @@ _:-ms-fullscreen, } .fieldset__label.is-disabled { - color: #7e96a7; + color: #5d7585; } .fieldset__description { @@ -185,7 +185,7 @@ _:-ms-fullscreen, } .fieldset--group .fieldset__legend--visible ~ .fieldset__wrapper { - border: solid 2px #7e96a7; + border: solid 2px #5d7585; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; } diff --git a/web/core/themes/olivero/css/components/fieldset.pcss.css b/web/core/themes/olivero/css/components/fieldset.pcss.css index be0bcb62bc17cc13d73903db6913956716249c14..35a70b2738f3343c4a6e9ffd03a2e2828df84e8f 100644 --- a/web/core/themes/olivero/css/components/fieldset.pcss.css +++ b/web/core/themes/olivero/css/components/fieldset.pcss.css @@ -14,7 +14,7 @@ padding-inline-start: 0; padding-inline-end: 0; color: inherit; - border: solid 2px var(--color--gray-30); + border: solid 2px var(--color--gray-25); border-radius: var(--border-radius); background-color: var(--color--white); } @@ -42,7 +42,7 @@ _:-ms-fullscreen, color: inherit; border-top-left-radius: var(--border-radius); border-top-right-radius: var(--border-radius); - background-color: var(--color--gray-30); + background-color: var(--color--gray-25); font-size: var(--font-size-l); font-weight: 700; line-height: var(--line-height-base); @@ -83,7 +83,7 @@ _:-ms-fullscreen, } .fieldset__label.is-disabled { - color: var(--color--gray-30); + color: var(--color--gray-25); } .fieldset__description { @@ -126,7 +126,7 @@ _:-ms-fullscreen, } .fieldset--group .fieldset__legend--visible ~ .fieldset__wrapper { - border: solid 2px var(--color--gray-30); + border: solid 2px var(--color--gray-25); border-bottom-right-radius: var(--border-radius); border-bottom-left-radius: var(--border-radius); } diff --git a/web/core/themes/olivero/css/components/form-select.css b/web/core/themes/olivero/css/components/form-select.css index 0fa278ff3a6c9557fe17d76b064c62d87020c005..e51b026e5d7e2b3b2e60bedce250d6a6eb6e8912 100644 --- a/web/core/themes/olivero/css/components/form-select.css +++ b/web/core/themes/olivero/css/components/form-select.css @@ -62,7 +62,7 @@ select:focus { select { - /* Disables border of select specifically for ms browser */ + /* Hides default chevron within Internet Explorer. */ } select::-ms-expand { @@ -99,17 +99,58 @@ select.form-element--small { select { - /* Necessary for IE11 to show chevron. */ + /* Necessary to show chevron in forced colors mode in modern browsers. */ } -@media screen and (-ms-high-contrast: active) { +@media (forced-colors: active) { + +[dir="ltr"] select { + padding-right: 1.125rem + } + +[dir="rtl"] select { + padding-left: 1.125rem + } select { - background-image: url("data:image/svg+xml,%3csvg width='18' height='11' viewBox='0 0 18 11' fill='none' xmlns='http://www.w3.org/2000/svg'%3e%3cpath d='M18 1.49699C18 1.35271 17.9279 1.19038 17.8196 1.08216L16.9178 0.18036C16.8096 0.0721439 16.6473 0 16.503 0C16.3587 0 16.1964 0.0721439 16.0882 0.18036L9 7.26854L1.91182 0.18036C1.80361 0.0721439 1.64128 0 1.49699 0C1.33467 0 1.19038 0.0721439 1.08216 0.18036L0.180361 1.08216C0.0721442 1.19038 0 1.35271 0 1.49699C0 1.64128 0.0721442 1.80361 0.180361 1.91182L8.58517 10.3166C8.69339 10.4248 8.85571 10.497 9 10.497C9.14429 10.497 9.30661 10.4248 9.41483 10.3166L17.8196 1.91182C17.9279 1.80361 18 1.64128 18 1.49699Z' fill='%235D7585'/%3e%3c/svg%3e") + background-image: none; + -webkit-appearance: listbox; + -moz-appearance: listbox; + appearance: listbox /* Default <select> appearance value for modern browsers. */ + + /* Lets browser set <select> appearance to whatever the browser's default is. */ } + @supports ((-webkit-appearance: revert) or (-moz-appearance: revert) or (appearance: revert)) { - select[multiple] { - background-image: none; +select { + -webkit-appearance: revert; + -moz-appearance: revert; + appearance: revert +} + } + } + +select { + + /* Necessary for Internet Explorer to show chevron. */ +} + +@media screen and (-ms-high-contrast: active) { + +[dir="ltr"] select { + padding-right: 0 + } + +[dir="rtl"] select { + padding-left: 0 + } + +select { + + /* Re-enable default chevron for Internet Explorer. */ +} + select::-ms-expand { + display: block; } } diff --git a/web/core/themes/olivero/css/components/form-select.pcss.css b/web/core/themes/olivero/css/components/form-select.pcss.css index 00a8c2833cfa26ad794b04192801e173a1fcdc83..87eba9ab62790893fb012d3c8367070b63b10e30 100644 --- a/web/core/themes/olivero/css/components/form-select.pcss.css +++ b/web/core/themes/olivero/css/components/form-select.pcss.css @@ -38,7 +38,7 @@ select { } } - /* Disables border of select specifically for ms browser */ + /* Hides default chevron within Internet Explorer. */ &::-ms-expand { display: none; } @@ -71,12 +71,25 @@ select { height: var(--sp2-5); } - /* Necessary for IE11 to show chevron. */ + /* Necessary to show chevron in forced colors mode in modern browsers. */ + @media (forced-colors: active) { + padding-inline-end: var(--sp); + background-image: none; + appearance: listbox; /* Default <select> appearance value for modern browsers. */ + + /* Lets browser set <select> appearance to whatever the browser's default is. */ + @supports (appearance: revert) { + appearance: revert; + } + } + + /* Necessary for Internet Explorer to show chevron. */ @media screen and (-ms-high-contrast: active) { - background-image: var(--form-element-select-icon); + padding-inline-end: 0; - &[multiple] { - background-image: none; + /* Re-enable default chevron for Internet Explorer. */ + &::-ms-expand { + display: block; } } } diff --git a/web/core/themes/olivero/css/components/header-search-wide.css b/web/core/themes/olivero/css/components/header-search-wide.css index 6d86cf313d6530627063ba85f1c449654d5b75c9..53c9ac63524aa155ec9473ab68cd31e13c71e84b 100644 --- a/web/core/themes/olivero/css/components/header-search-wide.css +++ b/web/core/themes/olivero/css/components/header-search-wide.css @@ -402,6 +402,17 @@ margin-left: auto; } +@media (forced-colors: active) { + +.block-search-wide__button { + background: ButtonFace +} + + .block-search-wide__button path { + fill: ButtonText; + } + } + /* Provide rudimentary access to site search if JS is disabled. */ html:not(.js) .search-block-form:focus-within .block-search-wide__wrapper { diff --git a/web/core/themes/olivero/css/components/header-search-wide.pcss.css b/web/core/themes/olivero/css/components/header-search-wide.pcss.css index ab5adf95ba90da547c0d0140359e804322e50416..bac07058c78a9e4aa445b047048aa261661de37b 100644 --- a/web/core/themes/olivero/css/components/header-search-wide.pcss.css +++ b/web/core/themes/olivero/css/components/header-search-wide.pcss.css @@ -256,6 +256,14 @@ margin-inline-start: auto; margin-inline-end: auto; } + + @media (forced-colors: active) { + background: ButtonFace; + + & path { + fill: ButtonText; + } + } } /* Provide rudimentary access to site search if JS is disabled. */ diff --git a/web/core/themes/olivero/css/components/site-header.css b/web/core/themes/olivero/css/components/site-header.css index 879cdd564dfa05a95b62589aa340674ab3c0d637..bc9a9fdd1e6feecc2af42a0769fef8c7923e3ac4 100644 --- a/web/core/themes/olivero/css/components/site-header.css +++ b/web/core/themes/olivero/css/components/site-header.css @@ -68,6 +68,7 @@ } .site-header__inner { + z-index: 1; /* Appear in front of Drupal's tabs. */ flex-grow: 1; width: calc(100vw - 5.625rem); background: #fff; diff --git a/web/core/themes/olivero/css/components/site-header.pcss.css b/web/core/themes/olivero/css/components/site-header.pcss.css index 0a4d69cd2f484888400a84cd49be2226c83a1dc2..7bc651315c4a18ad8822d1845b35e07c29878939 100644 --- a/web/core/themes/olivero/css/components/site-header.pcss.css +++ b/web/core/themes/olivero/css/components/site-header.pcss.css @@ -62,6 +62,7 @@ } .site-header__inner { + z-index: 1; /* Appear in front of Drupal's tabs. */ flex-grow: 1; width: calc(100vw - var(--content-left)); background: var(--color--white); diff --git a/web/core/themes/olivero/css/components/vertical-tabs.css b/web/core/themes/olivero/css/components/vertical-tabs.css index 7793996c04645ad81bd70d339872da0aac50d6c3..588be625dab2166ed7cf9b3037a01c367eccd16c 100644 --- a/web/core/themes/olivero/css/components/vertical-tabs.css +++ b/web/core/themes/olivero/css/components/vertical-tabs.css @@ -105,6 +105,7 @@ padding-top: 0.5625rem; padding-bottom: 0.5625rem; text-decoration: none; + color: #0f6292; background-color: #e7edf1 } diff --git a/web/core/themes/olivero/css/components/vertical-tabs.pcss.css b/web/core/themes/olivero/css/components/vertical-tabs.pcss.css index 52e0b73fa45a5ccbc2f0e8a4f722af3a1115607b..be8f2d9d5b1551dd936f5cff101e0b5fa7185043 100644 --- a/web/core/themes/olivero/css/components/vertical-tabs.pcss.css +++ b/web/core/themes/olivero/css/components/vertical-tabs.pcss.css @@ -68,6 +68,7 @@ padding-inline-start: var(--sp0-75); padding-inline-end: var(--sp0-75); text-decoration: none; + color: var(--color--blue-10); background-color: var(--color--gray-80); &:focus,