summaryrefslogtreecommitdiffstatshomepage
path: root/core
diff options
context:
space:
mode:
Diffstat (limited to 'core')
-rw-r--r--core/.cspell.json1
-rw-r--r--core/.deprecation-ignore.txt5
-rw-r--r--core/.phpstan-baseline.php102
-rw-r--r--core/assets/vendor/backbone/backbone-min.js2
-rw-r--r--core/assets/vendor/backbone/backbone-min.js.map2
-rw-r--r--core/assets/vendor/backbone/backbone.js8
-rw-r--r--core/assets/vendor/floating-ui/floating-ui.core.umd.min.js2
-rw-r--r--core/assets/vendor/floating-ui/floating-ui.dom.umd.min.js2
-rw-r--r--core/composer.json34
-rw-r--r--core/core.libraries.yml8
-rw-r--r--core/includes/common.inc4
-rw-r--r--core/lib/Drupal/Component/DependencyInjection/composer.json2
-rw-r--r--core/lib/Drupal/Component/Diff/composer.json2
-rw-r--r--core/lib/Drupal/Component/EventDispatcher/composer.json4
-rw-r--r--core/lib/Drupal/Component/HttpFoundation/composer.json2
-rw-r--r--core/lib/Drupal/Component/Plugin/composer.json2
-rw-r--r--core/lib/Drupal/Component/Serialization/composer.json2
-rw-r--r--core/lib/Drupal/Core/Composer/Composer.php4
-rw-r--r--core/lib/Drupal/Core/Hook/Attribute/Hook.php2
-rw-r--r--core/lib/Drupal/Core/Render/Element/StatusMessages.php10
-rw-r--r--core/lib/Drupal/Core/Render/Placeholder/ChainedPlaceholderStrategy.php15
-rw-r--r--core/lib/Drupal/Core/Render/PlaceholderGenerator.php11
-rw-r--r--core/lib/Drupal/Core/Render/Renderer.php134
-rw-r--r--core/lib/Drupal/Core/Render/RendererInterface.php2
-rw-r--r--core/lib/Drupal/Core/Template/TwigTransTokenParser.php6
-rw-r--r--core/lib/Drupal/Core/Test/PhpUnitTestRunner.php6
-rw-r--r--core/modules/big_pipe/big_pipe.module1
-rw-r--r--core/modules/big_pipe/tests/modules/big_pipe_messages_test/big_pipe_messages_test.info.yml5
-rw-r--r--core/modules/big_pipe/tests/modules/big_pipe_messages_test/src/Hook/BigPipeMessagesHooks.php42
-rw-r--r--core/modules/big_pipe/tests/src/Functional/BigPipeTest.php14
-rw-r--r--core/modules/big_pipe/tests/src/FunctionalJavascript/BigPipeRegressionTest.php1
-rw-r--r--core/modules/block/src/Hook/BlockHooks.php13
-rw-r--r--core/modules/block_content/config/optional/views.view.block_content.yml1
-rw-r--r--core/modules/block_content/tests/modules/block_content_test_views/test_views/views.view.test_block_content_redirect_destination.yml1
-rw-r--r--core/modules/ckeditor5/src/HTMLRestrictions.php2
-rw-r--r--core/modules/ckeditor5/src/Plugin/CKEditor5PluginDefinition.php3
-rw-r--r--core/modules/ckeditor5/src/SmartDefaultSettings.php2
-rw-r--r--core/modules/ckeditor5/tests/src/Unit/HTMLRestrictionsTest.php8
-rw-r--r--core/modules/comment/config/optional/views.view.comment.yml1
-rw-r--r--core/modules/comment/src/Plugin/views/field/LastTimestamp.php2
-rw-r--r--core/modules/comment/tests/modules/comment_test_views/test_views/views.view.test_comment_operations.yml1
-rw-r--r--core/modules/comment/tests/modules/comment_test_views/test_views/views.view.test_comment_schema.yml1
-rw-r--r--core/modules/comment/tests/modules/comment_test_views/test_views/views.view.test_new_comments.yml5
-rw-r--r--core/modules/content_moderation/config/optional/views.view.moderated_content.yml1
-rw-r--r--core/modules/content_moderation/tests/modules/content_moderation_test_views/config/install/views.view.latest.yml5
-rw-r--r--core/modules/content_moderation/tests/modules/content_moderation_test_views/config/install/views.view.test_content_moderation_state_sort_base_table.yml1
-rw-r--r--core/modules/content_moderation/tests/modules/content_moderation_test_views/config/install/views.view.test_content_moderation_state_sort_revision_table.yml1
-rw-r--r--core/modules/content_translation/tests/modules/content_translation_test_views/test_views/views.view.test_entity_translations_link.yml1
-rw-r--r--core/modules/contextual/src/Hook/ContextualThemeHooks.php14
-rw-r--r--core/modules/datetime/datetime.module4
-rw-r--r--core/modules/datetime/src/DateTimeViewsHelper.php3
-rw-r--r--core/modules/dblog/config/optional/views.view.watchdog.yml1
-rw-r--r--core/modules/field/src/Hook/FieldHooks.php19
-rw-r--r--core/modules/field_layout/src/Entity/FieldLayoutEntityDisplayTrait.php4
-rw-r--r--core/modules/field_ui/src/Form/FieldConfigEditForm.php3
-rw-r--r--core/modules/field_ui/src/Form/FieldStorageConfigEditForm.php3
-rw-r--r--core/modules/field_ui/src/Hook/FieldUiHooks.php4
-rw-r--r--core/modules/file/config/optional/views.view.files.yml2
-rw-r--r--core/modules/file/tests/modules/file_test_views/config/optional/views.view.test_file_user_file_data.yml1
-rw-r--r--core/modules/filter/filter.module2
-rw-r--r--core/modules/filter/src/Entity/FilterFormat.php2
-rw-r--r--core/modules/filter/src/FilterFormatInterface.php2
-rw-r--r--core/modules/image/image.install38
-rw-r--r--core/modules/image/src/Hook/ImageRequirements.php57
-rw-r--r--core/modules/image/src/Plugin/Field/FieldWidget/ImageWidget.php4
-rw-r--r--core/modules/image/tests/modules/image_test_views/test_views/views.view.test_image_user_image_data.yml1
-rw-r--r--core/modules/jsonapi/jsonapi.install51
-rw-r--r--core/modules/jsonapi/src/Hook/JsonapiHooks.php2
-rw-r--r--core/modules/jsonapi/src/Hook/JsonapiRequirements.php72
-rw-r--r--core/modules/jsonapi/src/Normalizer/Value/TemporaryArrayObjectThrowingExceptions.php2
-rw-r--r--core/modules/language/src/Form/NegotiationBrowserDeleteForm.php2
-rw-r--r--core/modules/language/src/LanguageNegotiatorInterface.php2
-rw-r--r--core/modules/layout_builder/src/Access/LayoutBuilderAccessCheck.php3
-rw-r--r--core/modules/layout_builder/src/Plugin/ConfigAction/AddComponent.php133
-rw-r--r--core/modules/layout_builder/src/Plugin/ConfigAction/Deriver/AddComponentDeriver.php48
-rw-r--r--core/modules/layout_builder/src/Plugin/Derivative/ExtraFieldBlockDeriver.php2
-rw-r--r--core/modules/layout_builder/src/SectionListTrait.php2
-rw-r--r--core/modules/layout_builder/src/SectionStorageInterface.php2
-rw-r--r--core/modules/layout_builder/tests/modules/layout_builder_defaults_test/config/schema/layout_builder_defaults_test.schema.yml8
-rw-r--r--core/modules/layout_builder/tests/src/Kernel/Plugin/ConfigAction/AddComponentTest.php401
-rw-r--r--core/modules/layout_discovery/layout_discovery.install22
-rw-r--r--core/modules/layout_discovery/src/Install/Requirements/LayoutDiscoveryRequirements.php28
-rw-r--r--core/modules/locale/locale.install66
-rw-r--r--core/modules/locale/src/Hook/LocaleRequirements.php82
-rw-r--r--core/modules/locale/src/LocaleConfigManager.php6
-rw-r--r--core/modules/locale/src/StreamWrapper/TranslationsStream.php1
-rw-r--r--core/modules/media/config/optional/views.view.media.yml1
-rw-r--r--core/modules/media/media.install98
-rw-r--r--core/modules/media/src/Hook/MediaRequirementsHooks.php98
-rw-r--r--core/modules/media/src/Install/Requirements/MediaRequirements.php39
-rw-r--r--core/modules/media/src/MediaSourceBase.php3
-rw-r--r--core/modules/media/tests/modules/media_test_views/config/install/views.view.test_media_bulk_form.yml2
-rw-r--r--core/modules/media_library/config/install/views.view.media_library.yml1
-rw-r--r--core/modules/menu_ui/src/Hook/MenuUiHooks.php3
-rw-r--r--core/modules/migrate/tests/src/Kernel/MigrateSourceTestBase.php6
-rw-r--r--core/modules/mysql/src/Driver/Database/mysql/Schema.php4
-rw-r--r--core/modules/mysql/src/Hook/MysqlRequirements.php (renamed from core/modules/mysql/mysql.install)36
-rw-r--r--core/modules/navigation/navigation.install19
-rw-r--r--core/modules/navigation/src/Hook/NavigationRequirements.php38
-rw-r--r--core/modules/navigation/tests/src/FunctionalJavascript/PerformanceTest.php2
-rw-r--r--core/modules/node/config/optional/views.view.content.yml1
-rw-r--r--core/modules/node/config/optional/views.view.glossary.yml1
-rw-r--r--core/modules/node/node.install127
-rw-r--r--core/modules/node/src/Hook/NodeRequirements.php154
-rw-r--r--core/modules/node/src/Plugin/views/wizard/Node.php10
-rw-r--r--core/modules/node/src/Plugin/views/wizard/NodeRevision.php5
-rw-r--r--core/modules/node/tests/modules/node_test_views/test_views/views.view.test_node_access_join.yml10
-rw-r--r--core/modules/node/tests/modules/node_test_views/test_views/views.view.test_node_bulk_form.yml2
-rw-r--r--core/modules/node/tests/src/Kernel/NodeRequirementsStatusFilterWarningTest.php3
-rw-r--r--core/modules/package_manager/package_manager.install82
-rw-r--r--core/modules/package_manager/src/Hook/PackageManagerRequirementsHooks.php136
-rw-r--r--core/modules/package_manager/src/Install/Requirements/PackageManagerRequirements.php52
-rw-r--r--core/modules/package_manager/src/Validator/ComposerPluginsValidator.php1
-rw-r--r--core/modules/pgsql/pgsql.install36
-rw-r--r--core/modules/pgsql/src/Driver/Database/pgsql/Schema.php3
-rw-r--r--core/modules/pgsql/src/Hook/PgsqlRequirementsHooks.php54
-rw-r--r--core/modules/pgsql/src/Install/Requirements/PgsqlRequirements.php49
-rw-r--r--core/modules/search/search.install32
-rw-r--r--core/modules/search/src/Entity/SearchPage.php4
-rw-r--r--core/modules/search/src/Hook/SearchRequirements.php50
-rw-r--r--core/modules/sqlite/src/Driver/Database/sqlite/Schema.php4
-rw-r--r--core/modules/system/src/EventSubscriber/AccessRouteAlterSubscriber.php2
-rw-r--r--core/modules/system/src/Plugin/migrate/process/d6/SystemUpdate7000.php2
-rw-r--r--core/modules/system/system.module10
-rw-r--r--core/modules/system/system.post_update.php2
-rw-r--r--core/modules/system/tests/modules/element_info_test/src/Element/DeprecatedExtendsFormElement.php5
-rw-r--r--core/modules/system/tests/modules/element_info_test/src/Element/DeprecatedExtendsRenderElement.php5
-rw-r--r--core/modules/system/tests/modules/entity_reference_test_views/test_views/views.view.test_entity_reference_group_by_empty_relationships.yml1
-rw-r--r--core/modules/system/tests/modules/render_placeholder_message_test/src/RenderPlaceholderMessageTestController.php4
-rw-r--r--core/modules/system/tests/modules/requirements1_test/requirements1_test.install2
-rw-r--r--core/modules/system/tests/modules/update_script_test/src/Hook/UpdateScriptTestRequirements.php59
-rw-r--r--core/modules/system/tests/modules/update_script_test/update_script_test.install42
-rw-r--r--core/modules/system/tests/modules/update_test_schema/src/Hook/UpdateTestSchemaRequirements.php33
-rw-r--r--core/modules/system/tests/modules/update_test_schema/update_test_schema.install19
-rw-r--r--core/modules/system/tests/src/Functional/FileTransfer/TestFileTransfer.php6
-rw-r--r--core/modules/system/tests/src/Functional/Render/PlaceholderMessageTest.php5
-rw-r--r--core/modules/update/src/Hook/UpdateHooks.php9
-rw-r--r--core/modules/update/src/Hook/UpdateRequirements.php161
-rw-r--r--core/modules/update/src/UpdateRoot.php2
-rw-r--r--core/modules/update/update.authorize.inc11
-rw-r--r--core/modules/update/update.fetch.inc9
-rw-r--r--core/modules/update/update.install139
-rw-r--r--core/modules/update/update.manager.inc12
-rw-r--r--core/modules/user/config/optional/views.view.user_admin_people.yml1
-rw-r--r--core/modules/user/src/Hook/UserRequirements.php67
-rw-r--r--core/modules/user/tests/modules/user_test_views/test_views/views.view.test_user_bulk_form.yml2
-rw-r--r--core/modules/user/tests/modules/user_test_views/test_views/views.view.test_user_fields_access.yml1
-rw-r--r--core/modules/user/tests/src/Kernel/UserRequirementsTest.php18
-rw-r--r--core/modules/user/user.install45
-rw-r--r--core/modules/views/config/schema/views.schema.yml15
-rw-r--r--core/modules/views/src/FieldViewsDataProvider.php31
-rw-r--r--core/modules/views/src/Hook/ViewsHooks.php16
-rw-r--r--core/modules/views/src/Hook/ViewsViewsHooks.php3
-rw-r--r--core/modules/views/src/Plugin/views/argument/LanguageArgument.php12
-rw-r--r--core/modules/views/src/Plugin/views/display/Block.php11
-rw-r--r--core/modules/views/src/Plugin/views/field/Boolean.php5
-rw-r--r--core/modules/views/src/Plugin/views/field/Url.php2
-rw-r--r--core/modules/views/src/Plugin/views/filter/FilterPluginBase.php10
-rw-r--r--core/modules/views/src/Plugin/views/style/Table.php2
-rw-r--r--core/modules/views/src/Plugin/views/wizard/WizardPluginBase.php5
-rw-r--r--core/modules/views/src/ViewExecutable.php2
-rw-r--r--core/modules/views/src/ViewsConfigUpdater.php10
-rw-r--r--core/modules/views/tests/fixtures/update/views-block-items-per-page.php48
-rw-r--r--core/modules/views/tests/modules/action_bulk_test/config/install/views.view.test_bulk_form.yml1
-rw-r--r--core/modules/views/tests/modules/views_test_config/test_views/views.view.test_click_sort.yml1
-rw-r--r--core/modules/views/tests/modules/views_test_config/test_views/views.view.test_click_sort_ajax.yml9
-rw-r--r--core/modules/views/tests/modules/views_test_config/test_views/views.view.test_content_ajax.yml1
-rw-r--r--core/modules/views/tests/modules/views_test_config/test_views/views.view.test_distinct_click_sorting.yml1
-rw-r--r--core/modules/views/tests/modules/views_test_config/test_views/views.view.test_entity_operations.yml1
-rw-r--r--core/modules/views/tests/modules/views_test_config/test_views/views.view.test_field_header.yml2
-rw-r--r--core/modules/views/tests/modules/views_test_config/test_views/views.view.test_filter_placeholder_text.yml1
-rw-r--r--core/modules/views/tests/modules/views_test_config/test_views/views.view.test_form_multiple.yml1
-rw-r--r--core/modules/views/tests/modules/views_test_config/test_views/views.view.test_glossary.yml1
-rw-r--r--core/modules/views/tests/modules/views_test_config/test_views/views.view.test_row_render_cache.yml1
-rw-r--r--core/modules/views/tests/modules/views_test_config/test_views/views.view.test_row_render_cache_none.yml1
-rw-r--r--core/modules/views/tests/modules/views_test_config/test_views/views.view.test_table.yml1
-rw-r--r--core/modules/views/tests/modules/views_test_config/test_views/views.view.test_view_render.yml2
-rw-r--r--core/modules/views/tests/src/Functional/Plugin/ContextualFiltersBlockContextTest.php2
-rw-r--r--core/modules/views/tests/src/Functional/Update/BlockItemsPerPageUpdateTest.php41
-rw-r--r--core/modules/views/tests/src/Functional/Wizard/ItemsPerPageTest.php121
-rw-r--r--core/modules/views/tests/src/Kernel/Plugin/ViewsBlockTest.php22
-rw-r--r--core/modules/views/tests/src/Unit/Plugin/views/display/BlockTest.php18
-rw-r--r--core/modules/views/views.api.php5
-rw-r--r--core/modules/views/views.post_update.php23
-rw-r--r--core/modules/views_ui/admin.inc4
-rw-r--r--core/modules/workspaces/src/EntityQuery/Tables.php2
-rw-r--r--core/modules/workspaces/src/Install/Requirements/WorkspacesRequirements.php31
-rw-r--r--core/modules/workspaces/src/Plugin/Validation/Constraint/EntityReferenceSupportedNewEntitiesConstraintValidator.php3
-rw-r--r--core/modules/workspaces/workspaces.install19
-rw-r--r--core/package.json24
-rw-r--r--core/phpcs.xml.dist2
-rw-r--r--core/phpstan.neon.dist1
-rw-r--r--core/phpunit.xml.dist23
-rw-r--r--core/profiles/demo_umami/config/install/block.block.umami_views_block__articles_aside_block_1.yml2
-rw-r--r--core/profiles/demo_umami/config/install/block.block.umami_views_block__promoted_items_block_1.yml2
-rw-r--r--core/profiles/demo_umami/config/install/block.block.umami_views_block__recipe_collections_block.yml2
-rw-r--r--core/profiles/demo_umami/config/install/core.entity_view_display.node.recipe.full.yml2
-rw-r--r--core/profiles/demo_umami/config/optional/views.view.media.yml1
-rw-r--r--core/profiles/demo_umami/demo_umami.install18
-rw-r--r--core/profiles/demo_umami/src/Hook/DemoUmamiRequirements.php39
-rw-r--r--core/profiles/standard/tests/src/FunctionalJavascript/StandardJavascriptTest.php25
-rw-r--r--core/profiles/standard/tests/src/FunctionalJavascript/StandardPerformanceTest.php6
-rw-r--r--core/profiles/tests/testing_requirements/src/Install/Requirements/TestingRequirementsRequirements.php28
-rw-r--r--core/profiles/tests/testing_requirements/testing_requirements.install25
-rwxr-xr-xcore/scripts/run-tests.sh2
-rw-r--r--core/tests/Drupal/BuildTests/Composer/Template/ComposerProjectTemplatesTest.php3
-rw-r--r--core/tests/Drupal/KernelTests/Core/Test/PhpUnitTestDiscoveryTest.php15
-rw-r--r--core/tests/Drupal/TestTools/Extension/DeprecationBridge/DeprecationHandler.php5
-rw-r--r--core/tests/Drupal/TestTools/PhpUnitCompatibility/PhpUnit11/TestCompatibilityTrait.php13
-rw-r--r--core/tests/Drupal/Tests/Component/Datetime/DateTimePlusTest.php8
-rw-r--r--core/tests/Drupal/Tests/Composer/Plugin/ExecTrait.php (renamed from core/tests/Drupal/Tests/Composer/Plugin/Scaffold/ExecTrait.php)7
-rw-r--r--core/tests/Drupal/Tests/Composer/Plugin/FixturesBase.php272
-rw-r--r--core/tests/Drupal/Tests/Composer/Plugin/Scaffold/Fixtures.php257
-rw-r--r--core/tests/Drupal/Tests/Composer/Plugin/Scaffold/Functional/ComposerHookTest.php2
-rw-r--r--core/tests/Drupal/Tests/Composer/Plugin/Scaffold/Functional/ManageGitIgnoreTest.php2
-rw-r--r--core/tests/Drupal/Tests/Composer/Plugin/Scaffold/Functional/ScaffoldUpgradeTest.php2
-rw-r--r--core/tests/Drupal/Tests/Composer/Plugin/Unpack/Fixtures.php38
-rw-r--r--core/tests/Drupal/Tests/Composer/Plugin/Unpack/Functional/UnpackRecipeTest.php676
-rw-r--r--core/tests/Drupal/Tests/Composer/Plugin/Unpack/SemVerTest.php51
-rw-r--r--core/tests/Drupal/Tests/Composer/Plugin/Unpack/fixtures/composer-root/composer.json.tmpl97
-rw-r--r--core/tests/Drupal/Tests/Composer/Plugin/Unpack/fixtures/modules/composer-module-a/composer.json6
-rw-r--r--core/tests/Drupal/Tests/Composer/Plugin/Unpack/fixtures/modules/composer-module-b/composer.json6
-rw-r--r--core/tests/Drupal/Tests/Composer/Plugin/Unpack/fixtures/recipes/composer-recipe-a/composer.json9
-rw-r--r--core/tests/Drupal/Tests/Composer/Plugin/Unpack/fixtures/recipes/composer-recipe-a/recipe.yml3
-rw-r--r--core/tests/Drupal/Tests/Composer/Plugin/Unpack/fixtures/recipes/composer-recipe-b/composer.json9
-rw-r--r--core/tests/Drupal/Tests/Composer/Plugin/Unpack/fixtures/recipes/composer-recipe-b/recipe.yml3
-rw-r--r--core/tests/Drupal/Tests/Composer/Plugin/Unpack/fixtures/recipes/composer-recipe-c/composer.json9
-rw-r--r--core/tests/Drupal/Tests/Composer/Plugin/Unpack/fixtures/recipes/composer-recipe-c/recipe.yml3
-rw-r--r--core/tests/Drupal/Tests/Composer/Plugin/Unpack/fixtures/recipes/composer-recipe-d/composer.json5
-rw-r--r--core/tests/Drupal/Tests/Composer/Plugin/Unpack/fixtures/recipes/composer-recipe-d/recipe.yml3
-rw-r--r--core/tests/Drupal/Tests/Composer/Plugin/Unpack/fixtures/themes/composer-theme-a/composer.json5
-rw-r--r--core/tests/Drupal/Tests/Core/Config/Entity/ConfigEntityStorageTest.php35
-rw-r--r--core/tests/Drupal/Tests/Core/Datetime/DrupalDateTimeTest.php8
-rw-r--r--core/tests/Drupal/Tests/Core/DependencyInjection/YamlFileLoaderTest.php4
-rw-r--r--core/tests/Drupal/Tests/Core/Entity/KeyValueStore/KeyValueEntityStorageTest.php25
-rw-r--r--core/tests/Drupal/Tests/Core/Entity/Sql/SqlContentEntityStorageTest.php5
-rw-r--r--core/tests/Drupal/Tests/Core/Render/Placeholder/ChainedPlaceholderStrategyTest.php14
-rw-r--r--core/tests/PHPStan/composer.json4
-rw-r--r--core/tests/fixtures/config_install/multilingual/views.view.content.yml1
-rw-r--r--core/tests/fixtures/config_install/multilingual/views.view.files.yml2
-rw-r--r--core/tests/fixtures/config_install/multilingual/views.view.glossary.yml1
-rw-r--r--core/tests/fixtures/config_install/multilingual/views.view.user_admin_people.yml1
-rw-r--r--core/tests/fixtures/config_install/multilingual/views.view.watchdog.yml1
-rw-r--r--core/yarn.lock505
244 files changed, 4393 insertions, 1836 deletions
diff --git a/core/.cspell.json b/core/.cspell.json
index 0b65759aa4f4..8baf6e3d342e 100644
--- a/core/.cspell.json
+++ b/core/.cspell.json
@@ -7,6 +7,7 @@
"**/.*.json",
".*ignore",
"composer.lock",
+ "composer/Metapackage/PinnedDevDependencies/composer.json",
"assets/vendor/**",
"misc/jquery.form.js",
"lib/Drupal/Component/Diff/**",
diff --git a/core/.deprecation-ignore.txt b/core/.deprecation-ignore.txt
index 0e70057ddac8..3eb0b15f07ab 100644
--- a/core/.deprecation-ignore.txt
+++ b/core/.deprecation-ignore.txt
@@ -29,6 +29,9 @@
# PHPUnit 10.
%The "PHPUnit\\Framework\\TestCase::__construct\(\)" method is considered internal.*You should not extend it from "Drupal\\[^"]+"%
+# PHPUnit 11.
+%The "PHPUnit\\Framework\\TestCase::__construct\(\)" method is considered final\. It may change without further notice as of its next major version\. You should not extend it from "Drupal\\[^"]+"%
+
# Symfony 7.2
%Since symfony/http-foundation 7.2: NativeSessionStorage's "sid_length" option is deprecated and will be ignored in Symfony 8.0.%
%Since symfony/http-foundation 7.2: NativeSessionStorage's "sid_bits_per_character" option is deprecated and will be ignored in Symfony 8.0.%
@@ -41,3 +44,5 @@
%The "Drupal\\Core\\Entity\\Query\\QueryBase::hasAllTags\(\)" method will require a new "string \.\.\. \$tags" argument in the next major version of its interface%
%The "Drupal\\Core\\Entity\\Query\\QueryBase::hasAnyTag\(\)" method will require a new "string \.\.\. \$tags" argument in the next major version of its interface%
+# Symfony 7.3
+%Since symfony/validator 7.3: Passing an array of options to configure the "[^"]+" constraint is deprecated, use named arguments instead.%
diff --git a/core/.phpstan-baseline.php b/core/.phpstan-baseline.php
index cbe268b00821..8319f5bbcdcc 100644
--- a/core/.phpstan-baseline.php
+++ b/core/.phpstan-baseline.php
@@ -7436,22 +7436,6 @@ $ignoreErrors[] = [
'path' => __DIR__ . '/lib/Drupal/Core/FileTransfer/ChmodInterface.php',
];
$ignoreErrors[] = [
- 'message' => '#^Class Drupal\\\\Core\\\\FileTransfer\\\\FTP extends deprecated class Drupal\\\\Core\\\\FileTransfer\\\\FileTransfer\\:
-in drupal\\:11\\.2\\.0 and is removed from drupal\\:12\\.0\\.0\\. There is no
- replacement\\. Use composer to manage the code for your site\\.$#',
- 'identifier' => 'class.extendsDeprecatedClass',
- 'count' => 1,
- 'path' => __DIR__ . '/lib/Drupal/Core/FileTransfer/FTP.php',
-];
-$ignoreErrors[] = [
- 'message' => '#^Class Drupal\\\\Core\\\\FileTransfer\\\\FTPExtension extends deprecated class Drupal\\\\Core\\\\FileTransfer\\\\FTP\\:
-in drupal\\:11\\.2\\.0 and is removed from drupal\\:12\\.0\\.0\\. There is no
- replacement\\. Use composer to manage the code for your site\\.$#',
- 'identifier' => 'class.extendsDeprecatedClass',
- 'count' => 1,
- 'path' => __DIR__ . '/lib/Drupal/Core/FileTransfer/FTPExtension.php',
-];
-$ignoreErrors[] = [
'message' => '#^Method Drupal\\\\Core\\\\FileTransfer\\\\FTPExtension\\:\\:chmodJailed\\(\\) has no return type specified\\.$#',
'identifier' => 'missingType.return',
'count' => 1,
@@ -7602,14 +7586,6 @@ $ignoreErrors[] = [
'path' => __DIR__ . '/lib/Drupal/Core/FileTransfer/Form/FileTransferAuthorizeForm.php',
];
$ignoreErrors[] = [
- 'message' => '#^Class Drupal\\\\Core\\\\FileTransfer\\\\Local extends deprecated class Drupal\\\\Core\\\\FileTransfer\\\\FileTransfer\\:
-in drupal\\:11\\.2\\.0 and is removed from drupal\\:12\\.0\\.0\\. There is no
- replacement\\. Use composer to manage the code for your site\\.$#',
- 'identifier' => 'class.extendsDeprecatedClass',
- 'count' => 1,
- 'path' => __DIR__ . '/lib/Drupal/Core/FileTransfer/Local.php',
-];
-$ignoreErrors[] = [
'message' => '#^Method Drupal\\\\Core\\\\FileTransfer\\\\Local\\:\\:chmodJailed\\(\\) has no return type specified\\.$#',
'identifier' => 'missingType.return',
'count' => 1,
@@ -7646,14 +7622,6 @@ $ignoreErrors[] = [
'path' => __DIR__ . '/lib/Drupal/Core/FileTransfer/Local.php',
];
$ignoreErrors[] = [
- 'message' => '#^Class Drupal\\\\Core\\\\FileTransfer\\\\SSH extends deprecated class Drupal\\\\Core\\\\FileTransfer\\\\FileTransfer\\:
-in drupal\\:11\\.2\\.0 and is removed from drupal\\:12\\.0\\.0\\. There is no
- replacement\\. Use composer to manage the code for your site\\.$#',
- 'identifier' => 'class.extendsDeprecatedClass',
- 'count' => 1,
- 'path' => __DIR__ . '/lib/Drupal/Core/FileTransfer/SSH.php',
-];
-$ignoreErrors[] = [
'message' => '#^Method Drupal\\\\Core\\\\FileTransfer\\\\SSH\\:\\:chmodJailed\\(\\) has no return type specified\\.$#',
'identifier' => 'missingType.return',
'count' => 1,
@@ -9844,12 +9812,6 @@ $ignoreErrors[] = [
'path' => __DIR__ . '/lib/Drupal/Core/Render/Renderer.php',
];
$ignoreErrors[] = [
- 'message' => '#^Method Drupal\\\\Core\\\\Render\\\\Renderer\\:\\:doRender\\(\\) has no return type specified\\.$#',
- 'identifier' => 'missingType.return',
- 'count' => 1,
- 'path' => __DIR__ . '/lib/Drupal/Core/Render/Renderer.php',
-];
-$ignoreErrors[] = [
'message' => '#^Method Drupal\\\\Core\\\\Render\\\\Renderer\\:\\:renderPlain\\(\\) has no return type specified\\.$#',
'identifier' => 'missingType.return',
'count' => 1,
@@ -10984,28 +10946,12 @@ $ignoreErrors[] = [
'path' => __DIR__ . '/lib/Drupal/Core/Update/UpdateServiceProvider.php',
];
$ignoreErrors[] = [
- 'message' => '#^Class Drupal\\\\Core\\\\Updater\\\\Module extends deprecated class Drupal\\\\Core\\\\Updater\\\\Updater\\:
-in drupal\\:11\\.2\\.0 and is removed from drupal\\:12\\.0\\.0\\. There is no
- replacement\\. Use composer to manage the code for your site\\.$#',
- 'identifier' => 'class.extendsDeprecatedClass',
- 'count' => 1,
- 'path' => __DIR__ . '/lib/Drupal/Core/Updater/Module.php',
-];
-$ignoreErrors[] = [
'message' => '#^Method Drupal\\\\Core\\\\Updater\\\\Module\\:\\:postUpdateTasks\\(\\) should return array but return statement is missing\\.$#',
'identifier' => 'return.missing',
'count' => 1,
'path' => __DIR__ . '/lib/Drupal/Core/Updater/Module.php',
];
$ignoreErrors[] = [
- 'message' => '#^Class Drupal\\\\Core\\\\Updater\\\\Theme extends deprecated class Drupal\\\\Core\\\\Updater\\\\Updater\\:
-in drupal\\:11\\.2\\.0 and is removed from drupal\\:12\\.0\\.0\\. There is no
- replacement\\. Use composer to manage the code for your site\\.$#',
- 'identifier' => 'class.extendsDeprecatedClass',
- 'count' => 1,
- 'path' => __DIR__ . '/lib/Drupal/Core/Updater/Theme.php',
-];
-$ignoreErrors[] = [
'message' => '#^Method Drupal\\\\Core\\\\Updater\\\\Updater\\:\\:getBackupDir\\(\\) has no return type specified\\.$#',
'identifier' => 'missingType.return',
'count' => 1,
@@ -11042,14 +10988,6 @@ $ignoreErrors[] = [
'path' => __DIR__ . '/lib/Drupal/Core/Updater/Updater.php',
];
$ignoreErrors[] = [
- 'message' => '#^Class Drupal\\\\Core\\\\Updater\\\\UpdaterFileTransferException extends deprecated class Drupal\\\\Core\\\\Updater\\\\UpdaterException\\:
-in drupal\\:11\\.2\\.0 and is removed from drupal\\:12\\.0\\.0\\. There is no
- replacement\\. Use composer to manage the code for your site\\.$#',
- 'identifier' => 'class.extendsDeprecatedClass',
- 'count' => 1,
- 'path' => __DIR__ . '/lib/Drupal/Core/Updater/UpdaterFileTransferException.php',
-];
-$ignoreErrors[] = [
'message' => '#^Method Drupal\\\\Core\\\\Updater\\\\UpdaterInterface\\:\\:getProjectName\\(\\) has no return type specified\\.$#',
'identifier' => 'missingType.return',
'count' => 1,
@@ -23398,12 +23336,6 @@ $ignoreErrors[] = [
'path' => __DIR__ . '/modules/locale/tests/src/Unit/Menu/LocaleLocalTasksTest.php',
];
$ignoreErrors[] = [
- 'message' => '#^Variable \\$error in empty\\(\\) always exists and is not falsy\\.$#',
- 'identifier' => 'empty.variable',
- 'count' => 1,
- 'path' => __DIR__ . '/modules/media/media.install',
-];
-$ignoreErrors[] = [
'message' => '#^Method Drupal\\\\media\\\\Controller\\\\MediaFilterController\\:\\:checkCsrf\\(\\) has no return type specified\\.$#',
'identifier' => 'missingType.return',
'count' => 1,
@@ -37816,22 +37748,6 @@ $ignoreErrors[] = [
'path' => __DIR__ . '/modules/update/tests/src/Unit/ProjectCoreCompatibilityTest.php',
];
$ignoreErrors[] = [
- 'message' => '#^Parameter \\$filetransfer of function update_authorize_batch_copy_project\\(\\) has typehint with deprecated class Drupal\\\\Core\\\\FileTransfer\\\\FileTransfer\\:
-in drupal\\:11\\.2\\.0 and is removed from drupal\\:12\\.0\\.0\\. There is no
- replacement\\. Use composer to manage the code for your site\\.$#',
- 'identifier' => 'parameter.deprecatedClass',
- 'count' => 1,
- 'path' => __DIR__ . '/modules/update/update.authorize.inc',
-];
-$ignoreErrors[] = [
- 'message' => '#^Parameter \\$filetransfer of function update_authorize_run_update\\(\\) has typehint with deprecated class Drupal\\\\Core\\\\FileTransfer\\\\FileTransfer\\:
-in drupal\\:11\\.2\\.0 and is removed from drupal\\:12\\.0\\.0\\. There is no
- replacement\\. Use composer to manage the code for your site\\.$#',
- 'identifier' => 'parameter.deprecatedClass',
- 'count' => 1,
- 'path' => __DIR__ . '/modules/update/update.authorize.inc',
-];
-$ignoreErrors[] = [
'message' => '#^Function _update_no_data\\(\\) has no return type specified\\.$#',
'identifier' => 'missingType.return',
'count' => 1,
@@ -53445,24 +53361,6 @@ $ignoreErrors[] = [
'path' => __DIR__ . '/tests/Drupal/Tests/Composer/Plugin/ProjectMessage/ConfigTest.php',
];
$ignoreErrors[] = [
- 'message' => '#^Method Drupal\\\\Tests\\\\Composer\\\\Plugin\\\\Scaffold\\\\Fixtures\\:\\:cloneFixtureProjects\\(\\) has no return type specified\\.$#',
- 'identifier' => 'missingType.return',
- 'count' => 1,
- 'path' => __DIR__ . '/tests/Drupal/Tests/Composer/Plugin/Scaffold/Fixtures.php',
-];
-$ignoreErrors[] = [
- 'message' => '#^Method Drupal\\\\Tests\\\\Composer\\\\Plugin\\\\Scaffold\\\\Fixtures\\:\\:createIsolatedComposerCacheDir\\(\\) has no return type specified\\.$#',
- 'identifier' => 'missingType.return',
- 'count' => 1,
- 'path' => __DIR__ . '/tests/Drupal/Tests/Composer/Plugin/Scaffold/Fixtures.php',
-];
-$ignoreErrors[] = [
- 'message' => '#^Method Drupal\\\\Tests\\\\Composer\\\\Plugin\\\\Scaffold\\\\Fixtures\\:\\:tearDown\\(\\) has no return type specified\\.$#',
- 'identifier' => 'missingType.return',
- 'count' => 1,
- 'path' => __DIR__ . '/tests/Drupal/Tests/Composer/Plugin/Scaffold/Fixtures.php',
-];
-$ignoreErrors[] = [
'message' => '#^Method Drupal\\\\Tests\\\\Composer\\\\Plugin\\\\Scaffold\\\\Functional\\\\ComposerHookTest\\:\\:assertScaffoldedFile\\(\\) has no return type specified\\.$#',
'identifier' => 'missingType.return',
'count' => 1,
diff --git a/core/assets/vendor/backbone/backbone-min.js b/core/assets/vendor/backbone/backbone-min.js
index 9bcce86e18fc..092b984feb47 100644
--- a/core/assets/vendor/backbone/backbone-min.js
+++ b/core/assets/vendor/backbone/backbone-min.js
@@ -1,2 +1,2 @@
-(function(r){var n=typeof self=="object"&&self.self===self&&self||typeof global=="object"&&global.global===global&&global;if(typeof define==="function"&&define.amd){define(["underscore","jquery","exports"],function(t,e,i){n.Backbone=r(n,i,t,e)})}else if(typeof exports!=="undefined"){var t=require("underscore"),e;try{e=require("jquery")}catch(t){}r(n,exports,t,e)}else{n.Backbone=r(n,{},n._,n.jQuery||n.Zepto||n.ender||n.$)}})(function(t,h,x,e){var i=t.Backbone;var a=Array.prototype.slice;h.VERSION="1.6.0";h.$=e;h.noConflict=function(){t.Backbone=i;return this};h.emulateHTTP=false;h.emulateJSON=false;var r=h.Events={};var o=/\s+/;var l;var u=function(t,e,i,r,n){var s=0,a;if(i&&typeof i==="object"){if(r!==void 0&&"context"in n&&n.context===void 0)n.context=r;for(a=x.keys(i);s<a.length;s++){e=u(t,e,a[s],i[a[s]],n)}}else if(i&&o.test(i)){for(a=i.split(o);s<a.length;s++){e=t(e,a[s],r,n)}}else{e=t(e,i,r,n)}return e};r.on=function(t,e,i){this._events=u(n,this._events||{},t,e,{context:i,ctx:this,listening:l});if(l){var r=this._listeners||(this._listeners={});r[l.id]=l;l.interop=false}return this};r.listenTo=function(t,e,i){if(!t)return this;var r=t._listenId||(t._listenId=x.uniqueId("l"));var n=this._listeningTo||(this._listeningTo={});var s=l=n[r];if(!s){this._listenId||(this._listenId=x.uniqueId("l"));s=l=n[r]=new p(this,t)}var a=c(t,e,i,this);l=void 0;if(a)throw a;if(s.interop)s.on(e,i);return this};var n=function(t,e,i,r){if(i){var n=t[e]||(t[e]=[]);var s=r.context,a=r.ctx,o=r.listening;if(o)o.count++;n.push({callback:i,context:s,ctx:s||a,listening:o})}return t};var c=function(t,e,i,r){try{t.on(e,i,r)}catch(t){return t}};r.off=function(t,e,i){if(!this._events)return this;this._events=u(s,this._events,t,e,{context:i,listeners:this._listeners});return this};r.stopListening=function(t,e,i){var r=this._listeningTo;if(!r)return this;var n=t?[t._listenId]:x.keys(r);for(var s=0;s<n.length;s++){var a=r[n[s]];if(!a)break;a.obj.off(e,i,this);if(a.interop)a.off(e,i)}if(x.isEmpty(r))this._listeningTo=void 0;return this};var s=function(t,e,i,r){if(!t)return;var n=r.context,s=r.listeners;var a=0,o;if(!e&&!n&&!i){for(o=x.keys(s);a<o.length;a++){s[o[a]].cleanup()}return}o=e?[e]:x.keys(t);for(;a<o.length;a++){e=o[a];var h=t[e];if(!h)break;var l=[];for(var u=0;u<h.length;u++){var c=h[u];if(i&&i!==c.callback&&i!==c.callback._callback||n&&n!==c.context){l.push(c)}else{var f=c.listening;if(f)f.off(e,i)}}if(l.length){t[e]=l}else{delete t[e]}}return t};r.once=function(t,e,i){var r=u(f,{},t,e,this.off.bind(this));if(typeof t==="string"&&i==null)e=void 0;return this.on(r,e,i)};r.listenToOnce=function(t,e,i){var r=u(f,{},e,i,this.stopListening.bind(this,t));return this.listenTo(t,r)};var f=function(t,e,i,r){if(i){var n=t[e]=x.once(function(){r(e,n);i.apply(this,arguments)});n._callback=i}return t};r.trigger=function(t){if(!this._events)return this;var e=Math.max(0,arguments.length-1);var i=Array(e);for(var r=0;r<e;r++)i[r]=arguments[r+1];u(d,this._events,t,void 0,i);return this};var d=function(t,e,i,r){if(t){var n=t[e];var s=t.all;if(n&&s)s=s.slice();if(n)v(n,r);if(s)v(s,[e].concat(r))}return t};var v=function(t,e){var i,r=-1,n=t.length,s=e[0],a=e[1],o=e[2];switch(e.length){case 0:while(++r<n)(i=t[r]).callback.call(i.ctx);return;case 1:while(++r<n)(i=t[r]).callback.call(i.ctx,s);return;case 2:while(++r<n)(i=t[r]).callback.call(i.ctx,s,a);return;case 3:while(++r<n)(i=t[r]).callback.call(i.ctx,s,a,o);return;default:while(++r<n)(i=t[r]).callback.apply(i.ctx,e);return}};var p=function(t,e){this.id=t._listenId;this.listener=t;this.obj=e;this.interop=true;this.count=0;this._events=void 0};p.prototype.on=r.on;p.prototype.off=function(t,e){var i;if(this.interop){this._events=u(s,this._events,t,e,{context:void 0,listeners:void 0});i=!this._events}else{this.count--;i=this.count===0}if(i)this.cleanup()};p.prototype.cleanup=function(){delete this.listener._listeningTo[this.obj._listenId];if(!this.interop)delete this.obj._listeners[this.id]};r.bind=r.on;r.unbind=r.off;x.extend(h,r);var g=h.Model=function(t,e){var i=t||{};e||(e={});this.preinitialize.apply(this,arguments);this.cid=x.uniqueId(this.cidPrefix);this.attributes={};if(e.collection)this.collection=e.collection;if(e.parse)i=this.parse(i,e)||{};var r=x.result(this,"defaults");i=x.defaults(x.extend({},r,i),r);this.set(i,e);this.changed={};this.initialize.apply(this,arguments)};x.extend(g.prototype,r,{changed:null,validationError:null,idAttribute:"id",cidPrefix:"c",preinitialize:function(){},initialize:function(){},toJSON:function(t){return x.clone(this.attributes)},sync:function(){return h.sync.apply(this,arguments)},get:function(t){return this.attributes[t]},escape:function(t){return x.escape(this.get(t))},has:function(t){return this.get(t)!=null},matches:function(t){return!!x.iteratee(t,this)(this.attributes)},set:function(t,e,i){if(t==null)return this;var r;if(typeof t==="object"){r=t;i=e}else{(r={})[t]=e}i||(i={});if(!this._validate(r,i))return false;var n=i.unset;var s=i.silent;var a=[];var o=this._changing;this._changing=true;if(!o){this._previousAttributes=x.clone(this.attributes);this.changed={}}var h=this.attributes;var l=this.changed;var u=this._previousAttributes;for(var c in r){e=r[c];if(!x.isEqual(h[c],e))a.push(c);if(!x.isEqual(u[c],e)){l[c]=e}else{delete l[c]}n?delete h[c]:h[c]=e}if(this.idAttribute in r){var f=this.id;this.id=this.get(this.idAttribute);this.trigger("changeId",this,f,i)}if(!s){if(a.length)this._pending=i;for(var d=0;d<a.length;d++){this.trigger("change:"+a[d],this,h[a[d]],i)}}if(o)return this;if(!s){while(this._pending){i=this._pending;this._pending=false;this.trigger("change",this,i)}}this._pending=false;this._changing=false;return this},unset:function(t,e){return this.set(t,void 0,x.extend({},e,{unset:true}))},clear:function(t){var e={};for(var i in this.attributes)e[i]=void 0;return this.set(e,x.extend({},t,{unset:true}))},hasChanged:function(t){if(t==null)return!x.isEmpty(this.changed);return x.has(this.changed,t)},changedAttributes:function(t){if(!t)return this.hasChanged()?x.clone(this.changed):false;var e=this._changing?this._previousAttributes:this.attributes;var i={};var r;for(var n in t){var s=t[n];if(x.isEqual(e[n],s))continue;i[n]=s;r=true}return r?i:false},previous:function(t){if(t==null||!this._previousAttributes)return null;return this._previousAttributes[t]},previousAttributes:function(){return x.clone(this._previousAttributes)},fetch:function(i){i=x.extend({parse:true},i);var r=this;var n=i.success;i.success=function(t){var e=i.parse?r.parse(t,i):t;if(!r.set(e,i))return false;if(n)n.call(i.context,r,t,i);r.trigger("sync",r,t,i)};G(this,i);return this.sync("read",this,i)},save:function(t,e,i){var r;if(t==null||typeof t==="object"){r=t;i=e}else{(r={})[t]=e}i=x.extend({validate:true,parse:true},i);var n=i.wait;if(r&&!n){if(!this.set(r,i))return false}else if(!this._validate(r,i)){return false}var s=this;var a=i.success;var o=this.attributes;i.success=function(t){s.attributes=o;var e=i.parse?s.parse(t,i):t;if(n)e=x.extend({},r,e);if(e&&!s.set(e,i))return false;if(a)a.call(i.context,s,t,i);s.trigger("sync",s,t,i)};G(this,i);if(r&&n)this.attributes=x.extend({},o,r);var h=this.isNew()?"create":i.patch?"patch":"update";if(h==="patch"&&!i.attrs)i.attrs=r;var l=this.sync(h,this,i);this.attributes=o;return l},destroy:function(e){e=e?x.clone(e):{};var i=this;var r=e.success;var n=e.wait;var s=function(){i.stopListening();i.trigger("destroy",i,i.collection,e)};e.success=function(t){if(n)s();if(r)r.call(e.context,i,t,e);if(!i.isNew())i.trigger("sync",i,t,e)};var t=false;if(this.isNew()){x.defer(e.success)}else{G(this,e);t=this.sync("delete",this,e)}if(!n)s();return t},url:function(){var t=x.result(this,"urlRoot")||x.result(this.collection,"url")||V();if(this.isNew())return t;var e=this.get(this.idAttribute);return t.replace(/[^\/]$/,"$&/")+encodeURIComponent(e)},parse:function(t,e){return t},clone:function(){return new this.constructor(this.attributes)},isNew:function(){return!this.has(this.idAttribute)},isValid:function(t){return this._validate({},x.extend({},t,{validate:true}))},_validate:function(t,e){if(!e.validate||!this.validate)return true;t=x.extend({},this.attributes,t);var i=this.validationError=this.validate(t,e)||null;if(!i)return true;this.trigger("invalid",this,i,x.extend(e,{validationError:i}));return false}});var m=h.Collection=function(t,e){e||(e={});this.preinitialize.apply(this,arguments);if(e.model)this.model=e.model;if(e.comparator!==void 0)this.comparator=e.comparator;this._reset();this.initialize.apply(this,arguments);if(t)this.reset(t,x.extend({silent:true},e))};var w={add:true,remove:true,merge:true};var _={add:true,remove:false};var E=function(t,e,i){i=Math.min(Math.max(i,0),t.length);var r=Array(t.length-i);var n=e.length;var s;for(s=0;s<r.length;s++)r[s]=t[s+i];for(s=0;s<n;s++)t[s+i]=e[s];for(s=0;s<r.length;s++)t[s+n+i]=r[s]};x.extend(m.prototype,r,{model:g,preinitialize:function(){},initialize:function(){},toJSON:function(e){return this.map(function(t){return t.toJSON(e)})},sync:function(){return h.sync.apply(this,arguments)},add:function(t,e){return this.set(t,x.extend({merge:false},e,_))},remove:function(t,e){e=x.extend({},e);var i=!x.isArray(t);t=i?[t]:t.slice();var r=this._removeModels(t,e);if(!e.silent&&r.length){e.changes={added:[],merged:[],removed:r};this.trigger("update",this,e)}return i?r[0]:r},set:function(t,e){if(t==null)return;e=x.extend({},w,e);if(e.parse&&!this._isModel(t)){t=this.parse(t,e)||[]}var i=!x.isArray(t);t=i?[t]:t.slice();var r=e.at;if(r!=null)r=+r;if(r>this.length)r=this.length;if(r<0)r+=this.length+1;var n=[];var s=[];var a=[];var o=[];var h={};var l=e.add;var u=e.merge;var c=e.remove;var f=false;var d=this.comparator&&r==null&&e.sort!==false;var v=x.isString(this.comparator)?this.comparator:null;var p,g;for(g=0;g<t.length;g++){p=t[g];var m=this.get(p);if(m){if(u&&p!==m){var _=this._isModel(p)?p.attributes:p;if(e.parse)_=m.parse(_,e);m.set(_,e);a.push(m);if(d&&!f)f=m.hasChanged(v)}if(!h[m.cid]){h[m.cid]=true;n.push(m)}t[g]=m}else if(l){p=t[g]=this._prepareModel(p,e);if(p){s.push(p);this._addReference(p,e);h[p.cid]=true;n.push(p)}}}if(c){for(g=0;g<this.length;g++){p=this.models[g];if(!h[p.cid])o.push(p)}if(o.length)this._removeModels(o,e)}var y=false;var b=!d&&l&&c;if(n.length&&b){y=this.length!==n.length||x.some(this.models,function(t,e){return t!==n[e]});this.models.length=0;E(this.models,n,0);this.length=this.models.length}else if(s.length){if(d)f=true;E(this.models,s,r==null?this.length:r);this.length=this.models.length}if(f)this.sort({silent:true});if(!e.silent){for(g=0;g<s.length;g++){if(r!=null)e.index=r+g;p=s[g];p.trigger("add",p,this,e)}if(f||y)this.trigger("sort",this,e);if(s.length||o.length||a.length){e.changes={added:s,removed:o,merged:a};this.trigger("update",this,e)}}return i?t[0]:t},reset:function(t,e){e=e?x.clone(e):{};for(var i=0;i<this.models.length;i++){this._removeReference(this.models[i],e)}e.previousModels=this.models;this._reset();t=this.add(t,x.extend({silent:true},e));if(!e.silent)this.trigger("reset",this,e);return t},push:function(t,e){return this.add(t,x.extend({at:this.length},e))},pop:function(t){var e=this.at(this.length-1);return this.remove(e,t)},unshift:function(t,e){return this.add(t,x.extend({at:0},e))},shift:function(t){var e=this.at(0);return this.remove(e,t)},slice:function(){return a.apply(this.models,arguments)},get:function(t){if(t==null)return void 0;return this._byId[t]||this._byId[this.modelId(this._isModel(t)?t.attributes:t,t.idAttribute)]||t.cid&&this._byId[t.cid]},has:function(t){return this.get(t)!=null},at:function(t){if(t<0)t+=this.length;return this.models[t]},where:function(t,e){return this[e?"find":"filter"](t)},findWhere:function(t){return this.where(t,true)},sort:function(t){var e=this.comparator;if(!e)throw new Error("Cannot sort a set without a comparator");t||(t={});var i=e.length;if(x.isFunction(e))e=e.bind(this);if(i===1||x.isString(e)){this.models=this.sortBy(e)}else{this.models.sort(e)}if(!t.silent)this.trigger("sort",this,t);return this},pluck:function(t){return this.map(t+"")},fetch:function(i){i=x.extend({parse:true},i);var r=i.success;var n=this;i.success=function(t){var e=i.reset?"reset":"set";n[e](t,i);if(r)r.call(i.context,n,t,i);n.trigger("sync",n,t,i)};G(this,i);return this.sync("read",this,i)},create:function(t,e){e=e?x.clone(e):{};var r=e.wait;t=this._prepareModel(t,e);if(!t)return false;if(!r)this.add(t,e);var n=this;var s=e.success;e.success=function(t,e,i){if(r){t.off("error",n._forwardPristineError,n);n.add(t,i)}if(s)s.call(i.context,t,e,i)};if(r){t.once("error",this._forwardPristineError,this)}t.save(null,e);return t},parse:function(t,e){return t},clone:function(){return new this.constructor(this.models,{model:this.model,comparator:this.comparator})},modelId:function(t,e){return t[e||this.model.prototype.idAttribute||"id"]},values:function(){return new b(this,S)},keys:function(){return new b(this,I)},entries:function(){return new b(this,k)},_reset:function(){this.length=0;this.models=[];this._byId={}},_prepareModel:function(t,e){if(this._isModel(t)){if(!t.collection)t.collection=this;return t}e=e?x.clone(e):{};e.collection=this;var i;if(this.model.prototype){i=new this.model(t,e)}else{i=this.model(t,e)}if(!i.validationError)return i;this.trigger("invalid",this,i.validationError,e);return false},_removeModels:function(t,e){var i=[];for(var r=0;r<t.length;r++){var n=this.get(t[r]);if(!n)continue;var s=this.indexOf(n);this.models.splice(s,1);this.length--;delete this._byId[n.cid];var a=this.modelId(n.attributes,n.idAttribute);if(a!=null)delete this._byId[a];if(!e.silent){e.index=s;n.trigger("remove",n,this,e)}i.push(n);this._removeReference(n,e)}if(t.length>0&&!e.silent)delete e.index;return i},_isModel:function(t){return t instanceof g},_addReference:function(t,e){this._byId[t.cid]=t;var i=this.modelId(t.attributes,t.idAttribute);if(i!=null)this._byId[i]=t;t.on("all",this._onModelEvent,this)},_removeReference:function(t,e){delete this._byId[t.cid];var i=this.modelId(t.attributes,t.idAttribute);if(i!=null)delete this._byId[i];if(this===t.collection)delete t.collection;t.off("all",this._onModelEvent,this)},_onModelEvent:function(t,e,i,r){if(e){if((t==="add"||t==="remove")&&i!==this)return;if(t==="destroy")this.remove(e,r);if(t==="changeId"){var n=this.modelId(e.previousAttributes(),e.idAttribute);var s=this.modelId(e.attributes,e.idAttribute);if(n!=null)delete this._byId[n];if(s!=null)this._byId[s]=e}}this.trigger.apply(this,arguments)},_forwardPristineError:function(t,e,i){if(this.has(t))return;this._onModelEvent("error",t,e,i)}});var y=typeof Symbol==="function"&&Symbol.iterator;if(y){m.prototype[y]=m.prototype.values}var b=function(t,e){this._collection=t;this._kind=e;this._index=0};var S=1;var I=2;var k=3;if(y){b.prototype[y]=function(){return this}}b.prototype.next=function(){if(this._collection){if(this._index<this._collection.length){var t=this._collection.at(this._index);this._index++;var e;if(this._kind===S){e=t}else{var i=this._collection.modelId(t.attributes,t.idAttribute);if(this._kind===I){e=i}else{e=[i,t]}}return{value:e,done:false}}this._collection=void 0}return{value:void 0,done:true}};var A=h.View=function(t){this.cid=x.uniqueId("view");this.preinitialize.apply(this,arguments);x.extend(this,x.pick(t,T));this._ensureElement();this.initialize.apply(this,arguments)};var P=/^(\S+)\s*(.*)$/;var T=["model","collection","el","id","attributes","className","tagName","events"];x.extend(A.prototype,r,{tagName:"div",$:function(t){return this.$el.find(t)},preinitialize:function(){},initialize:function(){},render:function(){return this},remove:function(){this._removeElement();this.stopListening();return this},_removeElement:function(){this.$el.remove()},setElement:function(t){this.undelegateEvents();this._setElement(t);this.delegateEvents();return this},_setElement:function(t){this.$el=t instanceof h.$?t:h.$(t);this.el=this.$el[0]},delegateEvents:function(t){t||(t=x.result(this,"events"));if(!t)return this;this.undelegateEvents();for(var e in t){var i=t[e];if(!x.isFunction(i))i=this[i];if(!i)continue;var r=e.match(P);this.delegate(r[1],r[2],i.bind(this))}return this},delegate:function(t,e,i){this.$el.on(t+".delegateEvents"+this.cid,e,i);return this},undelegateEvents:function(){if(this.$el)this.$el.off(".delegateEvents"+this.cid);return this},undelegate:function(t,e,i){this.$el.off(t+".delegateEvents"+this.cid,e,i);return this},_createElement:function(t){return document.createElement(t)},_ensureElement:function(){if(!this.el){var t=x.extend({},x.result(this,"attributes"));if(this.id)t.id=x.result(this,"id");if(this.className)t["class"]=x.result(this,"className");this.setElement(this._createElement(x.result(this,"tagName")));this._setAttributes(t)}else{this.setElement(x.result(this,"el"))}},_setAttributes:function(t){this.$el.attr(t)}});var H=function(r,t,n,s){switch(t){case 1:return function(){return r[n](this[s])};case 2:return function(t){return r[n](this[s],t)};case 3:return function(t,e){return r[n](this[s],C(t,this),e)};case 4:return function(t,e,i){return r[n](this[s],C(t,this),e,i)};default:return function(){var t=a.call(arguments);t.unshift(this[s]);return r[n].apply(r,t)}}};var $=function(i,r,t,n){x.each(t,function(t,e){if(r[e])i.prototype[e]=H(r,t,e,n)})};var C=function(e,t){if(x.isFunction(e))return e;if(x.isObject(e)&&!t._isModel(e))return R(e);if(x.isString(e))return function(t){return t.get(e)};return e};var R=function(t){var e=x.matches(t);return function(t){return e(t.attributes)}};var M={forEach:3,each:3,map:3,collect:3,reduce:0,foldl:0,inject:0,reduceRight:0,foldr:0,find:3,detect:3,filter:3,select:3,reject:3,every:3,all:3,some:3,any:3,include:3,includes:3,contains:3,invoke:0,max:3,min:3,toArray:1,size:1,first:3,head:3,take:3,initial:3,rest:3,tail:3,drop:3,last:3,without:0,difference:0,indexOf:3,shuffle:1,lastIndexOf:3,isEmpty:1,chain:1,sample:3,partition:3,groupBy:3,countBy:3,sortBy:3,indexBy:3,findIndex:3,findLastIndex:3};var N={keys:1,values:1,pairs:1,invert:1,pick:0,omit:0,chain:1,isEmpty:1};x.each([[m,M,"models"],[g,N,"attributes"]],function(t){var i=t[0],e=t[1],r=t[2];i.mixin=function(t){var e=x.reduce(x.functions(t),function(t,e){t[e]=0;return t},{});$(i,t,e,r)};$(i,x,e,r)});h.sync=function(t,e,r){var i=j[t];x.defaults(r||(r={}),{emulateHTTP:h.emulateHTTP,emulateJSON:h.emulateJSON});var n={type:i,dataType:"json"};if(!r.url){n.url=x.result(e,"url")||V()}if(r.data==null&&e&&(t==="create"||t==="update"||t==="patch")){n.contentType="application/json";n.data=JSON.stringify(r.attrs||e.toJSON(r))}if(r.emulateJSON){n.contentType="application/x-www-form-urlencoded";n.data=n.data?{model:n.data}:{}}if(r.emulateHTTP&&(i==="PUT"||i==="DELETE"||i==="PATCH")){n.type="POST";if(r.emulateJSON)n.data._method=i;var s=r.beforeSend;r.beforeSend=function(t){t.setRequestHeader("X-HTTP-Method-Override",i);if(s)return s.apply(this,arguments)}}if(n.type!=="GET"&&!r.emulateJSON){n.processData=false}var a=r.error;r.error=function(t,e,i){r.textStatus=e;r.errorThrown=i;if(a)a.call(r.context,t,e,i)};var o=r.xhr=h.ajax(x.extend(n,r));e.trigger("request",e,o,r);return o};var j={create:"POST",update:"PUT",patch:"PATCH",delete:"DELETE",read:"GET"};h.ajax=function(){return h.$.ajax.apply(h.$,arguments)};var O=h.Router=function(t){t||(t={});this.preinitialize.apply(this,arguments);if(t.routes)this.routes=t.routes;this._bindRoutes();this.initialize.apply(this,arguments)};var U=/\((.*?)\)/g;var z=/(\(\?)?:\w+/g;var q=/\*\w+/g;var F=/[\-{}\[\]+?.,\\\^$|#\s]/g;x.extend(O.prototype,r,{preinitialize:function(){},initialize:function(){},route:function(i,r,n){if(!x.isRegExp(i))i=this._routeToRegExp(i);if(x.isFunction(r)){n=r;r=""}if(!n)n=this[r];var s=this;h.history.route(i,function(t){var e=s._extractParameters(i,t);if(s.execute(n,e,r)!==false){s.trigger.apply(s,["route:"+r].concat(e));s.trigger("route",r,e);h.history.trigger("route",s,r,e)}});return this},execute:function(t,e,i){if(t)t.apply(this,e)},navigate:function(t,e){h.history.navigate(t,e);return this},_bindRoutes:function(){if(!this.routes)return;this.routes=x.result(this,"routes");var t,e=x.keys(this.routes);while((t=e.pop())!=null){this.route(t,this.routes[t])}},_routeToRegExp:function(t){t=t.replace(F,"\\$&").replace(U,"(?:$1)?").replace(z,function(t,e){return e?t:"([^/?]+)"}).replace(q,"([^?]*?)");return new RegExp("^"+t+"(?:\\?([\\s\\S]*))?$")},_extractParameters:function(t,e){var i=t.exec(e).slice(1);return x.map(i,function(t,e){if(e===i.length-1)return t||null;return t?decodeURIComponent(t):null})}});var B=h.History=function(){this.handlers=[];this.checkUrl=this.checkUrl.bind(this);if(typeof window!=="undefined"){this.location=window.location;this.history=window.history}};var J=/^[#\/]|\s+$/g;var L=/^\/+|\/+$/g;var W=/#.*$/;B.started=false;x.extend(B.prototype,r,{interval:50,atRoot:function(){var t=this.location.pathname.replace(/[^\/]$/,"$&/");return t===this.root&&!this.getSearch()},matchRoot:function(){var t=this.decodeFragment(this.location.pathname);var e=t.slice(0,this.root.length-1)+"/";return e===this.root},decodeFragment:function(t){return decodeURI(t.replace(/%25/g,"%2525"))},getSearch:function(){var t=this.location.href.replace(/#.*/,"").match(/\?.+/);return t?t[0]:""},getHash:function(t){var e=(t||this).location.href.match(/#(.*)$/);return e?e[1]:""},getPath:function(){var t=this.decodeFragment(this.location.pathname+this.getSearch()).slice(this.root.length-1);return t.charAt(0)==="/"?t.slice(1):t},getFragment:function(t){if(t==null){if(this._usePushState||!this._wantsHashChange){t=this.getPath()}else{t=this.getHash()}}return t.replace(J,"")},start:function(t){if(B.started)throw new Error("Backbone.history has already been started");B.started=true;this.options=x.extend({root:"/"},this.options,t);this.root=this.options.root;this._trailingSlash=this.options.trailingSlash;this._wantsHashChange=this.options.hashChange!==false;this._hasHashChange="onhashchange"in window&&(document.documentMode===void 0||document.documentMode>7);this._useHashChange=this._wantsHashChange&&this._hasHashChange;this._wantsPushState=!!this.options.pushState;this._hasPushState=!!(this.history&&this.history.pushState);this._usePushState=this._wantsPushState&&this._hasPushState;this.fragment=this.getFragment();this.root=("/"+this.root+"/").replace(L,"/");if(this._wantsHashChange&&this._wantsPushState){if(!this._hasPushState&&!this.atRoot()){var e=this.root.slice(0,-1)||"/";this.location.replace(e+"#"+this.getPath());return true}else if(this._hasPushState&&this.atRoot()){this.navigate(this.getHash(),{replace:true})}}if(!this._hasHashChange&&this._wantsHashChange&&!this._usePushState){this.iframe=document.createElement("iframe");this.iframe.src="javascript:0";this.iframe.style.display="none";this.iframe.tabIndex=-1;var i=document.body;var r=i.insertBefore(this.iframe,i.firstChild).contentWindow;r.document.open();r.document.close();r.location.hash="#"+this.fragment}var n=window.addEventListener||function(t,e){return attachEvent("on"+t,e)};if(this._usePushState){n("popstate",this.checkUrl,false)}else if(this._useHashChange&&!this.iframe){n("hashchange",this.checkUrl,false)}else if(this._wantsHashChange){this._checkUrlInterval=setInterval(this.checkUrl,this.interval)}if(!this.options.silent)return this.loadUrl()},stop:function(){var t=window.removeEventListener||function(t,e){return detachEvent("on"+t,e)};if(this._usePushState){t("popstate",this.checkUrl,false)}else if(this._useHashChange&&!this.iframe){t("hashchange",this.checkUrl,false)}if(this.iframe){document.body.removeChild(this.iframe);this.iframe=null}if(this._checkUrlInterval)clearInterval(this._checkUrlInterval);B.started=false},route:function(t,e){this.handlers.unshift({route:t,callback:e})},checkUrl:function(t){var e=this.getFragment();if(e===this.fragment&&this.iframe){e=this.getHash(this.iframe.contentWindow)}if(e===this.fragment){if(!this.matchRoot())return this.notfound();return false}if(this.iframe)this.navigate(e);this.loadUrl()},loadUrl:function(e){if(!this.matchRoot())return this.notfound();e=this.fragment=this.getFragment(e);return x.some(this.handlers,function(t){if(t.route.test(e)){t.callback(e);return true}})||this.notfound()},notfound:function(){this.trigger("notfound");return false},navigate:function(t,e){if(!B.started)return false;if(!e||e===true)e={trigger:!!e};t=this.getFragment(t||"");var i=this.root;if(!this._trailingSlash&&(t===""||t.charAt(0)==="?")){i=i.slice(0,-1)||"/"}var r=i+t;t=t.replace(W,"");var n=this.decodeFragment(t);if(this.fragment===n)return;this.fragment=n;if(this._usePushState){this.history[e.replace?"replaceState":"pushState"]({},document.title,r)}else if(this._wantsHashChange){this._updateHash(this.location,t,e.replace);if(this.iframe&&t!==this.getHash(this.iframe.contentWindow)){var s=this.iframe.contentWindow;if(!e.replace){s.document.open();s.document.close()}this._updateHash(s.location,t,e.replace)}}else{return this.location.assign(r)}if(e.trigger)return this.loadUrl(t)},_updateHash:function(t,e,i){if(i){var r=t.href.replace(/(javascript:|#).*$/,"");t.replace(r+"#"+e)}else{t.hash="#"+e}}});h.history=new B;var D=function(t,e){var i=this;var r;if(t&&x.has(t,"constructor")){r=t.constructor}else{r=function(){return i.apply(this,arguments)}}x.extend(r,i,e);r.prototype=x.create(i.prototype,t);r.prototype.constructor=r;r.__super__=i.prototype;return r};g.extend=m.extend=O.extend=A.extend=B.extend=D;var V=function(){throw new Error('A "url" property or function must be specified')};var G=function(e,i){var r=i.error;i.error=function(t){if(r)r.call(i.context,e,t,i);e.trigger("error",e,t,i)}};h._debug=function(){return{root:t,_:x}};return h});
+(function(r){var n=typeof self=="object"&&self.self===self&&self||typeof global=="object"&&global.global===global&&global;if(typeof define==="function"&&define.amd){define(["underscore","jquery","exports"],function(t,e,i){n.Backbone=r(n,i,t,e)})}else if(typeof exports!=="undefined"){var t=require("underscore"),e;try{e=require("jquery")}catch(t){}r(n,exports,t,e)}else{n.Backbone=r(n,{},n._,n.jQuery||n.Zepto||n.ender||n.$)}})(function(t,h,x,e){var i=t.Backbone;var a=Array.prototype.slice;h.VERSION="1.6.1";h.$=e;h.noConflict=function(){t.Backbone=i;return this};h.emulateHTTP=false;h.emulateJSON=false;var r=h.Events={};var o=/\s+/;var l;var u=function(t,e,i,r,n){var s=0,a;if(i&&typeof i==="object"){if(r!==void 0&&"context"in n&&n.context===void 0)n.context=r;for(a=x.keys(i);s<a.length;s++){e=u(t,e,a[s],i[a[s]],n)}}else if(i&&o.test(i)){for(a=i.split(o);s<a.length;s++){e=t(e,a[s],r,n)}}else{e=t(e,i,r,n)}return e};r.on=function(t,e,i){this._events=u(n,this._events||{},t,e,{context:i,ctx:this,listening:l});if(l){var r=this._listeners||(this._listeners={});r[l.id]=l;l.interop=false}return this};r.listenTo=function(t,e,i){if(!t)return this;var r=t._listenId||(t._listenId=x.uniqueId("l"));var n=this._listeningTo||(this._listeningTo={});var s=l=n[r];if(!s){this._listenId||(this._listenId=x.uniqueId("l"));s=l=n[r]=new p(this,t)}var a=c(t,e,i,this);l=void 0;if(a)throw a;if(s.interop)s.on(e,i);return this};var n=function(t,e,i,r){if(i){var n=t[e]||(t[e]=[]);var s=r.context,a=r.ctx,o=r.listening;if(o)o.count++;n.push({callback:i,context:s,ctx:s||a,listening:o})}return t};var c=function(t,e,i,r){try{t.on(e,i,r)}catch(t){return t}};r.off=function(t,e,i){if(!this._events)return this;this._events=u(s,this._events,t,e,{context:i,listeners:this._listeners});return this};r.stopListening=function(t,e,i){var r=this._listeningTo;if(!r)return this;var n=t?[t._listenId]:x.keys(r);for(var s=0;s<n.length;s++){var a=r[n[s]];if(!a)break;a.obj.off(e,i,this);if(a.interop)a.off(e,i)}if(x.isEmpty(r))this._listeningTo=void 0;return this};var s=function(t,e,i,r){if(!t)return;var n=r.context,s=r.listeners;var a=0,o;if(!e&&!n&&!i){for(o=x.keys(s);a<o.length;a++){s[o[a]].cleanup()}return}o=e?[e]:x.keys(t);for(;a<o.length;a++){e=o[a];var h=t[e];if(!h)break;var l=[];for(var u=0;u<h.length;u++){var c=h[u];if(i&&i!==c.callback&&i!==c.callback._callback||n&&n!==c.context){l.push(c)}else{var f=c.listening;if(f)f.off(e,i)}}if(l.length){t[e]=l}else{delete t[e]}}return t};r.once=function(t,e,i){var r=u(f,{},t,e,this.off.bind(this));if(typeof t==="string"&&i==null)e=void 0;return this.on(r,e,i)};r.listenToOnce=function(t,e,i){var r=u(f,{},e,i,this.stopListening.bind(this,t));return this.listenTo(t,r)};var f=function(t,e,i,r){if(i){var n=t[e]=x.once(function(){r(e,n);i.apply(this,arguments)});n._callback=i}return t};r.trigger=function(t){if(!this._events)return this;var e=Math.max(0,arguments.length-1);var i=Array(e);for(var r=0;r<e;r++)i[r]=arguments[r+1];u(d,this._events,t,void 0,i);return this};var d=function(t,e,i,r){if(t){var n=t[e];var s=t.all;if(n&&s)s=s.slice();if(n)v(n,r);if(s)v(s,[e].concat(r))}return t};var v=function(t,e){var i,r=-1,n=t.length,s=e[0],a=e[1],o=e[2];switch(e.length){case 0:while(++r<n)(i=t[r]).callback.call(i.ctx);return;case 1:while(++r<n)(i=t[r]).callback.call(i.ctx,s);return;case 2:while(++r<n)(i=t[r]).callback.call(i.ctx,s,a);return;case 3:while(++r<n)(i=t[r]).callback.call(i.ctx,s,a,o);return;default:while(++r<n)(i=t[r]).callback.apply(i.ctx,e);return}};var p=function(t,e){this.id=t._listenId;this.listener=t;this.obj=e;this.interop=true;this.count=0;this._events=void 0};p.prototype.on=r.on;p.prototype.off=function(t,e){var i;if(this.interop){this._events=u(s,this._events,t,e,{context:void 0,listeners:void 0});i=!this._events}else{this.count--;i=this.count===0}if(i)this.cleanup()};p.prototype.cleanup=function(){delete this.listener._listeningTo[this.obj._listenId];if(!this.interop)delete this.obj._listeners[this.id]};r.bind=r.on;r.unbind=r.off;x.extend(h,r);var g=h.Model=function(t,e){var i=t||{};e||(e={});this.preinitialize.apply(this,arguments);this.cid=x.uniqueId(this.cidPrefix);this.attributes={};if(e.collection)this.collection=e.collection;if(e.parse)i=this.parse(i,e)||{};var r=x.result(this,"defaults");i=x.defaults(x.extend({},r,i),r);this.set(i,e);this.changed={};this.initialize.apply(this,arguments)};x.extend(g.prototype,r,{changed:null,validationError:null,idAttribute:"id",cidPrefix:"c",preinitialize:function(){},initialize:function(){},toJSON:function(t){return x.clone(this.attributes)},sync:function(){return h.sync.apply(this,arguments)},get:function(t){return this.attributes[t]},escape:function(t){return x.escape(this.get(t))},has:function(t){return this.get(t)!=null},matches:function(t){return!!x.iteratee(t,this)(this.attributes)},set:function(t,e,i){if(t==null)return this;var r;if(typeof t==="object"){r=t;i=e}else{(r={})[t]=e}i||(i={});if(!this._validate(r,i))return false;var n=i.unset;var s=i.silent;var a=[];var o=this._changing;this._changing=true;if(!o){this._previousAttributes=x.clone(this.attributes);this.changed={}}var h=this.attributes;var l=this.changed;var u=this._previousAttributes;for(var c in r){e=r[c];if(!x.isEqual(h[c],e))a.push(c);if(!x.isEqual(u[c],e)){l[c]=e}else{delete l[c]}n?delete h[c]:h[c]=e}if(this.idAttribute in r){var f=this.id;this.id=this.get(this.idAttribute);if(this.id!==f){this.trigger("changeId",this,f,i)}}if(!s){if(a.length)this._pending=i;for(var d=0;d<a.length;d++){this.trigger("change:"+a[d],this,h[a[d]],i)}}if(o)return this;if(!s){while(this._pending){i=this._pending;this._pending=false;this.trigger("change",this,i)}}this._pending=false;this._changing=false;return this},unset:function(t,e){return this.set(t,void 0,x.extend({},e,{unset:true}))},clear:function(t){var e={};for(var i in this.attributes)e[i]=void 0;return this.set(e,x.extend({},t,{unset:true}))},hasChanged:function(t){if(t==null)return!x.isEmpty(this.changed);return x.has(this.changed,t)},changedAttributes:function(t){if(!t)return this.hasChanged()?x.clone(this.changed):false;var e=this._changing?this._previousAttributes:this.attributes;var i={};var r;for(var n in t){var s=t[n];if(x.isEqual(e[n],s))continue;i[n]=s;r=true}return r?i:false},previous:function(t){if(t==null||!this._previousAttributes)return null;return this._previousAttributes[t]},previousAttributes:function(){return x.clone(this._previousAttributes)},fetch:function(i){i=x.extend({parse:true},i);var r=this;var n=i.success;i.success=function(t){var e=i.parse?r.parse(t,i):t;if(!r.set(e,i))return false;if(n)n.call(i.context,r,t,i);r.trigger("sync",r,t,i)};G(this,i);return this.sync("read",this,i)},save:function(t,e,i){var r;if(t==null||typeof t==="object"){r=t;i=e}else{(r={})[t]=e}i=x.extend({validate:true,parse:true},i);var n=i.wait;if(r&&!n){if(!this.set(r,i))return false}else if(!this._validate(r,i)){return false}var s=this;var a=i.success;var o=this.attributes;i.success=function(t){s.attributes=o;var e=i.parse?s.parse(t,i):t;if(n)e=x.extend({},r,e);if(e&&!s.set(e,i))return false;if(a)a.call(i.context,s,t,i);s.trigger("sync",s,t,i)};G(this,i);if(r&&n)this.attributes=x.extend({},o,r);var h=this.isNew()?"create":i.patch?"patch":"update";if(h==="patch"&&!i.attrs)i.attrs=r;var l=this.sync(h,this,i);this.attributes=o;return l},destroy:function(e){e=e?x.clone(e):{};var i=this;var r=e.success;var n=e.wait;var s=function(){i.stopListening();i.trigger("destroy",i,i.collection,e)};e.success=function(t){if(n)s();if(r)r.call(e.context,i,t,e);if(!i.isNew())i.trigger("sync",i,t,e)};var t=false;if(this.isNew()){x.defer(e.success)}else{G(this,e);t=this.sync("delete",this,e)}if(!n)s();return t},url:function(){var t=x.result(this,"urlRoot")||x.result(this.collection,"url")||V();if(this.isNew())return t;var e=this.get(this.idAttribute);return t.replace(/[^\/]$/,"$&/")+encodeURIComponent(e)},parse:function(t,e){return t},clone:function(){return new this.constructor(this.attributes)},isNew:function(){return!this.has(this.idAttribute)},isValid:function(t){return this._validate({},x.extend({},t,{validate:true}))},_validate:function(t,e){if(!e.validate||!this.validate)return true;t=x.extend({},this.attributes,t);var i=this.validationError=this.validate(t,e)||null;if(!i)return true;this.trigger("invalid",this,i,x.extend(e,{validationError:i}));return false}});var m=h.Collection=function(t,e){e||(e={});this.preinitialize.apply(this,arguments);if(e.model)this.model=e.model;if(e.comparator!==void 0)this.comparator=e.comparator;this._reset();this.initialize.apply(this,arguments);if(t)this.reset(t,x.extend({silent:true},e))};var w={add:true,remove:true,merge:true};var _={add:true,remove:false};var E=function(t,e,i){i=Math.min(Math.max(i,0),t.length);var r=Array(t.length-i);var n=e.length;var s;for(s=0;s<r.length;s++)r[s]=t[s+i];for(s=0;s<n;s++)t[s+i]=e[s];for(s=0;s<r.length;s++)t[s+n+i]=r[s]};x.extend(m.prototype,r,{model:g,preinitialize:function(){},initialize:function(){},toJSON:function(e){return this.map(function(t){return t.toJSON(e)})},sync:function(){return h.sync.apply(this,arguments)},add:function(t,e){return this.set(t,x.extend({merge:false},e,_))},remove:function(t,e){e=x.extend({},e);var i=!x.isArray(t);t=i?[t]:t.slice();var r=this._removeModels(t,e);if(!e.silent&&r.length){e.changes={added:[],merged:[],removed:r};this.trigger("update",this,e)}return i?r[0]:r},set:function(t,e){if(t==null)return;e=x.extend({},w,e);if(e.parse&&!this._isModel(t)){t=this.parse(t,e)||[]}var i=!x.isArray(t);t=i?[t]:t.slice();var r=e.at;if(r!=null)r=+r;if(r>this.length)r=this.length;if(r<0)r+=this.length+1;var n=[];var s=[];var a=[];var o=[];var h={};var l=e.add;var u=e.merge;var c=e.remove;var f=false;var d=this.comparator&&r==null&&e.sort!==false;var v=x.isString(this.comparator)?this.comparator:null;var p,g;for(g=0;g<t.length;g++){p=t[g];var m=this.get(p);if(m){if(u&&p!==m){var _=this._isModel(p)?p.attributes:p;if(e.parse)_=m.parse(_,e);m.set(_,e);a.push(m);if(d&&!f)f=m.hasChanged(v)}if(!h[m.cid]){h[m.cid]=true;n.push(m)}t[g]=m}else if(l){p=t[g]=this._prepareModel(p,e);if(p){s.push(p);this._addReference(p,e);h[p.cid]=true;n.push(p)}}}if(c){for(g=0;g<this.length;g++){p=this.models[g];if(!h[p.cid])o.push(p)}if(o.length)this._removeModels(o,e)}var y=false;var b=!d&&l&&c;if(n.length&&b){y=this.length!==n.length||x.some(this.models,function(t,e){return t!==n[e]});this.models.length=0;E(this.models,n,0);this.length=this.models.length}else if(s.length){if(d)f=true;E(this.models,s,r==null?this.length:r);this.length=this.models.length}if(f)this.sort({silent:true});if(!e.silent){for(g=0;g<s.length;g++){if(r!=null)e.index=r+g;p=s[g];p.trigger("add",p,this,e)}if(f||y)this.trigger("sort",this,e);if(s.length||o.length||a.length){e.changes={added:s,removed:o,merged:a};this.trigger("update",this,e)}}return i?t[0]:t},reset:function(t,e){e=e?x.clone(e):{};for(var i=0;i<this.models.length;i++){this._removeReference(this.models[i],e)}e.previousModels=this.models;this._reset();t=this.add(t,x.extend({silent:true},e));if(!e.silent)this.trigger("reset",this,e);return t},push:function(t,e){return this.add(t,x.extend({at:this.length},e))},pop:function(t){var e=this.at(this.length-1);return this.remove(e,t)},unshift:function(t,e){return this.add(t,x.extend({at:0},e))},shift:function(t){var e=this.at(0);return this.remove(e,t)},slice:function(){return a.apply(this.models,arguments)},get:function(t){if(t==null)return void 0;return this._byId[t]||this._byId[this.modelId(this._isModel(t)?t.attributes:t,t.idAttribute)]||t.cid&&this._byId[t.cid]},has:function(t){return this.get(t)!=null},at:function(t){if(t<0)t+=this.length;return this.models[t]},where:function(t,e){return this[e?"find":"filter"](t)},findWhere:function(t){return this.where(t,true)},sort:function(t){var e=this.comparator;if(!e)throw new Error("Cannot sort a set without a comparator");t||(t={});var i=e.length;if(x.isFunction(e))e=e.bind(this);if(i===1||x.isString(e)){this.models=this.sortBy(e)}else{this.models.sort(e)}if(!t.silent)this.trigger("sort",this,t);return this},pluck:function(t){return this.map(t+"")},fetch:function(i){i=x.extend({parse:true},i);var r=i.success;var n=this;i.success=function(t){var e=i.reset?"reset":"set";n[e](t,i);if(r)r.call(i.context,n,t,i);n.trigger("sync",n,t,i)};G(this,i);return this.sync("read",this,i)},create:function(t,e){e=e?x.clone(e):{};var r=e.wait;t=this._prepareModel(t,e);if(!t)return false;if(!r)this.add(t,e);var n=this;var s=e.success;e.success=function(t,e,i){if(r){t.off("error",n._forwardPristineError,n);n.add(t,i)}if(s)s.call(i.context,t,e,i)};if(r){t.once("error",this._forwardPristineError,this)}t.save(null,e);return t},parse:function(t,e){return t},clone:function(){return new this.constructor(this.models,{model:this.model,comparator:this.comparator})},modelId:function(t,e){return t[e||this.model.prototype.idAttribute||"id"]},values:function(){return new b(this,S)},keys:function(){return new b(this,I)},entries:function(){return new b(this,k)},_reset:function(){this.length=0;this.models=[];this._byId={}},_prepareModel:function(t,e){if(this._isModel(t)){if(!t.collection)t.collection=this;return t}e=e?x.clone(e):{};e.collection=this;var i;if(this.model.prototype){i=new this.model(t,e)}else{i=this.model(t,e)}if(!i.validationError)return i;this.trigger("invalid",this,i.validationError,e);return false},_removeModels:function(t,e){var i=[];for(var r=0;r<t.length;r++){var n=this.get(t[r]);if(!n)continue;var s=this.indexOf(n);this.models.splice(s,1);this.length--;delete this._byId[n.cid];var a=this.modelId(n.attributes,n.idAttribute);if(a!=null)delete this._byId[a];if(!e.silent){e.index=s;n.trigger("remove",n,this,e)}i.push(n);this._removeReference(n,e)}if(t.length>0&&!e.silent)delete e.index;return i},_isModel:function(t){return t instanceof g},_addReference:function(t,e){this._byId[t.cid]=t;var i=this.modelId(t.attributes,t.idAttribute);if(i!=null)this._byId[i]=t;t.on("all",this._onModelEvent,this)},_removeReference:function(t,e){delete this._byId[t.cid];var i=this.modelId(t.attributes,t.idAttribute);if(i!=null)delete this._byId[i];if(this===t.collection)delete t.collection;t.off("all",this._onModelEvent,this)},_onModelEvent:function(t,e,i,r){if(e){if((t==="add"||t==="remove")&&i!==this)return;if(t==="destroy")this.remove(e,r);if(t==="changeId"){var n=this.modelId(e.previousAttributes(),e.idAttribute);var s=this.modelId(e.attributes,e.idAttribute);if(n!=null)delete this._byId[n];if(s!=null)this._byId[s]=e}}this.trigger.apply(this,arguments)},_forwardPristineError:function(t,e,i){if(this.has(t))return;this._onModelEvent("error",t,e,i)}});var y=typeof Symbol==="function"&&Symbol.iterator;if(y){m.prototype[y]=m.prototype.values}var b=function(t,e){this._collection=t;this._kind=e;this._index=0};var S=1;var I=2;var k=3;if(y){b.prototype[y]=function(){return this}}b.prototype.next=function(){if(this._collection){if(this._index<this._collection.length){var t=this._collection.at(this._index);this._index++;var e;if(this._kind===S){e=t}else{var i=this._collection.modelId(t.attributes,t.idAttribute);if(this._kind===I){e=i}else{e=[i,t]}}return{value:e,done:false}}this._collection=void 0}return{value:void 0,done:true}};var A=h.View=function(t){this.cid=x.uniqueId("view");this.preinitialize.apply(this,arguments);x.extend(this,x.pick(t,T));this._ensureElement();this.initialize.apply(this,arguments)};var P=/^(\S+)\s*(.*)$/;var T=["model","collection","el","id","attributes","className","tagName","events"];x.extend(A.prototype,r,{tagName:"div",$:function(t){return this.$el.find(t)},preinitialize:function(){},initialize:function(){},render:function(){return this},remove:function(){this._removeElement();this.stopListening();return this},_removeElement:function(){this.$el.remove()},setElement:function(t){this.undelegateEvents();this._setElement(t);this.delegateEvents();return this},_setElement:function(t){this.$el=t instanceof h.$?t:h.$(t);this.el=this.$el[0]},delegateEvents:function(t){t||(t=x.result(this,"events"));if(!t)return this;this.undelegateEvents();for(var e in t){var i=t[e];if(!x.isFunction(i))i=this[i];if(!i)continue;var r=e.match(P);this.delegate(r[1],r[2],i.bind(this))}return this},delegate:function(t,e,i){this.$el.on(t+".delegateEvents"+this.cid,e,i);return this},undelegateEvents:function(){if(this.$el)this.$el.off(".delegateEvents"+this.cid);return this},undelegate:function(t,e,i){this.$el.off(t+".delegateEvents"+this.cid,e,i);return this},_createElement:function(t){return document.createElement(t)},_ensureElement:function(){if(!this.el){var t=x.extend({},x.result(this,"attributes"));if(this.id)t.id=x.result(this,"id");if(this.className)t["class"]=x.result(this,"className");this.setElement(this._createElement(x.result(this,"tagName")));this._setAttributes(t)}else{this.setElement(x.result(this,"el"))}},_setAttributes:function(t){this.$el.attr(t)}});var H=function(r,t,n,s){switch(t){case 1:return function(){return r[n](this[s])};case 2:return function(t){return r[n](this[s],t)};case 3:return function(t,e){return r[n](this[s],C(t,this),e)};case 4:return function(t,e,i){return r[n](this[s],C(t,this),e,i)};default:return function(){var t=a.call(arguments);t.unshift(this[s]);return r[n].apply(r,t)}}};var $=function(i,r,t,n){x.each(t,function(t,e){if(r[e])i.prototype[e]=H(r,t,e,n)})};var C=function(e,t){if(x.isFunction(e))return e;if(x.isObject(e)&&!t._isModel(e))return R(e);if(x.isString(e))return function(t){return t.get(e)};return e};var R=function(t){var e=x.matches(t);return function(t){return e(t.attributes)}};var M={forEach:3,each:3,map:3,collect:3,reduce:0,foldl:0,inject:0,reduceRight:0,foldr:0,find:3,detect:3,filter:3,select:3,reject:3,every:3,all:3,some:3,any:3,include:3,includes:3,contains:3,invoke:0,max:3,min:3,toArray:1,size:1,first:3,head:3,take:3,initial:3,rest:3,tail:3,drop:3,last:3,without:0,difference:0,indexOf:3,shuffle:1,lastIndexOf:3,isEmpty:1,chain:1,sample:3,partition:3,groupBy:3,countBy:3,sortBy:3,indexBy:3,findIndex:3,findLastIndex:3};var N={keys:1,values:1,pairs:1,invert:1,pick:0,omit:0,chain:1,isEmpty:1};x.each([[m,M,"models"],[g,N,"attributes"]],function(t){var i=t[0],e=t[1],r=t[2];i.mixin=function(t){var e=x.reduce(x.functions(t),function(t,e){t[e]=0;return t},{});$(i,t,e,r)};$(i,x,e,r)});h.sync=function(t,e,r){var i=j[t];x.defaults(r||(r={}),{emulateHTTP:h.emulateHTTP,emulateJSON:h.emulateJSON});var n={type:i,dataType:"json"};if(!r.url){n.url=x.result(e,"url")||V()}if(r.data==null&&e&&(t==="create"||t==="update"||t==="patch")){n.contentType="application/json";n.data=JSON.stringify(r.attrs||e.toJSON(r))}if(r.emulateJSON){n.contentType="application/x-www-form-urlencoded";n.data=n.data?{model:n.data}:{}}if(r.emulateHTTP&&(i==="PUT"||i==="DELETE"||i==="PATCH")){n.type="POST";if(r.emulateJSON)n.data._method=i;var s=r.beforeSend;r.beforeSend=function(t){t.setRequestHeader("X-HTTP-Method-Override",i);if(s)return s.apply(this,arguments)}}if(n.type!=="GET"&&!r.emulateJSON){n.processData=false}var a=r.error;r.error=function(t,e,i){r.textStatus=e;r.errorThrown=i;if(a)a.call(r.context,t,e,i)};var o=r.xhr=h.ajax(x.extend(n,r));e.trigger("request",e,o,r);return o};var j={create:"POST",update:"PUT",patch:"PATCH",delete:"DELETE",read:"GET"};h.ajax=function(){return h.$.ajax.apply(h.$,arguments)};var O=h.Router=function(t){t||(t={});this.preinitialize.apply(this,arguments);if(t.routes)this.routes=t.routes;this._bindRoutes();this.initialize.apply(this,arguments)};var U=/\((.*?)\)/g;var z=/(\(\?)?:\w+/g;var q=/\*\w+/g;var F=/[\-{}\[\]+?.,\\\^$|#\s]/g;x.extend(O.prototype,r,{preinitialize:function(){},initialize:function(){},route:function(i,r,n){if(!x.isRegExp(i))i=this._routeToRegExp(i);if(x.isFunction(r)){n=r;r=""}if(!n)n=this[r];var s=this;h.history.route(i,function(t){var e=s._extractParameters(i,t);if(s.execute(n,e,r)!==false){s.trigger.apply(s,["route:"+r].concat(e));s.trigger("route",r,e);h.history.trigger("route",s,r,e)}});return this},execute:function(t,e,i){if(t)t.apply(this,e)},navigate:function(t,e){h.history.navigate(t,e);return this},_bindRoutes:function(){if(!this.routes)return;this.routes=x.result(this,"routes");var t,e=x.keys(this.routes);while((t=e.pop())!=null){this.route(t,this.routes[t])}},_routeToRegExp:function(t){t=t.replace(F,"\\$&").replace(U,"(?:$1)?").replace(z,function(t,e){return e?t:"([^/?]+)"}).replace(q,"([^?]*?)");return new RegExp("^"+t+"(?:\\?([\\s\\S]*))?$")},_extractParameters:function(t,e){var i=t.exec(e).slice(1);return x.map(i,function(t,e){if(e===i.length-1)return t||null;return t?decodeURIComponent(t):null})}});var B=h.History=function(){this.handlers=[];this.checkUrl=this.checkUrl.bind(this);if(typeof window!=="undefined"){this.location=window.location;this.history=window.history}};var J=/^[#\/]|\s+$/g;var L=/^\/+|\/+$/g;var W=/#.*$/;B.started=false;x.extend(B.prototype,r,{interval:50,atRoot:function(){var t=this.location.pathname.replace(/[^\/]$/,"$&/");return t===this.root&&!this.getSearch()},matchRoot:function(){var t=this.decodeFragment(this.location.pathname);var e=t.slice(0,this.root.length-1)+"/";return e===this.root},decodeFragment:function(t){return decodeURI(t.replace(/%25/g,"%2525"))},getSearch:function(){var t=this.location.href.replace(/#.*/,"").match(/\?.+/);return t?t[0]:""},getHash:function(t){var e=(t||this).location.href.match(/#(.*)$/);return e?e[1]:""},getPath:function(){var t=this.decodeFragment(this.location.pathname+this.getSearch()).slice(this.root.length-1);return t.charAt(0)==="/"?t.slice(1):t},getFragment:function(t){if(t==null){if(this._usePushState||!this._wantsHashChange){t=this.getPath()}else{t=this.getHash()}}return t.replace(J,"")},start:function(t){if(B.started)throw new Error("Backbone.history has already been started");B.started=true;this.options=x.extend({root:"/"},this.options,t);this.root=this.options.root;this._trailingSlash=this.options.trailingSlash;this._wantsHashChange=this.options.hashChange!==false;this._hasHashChange="onhashchange"in window&&(document.documentMode===void 0||document.documentMode>7);this._useHashChange=this._wantsHashChange&&this._hasHashChange;this._wantsPushState=!!this.options.pushState;this._hasPushState=!!(this.history&&this.history.pushState);this._usePushState=this._wantsPushState&&this._hasPushState;this.fragment=this.getFragment();this.root=("/"+this.root+"/").replace(L,"/");if(this._wantsHashChange&&this._wantsPushState){if(!this._hasPushState&&!this.atRoot()){var e=this.root.slice(0,-1)||"/";this.location.replace(e+"#"+this.getPath());return true}else if(this._hasPushState&&this.atRoot()){this.navigate(this.getHash(),{replace:true})}}if(!this._hasHashChange&&this._wantsHashChange&&!this._usePushState){this.iframe=document.createElement("iframe");this.iframe.src="javascript:0";this.iframe.style.display="none";this.iframe.tabIndex=-1;var i=document.body;var r=i.insertBefore(this.iframe,i.firstChild).contentWindow;r.document.open();r.document.close();r.location.hash="#"+this.fragment}var n=window.addEventListener||function(t,e){return attachEvent("on"+t,e)};if(this._usePushState){n("popstate",this.checkUrl,false)}else if(this._useHashChange&&!this.iframe){n("hashchange",this.checkUrl,false)}else if(this._wantsHashChange){this._checkUrlInterval=setInterval(this.checkUrl,this.interval)}if(!this.options.silent)return this.loadUrl()},stop:function(){var t=window.removeEventListener||function(t,e){return detachEvent("on"+t,e)};if(this._usePushState){t("popstate",this.checkUrl,false)}else if(this._useHashChange&&!this.iframe){t("hashchange",this.checkUrl,false)}if(this.iframe){document.body.removeChild(this.iframe);this.iframe=null}if(this._checkUrlInterval)clearInterval(this._checkUrlInterval);B.started=false},route:function(t,e){this.handlers.unshift({route:t,callback:e})},checkUrl:function(t){var e=this.getFragment();if(e===this.fragment&&this.iframe){e=this.getHash(this.iframe.contentWindow)}if(e===this.fragment){if(!this.matchRoot())return this.notfound();return false}if(this.iframe)this.navigate(e);this.loadUrl()},loadUrl:function(e){if(!this.matchRoot())return this.notfound();e=this.fragment=this.getFragment(e);return x.some(this.handlers,function(t){if(t.route.test(e)){t.callback(e);return true}})||this.notfound()},notfound:function(){this.trigger("notfound");return false},navigate:function(t,e){if(!B.started)return false;if(!e||e===true)e={trigger:!!e};t=this.getFragment(t||"");var i=this.root;if(!this._trailingSlash&&(t===""||t.charAt(0)==="?")){i=i.slice(0,-1)||"/"}var r=i+t;t=t.replace(W,"");var n=this.decodeFragment(t);if(this.fragment===n)return;this.fragment=n;if(this._usePushState){this.history[e.replace?"replaceState":"pushState"]({},document.title,r)}else if(this._wantsHashChange){this._updateHash(this.location,t,e.replace);if(this.iframe&&t!==this.getHash(this.iframe.contentWindow)){var s=this.iframe.contentWindow;if(!e.replace){s.document.open();s.document.close()}this._updateHash(s.location,t,e.replace)}}else{return this.location.assign(r)}if(e.trigger)return this.loadUrl(t)},_updateHash:function(t,e,i){if(i){var r=t.href.replace(/(javascript:|#).*$/,"");t.replace(r+"#"+e)}else{t.hash="#"+e}}});h.history=new B;var D=function(t,e){var i=this;var r;if(t&&x.has(t,"constructor")){r=t.constructor}else{r=function(){return i.apply(this,arguments)}}x.extend(r,i,e);r.prototype=x.create(i.prototype,t);r.prototype.constructor=r;r.__super__=i.prototype;return r};g.extend=m.extend=O.extend=A.extend=B.extend=D;var V=function(){throw new Error('A "url" property or function must be specified')};var G=function(e,i){var r=i.error;i.error=function(t){if(r)r.call(i.context,e,t,i);e.trigger("error",e,t,i)}};h._debug=function(){return{root:t,_:x}};return h});
//# sourceMappingURL=backbone-min.js.map \ No newline at end of file
diff --git a/core/assets/vendor/backbone/backbone-min.js.map b/core/assets/vendor/backbone/backbone-min.js.map
index 2ba6c3507b25..cfacd481728a 100644
--- a/core/assets/vendor/backbone/backbone-min.js.map
+++ b/core/assets/vendor/backbone/backbone-min.js.map
@@ -1 +1 @@
-{"version":3,"sources":["backbone.js"],"names":["factory","root","self","global","define","amd","_","$","exports","Backbone","require","e","jQuery","Zepto","ender","previousBackbone","slice","Array","prototype","VERSION","noConflict","this","emulateHTTP","emulateJSON","Events","eventSplitter","_listening","eventsApi","iteratee","events","name","callback","opts","i","names","context","keys","length","test","split","on","_events","onApi","ctx","listening","listeners","_listeners","id","interop","listenTo","obj","_listenId","uniqueId","listeningTo","_listeningTo","Listening","error","tryCatchOn","options","handlers","count","push","off","offApi","stopListening","ids","isEmpty","cleanup","remaining","j","handler","_callback","once","onceMap","bind","listenToOnce","map","offer","apply","arguments","trigger","Math","max","args","triggerApi","objEvents","allEvents","all","triggerEvents","concat","ev","l","a1","a2","a3","call","listener","unbind","extend","Model","attributes","attrs","preinitialize","cid","cidPrefix","collection","parse","defaults","result","set","changed","initialize","validationError","idAttribute","toJSON","clone","sync","get","attr","escape","has","matches","key","val","_validate","unset","silent","changes","changing","_changing","_previousAttributes","current","prev","isEqual","prevId","_pending","clear","hasChanged","changedAttributes","diff","old","previous","previousAttributes","fetch","model","success","resp","serverAttrs","wrapError","save","validate","wait","method","isNew","patch","xhr","destroy","defer","url","base","urlError","replace","encodeURIComponent","constructor","isValid","Collection","models","comparator","_reset","reset","setOptions","add","remove","merge","addOptions","splice","array","insert","at","min","tail","singular","isArray","removed","_removeModels","added","merged","_isModel","toAdd","toMerge","toRemove","modelMap","sort","sortable","sortAttr","isString","existing","_prepareModel","_addReference","orderChanged","some","m","index","_removeReference","previousModels","pop","unshift","shift","_byId","modelId","where","first","findWhere","Error","isFunction","sortBy","pluck","create","callbackOpts","_forwardPristineError","values","CollectionIterator","ITERATOR_VALUES","ITERATOR_KEYS","entries","ITERATOR_KEYSVALUES","indexOf","_onModelEvent","event","$$iterator","Symbol","iterator","kind","_collection","_kind","_index","next","value","done","View","pick","viewOptions","_ensureElement","delegateEventSplitter","tagName","selector","$el","find","render","_removeElement","setElement","element","undelegateEvents","_setElement","delegateEvents","el","match","delegate","eventName","undelegate","_createElement","document","createElement","className","_setAttributes","addMethod","attribute","cb","defaultVal","addUnderscoreMethods","Class","methods","each","instance","isObject","modelMatcher","matcher","collectionMethods","forEach","collect","reduce","foldl","inject","reduceRight","foldr","detect","filter","select","reject","every","any","include","includes","contains","invoke","toArray","size","head","take","initial","rest","drop","last","without","difference","shuffle","lastIndexOf","chain","sample","partition","groupBy","countBy","indexBy","findIndex","findLastIndex","modelMethods","pairs","invert","omit","config","Base","mixin","mappings","functions","memo","type","methodMap","params","dataType","data","contentType","JSON","stringify","_method","beforeSend","setRequestHeader","processData","textStatus","errorThrown","ajax","update","delete","read","Router","routes","_bindRoutes","optionalParam","namedParam","splatParam","escapeRegExp","route","isRegExp","_routeToRegExp","router","history","fragment","_extractParameters","execute","navigate","optional","RegExp","exec","param","decodeURIComponent","History","checkUrl","window","location","routeStripper","rootStripper","pathStripper","started","interval","atRoot","path","pathname","getSearch","matchRoot","decodeFragment","rootPath","decodeURI","href","getHash","getPath","charAt","getFragment","_usePushState","_wantsHashChange","start","_trailingSlash","trailingSlash","hashChange","_hasHashChange","documentMode","_useHashChange","_wantsPushState","pushState","_hasPushState","iframe","src","style","display","tabIndex","body","iWindow","insertBefore","firstChild","contentWindow","open","close","hash","addEventListener","attachEvent","_checkUrlInterval","setInterval","loadUrl","stop","removeEventListener","detachEvent","removeChild","clearInterval","notfound","decodedFragment","title","_updateHash","assign","protoProps","staticProps","parent","child","__super__","_debug"],"mappings":"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"} \ No newline at end of file
+{"version":3,"sources":["backbone.js"],"names":["factory","root","self","global","define","amd","_","$","exports","Backbone","require","e","jQuery","Zepto","ender","previousBackbone","slice","Array","prototype","VERSION","noConflict","this","emulateHTTP","emulateJSON","Events","eventSplitter","_listening","eventsApi","iteratee","events","name","callback","opts","i","names","context","keys","length","test","split","on","_events","onApi","ctx","listening","listeners","_listeners","id","interop","listenTo","obj","_listenId","uniqueId","listeningTo","_listeningTo","Listening","error","tryCatchOn","options","handlers","count","push","off","offApi","stopListening","ids","isEmpty","cleanup","remaining","j","handler","_callback","once","onceMap","bind","listenToOnce","map","offer","apply","arguments","trigger","Math","max","args","triggerApi","objEvents","allEvents","all","triggerEvents","concat","ev","l","a1","a2","a3","call","listener","unbind","extend","Model","attributes","attrs","preinitialize","cid","cidPrefix","collection","parse","defaults","result","set","changed","initialize","validationError","idAttribute","toJSON","clone","sync","get","attr","escape","has","matches","key","val","_validate","unset","silent","changes","changing","_changing","_previousAttributes","current","prev","isEqual","prevId","_pending","clear","hasChanged","changedAttributes","diff","old","previous","previousAttributes","fetch","model","success","resp","serverAttrs","wrapError","save","validate","wait","method","isNew","patch","xhr","destroy","defer","url","base","urlError","replace","encodeURIComponent","constructor","isValid","Collection","models","comparator","_reset","reset","setOptions","add","remove","merge","addOptions","splice","array","insert","at","min","tail","singular","isArray","removed","_removeModels","added","merged","_isModel","toAdd","toMerge","toRemove","modelMap","sort","sortable","sortAttr","isString","existing","_prepareModel","_addReference","orderChanged","some","m","index","_removeReference","previousModels","pop","unshift","shift","_byId","modelId","where","first","findWhere","Error","isFunction","sortBy","pluck","create","callbackOpts","_forwardPristineError","values","CollectionIterator","ITERATOR_VALUES","ITERATOR_KEYS","entries","ITERATOR_KEYSVALUES","indexOf","_onModelEvent","event","$$iterator","Symbol","iterator","kind","_collection","_kind","_index","next","value","done","View","pick","viewOptions","_ensureElement","delegateEventSplitter","tagName","selector","$el","find","render","_removeElement","setElement","element","undelegateEvents","_setElement","delegateEvents","el","match","delegate","eventName","undelegate","_createElement","document","createElement","className","_setAttributes","addMethod","attribute","cb","defaultVal","addUnderscoreMethods","Class","methods","each","instance","isObject","modelMatcher","matcher","collectionMethods","forEach","collect","reduce","foldl","inject","reduceRight","foldr","detect","filter","select","reject","every","any","include","includes","contains","invoke","toArray","size","head","take","initial","rest","drop","last","without","difference","shuffle","lastIndexOf","chain","sample","partition","groupBy","countBy","indexBy","findIndex","findLastIndex","modelMethods","pairs","invert","omit","config","Base","mixin","mappings","functions","memo","type","methodMap","params","dataType","data","contentType","JSON","stringify","_method","beforeSend","setRequestHeader","processData","textStatus","errorThrown","ajax","update","delete","read","Router","routes","_bindRoutes","optionalParam","namedParam","splatParam","escapeRegExp","route","isRegExp","_routeToRegExp","router","history","fragment","_extractParameters","execute","navigate","optional","RegExp","exec","param","decodeURIComponent","History","checkUrl","window","location","routeStripper","rootStripper","pathStripper","started","interval","atRoot","path","pathname","getSearch","matchRoot","decodeFragment","rootPath","decodeURI","href","getHash","getPath","charAt","getFragment","_usePushState","_wantsHashChange","start","_trailingSlash","trailingSlash","hashChange","_hasHashChange","documentMode","_useHashChange","_wantsPushState","pushState","_hasPushState","iframe","src","style","display","tabIndex","body","iWindow","insertBefore","firstChild","contentWindow","open","close","hash","addEventListener","attachEvent","_checkUrlInterval","setInterval","loadUrl","stop","removeEventListener","detachEvent","removeChild","clearInterval","notfound","decodedFragment","title","_updateHash","assign","protoProps","staticProps","parent","child","__super__","_debug"],"mappings":"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"} \ No newline at end of file
diff --git a/core/assets/vendor/backbone/backbone.js b/core/assets/vendor/backbone/backbone.js
index aa36f1e0f03d..5bfd974cdf0e 100644
--- a/core/assets/vendor/backbone/backbone.js
+++ b/core/assets/vendor/backbone/backbone.js
@@ -1,4 +1,4 @@
-// Backbone.js 1.6.0
+// Backbone.js 1.6.1
// (c) 2010-2024 Jeremy Ashkenas and DocumentCloud
// Backbone may be freely distributed under the MIT license.
@@ -44,7 +44,7 @@
var slice = Array.prototype.slice;
// Current version of the library. Keep in sync with `package.json`.
- Backbone.VERSION = '1.6.0';
+ Backbone.VERSION = '1.6.1';
// For Backbone's purposes, jQuery, Zepto, Ender, or My Library (kidding) owns
// the `$` variable.
@@ -523,7 +523,9 @@
if (this.idAttribute in attrs) {
var prevId = this.id;
this.id = this.get(this.idAttribute);
- this.trigger('changeId', this, prevId, options);
+ if (this.id !== prevId) {
+ this.trigger('changeId', this, prevId, options);
+ }
}
// Trigger all relevant attribute changes.
diff --git a/core/assets/vendor/floating-ui/floating-ui.core.umd.min.js b/core/assets/vendor/floating-ui/floating-ui.core.umd.min.js
index 799757607319..88ed920868b1 100644
--- a/core/assets/vendor/floating-ui/floating-ui.core.umd.min.js
+++ b/core/assets/vendor/floating-ui/floating-ui.core.umd.min.js
@@ -1 +1 @@
-!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).FloatingUICore={})}(this,(function(t){"use strict";const e=["top","right","bottom","left"],n=["start","end"],i=e.reduce(((t,e)=>t.concat(e,e+"-"+n[0],e+"-"+n[1])),[]),o=Math.min,r=Math.max,a={left:"right",right:"left",bottom:"top",top:"bottom"},l={start:"end",end:"start"};function s(t,e,n){return r(t,o(e,n))}function f(t,e){return"function"==typeof t?t(e):t}function c(t){return t.split("-")[0]}function u(t){return t.split("-")[1]}function m(t){return"x"===t?"y":"x"}function d(t){return"y"===t?"height":"width"}function g(t){return["top","bottom"].includes(c(t))?"y":"x"}function p(t){return m(g(t))}function h(t,e,n){void 0===n&&(n=!1);const i=u(t),o=p(t),r=d(o);let a="x"===o?i===(n?"end":"start")?"right":"left":"start"===i?"bottom":"top";return e.reference[r]>e.floating[r]&&(a=w(a)),[a,w(a)]}function y(t){return t.replace(/start|end/g,(t=>l[t]))}function w(t){return t.replace(/left|right|bottom|top/g,(t=>a[t]))}function x(t){return"number"!=typeof t?function(t){return{top:0,right:0,bottom:0,left:0,...t}}(t):{top:t,right:t,bottom:t,left:t}}function v(t){const{x:e,y:n,width:i,height:o}=t;return{width:i,height:o,top:n,left:e,right:e+i,bottom:n+o,x:e,y:n}}function b(t,e,n){let{reference:i,floating:o}=t;const r=g(e),a=p(e),l=d(a),s=c(e),f="y"===r,m=i.x+i.width/2-o.width/2,h=i.y+i.height/2-o.height/2,y=i[l]/2-o[l]/2;let w;switch(s){case"top":w={x:m,y:i.y-o.height};break;case"bottom":w={x:m,y:i.y+i.height};break;case"right":w={x:i.x+i.width,y:h};break;case"left":w={x:i.x-o.width,y:h};break;default:w={x:i.x,y:i.y}}switch(u(e)){case"start":w[a]-=y*(n&&f?-1:1);break;case"end":w[a]+=y*(n&&f?-1:1)}return w}async function A(t,e){var n;void 0===e&&(e={});const{x:i,y:o,platform:r,rects:a,elements:l,strategy:s}=t,{boundary:c="clippingAncestors",rootBoundary:u="viewport",elementContext:m="floating",altBoundary:d=!1,padding:g=0}=f(e,t),p=x(g),h=l[d?"floating"===m?"reference":"floating":m],y=v(await r.getClippingRect({element:null==(n=await(null==r.isElement?void 0:r.isElement(h)))||n?h:h.contextElement||await(null==r.getDocumentElement?void 0:r.getDocumentElement(l.floating)),boundary:c,rootBoundary:u,strategy:s})),w="floating"===m?{x:i,y:o,width:a.floating.width,height:a.floating.height}:a.reference,b=await(null==r.getOffsetParent?void 0:r.getOffsetParent(l.floating)),A=await(null==r.isElement?void 0:r.isElement(b))&&await(null==r.getScale?void 0:r.getScale(b))||{x:1,y:1},R=v(r.convertOffsetParentRelativeRectToViewportRelativeRect?await r.convertOffsetParentRelativeRectToViewportRelativeRect({elements:l,rect:w,offsetParent:b,strategy:s}):w);return{top:(y.top-R.top+p.top)/A.y,bottom:(R.bottom-y.bottom+p.bottom)/A.y,left:(y.left-R.left+p.left)/A.x,right:(R.right-y.right+p.right)/A.x}}function R(t,e){return{top:t.top-e.height,right:t.right-e.width,bottom:t.bottom-e.height,left:t.left-e.width}}function P(t){return e.some((e=>t[e]>=0))}function T(t){const e=o(...t.map((t=>t.left))),n=o(...t.map((t=>t.top)));return{x:e,y:n,width:r(...t.map((t=>t.right)))-e,height:r(...t.map((t=>t.bottom)))-n}}t.arrow=t=>({name:"arrow",options:t,async fn(e){const{x:n,y:i,placement:r,rects:a,platform:l,elements:c,middlewareData:m}=e,{element:g,padding:h=0}=f(t,e)||{};if(null==g)return{};const y=x(h),w={x:n,y:i},v=p(r),b=d(v),A=await l.getDimensions(g),R="y"===v,P=R?"top":"left",T=R?"bottom":"right",D=R?"clientHeight":"clientWidth",O=a.reference[b]+a.reference[v]-w[v]-a.floating[b],E=w[v]-a.reference[v],L=await(null==l.getOffsetParent?void 0:l.getOffsetParent(g));let k=L?L[D]:0;k&&await(null==l.isElement?void 0:l.isElement(L))||(k=c.floating[D]||a.floating[b]);const C=O/2-E/2,B=k/2-A[b]/2-1,H=o(y[P],B),S=o(y[T],B),F=H,j=k-A[b]-S,z=k/2-A[b]/2+C,M=s(F,z,j),V=!m.arrow&&null!=u(r)&&z!==M&&a.reference[b]/2-(z<F?H:S)-A[b]/2<0,W=V?z<F?z-F:z-j:0;return{[v]:w[v]+W,data:{[v]:M,centerOffset:z-M-W,...V&&{alignmentOffset:W}},reset:V}}}),t.autoPlacement=function(t){return void 0===t&&(t={}),{name:"autoPlacement",options:t,async fn(e){var n,o,r;const{rects:a,middlewareData:l,placement:s,platform:m,elements:d}=e,{crossAxis:g=!1,alignment:p,allowedPlacements:w=i,autoAlignment:x=!0,...v}=f(t,e),b=void 0!==p||w===i?function(t,e,n){return(t?[...n.filter((e=>u(e)===t)),...n.filter((e=>u(e)!==t))]:n.filter((t=>c(t)===t))).filter((n=>!t||u(n)===t||!!e&&y(n)!==n))}(p||null,x,w):w,R=await A(e,v),P=(null==(n=l.autoPlacement)?void 0:n.index)||0,T=b[P];if(null==T)return{};const D=h(T,a,await(null==m.isRTL?void 0:m.isRTL(d.floating)));if(s!==T)return{reset:{placement:b[0]}};const O=[R[c(T)],R[D[0]],R[D[1]]],E=[...(null==(o=l.autoPlacement)?void 0:o.overflows)||[],{placement:T,overflows:O}],L=b[P+1];if(L)return{data:{index:P+1,overflows:E},reset:{placement:L}};const k=E.map((t=>{const e=u(t.placement);return[t.placement,e&&g?t.overflows.slice(0,2).reduce(((t,e)=>t+e),0):t.overflows[0],t.overflows]})).sort(((t,e)=>t[1]-e[1])),C=(null==(r=k.filter((t=>t[2].slice(0,u(t[0])?2:3).every((t=>t<=0))))[0])?void 0:r[0])||k[0][0];return C!==s?{data:{index:P+1,overflows:E},reset:{placement:C}}:{}}}},t.computePosition=async(t,e,n)=>{const{placement:i="bottom",strategy:o="absolute",middleware:r=[],platform:a}=n,l=r.filter(Boolean),s=await(null==a.isRTL?void 0:a.isRTL(e));let f=await a.getElementRects({reference:t,floating:e,strategy:o}),{x:c,y:u}=b(f,i,s),m=i,d={},g=0;for(let n=0;n<l.length;n++){const{name:r,fn:p}=l[n],{x:h,y:y,data:w,reset:x}=await p({x:c,y:u,initialPlacement:i,placement:m,strategy:o,middlewareData:d,rects:f,platform:a,elements:{reference:t,floating:e}});c=null!=h?h:c,u=null!=y?y:u,d={...d,[r]:{...d[r],...w}},x&&g<=50&&(g++,"object"==typeof x&&(x.placement&&(m=x.placement),x.rects&&(f=!0===x.rects?await a.getElementRects({reference:t,floating:e,strategy:o}):x.rects),({x:c,y:u}=b(f,m,s))),n=-1)}return{x:c,y:u,placement:m,strategy:o,middlewareData:d}},t.detectOverflow=A,t.flip=function(t){return void 0===t&&(t={}),{name:"flip",options:t,async fn(e){var n,i;const{placement:o,middlewareData:r,rects:a,initialPlacement:l,platform:s,elements:m}=e,{mainAxis:d=!0,crossAxis:p=!0,fallbackPlacements:x,fallbackStrategy:v="bestFit",fallbackAxisSideDirection:b="none",flipAlignment:R=!0,...P}=f(t,e);if(null!=(n=r.arrow)&&n.alignmentOffset)return{};const T=c(o),D=g(l),O=c(l)===l,E=await(null==s.isRTL?void 0:s.isRTL(m.floating)),L=x||(O||!R?[w(l)]:function(t){const e=w(t);return[y(t),e,y(e)]}(l)),k="none"!==b;!x&&k&&L.push(...function(t,e,n,i){const o=u(t);let r=function(t,e,n){const i=["left","right"],o=["right","left"],r=["top","bottom"],a=["bottom","top"];switch(t){case"top":case"bottom":return n?e?o:i:e?i:o;case"left":case"right":return e?r:a;default:return[]}}(c(t),"start"===n,i);return o&&(r=r.map((t=>t+"-"+o)),e&&(r=r.concat(r.map(y)))),r}(l,R,b,E));const C=[l,...L],B=await A(e,P),H=[];let S=(null==(i=r.flip)?void 0:i.overflows)||[];if(d&&H.push(B[T]),p){const t=h(o,a,E);H.push(B[t[0]],B[t[1]])}if(S=[...S,{placement:o,overflows:H}],!H.every((t=>t<=0))){var F,j;const t=((null==(F=r.flip)?void 0:F.index)||0)+1,e=C[t];if(e)return{data:{index:t,overflows:S},reset:{placement:e}};let n=null==(j=S.filter((t=>t.overflows[0]<=0)).sort(((t,e)=>t.overflows[1]-e.overflows[1]))[0])?void 0:j.placement;if(!n)switch(v){case"bestFit":{var z;const t=null==(z=S.filter((t=>{if(k){const e=g(t.placement);return e===D||"y"===e}return!0})).map((t=>[t.placement,t.overflows.filter((t=>t>0)).reduce(((t,e)=>t+e),0)])).sort(((t,e)=>t[1]-e[1]))[0])?void 0:z[0];t&&(n=t);break}case"initialPlacement":n=l}if(o!==n)return{reset:{placement:n}}}return{}}}},t.hide=function(t){return void 0===t&&(t={}),{name:"hide",options:t,async fn(e){const{rects:n}=e,{strategy:i="referenceHidden",...o}=f(t,e);switch(i){case"referenceHidden":{const t=R(await A(e,{...o,elementContext:"reference"}),n.reference);return{data:{referenceHiddenOffsets:t,referenceHidden:P(t)}}}case"escaped":{const t=R(await A(e,{...o,altBoundary:!0}),n.floating);return{data:{escapedOffsets:t,escaped:P(t)}}}default:return{}}}}},t.inline=function(t){return void 0===t&&(t={}),{name:"inline",options:t,async fn(e){const{placement:n,elements:i,rects:a,platform:l,strategy:s}=e,{padding:u=2,x:m,y:d}=f(t,e),p=Array.from(await(null==l.getClientRects?void 0:l.getClientRects(i.reference))||[]),h=function(t){const e=t.slice().sort(((t,e)=>t.y-e.y)),n=[];let i=null;for(let t=0;t<e.length;t++){const o=e[t];!i||o.y-i.y>i.height/2?n.push([o]):n[n.length-1].push(o),i=o}return n.map((t=>v(T(t))))}(p),y=v(T(p)),w=x(u);const b=await l.getElementRects({reference:{getBoundingClientRect:function(){if(2===h.length&&h[0].left>h[1].right&&null!=m&&null!=d)return h.find((t=>m>t.left-w.left&&m<t.right+w.right&&d>t.top-w.top&&d<t.bottom+w.bottom))||y;if(h.length>=2){if("y"===g(n)){const t=h[0],e=h[h.length-1],i="top"===c(n),o=t.top,r=e.bottom,a=i?t.left:e.left,l=i?t.right:e.right;return{top:o,bottom:r,left:a,right:l,width:l-a,height:r-o,x:a,y:o}}const t="left"===c(n),e=r(...h.map((t=>t.right))),i=o(...h.map((t=>t.left))),a=h.filter((n=>t?n.left===i:n.right===e)),l=a[0].top,s=a[a.length-1].bottom;return{top:l,bottom:s,left:i,right:e,width:e-i,height:s-l,x:i,y:l}}return y}},floating:i.floating,strategy:s});return a.reference.x!==b.reference.x||a.reference.y!==b.reference.y||a.reference.width!==b.reference.width||a.reference.height!==b.reference.height?{reset:{rects:b}}:{}}}},t.limitShift=function(t){return void 0===t&&(t={}),{options:t,fn(e){const{x:n,y:i,placement:o,rects:r,middlewareData:a}=e,{offset:l=0,mainAxis:s=!0,crossAxis:u=!0}=f(t,e),d={x:n,y:i},p=g(o),h=m(p);let y=d[h],w=d[p];const x=f(l,e),v="number"==typeof x?{mainAxis:x,crossAxis:0}:{mainAxis:0,crossAxis:0,...x};if(s){const t="y"===h?"height":"width",e=r.reference[h]-r.floating[t]+v.mainAxis,n=r.reference[h]+r.reference[t]-v.mainAxis;y<e?y=e:y>n&&(y=n)}if(u){var b,A;const t="y"===h?"width":"height",e=["top","left"].includes(c(o)),n=r.reference[p]-r.floating[t]+(e&&(null==(b=a.offset)?void 0:b[p])||0)+(e?0:v.crossAxis),i=r.reference[p]+r.reference[t]+(e?0:(null==(A=a.offset)?void 0:A[p])||0)-(e?v.crossAxis:0);w<n?w=n:w>i&&(w=i)}return{[h]:y,[p]:w}}}},t.offset=function(t){return void 0===t&&(t=0),{name:"offset",options:t,async fn(e){var n,i;const{x:o,y:r,placement:a,middlewareData:l}=e,s=await async function(t,e){const{placement:n,platform:i,elements:o}=t,r=await(null==i.isRTL?void 0:i.isRTL(o.floating)),a=c(n),l=u(n),s="y"===g(n),m=["left","top"].includes(a)?-1:1,d=r&&s?-1:1,p=f(e,t);let{mainAxis:h,crossAxis:y,alignmentAxis:w}="number"==typeof p?{mainAxis:p,crossAxis:0,alignmentAxis:null}:{mainAxis:0,crossAxis:0,alignmentAxis:null,...p};return l&&"number"==typeof w&&(y="end"===l?-1*w:w),s?{x:y*d,y:h*m}:{x:h*m,y:y*d}}(e,t);return a===(null==(n=l.offset)?void 0:n.placement)&&null!=(i=l.arrow)&&i.alignmentOffset?{}:{x:o+s.x,y:r+s.y,data:{...s,placement:a}}}}},t.rectToClientRect=v,t.shift=function(t){return void 0===t&&(t={}),{name:"shift",options:t,async fn(e){const{x:n,y:i,placement:o}=e,{mainAxis:r=!0,crossAxis:a=!1,limiter:l={fn:t=>{let{x:e,y:n}=t;return{x:e,y:n}}},...u}=f(t,e),d={x:n,y:i},p=await A(e,u),h=g(c(o)),y=m(h);let w=d[y],x=d[h];if(r){const t="y"===y?"bottom":"right";w=s(w+p["y"===y?"top":"left"],w,w-p[t])}if(a){const t="y"===h?"bottom":"right";x=s(x+p["y"===h?"top":"left"],x,x-p[t])}const v=l.fn({...e,[y]:w,[h]:x});return{...v,data:{x:v.x-n,y:v.y-i}}}}},t.size=function(t){return void 0===t&&(t={}),{name:"size",options:t,async fn(e){const{placement:n,rects:i,platform:a,elements:l}=e,{apply:s=(()=>{}),...m}=f(t,e),d=await A(e,m),p=c(n),h=u(n),y="y"===g(n),{width:w,height:x}=i.floating;let v,b;"top"===p||"bottom"===p?(v=p,b=h===(await(null==a.isRTL?void 0:a.isRTL(l.floating))?"start":"end")?"left":"right"):(b=p,v="end"===h?"top":"bottom");const R=x-d.top-d.bottom,P=w-d.left-d.right,T=o(x-d[v],R),D=o(w-d[b],P),O=!e.middlewareData.shift;let E=T,L=D;if(y?L=h||O?o(D,P):P:E=h||O?o(T,R):R,O&&!h){const t=r(d.left,0),e=r(d.right,0),n=r(d.top,0),i=r(d.bottom,0);y?L=w-2*(0!==t||0!==e?t+e:r(d.left,d.right)):E=x-2*(0!==n||0!==i?n+i:r(d.top,d.bottom))}await s({...e,availableWidth:L,availableHeight:E});const k=await a.getDimensions(l.floating);return w!==k.width||x!==k.height?{reset:{rects:!0}}:{}}}}}));
+!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).FloatingUICore={})}(this,(function(t){"use strict";const e=["top","right","bottom","left"],n=["start","end"],i=e.reduce(((t,e)=>t.concat(e,e+"-"+n[0],e+"-"+n[1])),[]),o=Math.min,r=Math.max,a={left:"right",right:"left",bottom:"top",top:"bottom"},l={start:"end",end:"start"};function s(t,e,n){return r(t,o(e,n))}function f(t,e){return"function"==typeof t?t(e):t}function c(t){return t.split("-")[0]}function u(t){return t.split("-")[1]}function m(t){return"x"===t?"y":"x"}function d(t){return"y"===t?"height":"width"}function g(t){return["top","bottom"].includes(c(t))?"y":"x"}function p(t){return m(g(t))}function h(t,e,n){void 0===n&&(n=!1);const i=u(t),o=p(t),r=d(o);let a="x"===o?i===(n?"end":"start")?"right":"left":"start"===i?"bottom":"top";return e.reference[r]>e.floating[r]&&(a=w(a)),[a,w(a)]}function y(t){return t.replace(/start|end/g,(t=>l[t]))}function w(t){return t.replace(/left|right|bottom|top/g,(t=>a[t]))}function x(t){return"number"!=typeof t?function(t){return{top:0,right:0,bottom:0,left:0,...t}}(t):{top:t,right:t,bottom:t,left:t}}function v(t){const{x:e,y:n,width:i,height:o}=t;return{width:i,height:o,top:n,left:e,right:e+i,bottom:n+o,x:e,y:n}}function b(t,e,n){let{reference:i,floating:o}=t;const r=g(e),a=p(e),l=d(a),s=c(e),f="y"===r,m=i.x+i.width/2-o.width/2,h=i.y+i.height/2-o.height/2,y=i[l]/2-o[l]/2;let w;switch(s){case"top":w={x:m,y:i.y-o.height};break;case"bottom":w={x:m,y:i.y+i.height};break;case"right":w={x:i.x+i.width,y:h};break;case"left":w={x:i.x-o.width,y:h};break;default:w={x:i.x,y:i.y}}switch(u(e)){case"start":w[a]-=y*(n&&f?-1:1);break;case"end":w[a]+=y*(n&&f?-1:1)}return w}async function A(t,e){var n;void 0===e&&(e={});const{x:i,y:o,platform:r,rects:a,elements:l,strategy:s}=t,{boundary:c="clippingAncestors",rootBoundary:u="viewport",elementContext:m="floating",altBoundary:d=!1,padding:g=0}=f(e,t),p=x(g),h=l[d?"floating"===m?"reference":"floating":m],y=v(await r.getClippingRect({element:null==(n=await(null==r.isElement?void 0:r.isElement(h)))||n?h:h.contextElement||await(null==r.getDocumentElement?void 0:r.getDocumentElement(l.floating)),boundary:c,rootBoundary:u,strategy:s})),w="floating"===m?{x:i,y:o,width:a.floating.width,height:a.floating.height}:a.reference,b=await(null==r.getOffsetParent?void 0:r.getOffsetParent(l.floating)),A=await(null==r.isElement?void 0:r.isElement(b))&&await(null==r.getScale?void 0:r.getScale(b))||{x:1,y:1},R=v(r.convertOffsetParentRelativeRectToViewportRelativeRect?await r.convertOffsetParentRelativeRectToViewportRelativeRect({elements:l,rect:w,offsetParent:b,strategy:s}):w);return{top:(y.top-R.top+p.top)/A.y,bottom:(R.bottom-y.bottom+p.bottom)/A.y,left:(y.left-R.left+p.left)/A.x,right:(R.right-y.right+p.right)/A.x}}function R(t,e){return{top:t.top-e.height,right:t.right-e.width,bottom:t.bottom-e.height,left:t.left-e.width}}function P(t){return e.some((e=>t[e]>=0))}function D(t){const e=o(...t.map((t=>t.left))),n=o(...t.map((t=>t.top)));return{x:e,y:n,width:r(...t.map((t=>t.right)))-e,height:r(...t.map((t=>t.bottom)))-n}}t.arrow=t=>({name:"arrow",options:t,async fn(e){const{x:n,y:i,placement:r,rects:a,platform:l,elements:c,middlewareData:m}=e,{element:g,padding:h=0}=f(t,e)||{};if(null==g)return{};const y=x(h),w={x:n,y:i},v=p(r),b=d(v),A=await l.getDimensions(g),R="y"===v,P=R?"top":"left",D=R?"bottom":"right",T=R?"clientHeight":"clientWidth",O=a.reference[b]+a.reference[v]-w[v]-a.floating[b],E=w[v]-a.reference[v],L=await(null==l.getOffsetParent?void 0:l.getOffsetParent(g));let k=L?L[T]:0;k&&await(null==l.isElement?void 0:l.isElement(L))||(k=c.floating[T]||a.floating[b]);const C=O/2-E/2,B=k/2-A[b]/2-1,H=o(y[P],B),S=o(y[D],B),F=H,j=k-A[b]-S,z=k/2-A[b]/2+C,M=s(F,z,j),V=!m.arrow&&null!=u(r)&&z!==M&&a.reference[b]/2-(z<F?H:S)-A[b]/2<0,W=V?z<F?z-F:z-j:0;return{[v]:w[v]+W,data:{[v]:M,centerOffset:z-M-W,...V&&{alignmentOffset:W}},reset:V}}}),t.autoPlacement=function(t){return void 0===t&&(t={}),{name:"autoPlacement",options:t,async fn(e){var n,o,r;const{rects:a,middlewareData:l,placement:s,platform:m,elements:d}=e,{crossAxis:g=!1,alignment:p,allowedPlacements:w=i,autoAlignment:x=!0,...v}=f(t,e),b=void 0!==p||w===i?function(t,e,n){return(t?[...n.filter((e=>u(e)===t)),...n.filter((e=>u(e)!==t))]:n.filter((t=>c(t)===t))).filter((n=>!t||u(n)===t||!!e&&y(n)!==n))}(p||null,x,w):w,R=await A(e,v),P=(null==(n=l.autoPlacement)?void 0:n.index)||0,D=b[P];if(null==D)return{};const T=h(D,a,await(null==m.isRTL?void 0:m.isRTL(d.floating)));if(s!==D)return{reset:{placement:b[0]}};const O=[R[c(D)],R[T[0]],R[T[1]]],E=[...(null==(o=l.autoPlacement)?void 0:o.overflows)||[],{placement:D,overflows:O}],L=b[P+1];if(L)return{data:{index:P+1,overflows:E},reset:{placement:L}};const k=E.map((t=>{const e=u(t.placement);return[t.placement,e&&g?t.overflows.slice(0,2).reduce(((t,e)=>t+e),0):t.overflows[0],t.overflows]})).sort(((t,e)=>t[1]-e[1])),C=(null==(r=k.filter((t=>t[2].slice(0,u(t[0])?2:3).every((t=>t<=0))))[0])?void 0:r[0])||k[0][0];return C!==s?{data:{index:P+1,overflows:E},reset:{placement:C}}:{}}}},t.computePosition=async(t,e,n)=>{const{placement:i="bottom",strategy:o="absolute",middleware:r=[],platform:a}=n,l=r.filter(Boolean),s=await(null==a.isRTL?void 0:a.isRTL(e));let f=await a.getElementRects({reference:t,floating:e,strategy:o}),{x:c,y:u}=b(f,i,s),m=i,d={},g=0;for(let n=0;n<l.length;n++){const{name:r,fn:p}=l[n],{x:h,y:y,data:w,reset:x}=await p({x:c,y:u,initialPlacement:i,placement:m,strategy:o,middlewareData:d,rects:f,platform:a,elements:{reference:t,floating:e}});c=null!=h?h:c,u=null!=y?y:u,d={...d,[r]:{...d[r],...w}},x&&g<=50&&(g++,"object"==typeof x&&(x.placement&&(m=x.placement),x.rects&&(f=!0===x.rects?await a.getElementRects({reference:t,floating:e,strategy:o}):x.rects),({x:c,y:u}=b(f,m,s))),n=-1)}return{x:c,y:u,placement:m,strategy:o,middlewareData:d}},t.detectOverflow=A,t.flip=function(t){return void 0===t&&(t={}),{name:"flip",options:t,async fn(e){var n,i;const{placement:o,middlewareData:r,rects:a,initialPlacement:l,platform:s,elements:m}=e,{mainAxis:d=!0,crossAxis:p=!0,fallbackPlacements:x,fallbackStrategy:v="bestFit",fallbackAxisSideDirection:b="none",flipAlignment:R=!0,...P}=f(t,e);if(null!=(n=r.arrow)&&n.alignmentOffset)return{};const D=c(o),T=g(l),O=c(l)===l,E=await(null==s.isRTL?void 0:s.isRTL(m.floating)),L=x||(O||!R?[w(l)]:function(t){const e=w(t);return[y(t),e,y(e)]}(l)),k="none"!==b;!x&&k&&L.push(...function(t,e,n,i){const o=u(t);let r=function(t,e,n){const i=["left","right"],o=["right","left"],r=["top","bottom"],a=["bottom","top"];switch(t){case"top":case"bottom":return n?e?o:i:e?i:o;case"left":case"right":return e?r:a;default:return[]}}(c(t),"start"===n,i);return o&&(r=r.map((t=>t+"-"+o)),e&&(r=r.concat(r.map(y)))),r}(l,R,b,E));const C=[l,...L],B=await A(e,P),H=[];let S=(null==(i=r.flip)?void 0:i.overflows)||[];if(d&&H.push(B[D]),p){const t=h(o,a,E);H.push(B[t[0]],B[t[1]])}if(S=[...S,{placement:o,overflows:H}],!H.every((t=>t<=0))){var F,j;const t=((null==(F=r.flip)?void 0:F.index)||0)+1,e=C[t];if(e){var z;const n="alignment"===p&&T!==g(e),i=(null==(z=S[0])?void 0:z.overflows[0])>0;if(!n||i)return{data:{index:t,overflows:S},reset:{placement:e}}}let n=null==(j=S.filter((t=>t.overflows[0]<=0)).sort(((t,e)=>t.overflows[1]-e.overflows[1]))[0])?void 0:j.placement;if(!n)switch(v){case"bestFit":{var M;const t=null==(M=S.filter((t=>{if(k){const e=g(t.placement);return e===T||"y"===e}return!0})).map((t=>[t.placement,t.overflows.filter((t=>t>0)).reduce(((t,e)=>t+e),0)])).sort(((t,e)=>t[1]-e[1]))[0])?void 0:M[0];t&&(n=t);break}case"initialPlacement":n=l}if(o!==n)return{reset:{placement:n}}}return{}}}},t.hide=function(t){return void 0===t&&(t={}),{name:"hide",options:t,async fn(e){const{rects:n}=e,{strategy:i="referenceHidden",...o}=f(t,e);switch(i){case"referenceHidden":{const t=R(await A(e,{...o,elementContext:"reference"}),n.reference);return{data:{referenceHiddenOffsets:t,referenceHidden:P(t)}}}case"escaped":{const t=R(await A(e,{...o,altBoundary:!0}),n.floating);return{data:{escapedOffsets:t,escaped:P(t)}}}default:return{}}}}},t.inline=function(t){return void 0===t&&(t={}),{name:"inline",options:t,async fn(e){const{placement:n,elements:i,rects:a,platform:l,strategy:s}=e,{padding:u=2,x:m,y:d}=f(t,e),p=Array.from(await(null==l.getClientRects?void 0:l.getClientRects(i.reference))||[]),h=function(t){const e=t.slice().sort(((t,e)=>t.y-e.y)),n=[];let i=null;for(let t=0;t<e.length;t++){const o=e[t];!i||o.y-i.y>i.height/2?n.push([o]):n[n.length-1].push(o),i=o}return n.map((t=>v(D(t))))}(p),y=v(D(p)),w=x(u);const b=await l.getElementRects({reference:{getBoundingClientRect:function(){if(2===h.length&&h[0].left>h[1].right&&null!=m&&null!=d)return h.find((t=>m>t.left-w.left&&m<t.right+w.right&&d>t.top-w.top&&d<t.bottom+w.bottom))||y;if(h.length>=2){if("y"===g(n)){const t=h[0],e=h[h.length-1],i="top"===c(n),o=t.top,r=e.bottom,a=i?t.left:e.left,l=i?t.right:e.right;return{top:o,bottom:r,left:a,right:l,width:l-a,height:r-o,x:a,y:o}}const t="left"===c(n),e=r(...h.map((t=>t.right))),i=o(...h.map((t=>t.left))),a=h.filter((n=>t?n.left===i:n.right===e)),l=a[0].top,s=a[a.length-1].bottom;return{top:l,bottom:s,left:i,right:e,width:e-i,height:s-l,x:i,y:l}}return y}},floating:i.floating,strategy:s});return a.reference.x!==b.reference.x||a.reference.y!==b.reference.y||a.reference.width!==b.reference.width||a.reference.height!==b.reference.height?{reset:{rects:b}}:{}}}},t.limitShift=function(t){return void 0===t&&(t={}),{options:t,fn(e){const{x:n,y:i,placement:o,rects:r,middlewareData:a}=e,{offset:l=0,mainAxis:s=!0,crossAxis:u=!0}=f(t,e),d={x:n,y:i},p=g(o),h=m(p);let y=d[h],w=d[p];const x=f(l,e),v="number"==typeof x?{mainAxis:x,crossAxis:0}:{mainAxis:0,crossAxis:0,...x};if(s){const t="y"===h?"height":"width",e=r.reference[h]-r.floating[t]+v.mainAxis,n=r.reference[h]+r.reference[t]-v.mainAxis;y<e?y=e:y>n&&(y=n)}if(u){var b,A;const t="y"===h?"width":"height",e=["top","left"].includes(c(o)),n=r.reference[p]-r.floating[t]+(e&&(null==(b=a.offset)?void 0:b[p])||0)+(e?0:v.crossAxis),i=r.reference[p]+r.reference[t]+(e?0:(null==(A=a.offset)?void 0:A[p])||0)-(e?v.crossAxis:0);w<n?w=n:w>i&&(w=i)}return{[h]:y,[p]:w}}}},t.offset=function(t){return void 0===t&&(t=0),{name:"offset",options:t,async fn(e){var n,i;const{x:o,y:r,placement:a,middlewareData:l}=e,s=await async function(t,e){const{placement:n,platform:i,elements:o}=t,r=await(null==i.isRTL?void 0:i.isRTL(o.floating)),a=c(n),l=u(n),s="y"===g(n),m=["left","top"].includes(a)?-1:1,d=r&&s?-1:1,p=f(e,t);let{mainAxis:h,crossAxis:y,alignmentAxis:w}="number"==typeof p?{mainAxis:p,crossAxis:0,alignmentAxis:null}:{mainAxis:p.mainAxis||0,crossAxis:p.crossAxis||0,alignmentAxis:p.alignmentAxis};return l&&"number"==typeof w&&(y="end"===l?-1*w:w),s?{x:y*d,y:h*m}:{x:h*m,y:y*d}}(e,t);return a===(null==(n=l.offset)?void 0:n.placement)&&null!=(i=l.arrow)&&i.alignmentOffset?{}:{x:o+s.x,y:r+s.y,data:{...s,placement:a}}}}},t.rectToClientRect=v,t.shift=function(t){return void 0===t&&(t={}),{name:"shift",options:t,async fn(e){const{x:n,y:i,placement:o}=e,{mainAxis:r=!0,crossAxis:a=!1,limiter:l={fn:t=>{let{x:e,y:n}=t;return{x:e,y:n}}},...u}=f(t,e),d={x:n,y:i},p=await A(e,u),h=g(c(o)),y=m(h);let w=d[y],x=d[h];if(r){const t="y"===y?"bottom":"right";w=s(w+p["y"===y?"top":"left"],w,w-p[t])}if(a){const t="y"===h?"bottom":"right";x=s(x+p["y"===h?"top":"left"],x,x-p[t])}const v=l.fn({...e,[y]:w,[h]:x});return{...v,data:{x:v.x-n,y:v.y-i,enabled:{[y]:r,[h]:a}}}}}},t.size=function(t){return void 0===t&&(t={}),{name:"size",options:t,async fn(e){var n,i;const{placement:a,rects:l,platform:s,elements:m}=e,{apply:d=()=>{},...p}=f(t,e),h=await A(e,p),y=c(a),w=u(a),x="y"===g(a),{width:v,height:b}=l.floating;let R,P;"top"===y||"bottom"===y?(R=y,P=w===(await(null==s.isRTL?void 0:s.isRTL(m.floating))?"start":"end")?"left":"right"):(P=y,R="end"===w?"top":"bottom");const D=b-h.top-h.bottom,T=v-h.left-h.right,O=o(b-h[R],D),E=o(v-h[P],T),L=!e.middlewareData.shift;let k=O,C=E;if(null!=(n=e.middlewareData.shift)&&n.enabled.x&&(C=T),null!=(i=e.middlewareData.shift)&&i.enabled.y&&(k=D),L&&!w){const t=r(h.left,0),e=r(h.right,0),n=r(h.top,0),i=r(h.bottom,0);x?C=v-2*(0!==t||0!==e?t+e:r(h.left,h.right)):k=b-2*(0!==n||0!==i?n+i:r(h.top,h.bottom))}await d({...e,availableWidth:C,availableHeight:k});const B=await s.getDimensions(m.floating);return v!==B.width||b!==B.height?{reset:{rects:!0}}:{}}}}}));
diff --git a/core/assets/vendor/floating-ui/floating-ui.dom.umd.min.js b/core/assets/vendor/floating-ui/floating-ui.dom.umd.min.js
index 4ee3cac89670..6fd2a3648ff8 100644
--- a/core/assets/vendor/floating-ui/floating-ui.dom.umd.min.js
+++ b/core/assets/vendor/floating-ui/floating-ui.dom.umd.min.js
@@ -1 +1 @@
-!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports,require("@floating-ui/core")):"function"==typeof define&&define.amd?define(["exports","@floating-ui/core"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).FloatingUIDOM={},t.FloatingUICore)}(this,(function(t,e){"use strict";const n=Math.min,o=Math.max,i=Math.round,r=Math.floor,c=t=>({x:t,y:t});function l(){return"undefined"!=typeof window}function s(t){return d(t)?(t.nodeName||"").toLowerCase():"#document"}function f(t){var e;return(null==t||null==(e=t.ownerDocument)?void 0:e.defaultView)||window}function u(t){var e;return null==(e=(d(t)?t.ownerDocument:t.document)||window.document)?void 0:e.documentElement}function d(t){return!!l()&&(t instanceof Node||t instanceof f(t).Node)}function a(t){return!!l()&&(t instanceof Element||t instanceof f(t).Element)}function h(t){return!!l()&&(t instanceof HTMLElement||t instanceof f(t).HTMLElement)}function p(t){return!(!l()||"undefined"==typeof ShadowRoot)&&(t instanceof ShadowRoot||t instanceof f(t).ShadowRoot)}function g(t){const{overflow:e,overflowX:n,overflowY:o,display:i}=b(t);return/auto|scroll|overlay|hidden|clip/.test(e+o+n)&&!["inline","contents"].includes(i)}function m(t){return["table","td","th"].includes(s(t))}function y(t){return[":popover-open",":modal"].some((e=>{try{return t.matches(e)}catch(t){return!1}}))}function w(t){const e=x(),n=a(t)?b(t):t;return"none"!==n.transform||"none"!==n.perspective||!!n.containerType&&"normal"!==n.containerType||!e&&!!n.backdropFilter&&"none"!==n.backdropFilter||!e&&!!n.filter&&"none"!==n.filter||["transform","perspective","filter"].some((t=>(n.willChange||"").includes(t)))||["paint","layout","strict","content"].some((t=>(n.contain||"").includes(t)))}function x(){return!("undefined"==typeof CSS||!CSS.supports)&&CSS.supports("-webkit-backdrop-filter","none")}function v(t){return["html","body","#document"].includes(s(t))}function b(t){return f(t).getComputedStyle(t)}function T(t){return a(t)?{scrollLeft:t.scrollLeft,scrollTop:t.scrollTop}:{scrollLeft:t.scrollX,scrollTop:t.scrollY}}function L(t){if("html"===s(t))return t;const e=t.assignedSlot||t.parentNode||p(t)&&t.host||u(t);return p(e)?e.host:e}function R(t){const e=L(t);return v(e)?t.ownerDocument?t.ownerDocument.body:t.body:h(e)&&g(e)?e:R(e)}function C(t,e,n){var o;void 0===e&&(e=[]),void 0===n&&(n=!0);const i=R(t),r=i===(null==(o=t.ownerDocument)?void 0:o.body),c=f(i);if(r){const t=E(c);return e.concat(c,c.visualViewport||[],g(i)?i:[],t&&n?C(t):[])}return e.concat(i,C(i,[],n))}function E(t){return t.parent&&Object.getPrototypeOf(t.parent)?t.frameElement:null}function S(t){const e=b(t);let n=parseFloat(e.width)||0,o=parseFloat(e.height)||0;const r=h(t),c=r?t.offsetWidth:n,l=r?t.offsetHeight:o,s=i(n)!==c||i(o)!==l;return s&&(n=c,o=l),{width:n,height:o,$:s}}function F(t){return a(t)?t:t.contextElement}function O(t){const e=F(t);if(!h(e))return c(1);const n=e.getBoundingClientRect(),{width:o,height:r,$:l}=S(e);let s=(l?i(n.width):n.width)/o,f=(l?i(n.height):n.height)/r;return s&&Number.isFinite(s)||(s=1),f&&Number.isFinite(f)||(f=1),{x:s,y:f}}const D=c(0);function H(t){const e=f(t);return x()&&e.visualViewport?{x:e.visualViewport.offsetLeft,y:e.visualViewport.offsetTop}:D}function P(t,n,o,i){void 0===n&&(n=!1),void 0===o&&(o=!1);const r=t.getBoundingClientRect(),l=F(t);let s=c(1);n&&(i?a(i)&&(s=O(i)):s=O(t));const u=function(t,e,n){return void 0===e&&(e=!1),!(!n||e&&n!==f(t))&&e}(l,o,i)?H(l):c(0);let d=(r.left+u.x)/s.x,h=(r.top+u.y)/s.y,p=r.width/s.x,g=r.height/s.y;if(l){const t=f(l),e=i&&a(i)?f(i):i;let n=t,o=E(n);for(;o&&i&&e!==n;){const t=O(o),e=o.getBoundingClientRect(),i=b(o),r=e.left+(o.clientLeft+parseFloat(i.paddingLeft))*t.x,c=e.top+(o.clientTop+parseFloat(i.paddingTop))*t.y;d*=t.x,h*=t.y,p*=t.x,g*=t.y,d+=r,h+=c,n=f(o),o=E(n)}}return e.rectToClientRect({width:p,height:g,x:d,y:h})}function W(t,e){const n=T(t).scrollLeft;return e?e.left+n:P(u(t)).left+n}function M(t,e,n){void 0===n&&(n=!1);const o=t.getBoundingClientRect();return{x:o.left+e.scrollLeft-(n?0:W(t,o)),y:o.top+e.scrollTop}}function z(t,n,i){let r;if("viewport"===n)r=function(t,e){const n=f(t),o=u(t),i=n.visualViewport;let r=o.clientWidth,c=o.clientHeight,l=0,s=0;if(i){r=i.width,c=i.height;const t=x();(!t||t&&"fixed"===e)&&(l=i.offsetLeft,s=i.offsetTop)}return{width:r,height:c,x:l,y:s}}(t,i);else if("document"===n)r=function(t){const e=u(t),n=T(t),i=t.ownerDocument.body,r=o(e.scrollWidth,e.clientWidth,i.scrollWidth,i.clientWidth),c=o(e.scrollHeight,e.clientHeight,i.scrollHeight,i.clientHeight);let l=-n.scrollLeft+W(t);const s=-n.scrollTop;return"rtl"===b(i).direction&&(l+=o(e.clientWidth,i.clientWidth)-r),{width:r,height:c,x:l,y:s}}(u(t));else if(a(n))r=function(t,e){const n=P(t,!0,"fixed"===e),o=n.top+t.clientTop,i=n.left+t.clientLeft,r=h(t)?O(t):c(1);return{width:t.clientWidth*r.x,height:t.clientHeight*r.y,x:i*r.x,y:o*r.y}}(n,i);else{const e=H(t);r={x:n.x-e.x,y:n.y-e.y,width:n.width,height:n.height}}return e.rectToClientRect(r)}function A(t,e){const n=L(t);return!(n===e||!a(n)||v(n))&&("fixed"===b(n).position||A(n,e))}function V(t,e,n){const o=h(e),i=u(e),r="fixed"===n,l=P(t,!0,r,e);let f={scrollLeft:0,scrollTop:0};const d=c(0);if(o||!o&&!r)if(("body"!==s(e)||g(i))&&(f=T(e)),o){const t=P(e,!0,r,e);d.x=t.x+e.clientLeft,d.y=t.y+e.clientTop}else i&&(d.x=W(i));const a=!i||o||r?c(0):M(i,f);return{x:l.left+f.scrollLeft-d.x-a.x,y:l.top+f.scrollTop-d.y-a.y,width:l.width,height:l.height}}function B(t){return"static"===b(t).position}function N(t,e){if(!h(t)||"fixed"===b(t).position)return null;if(e)return e(t);let n=t.offsetParent;return u(t)===n&&(n=n.ownerDocument.body),n}function I(t,e){const n=f(t);if(y(t))return n;if(!h(t)){let e=L(t);for(;e&&!v(e);){if(a(e)&&!B(e))return e;e=L(e)}return n}let o=N(t,e);for(;o&&m(o)&&B(o);)o=N(o,e);return o&&v(o)&&B(o)&&!w(o)?n:o||function(t){let e=L(t);for(;h(e)&&!v(e);){if(w(e))return e;if(y(e))return null;e=L(e)}return null}(t)||n}const k={convertOffsetParentRelativeRectToViewportRelativeRect:function(t){let{elements:e,rect:n,offsetParent:o,strategy:i}=t;const r="fixed"===i,l=u(o),f=!!e&&y(e.floating);if(o===l||f&&r)return n;let d={scrollLeft:0,scrollTop:0},a=c(1);const p=c(0),m=h(o);if((m||!m&&!r)&&(("body"!==s(o)||g(l))&&(d=T(o)),h(o))){const t=P(o);a=O(o),p.x=t.x+o.clientLeft,p.y=t.y+o.clientTop}const w=!l||m||r?c(0):M(l,d,!0);return{width:n.width*a.x,height:n.height*a.y,x:n.x*a.x-d.scrollLeft*a.x+p.x+w.x,y:n.y*a.y-d.scrollTop*a.y+p.y+w.y}},getDocumentElement:u,getClippingRect:function(t){let{element:e,boundary:i,rootBoundary:r,strategy:c}=t;const l=[..."clippingAncestors"===i?y(e)?[]:function(t,e){const n=e.get(t);if(n)return n;let o=C(t,[],!1).filter((t=>a(t)&&"body"!==s(t))),i=null;const r="fixed"===b(t).position;let c=r?L(t):t;for(;a(c)&&!v(c);){const e=b(c),n=w(c);n||"fixed"!==e.position||(i=null),(r?!n&&!i:!n&&"static"===e.position&&i&&["absolute","fixed"].includes(i.position)||g(c)&&!n&&A(t,c))?o=o.filter((t=>t!==c)):i=e,c=L(c)}return e.set(t,o),o}(e,this._c):[].concat(i),r],f=l[0],u=l.reduce(((t,i)=>{const r=z(e,i,c);return t.top=o(r.top,t.top),t.right=n(r.right,t.right),t.bottom=n(r.bottom,t.bottom),t.left=o(r.left,t.left),t}),z(e,f,c));return{width:u.right-u.left,height:u.bottom-u.top,x:u.left,y:u.top}},getOffsetParent:I,getElementRects:async function(t){const e=this.getOffsetParent||I,n=this.getDimensions,o=await n(t.floating);return{reference:V(t.reference,await e(t.floating),t.strategy),floating:{x:0,y:0,width:o.width,height:o.height}}},getClientRects:function(t){return Array.from(t.getClientRects())},getDimensions:function(t){const{width:e,height:n}=S(t);return{width:e,height:n}},getScale:O,isElement:a,isRTL:function(t){return"rtl"===b(t).direction}};const q=e.detectOverflow,U=e.offset,j=e.autoPlacement,X=e.shift,Y=e.flip,$=e.size,_=e.hide,G=e.arrow,J=e.inline,K=e.limitShift;t.arrow=G,t.autoPlacement=j,t.autoUpdate=function(t,e,i,c){void 0===c&&(c={});const{ancestorScroll:l=!0,ancestorResize:s=!0,elementResize:f="function"==typeof ResizeObserver,layoutShift:d="function"==typeof IntersectionObserver,animationFrame:a=!1}=c,h=F(t),p=l||s?[...h?C(h):[],...C(e)]:[];p.forEach((t=>{l&&t.addEventListener("scroll",i,{passive:!0}),s&&t.addEventListener("resize",i)}));const g=h&&d?function(t,e){let i,c=null;const l=u(t);function s(){var t;clearTimeout(i),null==(t=c)||t.disconnect(),c=null}return function f(u,d){void 0===u&&(u=!1),void 0===d&&(d=1),s();const{left:a,top:h,width:p,height:g}=t.getBoundingClientRect();if(u||e(),!p||!g)return;const m={rootMargin:-r(h)+"px "+-r(l.clientWidth-(a+p))+"px "+-r(l.clientHeight-(h+g))+"px "+-r(a)+"px",threshold:o(0,n(1,d))||1};let y=!0;function w(t){const e=t[0].intersectionRatio;if(e!==d){if(!y)return f();e?f(!1,e):i=setTimeout((()=>{f(!1,1e-7)}),1e3)}y=!1}try{c=new IntersectionObserver(w,{...m,root:l.ownerDocument})}catch(t){c=new IntersectionObserver(w,m)}c.observe(t)}(!0),s}(h,i):null;let m,y=-1,w=null;f&&(w=new ResizeObserver((t=>{let[n]=t;n&&n.target===h&&w&&(w.unobserve(e),cancelAnimationFrame(y),y=requestAnimationFrame((()=>{var t;null==(t=w)||t.observe(e)}))),i()})),h&&!a&&w.observe(h),w.observe(e));let x=a?P(t):null;return a&&function e(){const n=P(t);!x||n.x===x.x&&n.y===x.y&&n.width===x.width&&n.height===x.height||i();x=n,m=requestAnimationFrame(e)}(),i(),()=>{var t;p.forEach((t=>{l&&t.removeEventListener("scroll",i),s&&t.removeEventListener("resize",i)})),null==g||g(),null==(t=w)||t.disconnect(),w=null,a&&cancelAnimationFrame(m)}},t.computePosition=(t,n,o)=>{const i=new Map,r={platform:k,...o},c={...r.platform,_c:i};return e.computePosition(t,n,{...r,platform:c})},t.detectOverflow=q,t.flip=Y,t.getOverflowAncestors=C,t.hide=_,t.inline=J,t.limitShift=K,t.offset=U,t.platform=k,t.shift=X,t.size=$}));
+!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports,require("@floating-ui/core")):"function"==typeof define&&define.amd?define(["exports","@floating-ui/core"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).FloatingUIDOM={},t.FloatingUICore)}(this,(function(t,e){"use strict";const n=Math.min,o=Math.max,i=Math.round,r=Math.floor,c=t=>({x:t,y:t});function l(){return"undefined"!=typeof window}function s(t){return a(t)?(t.nodeName||"").toLowerCase():"#document"}function f(t){var e;return(null==t||null==(e=t.ownerDocument)?void 0:e.defaultView)||window}function u(t){var e;return null==(e=(a(t)?t.ownerDocument:t.document)||window.document)?void 0:e.documentElement}function a(t){return!!l()&&(t instanceof Node||t instanceof f(t).Node)}function d(t){return!!l()&&(t instanceof Element||t instanceof f(t).Element)}function h(t){return!!l()&&(t instanceof HTMLElement||t instanceof f(t).HTMLElement)}function p(t){return!(!l()||"undefined"==typeof ShadowRoot)&&(t instanceof ShadowRoot||t instanceof f(t).ShadowRoot)}function g(t){const{overflow:e,overflowX:n,overflowY:o,display:i}=b(t);return/auto|scroll|overlay|hidden|clip/.test(e+o+n)&&!["inline","contents"].includes(i)}function m(t){return["table","td","th"].includes(s(t))}function y(t){return[":popover-open",":modal"].some((e=>{try{return t.matches(e)}catch(t){return!1}}))}function w(t){const e=x(),n=d(t)?b(t):t;return["transform","translate","scale","rotate","perspective"].some((t=>!!n[t]&&"none"!==n[t]))||!!n.containerType&&"normal"!==n.containerType||!e&&!!n.backdropFilter&&"none"!==n.backdropFilter||!e&&!!n.filter&&"none"!==n.filter||["transform","translate","scale","rotate","perspective","filter"].some((t=>(n.willChange||"").includes(t)))||["paint","layout","strict","content"].some((t=>(n.contain||"").includes(t)))}function x(){return!("undefined"==typeof CSS||!CSS.supports)&&CSS.supports("-webkit-backdrop-filter","none")}function v(t){return["html","body","#document"].includes(s(t))}function b(t){return f(t).getComputedStyle(t)}function T(t){return d(t)?{scrollLeft:t.scrollLeft,scrollTop:t.scrollTop}:{scrollLeft:t.scrollX,scrollTop:t.scrollY}}function L(t){if("html"===s(t))return t;const e=t.assignedSlot||t.parentNode||p(t)&&t.host||u(t);return p(e)?e.host:e}function R(t){const e=L(t);return v(e)?t.ownerDocument?t.ownerDocument.body:t.body:h(e)&&g(e)?e:R(e)}function C(t,e,n){var o;void 0===e&&(e=[]),void 0===n&&(n=!0);const i=R(t),r=i===(null==(o=t.ownerDocument)?void 0:o.body),c=f(i);if(r){const t=E(c);return e.concat(c,c.visualViewport||[],g(i)?i:[],t&&n?C(t):[])}return e.concat(i,C(i,[],n))}function E(t){return t.parent&&Object.getPrototypeOf(t.parent)?t.frameElement:null}function S(t){const e=b(t);let n=parseFloat(e.width)||0,o=parseFloat(e.height)||0;const r=h(t),c=r?t.offsetWidth:n,l=r?t.offsetHeight:o,s=i(n)!==c||i(o)!==l;return s&&(n=c,o=l),{width:n,height:o,$:s}}function F(t){return d(t)?t:t.contextElement}function O(t){const e=F(t);if(!h(e))return c(1);const n=e.getBoundingClientRect(),{width:o,height:r,$:l}=S(e);let s=(l?i(n.width):n.width)/o,f=(l?i(n.height):n.height)/r;return s&&Number.isFinite(s)||(s=1),f&&Number.isFinite(f)||(f=1),{x:s,y:f}}const D=c(0);function H(t){const e=f(t);return x()&&e.visualViewport?{x:e.visualViewport.offsetLeft,y:e.visualViewport.offsetTop}:D}function P(t,n,o,i){void 0===n&&(n=!1),void 0===o&&(o=!1);const r=t.getBoundingClientRect(),l=F(t);let s=c(1);n&&(i?d(i)&&(s=O(i)):s=O(t));const u=function(t,e,n){return void 0===e&&(e=!1),!(!n||e&&n!==f(t))&&e}(l,o,i)?H(l):c(0);let a=(r.left+u.x)/s.x,h=(r.top+u.y)/s.y,p=r.width/s.x,g=r.height/s.y;if(l){const t=f(l),e=i&&d(i)?f(i):i;let n=t,o=E(n);for(;o&&i&&e!==n;){const t=O(o),e=o.getBoundingClientRect(),i=b(o),r=e.left+(o.clientLeft+parseFloat(i.paddingLeft))*t.x,c=e.top+(o.clientTop+parseFloat(i.paddingTop))*t.y;a*=t.x,h*=t.y,p*=t.x,g*=t.y,a+=r,h+=c,n=f(o),o=E(n)}}return e.rectToClientRect({width:p,height:g,x:a,y:h})}function W(t,e){const n=T(t).scrollLeft;return e?e.left+n:P(u(t)).left+n}function M(t,e,n){void 0===n&&(n=!1);const o=t.getBoundingClientRect();return{x:o.left+e.scrollLeft-(n?0:W(t,o)),y:o.top+e.scrollTop}}function z(t,n,i){let r;if("viewport"===n)r=function(t,e){const n=f(t),o=u(t),i=n.visualViewport;let r=o.clientWidth,c=o.clientHeight,l=0,s=0;if(i){r=i.width,c=i.height;const t=x();(!t||t&&"fixed"===e)&&(l=i.offsetLeft,s=i.offsetTop)}return{width:r,height:c,x:l,y:s}}(t,i);else if("document"===n)r=function(t){const e=u(t),n=T(t),i=t.ownerDocument.body,r=o(e.scrollWidth,e.clientWidth,i.scrollWidth,i.clientWidth),c=o(e.scrollHeight,e.clientHeight,i.scrollHeight,i.clientHeight);let l=-n.scrollLeft+W(t);const s=-n.scrollTop;return"rtl"===b(i).direction&&(l+=o(e.clientWidth,i.clientWidth)-r),{width:r,height:c,x:l,y:s}}(u(t));else if(d(n))r=function(t,e){const n=P(t,!0,"fixed"===e),o=n.top+t.clientTop,i=n.left+t.clientLeft,r=h(t)?O(t):c(1);return{width:t.clientWidth*r.x,height:t.clientHeight*r.y,x:i*r.x,y:o*r.y}}(n,i);else{const e=H(t);r={x:n.x-e.x,y:n.y-e.y,width:n.width,height:n.height}}return e.rectToClientRect(r)}function A(t,e){const n=L(t);return!(n===e||!d(n)||v(n))&&("fixed"===b(n).position||A(n,e))}function B(t,e,n){const o=h(e),i=u(e),r="fixed"===n,l=P(t,!0,r,e);let f={scrollLeft:0,scrollTop:0};const a=c(0);function d(){a.x=W(i)}if(o||!o&&!r)if(("body"!==s(e)||g(i))&&(f=T(e)),o){const t=P(e,!0,r,e);a.x=t.x+e.clientLeft,a.y=t.y+e.clientTop}else i&&d();r&&!o&&i&&d();const p=!i||o||r?c(0):M(i,f);return{x:l.left+f.scrollLeft-a.x-p.x,y:l.top+f.scrollTop-a.y-p.y,width:l.width,height:l.height}}function V(t){return"static"===b(t).position}function N(t,e){if(!h(t)||"fixed"===b(t).position)return null;if(e)return e(t);let n=t.offsetParent;return u(t)===n&&(n=n.ownerDocument.body),n}function I(t,e){const n=f(t);if(y(t))return n;if(!h(t)){let e=L(t);for(;e&&!v(e);){if(d(e)&&!V(e))return e;e=L(e)}return n}let o=N(t,e);for(;o&&m(o)&&V(o);)o=N(o,e);return o&&v(o)&&V(o)&&!w(o)?n:o||function(t){let e=L(t);for(;h(e)&&!v(e);){if(w(e))return e;if(y(e))return null;e=L(e)}return null}(t)||n}const k={convertOffsetParentRelativeRectToViewportRelativeRect:function(t){let{elements:e,rect:n,offsetParent:o,strategy:i}=t;const r="fixed"===i,l=u(o),f=!!e&&y(e.floating);if(o===l||f&&r)return n;let a={scrollLeft:0,scrollTop:0},d=c(1);const p=c(0),m=h(o);if((m||!m&&!r)&&(("body"!==s(o)||g(l))&&(a=T(o)),h(o))){const t=P(o);d=O(o),p.x=t.x+o.clientLeft,p.y=t.y+o.clientTop}const w=!l||m||r?c(0):M(l,a,!0);return{width:n.width*d.x,height:n.height*d.y,x:n.x*d.x-a.scrollLeft*d.x+p.x+w.x,y:n.y*d.y-a.scrollTop*d.y+p.y+w.y}},getDocumentElement:u,getClippingRect:function(t){let{element:e,boundary:i,rootBoundary:r,strategy:c}=t;const l=[..."clippingAncestors"===i?y(e)?[]:function(t,e){const n=e.get(t);if(n)return n;let o=C(t,[],!1).filter((t=>d(t)&&"body"!==s(t))),i=null;const r="fixed"===b(t).position;let c=r?L(t):t;for(;d(c)&&!v(c);){const e=b(c),n=w(c);n||"fixed"!==e.position||(i=null),(r?!n&&!i:!n&&"static"===e.position&&i&&["absolute","fixed"].includes(i.position)||g(c)&&!n&&A(t,c))?o=o.filter((t=>t!==c)):i=e,c=L(c)}return e.set(t,o),o}(e,this._c):[].concat(i),r],f=l[0],u=l.reduce(((t,i)=>{const r=z(e,i,c);return t.top=o(r.top,t.top),t.right=n(r.right,t.right),t.bottom=n(r.bottom,t.bottom),t.left=o(r.left,t.left),t}),z(e,f,c));return{width:u.right-u.left,height:u.bottom-u.top,x:u.left,y:u.top}},getOffsetParent:I,getElementRects:async function(t){const e=this.getOffsetParent||I,n=this.getDimensions,o=await n(t.floating);return{reference:B(t.reference,await e(t.floating),t.strategy),floating:{x:0,y:0,width:o.width,height:o.height}}},getClientRects:function(t){return Array.from(t.getClientRects())},getDimensions:function(t){const{width:e,height:n}=S(t);return{width:e,height:n}},getScale:O,isElement:d,isRTL:function(t){return"rtl"===b(t).direction}};function q(t,e){return t.x===e.x&&t.y===e.y&&t.width===e.width&&t.height===e.height}const U=e.detectOverflow,j=e.offset,X=e.autoPlacement,Y=e.shift,$=e.flip,_=e.size,G=e.hide,J=e.arrow,K=e.inline,Q=e.limitShift;t.arrow=J,t.autoPlacement=X,t.autoUpdate=function(t,e,i,c){void 0===c&&(c={});const{ancestorScroll:l=!0,ancestorResize:s=!0,elementResize:f="function"==typeof ResizeObserver,layoutShift:a="function"==typeof IntersectionObserver,animationFrame:d=!1}=c,h=F(t),p=l||s?[...h?C(h):[],...C(e)]:[];p.forEach((t=>{l&&t.addEventListener("scroll",i,{passive:!0}),s&&t.addEventListener("resize",i)}));const g=h&&a?function(t,e){let i,c=null;const l=u(t);function s(){var t;clearTimeout(i),null==(t=c)||t.disconnect(),c=null}return function f(u,a){void 0===u&&(u=!1),void 0===a&&(a=1),s();const d=t.getBoundingClientRect(),{left:h,top:p,width:g,height:m}=d;if(u||e(),!g||!m)return;const y={rootMargin:-r(p)+"px "+-r(l.clientWidth-(h+g))+"px "+-r(l.clientHeight-(p+m))+"px "+-r(h)+"px",threshold:o(0,n(1,a))||1};let w=!0;function x(e){const n=e[0].intersectionRatio;if(n!==a){if(!w)return f();n?f(!1,n):i=setTimeout((()=>{f(!1,1e-7)}),1e3)}1!==n||q(d,t.getBoundingClientRect())||f(),w=!1}try{c=new IntersectionObserver(x,{...y,root:l.ownerDocument})}catch(t){c=new IntersectionObserver(x,y)}c.observe(t)}(!0),s}(h,i):null;let m,y=-1,w=null;f&&(w=new ResizeObserver((t=>{let[n]=t;n&&n.target===h&&w&&(w.unobserve(e),cancelAnimationFrame(y),y=requestAnimationFrame((()=>{var t;null==(t=w)||t.observe(e)}))),i()})),h&&!d&&w.observe(h),w.observe(e));let x=d?P(t):null;return d&&function e(){const n=P(t);x&&!q(x,n)&&i();x=n,m=requestAnimationFrame(e)}(),i(),()=>{var t;p.forEach((t=>{l&&t.removeEventListener("scroll",i),s&&t.removeEventListener("resize",i)})),null==g||g(),null==(t=w)||t.disconnect(),w=null,d&&cancelAnimationFrame(m)}},t.computePosition=(t,n,o)=>{const i=new Map,r={platform:k,...o},c={...r.platform,_c:i};return e.computePosition(t,n,{...r,platform:c})},t.detectOverflow=U,t.flip=$,t.getOverflowAncestors=C,t.hide=G,t.inline=K,t.limitShift=Q,t.offset=j,t.platform=k,t.shift=Y,t.size=_}));
diff --git a/core/composer.json b/core/composer.json
index 2ce89dd0c0e9..59bca714e5b9 100644
--- a/core/composer.json
+++ b/core/composer.json
@@ -19,37 +19,37 @@
"ext-xml": "*",
"ext-zlib": "*",
"php": ">=8.3.0",
- "symfony/console": "^7.2",
- "symfony/dependency-injection": "^7.2",
- "symfony/event-dispatcher": "^7.2",
- "symfony/filesystem": "^7.2",
- "symfony/finder": "^7.2",
- "symfony/http-foundation": "^7.2",
- "symfony/http-kernel": "^7.2",
- "symfony/mailer": "^7.2",
- "symfony/mime": "^7.2",
- "symfony/routing": "^7.2",
- "symfony/serializer": "^7.2",
- "symfony/validator": "^7.2",
- "symfony/process": "^7.2",
+ "symfony/console": "^7.3@beta",
+ "symfony/dependency-injection": "^7.3@beta",
+ "symfony/event-dispatcher": "^7.3@beta",
+ "symfony/filesystem": "^7.3@beta",
+ "symfony/finder": "^7.3@beta",
+ "symfony/http-foundation": "^7.3@beta",
+ "symfony/http-kernel": "^7.3@beta",
+ "symfony/mailer": "^7.3@beta",
+ "symfony/mime": "^7.3@beta",
+ "symfony/routing": "^7.3@beta",
+ "symfony/serializer": "^7.3@beta",
+ "symfony/validator": "^7.3@beta",
+ "symfony/process": "^7.3@beta",
"symfony/polyfill-iconv": "^1.26",
- "symfony/yaml": "^7.2",
+ "symfony/yaml": "^7.3@beta",
"revolt/event-loop": "^1.0",
- "twig/twig": "^3.15.0",
+ "twig/twig": "^3.21.0",
"doctrine/annotations": "^2.0",
"doctrine/lexer": "^2.0",
"guzzlehttp/guzzle": "^7.5",
"guzzlehttp/psr7": "^2.4.5",
"egulias/email-validator": "^3.2.1|^4.0",
"masterminds/html5": "^2.7",
- "symfony/psr-http-message-bridge": "^7.2",
+ "symfony/psr-http-message-bridge": "^7.3@beta",
"composer-runtime-api": "^2.1",
"composer/semver": "^3.3",
"asm89/stack-cors": "^2.3",
"pear/archive_tar": "^1.4.14",
"psr/log": "^3.0",
"mck89/peast": "^1.14",
- "sebastian/diff": "^4|^5",
+ "sebastian/diff": "^4 || ^5 || ^6 || ^7",
"php-tuf/composer-stager": "^2.0"
},
"conflict": {
diff --git a/core/core.libraries.yml b/core/core.libraries.yml
index 76aaa3965f01..f62da33d128d 100644
--- a/core/core.libraries.yml
+++ b/core/core.libraries.yml
@@ -7,10 +7,10 @@ internal.backbone:
# - https://www.drupal.org/project/drupal/issues/3204011
# - https://www.drupal.org/project/drupal/issues/3204015
remote: https://github.com/jashkenas/backbone
- version: "1.6.0"
+ version: "1.6.1"
license:
name: MIT
- url: https://raw.githubusercontent.com/jashkenas/backbone/1.6.0/LICENSE
+ url: https://raw.githubusercontent.com/jashkenas/backbone/1.6.1/LICENSE
gpl-compatible: true
js:
assets/vendor/backbone/backbone-min.js: { weight: -19, minified: true }
@@ -793,10 +793,10 @@ htmx:
internal.floating-ui:
remote: https://github.com/floating-ui/floating-ui
- version: "1.6.12"
+ version: "1.7.0"
license:
name: MIT
- url: https://github.com/floating-ui/floating-ui/blob/@floating-ui/dom@1.6.12/LICENSE
+ url: https://github.com/floating-ui/floating-ui/blob/@floating-ui/dom@1.7.0/LICENSE
gpl-compatible: true
js:
assets/vendor/floating-ui/floating-ui.core.umd.min.js: { minified: true }
diff --git a/core/includes/common.inc b/core/includes/common.inc
index caf7d72f4d14..47f360caffe9 100644
--- a/core/includes/common.inc
+++ b/core/includes/common.inc
@@ -488,7 +488,7 @@ function drupal_flush_all_caches($kernel = NULL): void {
* @see hook_updater_info_alter()
*/
function drupal_get_updaters() {
- @trigger_error(__METHOD__ . '() is deprecated in drupal:11.2.0 and is removed from drupal:12.0.0. There is no replacement. Use composer to manage the code for your site. See https://www.drupal.org/node/3512364', E_USER_DEPRECATED);
+ @trigger_error(__FUNCTION__ . '() is deprecated in drupal:11.2.0 and is removed from drupal:12.0.0. There is no replacement. Use composer to manage the code for your site. See https://www.drupal.org/node/3512364', E_USER_DEPRECATED);
$updaters = &drupal_static(__FUNCTION__);
if (!isset($updaters)) {
@@ -510,7 +510,7 @@ function drupal_get_updaters() {
* @see hook_filetransfer_info_alter()
*/
function drupal_get_filetransfer_info() {
- @trigger_error(__METHOD__ . '() is deprecated in drupal:11.2.0 and is removed from drupal:12.0.0. There is no replacement. Use composer to manage the code for your site. See https://www.drupal.org/node/3512364', E_USER_DEPRECATED);
+ @trigger_error(__FUNCTION__ . '() is deprecated in drupal:11.2.0 and is removed from drupal:12.0.0. There is no replacement. Use composer to manage the code for your site. See https://www.drupal.org/node/3512364', E_USER_DEPRECATED);
$info = &drupal_static(__FUNCTION__);
if (!isset($info)) {
diff --git a/core/lib/Drupal/Component/DependencyInjection/composer.json b/core/lib/Drupal/Component/DependencyInjection/composer.json
index 790fa6e38c8b..ccf16002da9c 100644
--- a/core/lib/Drupal/Component/DependencyInjection/composer.json
+++ b/core/lib/Drupal/Component/DependencyInjection/composer.json
@@ -14,7 +14,7 @@
},
"require": {
"php": ">=8.3.0",
- "symfony/dependency-injection": "^7.2",
+ "symfony/dependency-injection": "^7.3@beta",
"symfony/service-contracts": "v3.5.1"
},
"suggest": {
diff --git a/core/lib/Drupal/Component/Diff/composer.json b/core/lib/Drupal/Component/Diff/composer.json
index 3fd31e6c78fb..f4bc60d21bcb 100644
--- a/core/lib/Drupal/Component/Diff/composer.json
+++ b/core/lib/Drupal/Component/Diff/composer.json
@@ -8,7 +8,7 @@
"license": "GPL-2.0-or-later",
"require": {
"php": ">=8.3.0",
- "sebastian/diff": "^4|^5"
+ "sebastian/diff": "^4 || ^5 || ^6 || ^7"
},
"autoload": {
"psr-4": {
diff --git a/core/lib/Drupal/Component/EventDispatcher/composer.json b/core/lib/Drupal/Component/EventDispatcher/composer.json
index f25fd5f4eee7..b832a6c45b1b 100644
--- a/core/lib/Drupal/Component/EventDispatcher/composer.json
+++ b/core/lib/Drupal/Component/EventDispatcher/composer.json
@@ -8,8 +8,8 @@
"license": "GPL-2.0-or-later",
"require": {
"php": ">=8.3.0",
- "symfony/dependency-injection": "^7.2",
- "symfony/event-dispatcher": "^7.2",
+ "symfony/dependency-injection": "^7.3@beta",
+ "symfony/event-dispatcher": "^7.3@beta",
"symfony/event-dispatcher-contracts": "v3.5.1"
},
"autoload": {
diff --git a/core/lib/Drupal/Component/HttpFoundation/composer.json b/core/lib/Drupal/Component/HttpFoundation/composer.json
index 0d7ba034013d..283e0713ca5c 100644
--- a/core/lib/Drupal/Component/HttpFoundation/composer.json
+++ b/core/lib/Drupal/Component/HttpFoundation/composer.json
@@ -8,7 +8,7 @@
"license": "GPL-2.0-or-later",
"require": {
"php": ">=8.3.0",
- "symfony/http-foundation": "^7.2"
+ "symfony/http-foundation": "^7.3@beta"
},
"autoload": {
"psr-4": {
diff --git a/core/lib/Drupal/Component/Plugin/composer.json b/core/lib/Drupal/Component/Plugin/composer.json
index 8a5884ed17a6..6a4f1fc7e2cd 100644
--- a/core/lib/Drupal/Component/Plugin/composer.json
+++ b/core/lib/Drupal/Component/Plugin/composer.json
@@ -10,7 +10,7 @@
"license": "GPL-2.0-or-later",
"require": {
"php": ">=8.3.0",
- "symfony/validator": "^7.2"
+ "symfony/validator": "^7.3@beta"
},
"autoload": {
"psr-4": {
diff --git a/core/lib/Drupal/Component/Serialization/composer.json b/core/lib/Drupal/Component/Serialization/composer.json
index f60762f819bb..10068bba3ced 100644
--- a/core/lib/Drupal/Component/Serialization/composer.json
+++ b/core/lib/Drupal/Component/Serialization/composer.json
@@ -8,7 +8,7 @@
"license": "GPL-2.0-or-later",
"require": {
"php": ">=8.3.0",
- "symfony/yaml": "^7.2"
+ "symfony/yaml": "^7.3@beta"
},
"autoload": {
"psr-4": {
diff --git a/core/lib/Drupal/Core/Composer/Composer.php b/core/lib/Drupal/Core/Composer/Composer.php
index 6f2391885499..87b494e98421 100644
--- a/core/lib/Drupal/Core/Composer/Composer.php
+++ b/core/lib/Drupal/Core/Composer/Composer.php
@@ -91,7 +91,7 @@ class Composer {
return;
}
- // If the PHP version is 7.4 or above and PHPUnit is less than version 9
+ // If the PHP version is 8.4 or above and PHPUnit is less than version 11
// call the drupal-phpunit-upgrade script to upgrade PHPUnit.
if (!static::upgradePHPUnitCheck($phpunit_package->getVersion())) {
$event->getComposer()
@@ -115,7 +115,7 @@ class Composer {
* @internal
*/
public static function upgradePHPUnitCheck($phpunit_version) {
- return !(version_compare(PHP_MAJOR_VERSION . '.' . PHP_MINOR_VERSION, '7.4') >= 0 && version_compare($phpunit_version, '9.0') < 0);
+ return !(version_compare(PHP_MAJOR_VERSION . '.' . PHP_MINOR_VERSION, '8.4') >= 0 && version_compare($phpunit_version, '11.0') < 0);
}
}
diff --git a/core/lib/Drupal/Core/Hook/Attribute/Hook.php b/core/lib/Drupal/Core/Hook/Attribute/Hook.php
index c404a4a028ba..34dbc8ebf916 100644
--- a/core/lib/Drupal/Core/Hook/Attribute/Hook.php
+++ b/core/lib/Drupal/Core/Hook/Attribute/Hook.php
@@ -135,7 +135,7 @@ class Hook implements HookAttributeInterface {
public string $hook = '',
public string $method = '',
public ?string $module = NULL,
- public OrderInterface|null $order = NULL,
+ public ?OrderInterface $order = NULL,
) {
$this->hook = implode('_', array_filter([static::PREFIX, $hook, static::SUFFIX]));
if ($this->hook === '') {
diff --git a/core/lib/Drupal/Core/Render/Element/StatusMessages.php b/core/lib/Drupal/Core/Render/Element/StatusMessages.php
index 4eecd9f42f1a..0192346e2b31 100644
--- a/core/lib/Drupal/Core/Render/Element/StatusMessages.php
+++ b/core/lib/Drupal/Core/Render/Element/StatusMessages.php
@@ -3,6 +3,7 @@
namespace Drupal\Core\Render\Element;
use Drupal\Core\Render\Attribute\RenderElement;
+use Drupal\big_pipe\Render\Placeholder\BigPipeStrategy;
/**
* Provides a messages element.
@@ -52,6 +53,15 @@ class StatusMessages extends RenderElementBase {
$build = [
'#lazy_builder' => [static::class . '::renderMessages', [$element['#display']]],
'#create_placeholder' => TRUE,
+ // Prevent this placeholder being handled by big pipe. Messages are
+ // very quick to render and this allows pages without other placeholders
+ // to avoid loading big pipe's JavaScript altogether. Note that while the
+ // big pipe namespaced is reference, PHP happily uses the '::class' magic
+ // property without needing to load the class, so this works when big_pipe
+ // module is not installed.
+ '#placeholder_strategy_denylist' => [
+ BigPipeStrategy::class => TRUE,
+ ],
];
// Directly create a placeholder as we need this to be placeholdered
diff --git a/core/lib/Drupal/Core/Render/Placeholder/ChainedPlaceholderStrategy.php b/core/lib/Drupal/Core/Render/Placeholder/ChainedPlaceholderStrategy.php
index ba1417e45a79..c22e430ca2f3 100644
--- a/core/lib/Drupal/Core/Render/Placeholder/ChainedPlaceholderStrategy.php
+++ b/core/lib/Drupal/Core/Render/Placeholder/ChainedPlaceholderStrategy.php
@@ -4,6 +4,11 @@ namespace Drupal\Core\Render\Placeholder;
/**
* Renders placeholders using a chain of placeholder strategies.
+ *
+ * Render arrays may specify a denylist of placeholder strategies by using
+ * $element['#placeholder_strategy_denylist'][ClassName::class] = TRUE at the
+ * same level as #lazy_builder. When this is set, placeholder strategies
+ * specified will be skipped.
*/
class ChainedPlaceholderStrategy implements PlaceholderStrategyInterface {
@@ -43,7 +48,14 @@ class ChainedPlaceholderStrategy implements PlaceholderStrategyInterface {
// placeholders. The order of placeholder strategies is well defined and
// this uses a variation of the "chain of responsibility" design pattern.
foreach ($this->placeholderStrategies as $strategy) {
- $processed_placeholders = $strategy->processPlaceholders($placeholders);
+ $candidate_placeholders = [];
+ foreach ($placeholders as $key => $placeholder) {
+ if (empty($placeholder['#placeholder_strategy_denylist'][$strategy::class])) {
+ $candidate_placeholders[$key] = $placeholder;
+ }
+ }
+
+ $processed_placeholders = $strategy->processPlaceholders($candidate_placeholders);
assert(array_intersect_key($processed_placeholders, $placeholders) === $processed_placeholders, 'Processed placeholders must be a subset of all placeholders.');
$placeholders = array_diff_key($placeholders, $processed_placeholders);
$new_placeholders += $processed_placeholders;
@@ -52,6 +64,7 @@ class ChainedPlaceholderStrategy implements PlaceholderStrategyInterface {
break;
}
}
+ assert(empty($placeholders), 'It was not possible to replace all placeholders in ChainedPlaceholderStrategy::processPlaceholders()');
return $new_placeholders;
}
diff --git a/core/lib/Drupal/Core/Render/PlaceholderGenerator.php b/core/lib/Drupal/Core/Render/PlaceholderGenerator.php
index 2d93e6347132..63f8fc3cdac2 100644
--- a/core/lib/Drupal/Core/Render/PlaceholderGenerator.php
+++ b/core/lib/Drupal/Core/Render/PlaceholderGenerator.php
@@ -85,15 +85,18 @@ class PlaceholderGenerator implements PlaceholderGeneratorInterface {
* {@inheritdoc}
*/
public function createPlaceholder(array $element) {
- $placeholder_render_array = array_intersect_key($element, [
+ $placeholder_render_array = array_intersect_key($element, \array_flip([
// Placeholders are replaced with markup by executing the associated
// #lazy_builder callback, which generates a render array, and which the
// Renderer will render and replace the placeholder with.
- '#lazy_builder' => TRUE,
+ '#lazy_builder',
// The cacheability metadata for the placeholder. The rendered result of
// the placeholder may itself be cached, if [#cache][keys] are specified.
- '#cache' => TRUE,
- ]);
+ '#cache',
+ // The placeholder strategy denylist for this placeholder which can be
+ // used to skip placeholder strategies for specific render arrays.
+ '#placeholder_strategy_denylist',
+ ]));
if (isset($element['#lazy_builder_preview'])) {
$placeholder_render_array['#preview'] = $element['#lazy_builder_preview'];
diff --git a/core/lib/Drupal/Core/Render/Renderer.php b/core/lib/Drupal/Core/Render/Renderer.php
index 0c6e276e3bf6..d0150fe0127a 100644
--- a/core/lib/Drupal/Core/Render/Renderer.php
+++ b/core/lib/Drupal/Core/Render/Renderer.php
@@ -96,6 +96,9 @@ class Renderer implements RendererInterface {
* {@inheritdoc}
*/
public function renderRoot(&$elements) {
+ if (!$elements) {
+ return '';
+ }
// Disallow calling ::renderRoot() from within another ::renderRoot() call.
if ($this->isRenderingRoot) {
$this->isRenderingRoot = FALSE;
@@ -104,21 +107,39 @@ class Renderer implements RendererInterface {
// Render in its own render context.
$this->isRenderingRoot = TRUE;
- $output = $this->executeInRenderContext(new RenderContext(), function () use (&$elements) {
- return $this->render($elements, TRUE);
- });
- $this->isRenderingRoot = FALSE;
+ try {
+ $output = $this->renderInIsolation($elements);
+ }
+ // Since #pre_render, #post_render, #lazy_builder callbacks and theme
+ // functions or templates may be used for generating a render array's
+ // content, and we might be rendering the main content for the page, it is
+ // possible that any of them throw an exception that will cause a different
+ // page to be rendered (e.g. throwing
+ // \Symfony\Component\HttpKernel\Exception\NotFoundHttpException will cause
+ // the 404 page to be rendered). That page might also use
+ // Renderer::renderRoot() but if exceptions aren't caught here, it will be
+ // impossible to call Renderer::renderRoot() again.
+ // Hence, catch all exceptions, reset the isRenderingRoot property and
+ // re-throw exceptions.
+ finally {
+ $this->isRenderingRoot = FALSE;
+ }
- return $output;
+ if ((string) $output === '') {
+ return '';
+ }
+
+ return $elements['#markup'];
}
/**
* {@inheritdoc}
*/
public function renderInIsolation(&$elements) {
- return $this->executeInRenderContext(new RenderContext(), function () use (&$elements) {
- return $this->render($elements, TRUE);
- });
+ $context = new RenderContext();
+ return Markup::create($this->executeInRenderContext($context, function () use (&$elements, $context) {
+ return $this->doRenderRoot($elements, $context);
+ }));
}
/**
@@ -188,31 +209,59 @@ class Renderer implements RendererInterface {
* {@inheritdoc}
*/
public function render(&$elements, $is_root_call = FALSE) {
- // Since #pre_render, #post_render, #lazy_builder callbacks and theme
- // functions or templates may be used for generating a render array's
- // content, and we might be rendering the main content for the page, it is
- // possible that any of them throw an exception that will cause a different
- // page to be rendered (e.g. throwing
- // \Symfony\Component\HttpKernel\Exception\NotFoundHttpException will cause
- // the 404 page to be rendered). That page might also use
- // Renderer::renderRoot() but if exceptions aren't caught here, it will be
- // impossible to call Renderer::renderRoot() again.
- // Hence, catch all exceptions, reset the isRenderingRoot property and
- // re-throw exceptions.
- try {
- return $this->doRender($elements, $is_root_call);
+ $context = $this->getCurrentRenderContext();
+ if (!isset($context)) {
+ throw new \LogicException("Render context is empty, because render() was called outside of a renderRoot() or renderPlain() call. Use renderPlain()/renderRoot() or #lazy_builder/#pre_render instead.");
}
- catch (\Exception $e) {
- // Mark the ::rootRender() call finished due to this exception & re-throw.
- $this->isRenderingRoot = FALSE;
- throw $e;
+
+ if ($is_root_call) {
+ trigger_error(__METHOD__ . ' with $is_root_call is deprecated in drupal:11.2.0 and is removed from drupal:12.0.0. Use ' . __CLASS__ . '::renderRoot() instead. See https://www.drupal.org/node/3497318.');
+ return $this->doRenderRoot($elements, $context);
+ }
+
+ return $this->doRender($elements, $context);
+ }
+
+ /**
+ * See the docs for ::render().
+ */
+ protected function doRenderRoot(array &$elements, RenderContext $context): string|MarkupInterface {
+ if (!$elements) {
+ return '';
+ }
+ // Set the bubbleable rendering metadata that has configurable defaults
+ // to ensure that the final render array definitely has these configurable
+ // defaults, even when no subtree is render cached.
+ $required_cache_contexts = $this->rendererConfig['required_cache_contexts'];
+
+ if (isset($elements['#cache']['contexts'])) {
+ $elements['#cache']['contexts'] = Cache::mergeContexts($elements['#cache']['contexts'], $required_cache_contexts);
+ }
+ else {
+ $elements['#cache']['contexts'] = $required_cache_contexts;
+ }
+
+ // Render the elements normally.
+ $return = $this->doRender($elements, $context);
+
+ // If there is no output, return early as placeholders can't make a
+ // difference.
+ if ((string) $return === '') {
+ return $return;
}
+
+ // Only when we're in a root (non-recursive) Renderer::render() call,
+ // placeholders must be processed, to prevent breaking the render cache in
+ // case of nested elements with #cache set.
+ $this->replacePlaceholders($elements);
+
+ return $elements['#markup'];
}
/**
* See the docs for ::render().
*/
- protected function doRender(&$elements, $is_root_call = FALSE) {
+ protected function doRender(array &$elements, RenderContext $context): string|MarkupInterface {
if (empty($elements)) {
return '';
}
@@ -233,10 +282,6 @@ class Renderer implements RendererInterface {
$this->addCacheableDependency($elements, $elements['#access']);
if (!$elements['#access']->isAllowed()) {
// Abort, but bubble new cache metadata from the access result.
- $context = $this->getCurrentRenderContext();
- if (!isset($context)) {
- throw new \LogicException("Render context is empty, because render() was called outside of a renderRoot() or renderInIsolation() call. Use renderInIsolation()/renderRoot() or #lazy_builder/#pre_render instead.");
- }
$context->push(new BubbleableMetadata());
$context->update($elements);
$context->bubble();
@@ -264,7 +309,7 @@ class Renderer implements RendererInterface {
// has these configurable defaults, even when no subtree is render cached.
// - this is a render cacheable subtree, to ensure that the cached data has
// the configurable defaults (which may affect the ID and invalidation).
- if ($is_root_call || isset($elements['#cache']['keys'])) {
+ if (isset($elements['#cache']['keys'])) {
$required_cache_contexts = $this->rendererConfig['required_cache_contexts'];
if (isset($elements['#cache']['contexts'])) {
$elements['#cache']['contexts'] = Cache::mergeContexts($elements['#cache']['contexts'], $required_cache_contexts);
@@ -287,12 +332,6 @@ class Renderer implements RendererInterface {
$cached_element = $this->renderCache->get($elements);
if ($cached_element !== FALSE) {
$elements = $cached_element;
- // Only when we're in a root (non-recursive) Renderer::render() call,
- // placeholders must be processed, to prevent breaking the render cache
- // in case of nested elements with #cache set.
- if ($is_root_call) {
- $this->replacePlaceholders($elements);
- }
// Mark the element markup as safe if is it a string.
if (is_string($elements['#markup'])) {
$elements['#markup'] = Markup::create($elements['#markup']);
@@ -318,6 +357,7 @@ class Renderer implements RendererInterface {
'#cache' => TRUE,
'#lazy_builder' => TRUE,
'#lazy_builder_preview' => TRUE,
+ '#placeholder_strategy_denylist' => TRUE,
'#create_placeholder' => TRUE,
]);
@@ -338,6 +378,7 @@ class Renderer implements RendererInterface {
'#cache',
'#create_placeholder',
'#lazy_builder_preview',
+ '#placeholder_strategy_denylist',
'#preview',
// The keys below are not actually supported, but these are added
// automatically by the Renderer. Adding them as though they are
@@ -462,7 +503,7 @@ class Renderer implements RendererInterface {
// same process as Renderer::render() but is inlined for speed.
if ((!$theme_is_implemented || isset($elements['#render_children'])) && empty($elements['#children'])) {
foreach ($children as $key) {
- $elements['#children'] .= $this->doRender($elements[$key]);
+ $elements['#children'] .= $this->doRender($elements[$key], $context);
}
$elements['#children'] = Markup::create($elements['#children']);
}
@@ -556,23 +597,6 @@ class Renderer implements RendererInterface {
$context->update($elements);
}
- // Only when we're in a root (non-recursive) Renderer::render() call,
- // placeholders must be processed, to prevent breaking the render cache in
- // case of nested elements with #cache set.
- //
- // By running them here, we ensure that:
- // - they run when #cache is disabled,
- // - they run when #cache is enabled and there is a cache miss.
- // Only the case of a cache hit when #cache is enabled, is not handled here,
- // that is handled earlier in Renderer::render().
- if ($is_root_call) {
- $this->replacePlaceholders($elements);
- // @todo remove as part of https://www.drupal.org/node/2511330.
- if ($context->count() !== 1) {
- throw new \LogicException('A stray RendererInterface::render() invocation with $is_root_call = TRUE is causing bubbling of attached assets to break.');
- }
- }
-
// Rendering is finished, all necessary info collected!
$context->bubble();
diff --git a/core/lib/Drupal/Core/Render/RendererInterface.php b/core/lib/Drupal/Core/Render/RendererInterface.php
index 92fa661f6b0f..020e594755fb 100644
--- a/core/lib/Drupal/Core/Render/RendererInterface.php
+++ b/core/lib/Drupal/Core/Render/RendererInterface.php
@@ -323,7 +323,7 @@ interface RendererInterface {
* (Internal use only.) Whether this is a recursive call or not. See
* ::renderRoot().
*
- * @return \Drupal\Component\Render\MarkupInterface
+ * @return \Drupal\Component\Render\MarkupInterface|string
* The rendered HTML.
*
* @throws \LogicException
diff --git a/core/lib/Drupal/Core/Template/TwigTransTokenParser.php b/core/lib/Drupal/Core/Template/TwigTransTokenParser.php
index 0d764c36f90c..95b1eaa1bfc3 100644
--- a/core/lib/Drupal/Core/Template/TwigTransTokenParser.php
+++ b/core/lib/Drupal/Core/Template/TwigTransTokenParser.php
@@ -36,17 +36,17 @@ class TwigTransTokenParser extends AbstractTokenParser {
$plural = NULL;
if (!$stream->test(Token::BLOCK_END_TYPE) && $stream->test(Token::STRING_TYPE)) {
- $body = $this->parser->getExpressionParser()->parseExpression();
+ $body = $this->parser->parseExpression();
}
if (!$stream->test(Token::BLOCK_END_TYPE) && $stream->test(Token::NAME_TYPE, 'with')) {
$stream->next();
- $options = $this->parser->getExpressionParser()->parseExpression();
+ $options = $this->parser->parseExpression();
}
if (!$body) {
$stream->expect(Token::BLOCK_END_TYPE);
$body = $this->parser->subparse([$this, 'decideForFork']);
if ('plural' === $stream->next()->getValue()) {
- $count = $this->parser->getExpressionParser()->parseExpression();
+ $count = $this->parser->parseExpression();
$stream->expect(Token::BLOCK_END_TYPE);
$plural = $this->parser->subparse([$this, 'decideForEnd'], TRUE);
}
diff --git a/core/lib/Drupal/Core/Test/PhpUnitTestRunner.php b/core/lib/Drupal/Core/Test/PhpUnitTestRunner.php
index 3293b07b44bf..3f3a5ee9a96d 100644
--- a/core/lib/Drupal/Core/Test/PhpUnitTestRunner.php
+++ b/core/lib/Drupal/Core/Test/PhpUnitTestRunner.php
@@ -160,8 +160,12 @@ class PhpUnitTestRunner implements ContainerInjectionInterface {
// If the deprecation handler bridge is active, we need to fail when there
// are deprecations that get reported (i.e. not ignored or expected).
- if (DeprecationHandler::getConfiguration() !== FALSE) {
+ $deprecationConfiguration = DeprecationHandler::getConfiguration();
+ if ($deprecationConfiguration !== FALSE) {
$command[] = '--fail-on-deprecation';
+ if ($deprecationConfiguration['failOnPhpunitDeprecation']) {
+ $command[] = '--fail-on-phpunit-deprecation';
+ }
}
// Add to the command the file containing the test class to be run.
diff --git a/core/modules/big_pipe/big_pipe.module b/core/modules/big_pipe/big_pipe.module
index 75509d6f649e..d04104799cd3 100644
--- a/core/modules/big_pipe/big_pipe.module
+++ b/core/modules/big_pipe/big_pipe.module
@@ -22,6 +22,7 @@ function big_pipe_theme_suggestions_big_pipe_interface_preview(array $variables)
// Use simplified template suggestion, if any.
// For example, this simplifies
+ // phpcs:ignore Drupal.Files.LineLength
// big-pipe-interface-preview--Drupal-block-BlockViewBuilder--lazyBuilder--<BLOCK ID>.html.twig
// to
// big-pipe-interface-preview--block--<BLOCK ID>.html.twig
diff --git a/core/modules/big_pipe/tests/modules/big_pipe_messages_test/big_pipe_messages_test.info.yml b/core/modules/big_pipe/tests/modules/big_pipe_messages_test/big_pipe_messages_test.info.yml
new file mode 100644
index 000000000000..f7522b47887c
--- /dev/null
+++ b/core/modules/big_pipe/tests/modules/big_pipe_messages_test/big_pipe_messages_test.info.yml
@@ -0,0 +1,5 @@
+name: 'BigPipe messages test'
+type: module
+description: 'Forces the messages placeholder to go via the big pipe strategy for testing purposes.'
+package: Testing
+version: VERSION
diff --git a/core/modules/big_pipe/tests/modules/big_pipe_messages_test/src/Hook/BigPipeMessagesHooks.php b/core/modules/big_pipe/tests/modules/big_pipe_messages_test/src/Hook/BigPipeMessagesHooks.php
new file mode 100644
index 000000000000..bb8212bb2848
--- /dev/null
+++ b/core/modules/big_pipe/tests/modules/big_pipe_messages_test/src/Hook/BigPipeMessagesHooks.php
@@ -0,0 +1,42 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Drupal\big_pipe_messages_test\Hook;
+
+use Drupal\Core\Hook\Attribute\Hook;
+use Drupal\Core\Security\Attribute\TrustedCallback;
+
+/**
+ * Hook implementations for big_pipe_test.
+ */
+class BigPipeMessagesHooks {
+
+ /**
+ * Implements hook_element_info_alter().
+ */
+ #[Hook('element_info_alter')]
+ public function elementInfoAlter(array &$info): void {
+ $info['status_messages']['#pre_render'][] = static::class . '::preRenderMessages';
+ }
+
+ /**
+ * Pre render callback.
+ *
+ * Removes #placeholder_strategy from the messages element to force the
+ * messages placeholder to go via the big pipe strategy for testing purposes.
+ */
+ #[TrustedCallback]
+ public static function preRenderMessages(array $element): array {
+ if (isset($element['#attached']['placeholders'])) {
+ $key = key($element['#attached']['placeholders']);
+ unset($element['#attached']['placeholders'][$key]['#placeholder_strategy_denylist']);
+ }
+ if (isset($element['messages']['#attached']['placeholders'])) {
+ $key = key($element['messages']['#attached']['placeholders']);
+ unset($element['messages']['#attached']['placeholders'][$key]['#placeholder_strategy_denylist']);
+ }
+ return $element;
+ }
+
+}
diff --git a/core/modules/big_pipe/tests/src/Functional/BigPipeTest.php b/core/modules/big_pipe/tests/src/Functional/BigPipeTest.php
index 26e92fc14982..1ca75bb57262 100644
--- a/core/modules/big_pipe/tests/src/Functional/BigPipeTest.php
+++ b/core/modules/big_pipe/tests/src/Functional/BigPipeTest.php
@@ -32,7 +32,7 @@ class BigPipeTest extends BrowserTestBase {
/**
* {@inheritdoc}
*/
- protected static $modules = ['big_pipe', 'big_pipe_test', 'dblog'];
+ protected static $modules = ['big_pipe', 'big_pipe_messages_test', 'big_pipe_test', 'dblog'];
/**
* {@inheritdoc}
@@ -88,9 +88,10 @@ class BigPipeTest extends BrowserTestBase {
// 2. Session (authenticated).
$this->drupalLogin($this->rootUser);
+ $this->drupalGet(Url::fromRoute('big_pipe_test'));
$this->assertSessionCookieExists('1');
$this->assertBigPipeNoJsCookieExists('0');
- $this->assertSession()->responseContains('<noscript><meta http-equiv="Refresh" content="0; URL=' . base_path() . 'big_pipe/no-js?destination=' . UrlHelper::encodePath(base_path() . 'user/1?check_logged_in=1') . '" />' . "\n" . '</noscript>');
+ $this->assertSession()->responseContains('<noscript><meta http-equiv="Refresh" content="0; URL=' . base_path() . 'big_pipe/no-js?destination=' . UrlHelper::encodePath(base_path() . 'big_pipe_test') . '" />' . "\n" . '</noscript>');
$this->assertSession()->responseNotContains($no_js_to_js_markup);
$this->assertBigPipeNoJsMetaRefreshRedirect();
$this->assertBigPipeNoJsCookieExists('1');
@@ -103,10 +104,10 @@ class BigPipeTest extends BrowserTestBase {
// 3. Session (anonymous).
$this->drupalGet(Url::fromRoute('user.login', [], ['query' => ['trigger_session' => 1]]));
- $this->drupalGet(Url::fromRoute('user.login'));
+ $this->drupalGet(Url::fromRoute('big_pipe_test'));
$this->assertSessionCookieExists('1');
$this->assertBigPipeNoJsCookieExists('0');
- $this->assertSession()->responseContains('<noscript><meta http-equiv="Refresh" content="0; URL=' . base_path() . 'big_pipe/no-js?destination=' . base_path() . 'user/login" />' . "\n" . '</noscript>');
+ $this->assertSession()->responseContains('<noscript><meta http-equiv="Refresh" content="0; URL=' . base_path() . 'big_pipe/no-js?destination=' . base_path() . 'big_pipe_test" />' . "\n" . '</noscript>');
$this->assertSession()->responseNotContains($no_js_to_js_markup);
$this->assertBigPipeNoJsMetaRefreshRedirect();
$this->assertBigPipeNoJsCookieExists('1');
@@ -314,14 +315,9 @@ class BigPipeTest extends BrowserTestBase {
// @see performMetaRefresh()
$this->drupalGet(Url::fromRoute('big_pipe_test_multi_occurrence'));
- // cspell:disable-next-line
- $big_pipe_placeholder_id = 'callback=Drupal%5CCore%5CRender%5CElement%5CStatusMessages%3A%3ArenderMessages&amp;args%5B0%5D&amp;token=_HAdUpwWmet0TOTe2PSiJuMntExoshbm1kh2wQzzzAA';
- $expected_placeholder_replacement = '<script type="application/vnd.drupal-ajax" data-big-pipe-replacement-for-placeholder-with-id="' . $big_pipe_placeholder_id . '">';
$this->assertSession()->pageTextContains('The count is 1.');
$this->assertSession()->responseNotContains('The count is 2.');
$this->assertSession()->responseNotContains('The count is 3.');
- $raw_content = $this->getSession()->getPage()->getContent();
- $this->assertSame(1, substr_count($raw_content, $expected_placeholder_replacement), 'Only one placeholder replacement was found for the duplicate #lazy_builder arrays.');
// By calling performMetaRefresh() here, we simulate JavaScript being
// disabled, because as far as the BigPipe module is concerned, it is
diff --git a/core/modules/big_pipe/tests/src/FunctionalJavascript/BigPipeRegressionTest.php b/core/modules/big_pipe/tests/src/FunctionalJavascript/BigPipeRegressionTest.php
index 20244a8fa098..8e4abff5734f 100644
--- a/core/modules/big_pipe/tests/src/FunctionalJavascript/BigPipeRegressionTest.php
+++ b/core/modules/big_pipe/tests/src/FunctionalJavascript/BigPipeRegressionTest.php
@@ -22,6 +22,7 @@ class BigPipeRegressionTest extends WebDriverTestBase {
*/
protected static $modules = [
'big_pipe',
+ 'big_pipe_messages_test',
'big_pipe_regression_test',
];
diff --git a/core/modules/block/src/Hook/BlockHooks.php b/core/modules/block/src/Hook/BlockHooks.php
index 98cd3992fbc4..657109309a30 100644
--- a/core/modules/block/src/Hook/BlockHooks.php
+++ b/core/modules/block/src/Hook/BlockHooks.php
@@ -82,13 +82,14 @@ class BlockHooks {
* block.html.twig is used.
*
* Most themes use their own copy of block.html.twig. The default is located
- * inside "core/modules/block/templates/block.html.twig". Look in there for the
- * full list of available variables.
+ * inside "core/modules/block/templates/block.html.twig". Look in there for
+ * the full list of available variables.
*
* @param array $variables
* An associative array containing:
- * - elements: An associative array containing the properties of the element.
- * Properties used: #block, #configuration, #children, #plugin_id.
+ * - elements: An associative array containing the properties of the
+ * element. Properties used: #block, #configuration, #children,
+ * and #plugin_id.
*/
public function preprocessBlock(&$variables): void {
$variables['configuration'] = $variables['elements']['#configuration'];
@@ -98,8 +99,8 @@ class BlockHooks {
$variables['in_preview'] = $variables['elements']['#in_preview'] ?? FALSE;
$variables['label'] = !empty($variables['configuration']['label_display']) ? $variables['configuration']['label'] : '';
$variables['content'] = $variables['elements']['content'];
- // A block's label is configuration: it is static. Allow dynamic labels to be
- // set in the render array.
+ // A block's label is configuration: it is static. Allow dynamic labels to
+ // be set in the render array.
if (isset($variables['elements']['content']['#title']) && !empty($variables['configuration']['label_display'])) {
$variables['label'] = $variables['elements']['content']['#title'];
}
diff --git a/core/modules/block_content/config/optional/views.view.block_content.yml b/core/modules/block_content/config/optional/views.view.block_content.yml
index 1bccbb446467..a8da86eede0b 100644
--- a/core/modules/block_content/config/optional/views.view.block_content.yml
+++ b/core/modules/block_content/config/optional/views.view.block_content.yml
@@ -496,6 +496,7 @@ display:
empty_table: true
caption: ''
description: ''
+ class: ''
row:
type: fields
query:
diff --git a/core/modules/block_content/tests/modules/block_content_test_views/test_views/views.view.test_block_content_redirect_destination.yml b/core/modules/block_content/tests/modules/block_content_test_views/test_views/views.view.test_block_content_redirect_destination.yml
index 8f902c94bda2..df0c7314f9a6 100644
--- a/core/modules/block_content/tests/modules/block_content_test_views/test_views/views.view.test_block_content_redirect_destination.yml
+++ b/core/modules/block_content/tests/modules/block_content_test_views/test_views/views.view.test_block_content_redirect_destination.yml
@@ -63,6 +63,7 @@ display:
type: table
options:
grouping: { }
+ class: ''
row_class: ''
default_row_class: true
override: true
diff --git a/core/modules/ckeditor5/src/HTMLRestrictions.php b/core/modules/ckeditor5/src/HTMLRestrictions.php
index eb35e61b1e93..e10e81ef46da 100644
--- a/core/modules/ckeditor5/src/HTMLRestrictions.php
+++ b/core/modules/ckeditor5/src/HTMLRestrictions.php
@@ -1102,7 +1102,7 @@ final class HTMLRestrictions {
}
/**
- * Extracts the subset of plain tags (attributes omitted) from allowed elements.
+ * Extracts plain tags (attributes omitted) from allowed elements.
*
* @return \Drupal\ckeditor5\HTMLRestrictions
* The extracted subset of the given set of HTML restrictions.
diff --git a/core/modules/ckeditor5/src/Plugin/CKEditor5PluginDefinition.php b/core/modules/ckeditor5/src/Plugin/CKEditor5PluginDefinition.php
index fdce2aceade6..8ef1e8aea449 100644
--- a/core/modules/ckeditor5/src/Plugin/CKEditor5PluginDefinition.php
+++ b/core/modules/ckeditor5/src/Plugin/CKEditor5PluginDefinition.php
@@ -467,7 +467,8 @@ final class CKEditor5PluginDefinition extends PluginDefinition implements Plugin
* Whether this plugin has an asset library to load on the admin UI.
*
* @return bool
- * TRUE if the plugin has an asset library to load on the admin UI, FALSE otherwise.
+ * TRUE if the plugin has an asset library to load on the admin UI, FALSE
+ * otherwise.
*
* @see \Drupal\ckeditor5\Annotation\DrupalAspectsOfCKEditor5Plugin::$admin_library
*/
diff --git a/core/modules/ckeditor5/src/SmartDefaultSettings.php b/core/modules/ckeditor5/src/SmartDefaultSettings.php
index ec3f90fa250c..3c2d0d9ec2c0 100644
--- a/core/modules/ckeditor5/src/SmartDefaultSettings.php
+++ b/core/modules/ckeditor5/src/SmartDefaultSettings.php
@@ -76,7 +76,7 @@ final class SmartDefaultSettings {
}
/**
- * Computes the closest possible equivalent settings for switching to CKEditor 5.
+ * Computes the closest equivalent settings for switching to CKEditor 5.
*
* @param \Drupal\editor\EditorInterface|null $text_editor
* The editor being reconfigured for CKEditor 5; infer the settings based on
diff --git a/core/modules/ckeditor5/tests/src/Unit/HTMLRestrictionsTest.php b/core/modules/ckeditor5/tests/src/Unit/HTMLRestrictionsTest.php
index f512c008ceeb..dfd6c1db1da8 100644
--- a/core/modules/ckeditor5/tests/src/Unit/HTMLRestrictionsTest.php
+++ b/core/modules/ckeditor5/tests/src/Unit/HTMLRestrictionsTest.php
@@ -1131,8 +1131,8 @@ class HTMLRestrictionsTest extends UnitTestCase {
'expected_union' => 'a',
];
yield 'attribute restrictions are different: <ol type=*> vs <ol type="A"> — vice versa' => [
- 'b' => new HTMLRestrictions(['ol' => ['type' => ['A' => TRUE]]]),
- 'a' => new HTMLRestrictions(['ol' => ['type' => TRUE]]),
+ 'a' => new HTMLRestrictions(['ol' => ['type' => ['A' => TRUE]]]),
+ 'b' => new HTMLRestrictions(['ol' => ['type' => TRUE]]),
'expected_diff' => HTMLRestrictions::emptySet(),
'expected_intersection' => 'a',
'expected_union' => 'b',
@@ -1145,8 +1145,8 @@ class HTMLRestrictionsTest extends UnitTestCase {
'expected_union' => 'a',
];
yield 'attribute restrictions are different: <ol type=*> vs <ol type="1"> — vice versa' => [
- 'b' => new HTMLRestrictions(['ol' => ['type' => ['1' => TRUE]]]),
- 'a' => new HTMLRestrictions(['ol' => ['type' => TRUE]]),
+ 'a' => new HTMLRestrictions(['ol' => ['type' => ['1' => TRUE]]]),
+ 'b' => new HTMLRestrictions(['ol' => ['type' => TRUE]]),
'expected_diff' => HTMLRestrictions::emptySet(),
'expected_intersection' => 'a',
'expected_union' => 'b',
diff --git a/core/modules/comment/config/optional/views.view.comment.yml b/core/modules/comment/config/optional/views.view.comment.yml
index a54fb25883dd..f3edd69dc65f 100644
--- a/core/modules/comment/config/optional/views.view.comment.yml
+++ b/core/modules/comment/config/optional/views.view.comment.yml
@@ -823,6 +823,7 @@ display:
empty_table: true
caption: ''
description: ''
+ class: ''
row:
type: fields
query:
diff --git a/core/modules/comment/src/Plugin/views/field/LastTimestamp.php b/core/modules/comment/src/Plugin/views/field/LastTimestamp.php
index c0bd35f30cae..3f1698d12363 100644
--- a/core/modules/comment/src/Plugin/views/field/LastTimestamp.php
+++ b/core/modules/comment/src/Plugin/views/field/LastTimestamp.php
@@ -9,7 +9,7 @@ use Drupal\views\ResultRow;
use Drupal\views\ViewExecutable;
/**
- * Field handler to display the timestamp of a comment with the count of comments.
+ * Displays the timestamp of a comment with the count of comments.
*
* @ingroup views_field_handlers
*/
diff --git a/core/modules/comment/tests/modules/comment_test_views/test_views/views.view.test_comment_operations.yml b/core/modules/comment/tests/modules/comment_test_views/test_views/views.view.test_comment_operations.yml
index fdabeedeed4a..50b74fc66422 100644
--- a/core/modules/comment/tests/modules/comment_test_views/test_views/views.view.test_comment_operations.yml
+++ b/core/modules/comment/tests/modules/comment_test_views/test_views/views.view.test_comment_operations.yml
@@ -68,6 +68,7 @@ display:
type: table
options:
grouping: { }
+ class: ''
row_class: ''
default_row_class: true
override: true
diff --git a/core/modules/comment/tests/modules/comment_test_views/test_views/views.view.test_comment_schema.yml b/core/modules/comment/tests/modules/comment_test_views/test_views/views.view.test_comment_schema.yml
index 04fcb57e2a8b..e174974fe2f8 100644
--- a/core/modules/comment/tests/modules/comment_test_views/test_views/views.view.test_comment_schema.yml
+++ b/core/modules/comment/tests/modules/comment_test_views/test_views/views.view.test_comment_schema.yml
@@ -235,6 +235,7 @@ display:
type: table
options:
grouping: { }
+ class: ''
row_class: ''
default_row_class: false
columns:
diff --git a/core/modules/comment/tests/modules/comment_test_views/test_views/views.view.test_new_comments.yml b/core/modules/comment/tests/modules/comment_test_views/test_views/views.view.test_new_comments.yml
index b2e2cf4a41c2..fb0736cf4486 100644
--- a/core/modules/comment/tests/modules/comment_test_views/test_views/views.view.test_new_comments.yml
+++ b/core/modules/comment/tests/modules/comment_test_views/test_views/views.view.test_new_comments.yml
@@ -38,6 +38,11 @@ display:
type: full
style:
type: table
+ options:
+ grouping: { }
+ class: ''
+ row_class: ''
+ default_row_class: true
row:
type: fields
fields:
diff --git a/core/modules/content_moderation/config/optional/views.view.moderated_content.yml b/core/modules/content_moderation/config/optional/views.view.moderated_content.yml
index ae4fda31e816..f7e80ac1162c 100644
--- a/core/modules/content_moderation/config/optional/views.view.moderated_content.yml
+++ b/core/modules/content_moderation/config/optional/views.view.moderated_content.yml
@@ -765,6 +765,7 @@ display:
empty_table: true
caption: ''
description: ''
+ class: ''
row:
type: fields
query:
diff --git a/core/modules/content_moderation/tests/modules/content_moderation_test_views/config/install/views.view.latest.yml b/core/modules/content_moderation/tests/modules/content_moderation_test_views/config/install/views.view.latest.yml
index 83a6b62da89b..8d2a8519c32f 100644
--- a/core/modules/content_moderation/tests/modules/content_moderation_test_views/config/install/views.view.latest.yml
+++ b/core/modules/content_moderation/tests/modules/content_moderation_test_views/config/install/views.view.latest.yml
@@ -348,6 +348,11 @@ display:
group_items: { }
style:
type: table
+ options:
+ grouping: { }
+ class: ''
+ row_class: ''
+ default_row_class: true
row:
type: fields
query:
diff --git a/core/modules/content_moderation/tests/modules/content_moderation_test_views/config/install/views.view.test_content_moderation_state_sort_base_table.yml b/core/modules/content_moderation/tests/modules/content_moderation_test_views/config/install/views.view.test_content_moderation_state_sort_base_table.yml
index 16ba5ba97ac1..9c1cb4e26dce 100644
--- a/core/modules/content_moderation/tests/modules/content_moderation_test_views/config/install/views.view.test_content_moderation_state_sort_base_table.yml
+++ b/core/modules/content_moderation/tests/modules/content_moderation_test_views/config/install/views.view.test_content_moderation_state_sort_base_table.yml
@@ -206,6 +206,7 @@ display:
type: table
options:
grouping: { }
+ class: ''
row_class: ''
default_row_class: true
columns:
diff --git a/core/modules/content_moderation/tests/modules/content_moderation_test_views/config/install/views.view.test_content_moderation_state_sort_revision_table.yml b/core/modules/content_moderation/tests/modules/content_moderation_test_views/config/install/views.view.test_content_moderation_state_sort_revision_table.yml
index 1e7e3b48ceaf..c2f10a8a729c 100644
--- a/core/modules/content_moderation/tests/modules/content_moderation_test_views/config/install/views.view.test_content_moderation_state_sort_revision_table.yml
+++ b/core/modules/content_moderation/tests/modules/content_moderation_test_views/config/install/views.view.test_content_moderation_state_sort_revision_table.yml
@@ -205,6 +205,7 @@ display:
type: table
options:
grouping: { }
+ class: ''
row_class: ''
default_row_class: true
columns:
diff --git a/core/modules/content_translation/tests/modules/content_translation_test_views/test_views/views.view.test_entity_translations_link.yml b/core/modules/content_translation/tests/modules/content_translation_test_views/test_views/views.view.test_entity_translations_link.yml
index ffa73e8edef8..d5c5c667a00d 100644
--- a/core/modules/content_translation/tests/modules/content_translation_test_views/test_views/views.view.test_entity_translations_link.yml
+++ b/core/modules/content_translation/tests/modules/content_translation_test_views/test_views/views.view.test_entity_translations_link.yml
@@ -37,6 +37,7 @@ display:
style:
type: table
options:
+ class: ''
columns:
name: name
translation_link: translation_link
diff --git a/core/modules/contextual/src/Hook/ContextualThemeHooks.php b/core/modules/contextual/src/Hook/ContextualThemeHooks.php
index 47db1f9bde6a..760a42c97854 100644
--- a/core/modules/contextual/src/Hook/ContextualThemeHooks.php
+++ b/core/modules/contextual/src/Hook/ContextualThemeHooks.php
@@ -38,14 +38,16 @@ class ContextualThemeHooks {
if (isset($element) && is_array($element) && !empty($element['#contextual_links'])) {
$variables['#cache']['contexts'][] = 'user.permissions';
if ($this->currentUser->hasPermission('access contextual links')) {
- // Mark this element as potentially having contextual links attached to it.
+ // Mark this element as potentially having contextual links attached to
+ // it.
$variables['attributes']['class'][] = 'contextual-region';
- // Renders a contextual links placeholder unconditionally, thus not breaking
- // the render cache. Although the empty placeholder is rendered for all
- // users, contextual_page_attachments() only adds the asset library for
- // users with the 'access contextual links' permission, thus preventing
- // unnecessary HTTP requests for users without that permission.
+ // Renders a contextual links placeholder unconditionally, thus not
+ // breaking the render cache. Although the empty placeholder is rendered
+ // for all users, contextual_page_attachments() only adds the asset
+ // library for users with the 'access contextual links' permission, thus
+ // preventing unnecessary HTTP requests for users without that
+ // permission.
$variables['title_suffix']['contextual_links'] = [
'#type' => 'contextual_links_placeholder',
'#id' => _contextual_links_to_id($element['#contextual_links']),
diff --git a/core/modules/datetime/datetime.module b/core/modules/datetime/datetime.module
index 94cfaf6e9063..b5ff7ffa23cb 100644
--- a/core/modules/datetime/datetime.module
+++ b/core/modules/datetime/datetime.module
@@ -17,7 +17,9 @@ use Drupal\field\FieldStorageConfigInterface;
* @param \Drupal\field\FieldStorageConfigInterface $field_storage
* The field storage config entity.
* @param array $data
- * Field view data or FieldViewsDataProvider::defaultFieldImplementation($field_storage) if empty.
+ * Field view data or
+ * FieldViewsDataProvider::defaultFieldImplementation($field_storage) if
+ * empty.
* @param string $column_name
* The schema column name with the datetime value.
*
diff --git a/core/modules/datetime/src/DateTimeViewsHelper.php b/core/modules/datetime/src/DateTimeViewsHelper.php
index 61d959e70efd..5e2f49b9e1d8 100644
--- a/core/modules/datetime/src/DateTimeViewsHelper.php
+++ b/core/modules/datetime/src/DateTimeViewsHelper.php
@@ -29,7 +29,8 @@ class DateTimeViewsHelper {
* @param \Drupal\field\FieldStorageConfigInterface $field_storage
* The field storage config entity.
* @param array $data
- * Field view data or views_field_default_views_data($field_storage) if empty.
+ * Field view data or views_field_default_views_data($field_storage) if
+ * empty.
* @param string $column_name
* The schema column name with the datetime value.
*
diff --git a/core/modules/dblog/config/optional/views.view.watchdog.yml b/core/modules/dblog/config/optional/views.view.watchdog.yml
index 3050881f0de7..224519da1e5c 100644
--- a/core/modules/dblog/config/optional/views.view.watchdog.yml
+++ b/core/modules/dblog/config/optional/views.view.watchdog.yml
@@ -654,6 +654,7 @@ display:
empty_table: false
caption: ''
description: ''
+ class: ''
row:
type: fields
query:
diff --git a/core/modules/field/src/Hook/FieldHooks.php b/core/modules/field/src/Hook/FieldHooks.php
index aa759522f458..274482f9ada6 100644
--- a/core/modules/field/src/Hook/FieldHooks.php
+++ b/core/modules/field/src/Hook/FieldHooks.php
@@ -313,8 +313,8 @@ class FieldHooks {
/**
* Implements hook_ENTITY_TYPE_update() for 'field_storage_config'.
*
- * Reset the field handler settings, when the storage target_type is changed on
- * an entity reference field.
+ * Reset the field handler settings, when the storage target_type is changed
+ * on an entity reference field.
*/
#[Hook('field_storage_config_update')]
public function fieldStorageConfigUpdate(FieldStorageConfigInterface $field_storage): void {
@@ -330,13 +330,14 @@ class FieldHooks {
if ($class !== $item_class && !is_subclass_of($class, $item_class)) {
return;
}
- // If target_type changed, reset the handler in the fields using that storage.
+ // If target_type changed, reset the handler in the fields using that
+ // storage.
if ($field_storage->getSetting('target_type') !== $field_storage->getOriginal()->getSetting('target_type')) {
foreach ($field_storage->getBundles() as $bundle) {
$field = FieldConfig::loadByName($field_storage->getTargetEntityTypeId(), $bundle, $field_storage->getName());
- // Reset the handler settings. This triggers field_field_config_presave(),
- // which will take care of reassigning the handler to the correct
- // derivative for the new target_type.
+ // Reset the handler settings. This triggers
+ // field_field_config_presave(), which will take care of reassigning the
+ // handler to the correct derivative for the new target_type.
$field->setSetting('handler_settings', []);
$field->save();
}
@@ -394,9 +395,9 @@ class FieldHooks {
return;
}
// In case we removed all the target bundles allowed by the field in
- // EntityReferenceItem::onDependencyRemoval() or field_entity_bundle_delete()
- // we have to log a critical message because the field will not function
- // correctly anymore.
+ // EntityReferenceItem::onDependencyRemoval() or
+ // field_entity_bundle_delete() we have to log a critical message because
+ // the field will not function correctly anymore.
$handler_settings = $field->getSetting('handler_settings');
if (isset($handler_settings['target_bundles']) && $handler_settings['target_bundles'] === []) {
\Drupal::logger('entity_reference')->critical('The %field_name entity reference field (entity_type: %entity_type, bundle: %bundle) no longer has any valid bundle it can reference. The field is not working correctly anymore and has to be adjusted.', [
diff --git a/core/modules/field_layout/src/Entity/FieldLayoutEntityDisplayTrait.php b/core/modules/field_layout/src/Entity/FieldLayoutEntityDisplayTrait.php
index 652b72b49ba0..c2fddcda697f 100644
--- a/core/modules/field_layout/src/Entity/FieldLayoutEntityDisplayTrait.php
+++ b/core/modules/field_layout/src/Entity/FieldLayoutEntityDisplayTrait.php
@@ -70,7 +70,7 @@ trait FieldLayoutEntityDisplayTrait {
}
/**
- * Implements \Drupal\field_layout\Display\EntityDisplayWithLayoutInterface::setLayout().
+ * {@inheritdoc}
*/
public function setLayout(LayoutInterface $layout) {
$this->setLayoutId($layout->getPluginId(), $layout->getConfiguration());
@@ -78,7 +78,7 @@ trait FieldLayoutEntityDisplayTrait {
}
/**
- * Implements \Drupal\field_layout\Display\EntityDisplayWithLayoutInterface::getLayout().
+ * {@inheritdoc}
*/
public function getLayout() {
return $this->doGetLayout($this->getLayoutId(), $this->getLayoutSettings());
diff --git a/core/modules/field_ui/src/Form/FieldConfigEditForm.php b/core/modules/field_ui/src/Form/FieldConfigEditForm.php
index 04e6ebb92f8f..d723d93fa900 100644
--- a/core/modules/field_ui/src/Form/FieldConfigEditForm.php
+++ b/core/modules/field_ui/src/Form/FieldConfigEditForm.php
@@ -530,7 +530,8 @@ class FieldConfigEditForm extends EntityForm {
* The parent entity that the field is attached to.
*
* @return \Drupal\Core\TypedData\TypedDataInterface
- * The typed data object representing the field configuration and its default value.
+ * The typed data object representing the field configuration and its
+ * default value.
*/
private function getTypedData(FieldConfigInterface $field_config, FieldableEntityInterface $parent): TypedDataInterface {
// Make sure that typed data manager is re-generating the instance. This
diff --git a/core/modules/field_ui/src/Form/FieldStorageConfigEditForm.php b/core/modules/field_ui/src/Form/FieldStorageConfigEditForm.php
index 4e9aafe8dbc6..2dd200cfe3ef 100644
--- a/core/modules/field_ui/src/Form/FieldStorageConfigEditForm.php
+++ b/core/modules/field_ui/src/Form/FieldStorageConfigEditForm.php
@@ -263,7 +263,8 @@ class FieldStorageConfigEditForm extends EntityForm {
* returns that cardinality or NULL if no cardinality has been enforced.
*
* @return int|null
- * The enforced cardinality as an integer, or NULL if no cardinality is enforced.
+ * The enforced cardinality as an integer, or NULL if no cardinality is
+ * enforced.
*/
protected function getEnforcedCardinality() {
/** @var \Drupal\Core\Field\FieldTypePluginManager $field_type_manager */
diff --git a/core/modules/field_ui/src/Hook/FieldUiHooks.php b/core/modules/field_ui/src/Hook/FieldUiHooks.php
index 3210e2f7c5b4..374cb331fc57 100644
--- a/core/modules/field_ui/src/Hook/FieldUiHooks.php
+++ b/core/modules/field_ui/src/Hook/FieldUiHooks.php
@@ -238,8 +238,8 @@ class FieldUiHooks {
#[Hook('form_field_ui_field_storage_add_form_alter')]
public function formFieldUiFieldStorageAddFormAlter(array &$form) : void {
$optgroup = (string) $this->t('Reference');
- // Move the "Entity reference" option to the end of the list and rename it to
- // "Other".
+ // Move the "Entity reference" option to the end of the list and rename it
+ // to "Other".
unset($form['add']['new_storage_type']['#options'][$optgroup]['entity_reference']);
$form['add']['new_storage_type']['#options'][$optgroup]['entity_reference'] = $this->t('Other…');
}
diff --git a/core/modules/file/config/optional/views.view.files.yml b/core/modules/file/config/optional/views.view.files.yml
index be8ba838966e..c24c710714ad 100644
--- a/core/modules/file/config/optional/views.view.files.yml
+++ b/core/modules/file/config/optional/views.view.files.yml
@@ -761,6 +761,7 @@ display:
empty_table: true
caption: ''
description: ''
+ class: ''
row:
type: fields
query:
@@ -1152,6 +1153,7 @@ display:
empty_table: true
caption: ''
description: ''
+ class: ''
row:
type: fields
options: { }
diff --git a/core/modules/file/tests/modules/file_test_views/config/optional/views.view.test_file_user_file_data.yml b/core/modules/file/tests/modules/file_test_views/config/optional/views.view.test_file_user_file_data.yml
index 7924edd65726..78fd4196bf70 100644
--- a/core/modules/file/tests/modules/file_test_views/config/optional/views.view.test_file_user_file_data.yml
+++ b/core/modules/file/tests/modules/file_test_views/config/optional/views.view.test_file_user_file_data.yml
@@ -28,6 +28,7 @@ display:
type: table
options:
grouping: { }
+ class: ''
row_class: ''
default_row_class: true
override: true
diff --git a/core/modules/filter/filter.module b/core/modules/filter/filter.module
index 89b8e2e1e6f9..38da47a437ed 100644
--- a/core/modules/filter/filter.module
+++ b/core/modules/filter/filter.module
@@ -637,7 +637,7 @@ function _filter_autop($text) {
// and comments. We don't apply any processing to the contents of these tags
// to avoid messing up code. We look for matched pairs and allow basic
// nesting. For example:
- // "processed <pre> ignored <script> ignored </script> ignored </pre> processed"
+ // "processed<pre>ignored<script>ignored</script>ignored</pre>processed"
$chunks = preg_split('@(<!--.*?-->|</?(?:pre|script|style|object|iframe|drupal-media|svg|!--)[^>]*>)@i', $text, -1, PREG_SPLIT_DELIM_CAPTURE);
// Note: PHP ensures the array consists of alternating delimiters and literals
// and begins and ends with a literal (inserting NULL as required).
diff --git a/core/modules/filter/src/Entity/FilterFormat.php b/core/modules/filter/src/Entity/FilterFormat.php
index 12e7846163b6..8b785122dc49 100644
--- a/core/modules/filter/src/Entity/FilterFormat.php
+++ b/core/modules/filter/src/Entity/FilterFormat.php
@@ -99,7 +99,7 @@ class FilterFormat extends ConfigEntityBase implements FilterFormatInterface, En
protected $weight = 0;
/**
- * List of user role IDs to grant access to use this format on initial creation.
+ * List of role IDs to grant access to use this format on initial creation.
*
* This property is always empty and unused for existing text formats.
*
diff --git a/core/modules/filter/src/FilterFormatInterface.php b/core/modules/filter/src/FilterFormatInterface.php
index 62a704a6fcbe..8c63f33f0a6b 100644
--- a/core/modules/filter/src/FilterFormatInterface.php
+++ b/core/modules/filter/src/FilterFormatInterface.php
@@ -10,7 +10,7 @@ use Drupal\Core\Config\Entity\ConfigEntityInterface;
interface FilterFormatInterface extends ConfigEntityInterface {
/**
- * Returns the ordered collection of filter plugin instances or an individual plugin instance.
+ * Returns a sorted collection of filter plugins or an individual instance.
*
* @param string $instance_id
* (optional) The ID of a filter plugin instance to return.
diff --git a/core/modules/image/image.install b/core/modules/image/image.install
index b99e17924ef6..be8fd0272343 100644
--- a/core/modules/image/image.install
+++ b/core/modules/image/image.install
@@ -33,44 +33,6 @@ function image_uninstall(): void {
}
/**
- * Implements hook_requirements().
- */
-function image_requirements($phase): array {
- if ($phase != 'runtime') {
- return [];
- }
-
- $toolkit = \Drupal::service('image.toolkit.manager')->getDefaultToolkit();
- if ($toolkit) {
- $plugin_definition = $toolkit->getPluginDefinition();
- $requirements = [
- 'image.toolkit' => [
- 'title' => t('Image toolkit'),
- 'value' => $toolkit->getPluginId(),
- 'description' => $plugin_definition['title'],
- ],
- ];
-
- foreach ($toolkit->getRequirements() as $key => $requirement) {
- $namespaced_key = 'image.toolkit.' . $toolkit->getPluginId() . '.' . $key;
- $requirements[$namespaced_key] = $requirement;
- }
- }
- else {
- $requirements = [
- 'image.toolkit' => [
- 'title' => t('Image toolkit'),
- 'value' => t('None'),
- 'description' => t("No image toolkit is configured on the site. Check PHP installed extensions or add a contributed toolkit that doesn't require a PHP extension. Make sure that at least one valid image toolkit is installed."),
- 'severity' => REQUIREMENT_ERROR,
- ],
- ];
- }
-
- return $requirements;
-}
-
-/**
* Implements hook_update_last_removed().
*/
function image_update_last_removed(): int {
diff --git a/core/modules/image/src/Hook/ImageRequirements.php b/core/modules/image/src/Hook/ImageRequirements.php
new file mode 100644
index 000000000000..cf631bfe3754
--- /dev/null
+++ b/core/modules/image/src/Hook/ImageRequirements.php
@@ -0,0 +1,57 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Drupal\image\Hook;
+
+use Drupal\Core\Hook\Attribute\Hook;
+use Drupal\Core\ImageToolkit\ImageToolkitManager;
+use Drupal\Core\StringTranslation\StringTranslationTrait;
+
+/**
+ * Requirements for the Image module.
+ */
+class ImageRequirements {
+
+ use StringTranslationTrait;
+
+ public function __construct(
+ protected readonly ImageToolkitManager $imageToolkitManager,
+ ) {}
+
+ /**
+ * Implements hook_runtime_requirements().
+ */
+ #[Hook('runtime_requirements')]
+ public function runtime(): array {
+ $toolkit = $this->imageToolkitManager->getDefaultToolkit();
+ if ($toolkit) {
+ $plugin_definition = $toolkit->getPluginDefinition();
+ $requirements = [
+ 'image.toolkit' => [
+ 'title' => $this->t('Image toolkit'),
+ 'value' => $toolkit->getPluginId(),
+ 'description' => $plugin_definition['title'],
+ ],
+ ];
+
+ foreach ($toolkit->getRequirements() as $key => $requirement) {
+ $namespaced_key = 'image.toolkit.' . $toolkit->getPluginId() . '.' . $key;
+ $requirements[$namespaced_key] = $requirement;
+ }
+ }
+ else {
+ $requirements = [
+ 'image.toolkit' => [
+ 'title' => $this->t('Image toolkit'),
+ 'value' => $this->t('None'),
+ 'description' => $this->t("No image toolkit is configured on the site. Check PHP installed extensions or add a contributed toolkit that doesn't require a PHP extension. Make sure that at least one valid image toolkit is installed."),
+ 'severity' => REQUIREMENT_ERROR,
+ ],
+ ];
+ }
+
+ return $requirements;
+ }
+
+}
diff --git a/core/modules/image/src/Plugin/Field/FieldWidget/ImageWidget.php b/core/modules/image/src/Plugin/Field/FieldWidget/ImageWidget.php
index 4830686472ce..9d58ad40cc70 100644
--- a/core/modules/image/src/Plugin/Field/FieldWidget/ImageWidget.php
+++ b/core/modules/image/src/Plugin/Field/FieldWidget/ImageWidget.php
@@ -108,9 +108,7 @@ class ImageWidget extends FileWidget {
}
/**
- * Overrides \Drupal\file\Plugin\Field\FieldWidget\FileWidget::formMultipleElements().
- *
- * Special handling for draggable multiple widgets and 'add more' button.
+ * {@inheritdoc}
*/
protected function formMultipleElements(FieldItemListInterface $items, array &$form, FormStateInterface $form_state) {
$elements = parent::formMultipleElements($items, $form, $form_state);
diff --git a/core/modules/image/tests/modules/image_test_views/test_views/views.view.test_image_user_image_data.yml b/core/modules/image/tests/modules/image_test_views/test_views/views.view.test_image_user_image_data.yml
index cd3cf68914c4..97b13e5332e3 100644
--- a/core/modules/image/tests/modules/image_test_views/test_views/views.view.test_image_user_image_data.yml
+++ b/core/modules/image/tests/modules/image_test_views/test_views/views.view.test_image_user_image_data.yml
@@ -28,6 +28,7 @@ display:
type: table
options:
grouping: { }
+ class: ''
row_class: ''
default_row_class: true
override: true
diff --git a/core/modules/jsonapi/jsonapi.install b/core/modules/jsonapi/jsonapi.install
index c5a60c424fbe..47efd893ae0e 100644
--- a/core/modules/jsonapi/jsonapi.install
+++ b/core/modules/jsonapi/jsonapi.install
@@ -5,8 +5,6 @@
* Module install file.
*/
-use Drupal\Core\Url;
-
/**
* Implements hook_install().
*/
@@ -28,55 +26,6 @@ function jsonapi_install(): void {
}
/**
- * Implements hook_requirements().
- */
-function jsonapi_requirements($phase): array {
- $requirements = [];
- if ($phase === 'runtime') {
- $module_handler = \Drupal::moduleHandler();
- $potential_conflicts = [
- 'content_translation',
- 'config_translation',
- 'language',
- ];
- $should_warn = array_reduce($potential_conflicts, function ($should_warn, $module_name) use ($module_handler) {
- return $should_warn ?: $module_handler->moduleExists($module_name);
- }, FALSE);
- if ($should_warn) {
- $requirements['jsonapi_multilingual_support'] = [
- 'title' => t('JSON:API multilingual support'),
- 'value' => t('Limited'),
- 'severity' => REQUIREMENT_INFO,
- 'description' => t('Some multilingual features currently do not work well with JSON:API. See the <a href=":jsonapi-docs">JSON:API multilingual support documentation</a> for more information on the current status of multilingual support.', [
- ':jsonapi-docs' => 'https://www.drupal.org/docs/8/modules/jsonapi/translations',
- ]),
- ];
- }
- $requirements['jsonapi_revision_support'] = [
- 'title' => t('JSON:API revision support'),
- 'value' => t('Limited'),
- 'severity' => REQUIREMENT_INFO,
- 'description' => t('Revision support is currently read-only and only for the "Content" and "Media" entity types in JSON:API. See the <a href=":jsonapi-docs">JSON:API revision support documentation</a> for more information on the current status of revision support.', [
- ':jsonapi-docs' => 'https://www.drupal.org/docs/8/modules/jsonapi/revisions',
- ]),
- ];
- $requirements['jsonapi_read_only_mode'] = [
- 'title' => t('JSON:API allowed operations'),
- 'value' => t('Read-only'),
- 'severity' => REQUIREMENT_INFO,
- ];
- if (!\Drupal::configFactory()->get('jsonapi.settings')->get('read_only')) {
- $requirements['jsonapi_read_only_mode']['value'] = t('All (create, read, update, delete)');
- $requirements['jsonapi_read_only_mode']['description'] = t('It is recommended to <a href=":configure-url">configure</a> JSON:API to only accept all operations if the site requires it. <a href=":docs">Learn more about securing your site with JSON:API.</a>', [
- ':docs' => 'https://www.drupal.org/docs/8/modules/jsonapi/security-considerations',
- ':configure-url' => Url::fromRoute('jsonapi.settings')->toString(),
- ]);
- }
- }
- return $requirements;
-}
-
-/**
* Implements hook_update_last_removed().
*/
function jsonapi_update_last_removed(): int {
diff --git a/core/modules/jsonapi/src/Hook/JsonapiHooks.php b/core/modules/jsonapi/src/Hook/JsonapiHooks.php
index 7db75a3297be..7db5b77b0e80 100644
--- a/core/modules/jsonapi/src/Hook/JsonapiHooks.php
+++ b/core/modules/jsonapi/src/Hook/JsonapiHooks.php
@@ -218,7 +218,7 @@ class JsonapiHooks {
public function jsonapiShortcutFilterAccess(EntityTypeInterface $entity_type, AccountInterface $account): array {
// @see \Drupal\shortcut\ShortcutAccessControlHandler::checkAccess()
// \Drupal\jsonapi\Access\TemporaryQueryGuard adds the condition for
- // (shortcut_set = $shortcut_set_storage->getDisplayedToUser($current_user)),
+ // "shortcut_set = $shortcut_set_storage->getDisplayedToUser($current_user)"
// so this does not have to.
return [
JSONAPI_FILTER_AMONG_ALL => AccessResult::allowedIfHasPermission($account, 'administer shortcuts')->orIf(AccessResult::allowedIfHasPermissions($account, [
diff --git a/core/modules/jsonapi/src/Hook/JsonapiRequirements.php b/core/modules/jsonapi/src/Hook/JsonapiRequirements.php
new file mode 100644
index 000000000000..5cc0225e1839
--- /dev/null
+++ b/core/modules/jsonapi/src/Hook/JsonapiRequirements.php
@@ -0,0 +1,72 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Drupal\jsonapi\Hook;
+
+use Drupal\Core\Config\ConfigFactoryInterface;
+use Drupal\Core\Extension\ModuleHandlerInterface;
+use Drupal\Core\Hook\Attribute\Hook;
+use Drupal\Core\StringTranslation\StringTranslationTrait;
+use Drupal\Core\Url;
+
+/**
+ * Requirements for the JSON:API module.
+ */
+class JsonapiRequirements {
+
+ use StringTranslationTrait;
+
+ public function __construct(
+ protected readonly ConfigFactoryInterface $configFactory,
+ protected readonly ModuleHandlerInterface $moduleHandler,
+ ) {}
+
+ /**
+ * Implements hook_runtime_requirements().
+ */
+ #[Hook('runtime_requirements')]
+ public function runtime(): array {
+ $requirements = [];
+ $potential_conflicts = [
+ 'content_translation',
+ 'config_translation',
+ 'language',
+ ];
+ $should_warn = array_reduce($potential_conflicts, function ($should_warn, $module_name) {
+ return $should_warn ?: $this->moduleHandler->moduleExists($module_name);
+ }, FALSE);
+ if ($should_warn) {
+ $requirements['jsonapi_multilingual_support'] = [
+ 'title' => $this->t('JSON:API multilingual support'),
+ 'value' => $this->t('Limited'),
+ 'severity' => REQUIREMENT_INFO,
+ 'description' => $this->t('Some multilingual features currently do not work well with JSON:API. See the <a href=":jsonapi-docs">JSON:API multilingual support documentation</a> for more information on the current status of multilingual support.', [
+ ':jsonapi-docs' => 'https://www.drupal.org/docs/core-modules-and-themes/core-modules/jsonapi-module/translations',
+ ]),
+ ];
+ }
+ $requirements['jsonapi_revision_support'] = [
+ 'title' => $this->t('JSON:API revision support'),
+ 'value' => $this->t('Limited'),
+ 'severity' => REQUIREMENT_INFO,
+ 'description' => $this->t('Revision support is currently read-only and only for the "Content" and "Media" entity types in JSON:API. See the <a href=":jsonapi-docs">JSON:API revision support documentation</a> for more information on the current status of revision support.', [
+ ':jsonapi-docs' => 'https://www.drupal.org/docs/core-modules-and-themes/core-modules/jsonapi-module/revisions',
+ ]),
+ ];
+ $requirements['jsonapi_read_only_mode'] = [
+ 'title' => $this->t('JSON:API allowed operations'),
+ 'value' => $this->t('Read-only'),
+ 'severity' => REQUIREMENT_INFO,
+ ];
+ if (!$this->configFactory->get('jsonapi.settings')->get('read_only')) {
+ $requirements['jsonapi_read_only_mode']['value'] = $this->t('All (create, read, update, delete)');
+ $requirements['jsonapi_read_only_mode']['description'] = $this->t('It is recommended to <a href=":configure-url">configure</a> JSON:API to only accept all operations if the site requires it. <a href=":docs">Learn more about securing your site with JSON:API.</a>', [
+ ':docs' => 'https://www.drupal.org/docs/core-modules-and-themes/core-modules/jsonapi-module/security-considerations',
+ ':configure-url' => Url::fromRoute('jsonapi.settings')->toString(),
+ ]);
+ }
+ return $requirements;
+ }
+
+}
diff --git a/core/modules/jsonapi/src/Normalizer/Value/TemporaryArrayObjectThrowingExceptions.php b/core/modules/jsonapi/src/Normalizer/Value/TemporaryArrayObjectThrowingExceptions.php
index d6e766416b0b..0f3c74f1ef53 100644
--- a/core/modules/jsonapi/src/Normalizer/Value/TemporaryArrayObjectThrowingExceptions.php
+++ b/core/modules/jsonapi/src/Normalizer/Value/TemporaryArrayObjectThrowingExceptions.php
@@ -97,7 +97,7 @@ class TemporaryArrayObjectThrowingExceptions extends \ArrayObject {
}
/**
- * Gets the class name of the array iterator that is used by \ArrayObject::getIterator().
+ * Gets the class name of the iterator used by \ArrayObject::getIterator().
*
* @throws \Exception
* This class does not support this action but it must implement it, because
diff --git a/core/modules/language/src/Form/NegotiationBrowserDeleteForm.php b/core/modules/language/src/Form/NegotiationBrowserDeleteForm.php
index fc87845eb610..28c63580ba70 100644
--- a/core/modules/language/src/Form/NegotiationBrowserDeleteForm.php
+++ b/core/modules/language/src/Form/NegotiationBrowserDeleteForm.php
@@ -8,7 +8,7 @@ use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Url;
/**
- * Defines a confirmation form for deleting a browser language negotiation mapping.
+ * The confirmation form for deleting a browser language negotiation mapping.
*
* @internal
*/
diff --git a/core/modules/language/src/LanguageNegotiatorInterface.php b/core/modules/language/src/LanguageNegotiatorInterface.php
index 6893639889cd..b0ca173d3dbc 100644
--- a/core/modules/language/src/LanguageNegotiatorInterface.php
+++ b/core/modules/language/src/LanguageNegotiatorInterface.php
@@ -165,7 +165,7 @@ interface LanguageNegotiatorInterface {
public function getPrimaryNegotiationMethod($type);
/**
- * Checks whether a language negotiation method is enabled for a language type.
+ * Checks if a language negotiation method is enabled for a language type.
*
* @param string $method_id
* The language negotiation method ID.
diff --git a/core/modules/layout_builder/src/Access/LayoutBuilderAccessCheck.php b/core/modules/layout_builder/src/Access/LayoutBuilderAccessCheck.php
index 7bbb4f514718..193674e5a03c 100644
--- a/core/modules/layout_builder/src/Access/LayoutBuilderAccessCheck.php
+++ b/core/modules/layout_builder/src/Access/LayoutBuilderAccessCheck.php
@@ -54,7 +54,8 @@ class LayoutBuilderAccessCheck implements AccessInterface {
$access = $access->andIf(AccessResult::allowedIfHasPermission($account, 'configure any layout'));
}
- // Disables access to inline blocks add_block routes if the section storage opts out.
+ // Disables access to inline blocks add_block routes if the section storage
+ // opts out.
// Check if inline block access should be disabled.
if ($operation === 'add_block' && !($section_storage->getPluginDefinition()->get('allow_inline_blocks') ?? TRUE)) {
$route_name = $this->route_match->getRouteName();
diff --git a/core/modules/layout_builder/src/Plugin/ConfigAction/AddComponent.php b/core/modules/layout_builder/src/Plugin/ConfigAction/AddComponent.php
new file mode 100644
index 000000000000..57781f55afe6
--- /dev/null
+++ b/core/modules/layout_builder/src/Plugin/ConfigAction/AddComponent.php
@@ -0,0 +1,133 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Drupal\layout_builder\Plugin\ConfigAction;
+
+use Drupal\Component\Uuid\UuidInterface;
+use Drupal\Core\Config\Action\Attribute\ConfigAction;
+use Drupal\Core\Config\Action\ConfigActionException;
+use Drupal\Core\Config\Action\ConfigActionPluginInterface;
+use Drupal\Core\StringTranslation\TranslatableMarkup;
+use Drupal\layout_builder\Plugin\ConfigAction\Deriver\AddComponentDeriver;
+use Drupal\Core\Config\ConfigManagerInterface;
+use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
+use Drupal\layout_builder\SectionComponent;
+use Drupal\layout_builder\SectionListInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Adds a component to a layout builder section.
+ *
+ * An example of using this in a recipe's config actions would be:
+ * @code
+ * dashboard.dashboard.welcome:
+ * addComponentToLayout:
+ * section: 0
+ * position: 4
+ * component:
+ * region:
+ * layout_twocol_section: 'second'
+ * default_region: content
+ * configuration:
+ * id: dashboard_text_block
+ * label: 'My new dashboard block'
+ * label_display: 'visible'
+ * provider: 'dashboard'
+ * context_mapping: { }
+ * text:
+ * value: '<p>My new block text</p>'
+ * format: 'basic_html'
+ * @endcode
+ * This will add a component to a layout region, given by the `section` index.
+ * The `position` will determine where it will be inserted, starting at 0. If is
+ * higher than the actual number of components in the region, it will be placed
+ * last.
+ * The `component` defines the actual component we are adding to the layout.
+ * Sections can have multiple regions. A `region` mapping will determine which
+ * region to use based on the id of the layout. If no matching is found, it will
+ * use the `default_region`.
+ * The `configuration` array will include the plugin configuration, including a
+ * mandatory `id` for the plugin ID. It should validate against the config
+ * schema of the plugin.
+ * The `additional` array will be copied as is, as that is ignored by config
+ * schema.
+ *
+ * @internal
+ * This API is experimental.
+ */
+#[ConfigAction(
+ id: 'add_layout_component',
+ admin_label: new TranslatableMarkup('Add component to layout'),
+ deriver: AddComponentDeriver::class,
+)]
+final class AddComponent implements ConfigActionPluginInterface, ContainerFactoryPluginInterface {
+
+ public function __construct(
+ private readonly ConfigManagerInterface $configManager,
+ private readonly UuidInterface $uuidGenerator,
+ private readonly string $pluginId,
+ ) {}
+
+ /**
+ * {@inheritdoc}
+ */
+ public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition): static {
+ assert(is_array($plugin_definition));
+ return new static(
+ $container->get(ConfigManagerInterface::class),
+ $container->get(UuidInterface::class),
+ $plugin_id,
+ );
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function apply(string $configName, mixed $value): void {
+ assert(is_array($value));
+ $section_delta = $value['section'];
+ $position = $value['position'];
+
+ assert(is_int($section_delta));
+ assert(is_int($position));
+
+ $entity = $this->configManager->loadConfigEntityByName($configName);
+ if (!$entity instanceof SectionListInterface) {
+ throw new ConfigActionException("No entity found for applying the addComponentToLayout action.");
+ }
+
+ $section = $entity->getSection($section_delta);
+ $component = $value['component'];
+ $region = $component['default_region'] ?? NULL;
+ if (array_key_exists('region', $component) && is_array($component['region'])) {
+ // Since the recipe author might not know ahead of time what layout the
+ // section is using, they should supply a map whose keys are layout IDs
+ // and values are region names, so we know where to place this component.
+ // If the section layout ID is not in the map, they should supply the
+ // name of a fallback region. If all that fails, give up with an
+ // exception.
+ $region = $component['region'][$section->getLayoutId()] ??
+ $component['default_region'] ??
+ throw new ConfigActionException("Cannot determine which region of the section to place this component into, because no default region was provided.");
+ }
+ if ($region === NULL) {
+ throw new ConfigActionException("Cannot determine which region of the section to place this component into, because no region was provided.");
+ }
+ if (!isset($value['component']['configuration']) || !isset($value['component']['configuration']['id'])) {
+ throw new ConfigActionException("Cannot determine the component configuration, or misses a plugin ID.");
+ }
+ // If no weight were set, there would be a warning. So we set a
+ // default, which will be overridden in insertComponent anyway.
+ // We also need to generate the UUID here, or it could be null.
+ $uuid = $component['uuid'] ?? $this->uuidGenerator->generate();
+ $component = new SectionComponent($uuid, $region, $component['configuration'], $component['additional'] ?? []);
+ // If the position is higher than the number of components, just put it last
+ // instead of failing.
+ $position = min($position, count($section->getComponentsByRegion($region)));
+ $section->insertComponent($position, $component);
+ $entity->setSection($section_delta, $section);
+ $entity->save();
+ }
+
+}
diff --git a/core/modules/layout_builder/src/Plugin/ConfigAction/Deriver/AddComponentDeriver.php b/core/modules/layout_builder/src/Plugin/ConfigAction/Deriver/AddComponentDeriver.php
new file mode 100644
index 000000000000..5cc1443b64df
--- /dev/null
+++ b/core/modules/layout_builder/src/Plugin/ConfigAction/Deriver/AddComponentDeriver.php
@@ -0,0 +1,48 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Drupal\layout_builder\Plugin\ConfigAction\Deriver;
+
+use Drupal\Component\Plugin\Derivative\DeriverBase;
+use Drupal\Core\Config\Entity\ConfigEntityInterface;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Drupal\Core\Plugin\Discovery\ContainerDeriverInterface;
+use Drupal\layout_builder\SectionListInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * @internal
+ * This API is experimental.
+ */
+final class AddComponentDeriver extends DeriverBase implements ContainerDeriverInterface {
+
+ public function __construct(
+ private readonly EntityTypeManagerInterface $entityTypeManager,
+ ) {}
+
+ /**
+ * {@inheritdoc}
+ */
+ public static function create(ContainerInterface $container, $base_plugin_id): static {
+ return new static(
+ $container->get(EntityTypeManagerInterface::class),
+ );
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getDerivativeDefinitions($base_plugin_definition): array {
+ $entity_types = [];
+ foreach ($this->entityTypeManager->getDefinitions() as $entity_type) {
+ if ($entity_type->entityClassImplements(ConfigEntityInterface::class) && $entity_type->entityClassImplements(SectionListInterface::class)) {
+ $entity_types[] = $entity_type->id();
+ }
+ }
+ $base_plugin_definition['entity_types'] = $entity_types;
+ $this->derivatives['addComponentToLayout'] = $base_plugin_definition;
+ return $this->derivatives;
+ }
+
+}
diff --git a/core/modules/layout_builder/src/Plugin/Derivative/ExtraFieldBlockDeriver.php b/core/modules/layout_builder/src/Plugin/Derivative/ExtraFieldBlockDeriver.php
index 087ef13d85e5..9a0dd6a9782c 100644
--- a/core/modules/layout_builder/src/Plugin/Derivative/ExtraFieldBlockDeriver.php
+++ b/core/modules/layout_builder/src/Plugin/Derivative/ExtraFieldBlockDeriver.php
@@ -146,7 +146,7 @@ class ExtraFieldBlockDeriver extends DeriverBase implements ContainerDeriverInte
}
/**
- * Gets a list of entity type and bundle tuples that have layout builder enabled.
+ * Gets the list of entity type and bundle tuples with layout builder enabled.
*
* @return array
* A structured array with entity type as first key, bundle as second.
diff --git a/core/modules/layout_builder/src/SectionListTrait.php b/core/modules/layout_builder/src/SectionListTrait.php
index cf338fa7654e..625fb58dc233 100644
--- a/core/modules/layout_builder/src/SectionListTrait.php
+++ b/core/modules/layout_builder/src/SectionListTrait.php
@@ -57,7 +57,7 @@ trait SectionListTrait {
*
* @return $this
*/
- protected function setSection($delta, Section $section) {
+ public function setSection($delta, Section $section) {
$sections = $this->getSections();
$sections[$delta] = $section;
$this->setSections($sections);
diff --git a/core/modules/layout_builder/src/SectionStorageInterface.php b/core/modules/layout_builder/src/SectionStorageInterface.php
index ffd559f63aa3..2c9293749649 100644
--- a/core/modules/layout_builder/src/SectionStorageInterface.php
+++ b/core/modules/layout_builder/src/SectionStorageInterface.php
@@ -136,7 +136,7 @@ interface SectionStorageInterface extends SectionListInterface, PluginInspection
public function isApplicable(RefinableCacheableDependencyInterface $cacheability);
/**
- * Overrides \Drupal\Component\Plugin\PluginInspectionInterface::getPluginDefinition().
+ * {@inheritdoc}
*
* @return \Drupal\layout_builder\SectionStorage\SectionStorageDefinition
* The section storage definition.
diff --git a/core/modules/layout_builder/tests/modules/layout_builder_defaults_test/config/schema/layout_builder_defaults_test.schema.yml b/core/modules/layout_builder/tests/modules/layout_builder_defaults_test/config/schema/layout_builder_defaults_test.schema.yml
index cd7d15152603..804642885ef7 100644
--- a/core/modules/layout_builder/tests/modules/layout_builder_defaults_test/config/schema/layout_builder_defaults_test.schema.yml
+++ b/core/modules/layout_builder/tests/modules/layout_builder_defaults_test/config/schema/layout_builder_defaults_test.schema.yml
@@ -4,3 +4,11 @@ layout_builder.section.third_party.layout_builder_defaults_test:
which_party:
label: 'Which party?'
type: string
+
+block.settings.my_plugin_id:
+ type: block_settings
+ label: 'My plugin ID for AddComponentTest'
+ mapping:
+ some_configuration:
+ type: string
+ label: 'Some configuration value'
diff --git a/core/modules/layout_builder/tests/src/Kernel/Plugin/ConfigAction/AddComponentTest.php b/core/modules/layout_builder/tests/src/Kernel/Plugin/ConfigAction/AddComponentTest.php
new file mode 100644
index 000000000000..e91daa37ef7c
--- /dev/null
+++ b/core/modules/layout_builder/tests/src/Kernel/Plugin/ConfigAction/AddComponentTest.php
@@ -0,0 +1,401 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Drupal\Tests\layout_builder\Kernel\Plugin\ConfigAction;
+
+use Drupal\Core\Config\Action\ConfigActionException;
+use Drupal\Core\Config\Action\ConfigActionManager;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Drupal\entity_test\EntityTestHelper;
+use Drupal\KernelTests\KernelTestBase;
+use Drupal\layout_builder\Plugin\SectionStorage\DefaultsSectionStorage;
+use Drupal\layout_builder\Section;
+use Drupal\layout_builder\SectionListInterface;
+use Drupal\layout_builder\SectionStorage\SectionStorageManagerInterface;
+
+/**
+ * @coversDefaultClass \Drupal\layout_builder\Plugin\ConfigAction\AddComponent
+ *
+ * @group layout_builder
+ */
+class AddComponentTest extends KernelTestBase {
+
+ /**
+ * {@inheritdoc}
+ */
+ protected static $modules = [
+ 'layout_discovery',
+ 'layout_builder',
+ 'layout_builder_defaults_test',
+ 'entity_test',
+ 'field',
+ 'system',
+ 'user',
+ ];
+
+ /**
+ * The plugin.
+ */
+ private readonly DefaultsSectionStorage $plugin;
+
+ /**
+ * The config action manager.
+ */
+ private readonly ConfigActionManager $configActionManager;
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function setUp(): void {
+ parent::setUp();
+
+ EntityTestHelper::createBundle('bundle_with_extra_fields');
+ $this->installEntitySchema('entity_test');
+ $this->installEntitySchema('user');
+ $this->installConfig(['layout_builder_defaults_test']);
+
+ $this->plugin = $this->container->get(SectionStorageManagerInterface::class)->createInstance('defaults');
+ $this->configActionManager = $this->container->get('plugin.manager.config_action');
+
+ // Add some extra empty sections.
+ $view_display = $this->container->get(EntityTypeManagerInterface::class)
+ ->getStorage('entity_view_display')
+ ->load('entity_test.bundle_with_extra_fields.default');
+ assert($view_display instanceof SectionListInterface);
+ $view_display->insertSection(1, new Section('layout_onecol'));
+ $view_display->insertSection(2, new Section('layout_threecol_25_50_25'));
+ $view_display->save();
+ }
+
+ /**
+ * Tests adding a component to a view display using a config action.
+ *
+ * @dataProvider provider
+ */
+ public function testAddComponent(array $config_action_value, string $expected_region, int $added_component_expected_weight, int $existing_component_expected_weight, ?array $expected_error = NULL): void {
+ if ($expected_error !== NULL) {
+ $this->expectException($expected_error[0]);
+ $this->expectExceptionMessage($expected_error[1]);
+ }
+ $this->configActionManager->applyAction(
+ 'addComponentToLayout',
+ 'core.entity_view_display.entity_test.bundle_with_extra_fields.default',
+ $config_action_value,
+ );
+
+ $view_display = $this->container->get(EntityTypeManagerInterface::class)
+ ->getStorage('entity_view_display')
+ ->load('entity_test.bundle_with_extra_fields.default');
+ $this->plugin->setContextValue('display', $view_display);
+ $components = $this->plugin->getSection(0)->getComponents();
+ $uuid = end($components)->getUuid();
+
+ // If we pass the same existing UUID, we replace it.
+ $is_replacing = $added_component_expected_weight === $existing_component_expected_weight;
+ $expected_existing_plugin = $is_replacing ? 'my_plugin_id' : 'extra_field_block:entity_test:bundle_with_extra_fields:display_extra_field';
+ $this->assertCount($is_replacing ? 1 : 2, $components);
+ $this->assertSame($expected_existing_plugin, $components['1445597a-c674-431d-ac0a-277d99347a7f']->getPluginId());
+ $this->assertSame('my_plugin_id', $components[$uuid]->getPluginId());
+ $this->assertSame($expected_region, $components[$uuid]->getRegion());
+ $this->assertSame($added_component_expected_weight, $components[$uuid]->getWeight());
+ // Assert weight of the existing component in the layout_twocol_section
+ // first region.
+ $this->assertSame($existing_component_expected_weight, $components['1445597a-c674-431d-ac0a-277d99347a7f']->getWeight());
+ // Assert the component configuration (defined with its config schema), and the
+ // additional configuration (ignored in config schema)
+ $this->assertSame($config_action_value['component']['configuration'], $components[$uuid]->get('configuration'));
+ $this->assertSame($config_action_value['component']['additional'] ?? [], $components[$uuid]->get('additional'));
+ }
+
+ /**
+ * Data provider for testAddComponent.
+ */
+ public static function provider(): \Generator {
+ yield 'add component at first position of a non-empty region' => [
+ [
+ 'section' => 0,
+ 'position' => 0,
+ 'component' => [
+ 'region' => [
+ 'layout_test_plugin' => 'content',
+ 'layout_twocol_section' => 'first',
+ ],
+ 'default_region' => 'content',
+ 'configuration' => [
+ 'id' => 'my_plugin_id',
+ ],
+ ],
+ ],
+ 'first',
+ 1,
+ 2,
+ ];
+ yield 'edit existing component by giving the same uuid' => [
+ [
+ 'section' => 0,
+ 'position' => 0,
+ 'component' => [
+ 'uuid' => '1445597a-c674-431d-ac0a-277d99347a7f',
+ 'region' => [
+ 'layout_test_plugin' => 'content',
+ 'layout_twocol_section' => 'first',
+ ],
+ 'default_region' => 'content',
+ 'configuration' => [
+ 'id' => 'my_plugin_id',
+ ],
+ ],
+ ],
+ 'first',
+ 1,
+ 1,
+ ];
+ yield 'add component at second position of a non-empty region' => [
+ [
+ 'section' => 0,
+ 'position' => 1,
+ 'component' => [
+ 'region' => [
+ 'layout_test_plugin' => 'content',
+ 'layout_twocol_section' => 'first',
+ ],
+ 'default_region' => 'content',
+ 'configuration' => [
+ 'id' => 'my_plugin_id',
+ 'some_configuration' => 'my_configuration_value',
+ ],
+ 'additional' => [
+ 'some_additional_value' => 'my_custom_value',
+ ],
+ ],
+ ],
+ 'first',
+ 2,
+ 1,
+ ];
+ yield 'add component at a position larger than the region size on an empty region' => [
+ [
+ 'section' => 0,
+ 'position' => 4,
+ 'component' => [
+ 'region' => [
+ 'layout_test_plugin' => 'content',
+ 'layout_twocol_section' => 'second',
+ ],
+ 'default_region' => 'content',
+ 'configuration' => [
+ 'id' => 'my_plugin_id',
+ 'some_configuration' => 'my_configuration_value',
+ ],
+ 'additional' => [
+ 'some_additional_value' => 'my_custom_value',
+ ],
+ ],
+ ],
+ 'second',
+ // As there is no other block in that section's region, weight is 0 no matter
+ // of the 4th position we asked for.
+ 0,
+ 1,
+ ];
+ yield 'add component at a region not defined in the mapping' => [
+ [
+ 'section' => 0,
+ 'position' => 4,
+ 'component' => [
+ 'region' => [
+ 'layout_test_plugin' => 'content',
+ ],
+ 'default_region' => 'second',
+ 'configuration' => [
+ 'id' => 'my_plugin_id',
+ ],
+ ],
+ ],
+ // Assigned to the default region, as no mapping matched.
+ 'second',
+ 0,
+ 1,
+ ];
+ yield 'add component at a region defined in the mapping while no default region exist' => [
+ [
+ 'section' => 0,
+ 'position' => 4,
+ 'component' => [
+ 'region' => [
+ 'layout_twocol_section' => 'second',
+ ],
+ 'configuration' => [
+ 'id' => 'my_plugin_id',
+ ],
+ ],
+ ],
+ // Assigned to the matching region, even if no default_region.
+ 'second',
+ 0,
+ 1,
+ ];
+ yield 'add component with only default_region and no region mapping' => [
+ [
+ 'section' => 0,
+ 'position' => 4,
+ 'component' => [
+ 'default_region' => 'second',
+ 'configuration' => [
+ 'id' => 'my_plugin_id',
+ ],
+ ],
+ ],
+ // Assigned to the default region, even with no mapping.
+ 'second',
+ 0,
+ 1,
+ ];
+ yield 'exception when cannot determine a region with mapping and default' => [
+ [
+ 'section' => 0,
+ 'position' => 4,
+ 'component' => [
+ 'region' => [
+ 'layout_test_plugin' => 'content',
+ ],
+ 'configuration' => [
+ 'id' => 'my_plugin_id',
+ ],
+ ],
+ ],
+ 'second',
+ 0,
+ 1,
+ // No default_region, no matching region, so we error.
+ [
+ ConfigActionException::class,
+ 'Cannot determine which region of the section to place this component into, because no default region was provided.',
+ ],
+ yield 'exception when no region given' => [
+ [
+ 'section' => 0,
+ 'position' => 4,
+ 'component' => [
+ 'configuration' => [
+ 'id' => 'my_plugin_id',
+ ],
+ ],
+ ],
+ 'second',
+ 0,
+ 1,
+ // No default_region, no matching region, so we error.
+ [
+ ConfigActionException::class,
+ 'Cannot determine which region of the section to place this component into, because no region was provided.',
+ ],
+ ],
+ yield 'exception when no configuration given' => [
+ [
+ 'section' => 0,
+ 'position' => 4,
+ 'component' => [
+ 'region' => [
+ 'layout_test_plugin' => 'content',
+ ],
+ 'default_region' => 'content',
+ ],
+ ],
+ 'second',
+ 0,
+ 1,
+ // No component configuration.
+ [
+ ConfigActionException::class,
+ 'Cannot determine the component configuration, or misses a plugin ID.',
+ ],
+ ],
+ yield 'exception when no id in configuration is given' => [
+ [
+ 'section' => 0,
+ 'position' => 4,
+ 'component' => [
+ 'region' => [
+ 'layout_test_plugin' => 'content',
+ ],
+ 'default_region' => 'content',
+ 'configuration' => [
+ 'no_id' => 'my_plugin_id',
+ ],
+ ],
+ ],
+ 'second',
+ 0,
+ 1,
+ // No component configuration id.
+ [
+ ConfigActionException::class,
+ 'Cannot determine the component configuration, or misses a plugin ID.',
+ ],
+ ],
+
+ ];
+ }
+
+ /**
+ * Tests that adding a component to another section works as expected.
+ */
+ public function testAddComponentToEmptyRegionThatIsNotFirst(): void {
+ $this->configActionManager->applyAction(
+ 'addComponentToLayout',
+ 'core.entity_view_display.entity_test.bundle_with_extra_fields.default',
+ [
+ 'section' => 2,
+ 'position' => 4,
+ 'component' => [
+ 'region' => [
+ 'layout_twocol_section' => 'second',
+ 'layout_threecol_25_50_25' => 'bottom',
+ ],
+ 'default_region' => 'content',
+ 'configuration' => [
+ 'id' => 'my_plugin_id',
+ ],
+ ],
+ ]);
+ $view_display = $this->container->get(EntityTypeManagerInterface::class)
+ ->getStorage('entity_view_display')
+ ->load('entity_test.bundle_with_extra_fields.default');
+ $this->plugin->setContextValue('display', $view_display);
+
+ $this->assertCount(1, $this->plugin->getSection(0)->getComponents());
+ $this->assertCount(0, $this->plugin->getSection(1)->getComponents());
+ $this->assertCount(1, $this->plugin->getSection(2)->getComponents());
+
+ $components = $this->plugin->getSection(2)->getComponents();
+ $uuid = end($components)->getUuid();
+
+ $this->assertSame('bottom', $components[$uuid]->getRegion());
+ $this->assertSame(0, $components[$uuid]->getWeight());
+ $this->assertSame(['id' => 'my_plugin_id'], $components[$uuid]->get('configuration'));
+ }
+
+ /**
+ * Tests that applying the config action to a missing entity fails.
+ */
+ public function testActionFailsIfEntityNotFound(): void {
+ $this->expectException(ConfigActionException::class);
+ $this->expectExceptionMessage('No entity found for applying the addComponentToLayout action.');
+ $this->configActionManager->applyAction(
+ 'addComponentToLayout',
+ 'core.entity_view_display.entity_test.bundle_with_extra_fields.missing_view_mode',
+ [
+ 'section' => 0,
+ 'position' => 4,
+ 'component' => [
+ 'default_region' => 'content',
+ 'configuration' => [
+ 'id' => 'my_plugin_id',
+ ],
+ ],
+ ]);
+ }
+
+}
diff --git a/core/modules/layout_discovery/layout_discovery.install b/core/modules/layout_discovery/layout_discovery.install
deleted file mode 100644
index 28222bc093ba..000000000000
--- a/core/modules/layout_discovery/layout_discovery.install
+++ /dev/null
@@ -1,22 +0,0 @@
-<?php
-
-/**
- * @file
- * Install, update, and uninstall functions for the Layout Discovery module.
- */
-
-/**
- * Implements hook_requirements().
- */
-function layout_discovery_requirements($phase): array {
- $requirements = [];
- if ($phase === 'install') {
- if (\Drupal::moduleHandler()->moduleExists('layout_plugin')) {
- $requirements['layout_discovery'] = [
- 'description' => t('Layout Discovery cannot be installed because the Layout Plugin module is installed and incompatible.'),
- 'severity' => REQUIREMENT_ERROR,
- ];
- }
- }
- return $requirements;
-}
diff --git a/core/modules/layout_discovery/src/Install/Requirements/LayoutDiscoveryRequirements.php b/core/modules/layout_discovery/src/Install/Requirements/LayoutDiscoveryRequirements.php
new file mode 100644
index 000000000000..d6b8ef7fc24d
--- /dev/null
+++ b/core/modules/layout_discovery/src/Install/Requirements/LayoutDiscoveryRequirements.php
@@ -0,0 +1,28 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Drupal\layout_discovery\Install\Requirements;
+
+use Drupal\Core\Extension\InstallRequirementsInterface;
+
+/**
+ * Install time requirements for the layout_discovery module.
+ */
+class LayoutDiscoveryRequirements implements InstallRequirementsInterface {
+
+ /**
+ * {@inheritdoc}
+ */
+ public static function getRequirements(): array {
+ $requirements = [];
+ if (\Drupal::moduleHandler()->moduleExists('layout_plugin')) {
+ $requirements['layout_discovery'] = [
+ 'description' => t('Layout Discovery cannot be installed because the Layout Plugin module is installed and incompatible.'),
+ 'severity' => REQUIREMENT_ERROR,
+ ];
+ }
+ return $requirements;
+ }
+
+}
diff --git a/core/modules/locale/locale.install b/core/modules/locale/locale.install
index f583df4426eb..457d37890fa0 100644
--- a/core/modules/locale/locale.install
+++ b/core/modules/locale/locale.install
@@ -8,72 +8,6 @@
use Drupal\Core\File\Exception\FileException;
use Drupal\Core\File\FileSystemInterface;
use Drupal\Core\Hook\Attribute\ProceduralHookScanStop;
-use Drupal\Core\Link;
-use Drupal\Core\Url;
-
-/**
- * Implements hook_requirements().
- */
-function locale_requirements($phase): array {
- $requirements = [];
- if ($phase == 'runtime') {
- $available_updates = [];
- $untranslated = [];
- $languages = locale_translatable_language_list();
-
- if ($languages) {
- // Determine the status of the translation updates per language.
- $status = locale_translation_get_status();
- if ($status) {
- foreach ($status as $project) {
- foreach ($project as $langcode => $project_info) {
- if (empty($project_info->type)) {
- $untranslated[$langcode] = $languages[$langcode]->getName();
- }
- elseif ($project_info->type == LOCALE_TRANSLATION_LOCAL || $project_info->type == LOCALE_TRANSLATION_REMOTE) {
- $available_updates[$langcode] = $languages[$langcode]->getName();
- }
- }
- }
-
- if ($available_updates || $untranslated) {
- if ($available_updates) {
- $requirements['locale_translation'] = [
- 'title' => t('Translation update status'),
- 'value' => Link::fromTextAndUrl(t('Updates available'), Url::fromRoute('locale.translate_status'))->toString(),
- 'severity' => REQUIREMENT_WARNING,
- 'description' => t('Updates available for: @languages. See the <a href=":updates">Available translation updates</a> page for more information.', ['@languages' => implode(', ', $available_updates), ':updates' => Url::fromRoute('locale.translate_status')->toString()]),
- ];
- }
- else {
- $requirements['locale_translation'] = [
- 'title' => t('Translation update status'),
- 'value' => t('Missing translations'),
- 'severity' => REQUIREMENT_INFO,
- 'description' => t('Missing translations for: @languages. See the <a href=":updates">Available translation updates</a> page for more information.', ['@languages' => implode(', ', $untranslated), ':updates' => Url::fromRoute('locale.translate_status')->toString()]),
- ];
- }
- }
- else {
- $requirements['locale_translation'] = [
- 'title' => t('Translation update status'),
- 'value' => t('Up to date'),
- 'severity' => REQUIREMENT_OK,
- ];
- }
- }
- else {
- $requirements['locale_translation'] = [
- 'title' => t('Translation update status'),
- 'value' => Link::fromTextAndUrl(t('Can not determine status'), Url::fromRoute('locale.translate_status'))->toString(),
- 'severity' => REQUIREMENT_WARNING,
- 'description' => t('No translation status is available. See the <a href=":updates">Available translation updates</a> page for more information.', [':updates' => Url::fromRoute('locale.translate_status')->toString()]),
- ];
- }
- }
- }
- return $requirements;
-}
/**
* Implements hook_install().
diff --git a/core/modules/locale/src/Hook/LocaleRequirements.php b/core/modules/locale/src/Hook/LocaleRequirements.php
new file mode 100644
index 000000000000..6664a64d42bb
--- /dev/null
+++ b/core/modules/locale/src/Hook/LocaleRequirements.php
@@ -0,0 +1,82 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Drupal\locale\Hook;
+
+use Drupal\Core\Hook\Attribute\Hook;
+use Drupal\Core\Link;
+use Drupal\Core\StringTranslation\StringTranslationTrait;
+use Drupal\Core\Url;
+
+/**
+ * Requirements for the Locale module.
+ */
+class LocaleRequirements {
+
+ use StringTranslationTrait;
+
+ /**
+ * Implements hook_runtime_requirements().
+ */
+ #[Hook('runtime_requirements')]
+ public function runtime(): array {
+ $requirements = [];
+ $available_updates = [];
+ $untranslated = [];
+ $languages = locale_translatable_language_list();
+
+ if ($languages) {
+ // Determine the status of the translation updates per language.
+ $status = locale_translation_get_status();
+ if ($status) {
+ foreach ($status as $project) {
+ foreach ($project as $langcode => $project_info) {
+ if (empty($project_info->type)) {
+ $untranslated[$langcode] = $languages[$langcode]->getName();
+ }
+ elseif ($project_info->type == LOCALE_TRANSLATION_LOCAL || $project_info->type == LOCALE_TRANSLATION_REMOTE) {
+ $available_updates[$langcode] = $languages[$langcode]->getName();
+ }
+ }
+ }
+
+ if ($available_updates || $untranslated) {
+ if ($available_updates) {
+ $requirements['locale_translation'] = [
+ 'title' => $this->t('Translation update status'),
+ 'value' => Link::fromTextAndUrl($this->t('Updates available'), Url::fromRoute('locale.translate_status'))->toString(),
+ 'severity' => REQUIREMENT_WARNING,
+ 'description' => $this->t('Updates available for: @languages. See the <a href=":updates">Available translation updates</a> page for more information.', ['@languages' => implode(', ', $available_updates), ':updates' => Url::fromRoute('locale.translate_status')->toString()]),
+ ];
+ }
+ else {
+ $requirements['locale_translation'] = [
+ 'title' => $this->t('Translation update status'),
+ 'value' => $this->t('Missing translations'),
+ 'severity' => REQUIREMENT_INFO,
+ 'description' => $this->t('Missing translations for: @languages. See the <a href=":updates">Available translation updates</a> page for more information.', ['@languages' => implode(', ', $untranslated), ':updates' => Url::fromRoute('locale.translate_status')->toString()]),
+ ];
+ }
+ }
+ else {
+ $requirements['locale_translation'] = [
+ 'title' => $this->t('Translation update status'),
+ 'value' => $this->t('Up to date'),
+ 'severity' => REQUIREMENT_OK,
+ ];
+ }
+ }
+ else {
+ $requirements['locale_translation'] = [
+ 'title' => $this->t('Translation update status'),
+ 'value' => Link::fromTextAndUrl($this->t('Can not determine status'), Url::fromRoute('locale.translate_status'))->toString(),
+ 'severity' => REQUIREMENT_WARNING,
+ 'description' => $this->t('No translation status is available. See the <a href=":updates">Available translation updates</a> page for more information.', [':updates' => Url::fromRoute('locale.translate_status')->toString()]),
+ ];
+ }
+ }
+ return $requirements;
+ }
+
+}
diff --git a/core/modules/locale/src/LocaleConfigManager.php b/core/modules/locale/src/LocaleConfigManager.php
index 6756e85aa41c..133bf7518e94 100644
--- a/core/modules/locale/src/LocaleConfigManager.php
+++ b/core/modules/locale/src/LocaleConfigManager.php
@@ -289,9 +289,9 @@ class LocaleConfigManager {
* Gets configuration names associated with components.
*
* @param array $components
- * (optional) An associative array containing component types as keys and lists
- * of components as values. If not provided or is empty, the method returns all
- * configuration names.
+ * (optional) An associative array containing component types as keys and
+ * lists of components as values. If not provided or is empty, the method
+ * returns all configuration names.
*
* @return array
* Array of configuration object names.
diff --git a/core/modules/locale/src/StreamWrapper/TranslationsStream.php b/core/modules/locale/src/StreamWrapper/TranslationsStream.php
index 980c048e290b..40517477379c 100644
--- a/core/modules/locale/src/StreamWrapper/TranslationsStream.php
+++ b/core/modules/locale/src/StreamWrapper/TranslationsStream.php
@@ -44,6 +44,7 @@ class TranslationsStream extends LocalStream {
}
/**
+ * phpcs:ignore Drupal.Files.LineLength
* Implements Drupal\Core\StreamWrapper\StreamWrapperInterface::getExternalUrl().
*
* @throws \LogicException
diff --git a/core/modules/media/config/optional/views.view.media.yml b/core/modules/media/config/optional/views.view.media.yml
index c7a32129b33e..329ac2433870 100644
--- a/core/modules/media/config/optional/views.view.media.yml
+++ b/core/modules/media/config/optional/views.view.media.yml
@@ -857,6 +857,7 @@ display:
empty_table: true
caption: ''
description: ''
+ class: ''
row:
type: fields
query:
diff --git a/core/modules/media/media.install b/core/modules/media/media.install
index 48ed664fdef7..62b2eece6193 100644
--- a/core/modules/media/media.install
+++ b/core/modules/media/media.install
@@ -9,107 +9,9 @@ use Drupal\Core\File\Exception\FileException;
use Drupal\Core\File\FileExists;
use Drupal\Core\File\FileSystemInterface;
use Drupal\Core\Hook\Attribute\ProceduralHookScanStop;
-use Drupal\Core\StringTranslation\TranslatableMarkup;
-use Drupal\Core\Url;
-use Drupal\image\Plugin\Field\FieldType\ImageItem;
-use Drupal\media\Entity\MediaType;
use Drupal\user\RoleInterface;
/**
- * Implements hook_requirements().
- */
-function media_requirements($phase): array {
- $requirements = [];
- if ($phase == 'install') {
- $destination = 'public://media-icons/generic';
- \Drupal::service('file_system')->prepareDirectory($destination, FileSystemInterface::CREATE_DIRECTORY | FileSystemInterface::MODIFY_PERMISSIONS);
- $is_writable = is_writable($destination);
- $is_directory = is_dir($destination);
- if (!$is_writable || !$is_directory) {
- if (!$is_directory) {
- $error = t('The directory %directory does not exist.', ['%directory' => $destination]);
- }
- else {
- $error = t('The directory %directory is not writable.', ['%directory' => $destination]);
- }
- $description = t('An automated attempt to create this directory failed, possibly due to a permissions problem. To proceed with the installation, either create the directory and modify its permissions manually or ensure that the installer has the permissions to create it automatically. For more information, see INSTALL.txt or the <a href=":handbook_url">online handbook</a>.', [':handbook_url' => 'https://www.drupal.org/server-permissions']);
- if (!empty($error)) {
- $description = $error . ' ' . $description;
- $requirements['media']['description'] = $description;
- $requirements['media']['severity'] = REQUIREMENT_ERROR;
- }
- }
- }
- elseif ($phase === 'runtime') {
- $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)) {
- $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;
- }
-
- $component = $display->getComponent($source_field_definition->getName());
- if (empty($component) || $component['type'] !== 'image' || !empty($component['settings']['image_style'])) {
- continue;
- }
-
- $action_item = '';
- if ($module_handler->moduleExists('field_ui') && \Drupal::currentUser()->hasPermission('administer media display')) {
- $url = Url::fromRoute('entity.entity_view_display.media.default', [
- 'media_type' => $type->id(),
- ])->toString();
- $action_item = new TranslatableMarkup('If you would like to change this, <a href=":display">add an image style to the %field_name field</a>.',
- [
- '%field_name' => $source_field_definition->label(),
- ':display' => $url,
- ]);
- }
- $requirements['media_default_image_style_' . $type->id()] = [
- 'title' => t('Media'),
- 'description' => new TranslatableMarkup('The default display for the %type media type is not currently using an image style on the %field_name field. Not using an image style can lead to much larger file downloads. @action_item',
- [
- '%field_name' => $source_field_definition->label(),
- '@action_item' => $action_item,
- '%type' => $type->label(),
- ]
- ),
- 'severity' => REQUIREMENT_WARNING,
- ];
- }
-
- }
-
- return $requirements;
-}
-
-/**
* Implements hook_install().
*/
#[ProceduralHookScanStop]
diff --git a/core/modules/media/src/Hook/MediaRequirementsHooks.php b/core/modules/media/src/Hook/MediaRequirementsHooks.php
new file mode 100644
index 000000000000..cedbb2fd8200
--- /dev/null
+++ b/core/modules/media/src/Hook/MediaRequirementsHooks.php
@@ -0,0 +1,98 @@
+<?php
+
+namespace Drupal\media\Hook;
+
+use Drupal\Core\Entity\EntityDisplayRepositoryInterface;
+use Drupal\Core\Extension\ModuleHandlerInterface;
+use Drupal\Core\Hook\Attribute\Hook;
+use Drupal\Core\Session\AccountInterface;
+use Drupal\Core\StringTranslation\StringTranslationTrait;
+use Drupal\Core\StringTranslation\TranslatableMarkup;
+use Drupal\Core\Url;
+use Drupal\image\Plugin\Field\FieldType\ImageItem;
+use Drupal\media\Entity\MediaType;
+
+/**
+ * Requirements checks for Media module.
+ */
+class MediaRequirementsHooks {
+
+ use StringTranslationTrait;
+
+ public function __construct(
+ protected readonly AccountInterface $currentUser,
+ protected readonly ModuleHandlerInterface $moduleHandler,
+ protected readonly EntityDisplayRepositoryInterface $entityDisplayRepository,
+ ) {}
+
+ /**
+ * Implements hook_runtime_requirements().
+ */
+ #[Hook('runtime_requirements')]
+ public function runtime(): array {
+ $requirements = [];
+ foreach (MediaType::loadMultiple() as $type) {
+ // Load the default display.
+ $display = $this->entityDisplayRepository->getViewDisplay('media', $type->id());
+
+ // Check for missing source field definition.
+ $source_field_definition = $type->getSource()->getSourceFieldDefinition($type);
+ if (empty($source_field_definition)) {
+ $requirements['media_missing_source_field_' . $type->id()] = [
+ 'title' => $this->t('Media'),
+ 'description' => $this->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;
+ }
+
+ $component = $display->getComponent($source_field_definition->getName());
+ if (empty($component) || $component['type'] !== 'image' || !empty($component['settings']['image_style'])) {
+ continue;
+ }
+
+ $action_item = '';
+ if ($this->moduleHandler->moduleExists('field_ui') && $this->currentUser->hasPermission('administer media display')) {
+ $url = Url::fromRoute('entity.entity_view_display.media.default', [
+ 'media_type' => $type->id(),
+ ])->toString();
+ $action_item = new TranslatableMarkup('If you would like to change this, <a href=":display">add an image style to the %field_name field</a>.',
+ [
+ '%field_name' => $source_field_definition->label(),
+ ':display' => $url,
+ ]);
+ }
+ $requirements['media_default_image_style_' . $type->id()] = [
+ 'title' => $this->t('Media'),
+ 'description' => new TranslatableMarkup('The default display for the %type media type is not currently using an image style on the %field_name field. Not using an image style can lead to much larger file downloads. @action_item',
+ [
+ '%field_name' => $source_field_definition->label(),
+ '@action_item' => $action_item,
+ '%type' => $type->label(),
+ ]
+ ),
+ 'severity' => REQUIREMENT_WARNING,
+ ];
+ }
+
+ return $requirements;
+ }
+
+}
diff --git a/core/modules/media/src/Install/Requirements/MediaRequirements.php b/core/modules/media/src/Install/Requirements/MediaRequirements.php
new file mode 100644
index 000000000000..a69a79aaf811
--- /dev/null
+++ b/core/modules/media/src/Install/Requirements/MediaRequirements.php
@@ -0,0 +1,39 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Drupal\media\Install\Requirements;
+
+use Drupal\Core\Extension\InstallRequirementsInterface;
+use Drupal\Core\File\FileSystemInterface;
+
+/**
+ * Install time requirements for the media module.
+ */
+class MediaRequirements implements InstallRequirementsInterface {
+
+ /**
+ * {@inheritdoc}
+ */
+ public static function getRequirements(): array {
+ $requirements = [];
+ $destination = 'public://media-icons/generic';
+ \Drupal::service('file_system')->prepareDirectory($destination, FileSystemInterface::CREATE_DIRECTORY | FileSystemInterface::MODIFY_PERMISSIONS);
+ $is_writable = is_writable($destination);
+ $is_directory = is_dir($destination);
+ if (!$is_writable || !$is_directory) {
+ if (!$is_directory) {
+ $error = t('The directory %directory does not exist.', ['%directory' => $destination]);
+ }
+ else {
+ $error = t('The directory %directory is not writable.', ['%directory' => $destination]);
+ }
+ $description = t('An automated attempt to create this directory failed, possibly due to a permissions problem. To proceed with the installation, either create the directory and modify its permissions manually or ensure that the installer has the permissions to create it automatically. For more information, see INSTALL.txt or the <a href=":handbook_url">online handbook</a>.', [':handbook_url' => 'https://www.drupal.org/server-permissions']);
+ $description = $error . ' ' . $description;
+ $requirements['media']['description'] = $description;
+ $requirements['media']['severity'] = REQUIREMENT_ERROR;
+ }
+ return $requirements;
+ }
+
+}
diff --git a/core/modules/media/src/MediaSourceBase.php b/core/modules/media/src/MediaSourceBase.php
index f7197963cca8..181ebed86253 100644
--- a/core/modules/media/src/MediaSourceBase.php
+++ b/core/modules/media/src/MediaSourceBase.php
@@ -321,7 +321,8 @@ abstract class MediaSourceBase extends PluginBase implements MediaSourceInterfac
if ($tries) {
$id .= '_' . $tries;
- // Ensure the suffixed field name does not exceed the maximum allowed length.
+ // Ensure the suffixed field name does not exceed the maximum allowed
+ // length.
if (strlen($id) > EntityTypeInterface::ID_MAX_LENGTH) {
$id = substr($base_id, 0, (EntityTypeInterface::ID_MAX_LENGTH - strlen('_' . $tries))) . '_' . $tries;
}
diff --git a/core/modules/media/tests/modules/media_test_views/config/install/views.view.test_media_bulk_form.yml b/core/modules/media/tests/modules/media_test_views/config/install/views.view.test_media_bulk_form.yml
index 8f7f0c8493df..f194dfb53fa0 100644
--- a/core/modules/media/tests/modules/media_test_views/config/install/views.view.test_media_bulk_form.yml
+++ b/core/modules/media/tests/modules/media_test_views/config/install/views.view.test_media_bulk_form.yml
@@ -138,6 +138,8 @@ display:
arguments: { }
style:
type: table
+ options:
+ class: ''
row:
type: fields
relationships: { }
diff --git a/core/modules/media_library/config/install/views.view.media_library.yml b/core/modules/media_library/config/install/views.view.media_library.yml
index 5450c17c9379..70974811ee4f 100644
--- a/core/modules/media_library/config/install/views.view.media_library.yml
+++ b/core/modules/media_library/config/install/views.view.media_library.yml
@@ -1346,6 +1346,7 @@ display:
options:
row_class: 'media-library-item media-library-item--table js-media-library-item js-click-to-select'
default_row_class: true
+ class: ''
row:
type: fields
defaults:
diff --git a/core/modules/menu_ui/src/Hook/MenuUiHooks.php b/core/modules/menu_ui/src/Hook/MenuUiHooks.php
index 46f2c85d1d76..09e7c99f898c 100644
--- a/core/modules/menu_ui/src/Hook/MenuUiHooks.php
+++ b/core/modules/menu_ui/src/Hook/MenuUiHooks.php
@@ -115,7 +115,8 @@ class MenuUiHooks {
return $entity->access('update', NULL, TRUE)->andIf($entity->access('delete', NULL, TRUE));
}
else {
- // If the node has no corresponding menu link, users needs to permission to create one.
+ // If the node has no corresponding menu link, users needs to permission
+ // to create one.
return $this->entityTypeManager->getAccessControlHandler('menu_link_content')->createAccess(NULL, NULL, [], TRUE);
}
}
diff --git a/core/modules/migrate/tests/src/Kernel/MigrateSourceTestBase.php b/core/modules/migrate/tests/src/Kernel/MigrateSourceTestBase.php
index 1999e3ad86f4..9e508da9b225 100644
--- a/core/modules/migrate/tests/src/Kernel/MigrateSourceTestBase.php
+++ b/core/modules/migrate/tests/src/Kernel/MigrateSourceTestBase.php
@@ -85,9 +85,9 @@ abstract class MigrateSourceTestBase extends KernelTestBase {
* The fully qualified class name of the plugin to be tested.
*/
protected function getPluginClass() {
- $covers = $this->getTestClassCovers();
- if (!empty($covers)) {
- return $covers[0];
+ $covers = $this->valueObjectForEvents()->metadata()->isCovers()->isClassLevel()->asArray();
+ if (isset($covers[0])) {
+ return $covers[0]->target();
}
else {
$this->fail('No plugin class was specified');
diff --git a/core/modules/mysql/src/Driver/Database/mysql/Schema.php b/core/modules/mysql/src/Driver/Database/mysql/Schema.php
index 85e71af18d12..a8b9c07564e8 100644
--- a/core/modules/mysql/src/Driver/Database/mysql/Schema.php
+++ b/core/modules/mysql/src/Driver/Database/mysql/Schema.php
@@ -67,7 +67,7 @@ class Schema extends DatabaseSchema {
}
/**
- * Build a condition to match a table name against a standard information_schema.
+ * Builds a condition to match a table name with the information schema.
*
* MySQL uses databases like schemas rather than catalogs so when we build a
* condition to query the information_schema.tables, we set the default
@@ -133,7 +133,7 @@ class Schema extends DatabaseSchema {
}
/**
- * Create an SQL string for a field to be used in table creation or alteration.
+ * Creates an SQL string for a field used in table creation or alteration.
*
* @param string $name
* Name of the field.
diff --git a/core/modules/mysql/mysql.install b/core/modules/mysql/src/Hook/MysqlRequirements.php
index 547f9cab1cd0..ef305d41a34b 100644
--- a/core/modules/mysql/mysql.install
+++ b/core/modules/mysql/src/Hook/MysqlRequirements.php
@@ -1,20 +1,27 @@
<?php
-/**
- * @file
- * Install, update and uninstall functions for the mysql module.
- */
+declare(strict_types=1);
+
+namespace Drupal\mysql\Hook;
use Drupal\Core\Database\Database;
+use Drupal\Core\Hook\Attribute\Hook;
use Drupal\Core\Render\Markup;
+use Drupal\Core\StringTranslation\StringTranslationTrait;
/**
- * Implements hook_requirements().
+ * Requirements for the MySQL module.
*/
-function mysql_requirements($phase): array {
- $requirements = [];
+class MysqlRequirements {
- if ($phase === 'runtime') {
+ use StringTranslationTrait;
+
+ /**
+ * Implements hook_runtime_requirements().
+ */
+ #[Hook('runtime_requirements')]
+ public function runtime(): array {
+ $requirements = [];
// Test with MySql databases.
if (Database::isActiveConnection()) {
$connection = Database::getConnection();
@@ -51,27 +58,28 @@ function mysql_requirements($phase): array {
}
else {
$severity_level = REQUIREMENT_ERROR;
- $description[] = t('This is not supported by Drupal.');
+ $description[] = $this->t('This is not supported by Drupal.');
}
- $description[] = t('The recommended level for Drupal is "READ COMMITTED".');
+ $description[] = $this->t('The recommended level for Drupal is "READ COMMITTED".');
}
if (!empty($tables_missing_primary_key)) {
- $description[] = t('For this to work correctly, all tables must have a primary key. The following table(s) do not have a primary key: @tables.', ['@tables' => implode(', ', $tables_missing_primary_key)]);
+ $description[] = $this->t('For this to work correctly, all tables must have a primary key. The following table(s) do not have a primary key: @tables.', ['@tables' => implode(', ', $tables_missing_primary_key)]);
}
- $description[] = t('See the <a href=":performance_doc">setting MySQL transaction isolation level</a> page for more information.', [
+ $description[] = $this->t('See the <a href=":performance_doc">setting MySQL transaction isolation level</a> page for more information.', [
':performance_doc' => 'https://www.drupal.org/docs/system-requirements/setting-the-mysql-transaction-isolation-level',
]);
$requirements['mysql_transaction_level'] = [
- 'title' => t('Transaction isolation level'),
+ 'title' => $this->t('Transaction isolation level'),
'severity' => $severity_level,
'value' => $isolation_level,
'description' => Markup::create(implode(' ', $description)),
];
}
+
+ return $requirements;
}
- return $requirements;
}
diff --git a/core/modules/navigation/navigation.install b/core/modules/navigation/navigation.install
index e4280b472ac2..77390203123a 100644
--- a/core/modules/navigation/navigation.install
+++ b/core/modules/navigation/navigation.install
@@ -22,25 +22,6 @@ function navigation_install(bool $is_syncing): void {
}
/**
- * Implements hook_requirements().
- */
-function navigation_requirements($phase): array {
- $requirements = [];
-
- if ($phase === 'runtime') {
- if (\Drupal::moduleHandler()->moduleExists('toolbar')) {
- $requirements['toolbar'] = [
- 'title' => t('Toolbar and Navigation modules are both installed'),
- 'value' => t('The Navigation module is a complete replacement for the Toolbar module and disables its functionality when both modules are installed. If you are planning to continue using Navigation module, you can uninstall the Toolbar module now.'),
- 'severity' => REQUIREMENT_WARNING,
- ];
- }
- }
-
- return $requirements;
-}
-
-/**
* Reorganizes the values for the logo settings.
*/
function navigation_update_11001(array &$sandbox): void {
diff --git a/core/modules/navigation/src/Hook/NavigationRequirements.php b/core/modules/navigation/src/Hook/NavigationRequirements.php
new file mode 100644
index 000000000000..ae04608bfd66
--- /dev/null
+++ b/core/modules/navigation/src/Hook/NavigationRequirements.php
@@ -0,0 +1,38 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Drupal\navigation\Hook;
+
+use Drupal\Core\Extension\ModuleHandlerInterface;
+use Drupal\Core\Hook\Attribute\Hook;
+use Drupal\Core\StringTranslation\StringTranslationTrait;
+
+/**
+ * Requirements for the navigation module.
+ */
+class NavigationRequirements {
+
+ use StringTranslationTrait;
+
+ public function __construct(
+ protected readonly ModuleHandlerInterface $moduleHandler,
+ ) {}
+
+ /**
+ * Implements hook_runtime_requirements().
+ */
+ #[Hook('runtime_requirements')]
+ public function runtime(): array {
+ $requirements = [];
+ if ($this->moduleHandler->moduleExists('toolbar')) {
+ $requirements['toolbar'] = [
+ 'title' => $this->t('Toolbar and Navigation modules are both installed'),
+ 'value' => $this->t('The Navigation module is a complete replacement for the Toolbar module and disables its functionality when both modules are installed. If you are planning to continue using Navigation module, you can uninstall the Toolbar module now.'),
+ 'severity' => REQUIREMENT_WARNING,
+ ];
+ }
+ return $requirements;
+ }
+
+}
diff --git a/core/modules/navigation/tests/src/FunctionalJavascript/PerformanceTest.php b/core/modules/navigation/tests/src/FunctionalJavascript/PerformanceTest.php
index f1ca3242a2a8..2371bef31aac 100644
--- a/core/modules/navigation/tests/src/FunctionalJavascript/PerformanceTest.php
+++ b/core/modules/navigation/tests/src/FunctionalJavascript/PerformanceTest.php
@@ -91,7 +91,7 @@ class PerformanceTest extends PerformanceTestBase {
'CacheTagInvalidationCount' => 0,
'CacheTagLookupQueryCount' => 14,
'ScriptCount' => 3,
- 'ScriptBytes' => 215500,
+ 'ScriptBytes' => 213500,
'StylesheetCount' => 2,
'StylesheetBytes' => 46000,
];
diff --git a/core/modules/node/config/optional/views.view.content.yml b/core/modules/node/config/optional/views.view.content.yml
index d0bbb09cf5a0..09ba3dcc35ae 100644
--- a/core/modules/node/config/optional/views.view.content.yml
+++ b/core/modules/node/config/optional/views.view.content.yml
@@ -625,6 +625,7 @@ display:
empty_table: true
caption: ''
description: ''
+ class: ''
row:
type: fields
query:
diff --git a/core/modules/node/config/optional/views.view.glossary.yml b/core/modules/node/config/optional/views.view.glossary.yml
index 2698473d5271..868d51fb9e26 100644
--- a/core/modules/node/config/optional/views.view.glossary.yml
+++ b/core/modules/node/config/optional/views.view.glossary.yml
@@ -343,6 +343,7 @@ display:
summary: ''
order: asc
empty_table: false
+ class: ''
row:
type: fields
options:
diff --git a/core/modules/node/node.install b/core/modules/node/node.install
index fb5c14c47675..4e232bdc2de9 100644
--- a/core/modules/node/node.install
+++ b/core/modules/node/node.install
@@ -5,137 +5,10 @@
* Install, update and uninstall functions for the node module.
*/
-use Drupal\Core\Link;
-use Drupal\Core\StringTranslation\PluralTranslatableMarkup;
-use Drupal\Core\Url;
use Drupal\Core\Database\Database;
use Drupal\user\RoleInterface;
/**
- * Implements hook_requirements().
- */
-function node_requirements($phase): array {
- $requirements = [];
- if ($phase === 'runtime') {
- // Only show rebuild button if there are either 0, or 2 or more, rows
- // in the {node_access} table, or if there are modules that
- // implement hook_node_grants().
- $grant_count = \Drupal::entityTypeManager()->getAccessControlHandler('node')->countGrants();
- $has_node_grants_implementations = \Drupal::moduleHandler()->hasImplementations('node_grants');
- if ($grant_count != 1 || $has_node_grants_implementations) {
- $value = \Drupal::translation()->formatPlural($grant_count, 'One permission in use', '@count permissions in use', ['@count' => $grant_count]);
- }
- else {
- $value = t('Disabled');
- }
-
- $requirements['node_access'] = [
- 'title' => t('Node Access Permissions'),
- 'value' => $value,
- 'description' => t('If the site is experiencing problems with permissions to content, you may have to rebuild the permissions cache. Rebuilding will remove all privileges to content and replace them with permissions based on the current modules and settings. Rebuilding may take some time if there is a lot of content or complex permission settings. After rebuilding has completed, content will automatically use the new permissions. <a href=":rebuild">Rebuild permissions</a>', [
- ':rebuild' => Url::fromRoute('node.configure_rebuild_confirm')->toString(),
- ]),
- ];
-
- // Report when the "Published status or admin user" has no impact on the
- // result of dependent views due to active node access modules.
- // @see https://www.drupal.org/node/3472976
- if ($has_node_grants_implementations && \Drupal::moduleHandler()->moduleExists('views')) {
- $node_status_filter_problematic_views = [];
- $active_view_ids = \Drupal::entityQuery('view')
- ->condition('status', TRUE)
- ->accessCheck(FALSE)
- ->execute();
-
- $views_storage = \Drupal::entityTypeManager()->getStorage('view');
- foreach ($views_storage->loadMultiple($active_view_ids) as $view) {
- foreach ($view->get('display') as $display_id => $display) {
- if (array_key_exists('filters', $display['display_options'])) {
- foreach ($display['display_options']['filters'] as $filter) {
- if (array_key_exists('plugin_id', $filter) && $filter['plugin_id'] === 'node_status') {
- $node_status_filter_problematic_views[$view->id()][$display_id] = [
- 'view_label' => $view->label(),
- 'display_name' => $display['display_title'] ?? $display_id,
- ];
- break;
- }
- }
- }
- }
- }
-
- if ($node_status_filter_problematic_views !== []) {
- $node_access_implementations = [];
- $module_data = \Drupal::service('extension.list.module')->getAllInstalledInfo();
- foreach (['node_grants', 'node_grants_alter'] as $hook) {
- \Drupal::moduleHandler()->invokeAllWith(
- $hook,
- static function (callable $hook, string $module) use (&$node_access_implementations, $module_data) {
- $node_access_implementations[$module] = $module_data[$module]['name'];
- }
- );
- }
- uasort($node_access_implementations, 'strnatcasecmp');
- $views_ui_enabled = \Drupal::moduleHandler()->moduleExists('views_ui');
- $node_status_filter_problematic_views_list = [];
- foreach ($node_status_filter_problematic_views as $view_id => $displays) {
- foreach ($displays as $display_id => $info) {
- $text = "{$info['view_label']} ({$info['display_name']})";
- if ($views_ui_enabled) {
- $url = Url::fromRoute('entity.view.edit_display_form', [
- 'view' => $view_id,
- 'display_id' => $display_id,
- ]);
- if ($url->access()) {
- $node_status_filter_problematic_views_list[] = Link::fromTextAndUrl($text, $url)->toString();
- }
- else {
- $node_status_filter_problematic_views_list[] = $text;
- }
- }
- else {
- $node_status_filter_problematic_views_list[] = $text;
- }
- }
- }
-
- $node_status_filter_problematic_views_count = count($node_status_filter_problematic_views_list);
- $node_status_filter_description_arguments = [
- '%modules' => implode(', ', $node_access_implementations),
- '%status_filter' => t('Published status or admin user'),
- ];
-
- if ($node_status_filter_problematic_views_count > 1) {
- $node_status_filter_problematic_views_list = [
- '#theme' => 'item_list',
- '#items' => $node_status_filter_problematic_views_list,
- ];
- $node_status_filter_description_arguments['@views'] = \Drupal::service('renderer')->renderInIsolation($node_status_filter_problematic_views_list);
- }
- else {
- $node_status_filter_description_arguments['%view'] = reset($node_status_filter_problematic_views_list);
- }
-
- $node_status_filter_description = new PluralTranslatableMarkup(
- $node_status_filter_problematic_views_count,
- 'The %view view uses the %status_filter filter but it has no effect because the following module(s) control access: %modules. Review and consider removing the filter.',
- 'The following views use the %status_filter filter but it has no effect because the following module(s) control access: %modules. Review and consider removing the filter from these views: @views',
- $node_status_filter_description_arguments,
- );
-
- $requirements['node_status_filter'] = [
- 'title' => t('Content status filter'),
- 'value' => t('Redundant filters detected'),
- 'description' => $node_status_filter_description,
- 'severity' => REQUIREMENT_WARNING,
- ];
- }
- }
- }
- return $requirements;
-}
-
-/**
* Implements hook_schema().
*/
function node_schema(): array {
diff --git a/core/modules/node/src/Hook/NodeRequirements.php b/core/modules/node/src/Hook/NodeRequirements.php
new file mode 100644
index 000000000000..aa8b39d5682f
--- /dev/null
+++ b/core/modules/node/src/Hook/NodeRequirements.php
@@ -0,0 +1,154 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Drupal\node\Hook;
+
+use Drupal\Core\Hook\Attribute\Hook;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Drupal\Core\Extension\ModuleExtensionList;
+use Drupal\Core\Extension\ModuleHandlerInterface;
+use Drupal\Core\Link;
+use Drupal\Core\StringTranslation\PluralTranslatableMarkup;
+use Drupal\Core\StringTranslation\StringTranslationTrait;
+use Drupal\Core\StringTranslation\TranslationInterface;
+use Drupal\Core\Url;
+
+/**
+ * Requirements for the Node module.
+ */
+class NodeRequirements {
+
+ use StringTranslationTrait;
+
+ public function __construct(
+ protected readonly EntityTypeManagerInterface $entityTypeManager,
+ protected readonly ModuleHandlerInterface $moduleHandler,
+ protected readonly TranslationInterface $translation,
+ protected readonly ModuleExtensionList $moduleExtensionList,
+ ) {}
+
+ /**
+ * Implements hook_runtime_requirements().
+ */
+ #[Hook('runtime_requirements')]
+ public function runtime(): array {
+ $requirements = [];
+ // Only show rebuild button if there are either 0, or 2 or more, rows
+ // in the {node_access} table, or if there are modules that
+ // implement hook_node_grants().
+ $grant_count = $this->entityTypeManager->getAccessControlHandler('node')->countGrants();
+ $has_node_grants_implementations = $this->moduleHandler->hasImplementations('node_grants');
+ if ($grant_count != 1 || $has_node_grants_implementations) {
+ $value = $this->translation->formatPlural($grant_count, 'One permission in use', '@count permissions in use', ['@count' => $grant_count]);
+ }
+ else {
+ $value = $this->t('Disabled');
+ }
+
+ $requirements['node_access'] = [
+ 'title' => $this->t('Node Access Permissions'),
+ 'value' => $value,
+ 'description' => $this->t('If the site is experiencing problems with permissions to content, you may have to rebuild the permissions cache. Rebuilding will remove all privileges to content and replace them with permissions based on the current modules and settings. Rebuilding may take some time if there is a lot of content or complex permission settings. After rebuilding has completed, content will automatically use the new permissions. <a href=":rebuild">Rebuild permissions</a>', [
+ ':rebuild' => Url::fromRoute('node.configure_rebuild_confirm')->toString(),
+ ]),
+ ];
+
+ // Report when the "Published status or admin user" has no impact on the
+ // result of dependent views due to active node access modules.
+ // @see https://www.drupal.org/node/3472976
+ if ($has_node_grants_implementations && $this->moduleHandler->moduleExists('views')) {
+ $node_status_filter_problematic_views = [];
+ $query = $this->entityTypeManager->getStorage('view')->getQuery();
+ $query->condition('status', TRUE);
+ $query->accessCheck(FALSE);
+ $active_view_ids = $query->execute();
+
+ $views_storage = $this->entityTypeManager->getStorage('view');
+ foreach ($views_storage->loadMultiple($active_view_ids) as $view) {
+ foreach ($view->get('display') as $display_id => $display) {
+ if (array_key_exists('filters', $display['display_options'])) {
+ foreach ($display['display_options']['filters'] as $filter) {
+ if (array_key_exists('plugin_id', $filter) && $filter['plugin_id'] === 'node_status') {
+ $node_status_filter_problematic_views[$view->id()][$display_id] = [
+ 'view_label' => $view->label(),
+ 'display_name' => $display['display_title'] ?? $display_id,
+ ];
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ if ($node_status_filter_problematic_views !== []) {
+ $node_access_implementations = [];
+ $module_data = $this->moduleExtensionList->getAllInstalledInfo();
+ foreach (['node_grants', 'node_grants_alter'] as $hook) {
+ $this->moduleHandler->invokeAllWith(
+ $hook,
+ static function (callable $hook, string $module) use (&$node_access_implementations, $module_data) {
+ $node_access_implementations[$module] = $module_data[$module]['name'];
+ }
+ );
+ }
+ uasort($node_access_implementations, 'strnatcasecmp');
+ $views_ui_enabled = $this->moduleHandler->moduleExists('views_ui');
+ $node_status_filter_problematic_views_list = [];
+ foreach ($node_status_filter_problematic_views as $view_id => $displays) {
+ foreach ($displays as $display_id => $info) {
+ $text = "{$info['view_label']} ({$info['display_name']})";
+ if ($views_ui_enabled) {
+ $url = Url::fromRoute('entity.view.edit_display_form', [
+ 'view' => $view_id,
+ 'display_id' => $display_id,
+ ]);
+ if ($url->access()) {
+ $node_status_filter_problematic_views_list[] = Link::fromTextAndUrl($text, $url)->toString();
+ }
+ else {
+ $node_status_filter_problematic_views_list[] = $text;
+ }
+ }
+ else {
+ $node_status_filter_problematic_views_list[] = $text;
+ }
+ }
+ }
+
+ $node_status_filter_problematic_views_count = count($node_status_filter_problematic_views_list);
+ $node_status_filter_description_arguments = [
+ '%modules' => implode(', ', $node_access_implementations),
+ '%status_filter' => $this->t('Published status or admin user'),
+ ];
+
+ if ($node_status_filter_problematic_views_count > 1) {
+ $node_status_filter_problematic_views_list = [
+ '#theme' => 'item_list',
+ '#items' => $node_status_filter_problematic_views_list,
+ ];
+ $node_status_filter_description_arguments['@views'] = \Drupal::service('renderer')->renderInIsolation($node_status_filter_problematic_views_list);
+ }
+ else {
+ $node_status_filter_description_arguments['%view'] = reset($node_status_filter_problematic_views_list);
+ }
+
+ $node_status_filter_description = new PluralTranslatableMarkup(
+ $node_status_filter_problematic_views_count,
+ 'The %view view uses the %status_filter filter but it has no effect because the following module(s) control access: %modules. Review and consider removing the filter.',
+ 'The following views use the %status_filter filter but it has no effect because the following module(s) control access: %modules. Review and consider removing the filter from these views: @views',
+ $node_status_filter_description_arguments,
+ );
+
+ $requirements['node_status_filter'] = [
+ 'title' => $this->t('Content status filter'),
+ 'value' => $this->t('Redundant filters detected'),
+ 'description' => $node_status_filter_description,
+ 'severity' => REQUIREMENT_WARNING,
+ ];
+ }
+ }
+ return $requirements;
+ }
+
+}
diff --git a/core/modules/node/src/Plugin/views/wizard/Node.php b/core/modules/node/src/Plugin/views/wizard/Node.php
index 84a21c3f7f2e..d66fa956f9aa 100644
--- a/core/modules/node/src/Plugin/views/wizard/Node.php
+++ b/core/modules/node/src/Plugin/views/wizard/Node.php
@@ -89,11 +89,7 @@ class Node extends WizardPluginBase {
}
/**
- * Overrides Drupal\views\Plugin\views\wizard\WizardPluginBase::getAvailableSorts().
- *
- * @return array
- * An array whose keys are the available sort options and whose
- * corresponding values are human readable labels.
+ * {@inheritdoc}
*/
public function getAvailableSorts() {
// You can't execute functions in properties, so override the method
@@ -238,9 +234,7 @@ class Node extends WizardPluginBase {
}
/**
- * Overrides Drupal\views\Plugin\views\wizard\WizardPluginBase::buildFilters().
- *
- * Add some options for filter by taxonomy terms.
+ * {@inheritdoc}
*/
protected function buildFilters(&$form, FormStateInterface $form_state) {
parent::buildFilters($form, $form_state);
diff --git a/core/modules/node/src/Plugin/views/wizard/NodeRevision.php b/core/modules/node/src/Plugin/views/wizard/NodeRevision.php
index c10389cc2392..96422504cf5e 100644
--- a/core/modules/node/src/Plugin/views/wizard/NodeRevision.php
+++ b/core/modules/node/src/Plugin/views/wizard/NodeRevision.php
@@ -29,11 +29,10 @@ class NodeRevision extends WizardPluginBase {
protected $createdColumn = 'changed';
/**
- * Overrides Drupal\views\Plugin\views\wizard\WizardPluginBase::rowStyleOptions().
- *
- * Node revisions do not support full posts or teasers, so remove them.
+ * {@inheritdoc}
*/
protected function rowStyleOptions() {
+ // Node revisions do not support full posts or teasers, so remove them.
$options = parent::rowStyleOptions();
unset($options['teasers']);
unset($options['full_posts']);
diff --git a/core/modules/node/tests/modules/node_test_views/test_views/views.view.test_node_access_join.yml b/core/modules/node/tests/modules/node_test_views/test_views/views.view.test_node_access_join.yml
index 766f656fc2bf..65ca46658711 100644
--- a/core/modules/node/tests/modules/node_test_views/test_views/views.view.test_node_access_join.yml
+++ b/core/modules/node/tests/modules/node_test_views/test_views/views.view.test_node_access_join.yml
@@ -68,6 +68,16 @@ display:
quantity: 9
style:
type: table
+ options:
+ grouping: { }
+ class: ''
+ row_class: ''
+ default_row_class: true
+ override: true
+ sticky: false
+ caption: ''
+ summary: ''
+ description: ''
row:
type: fields
fields:
diff --git a/core/modules/node/tests/modules/node_test_views/test_views/views.view.test_node_bulk_form.yml b/core/modules/node/tests/modules/node_test_views/test_views/views.view.test_node_bulk_form.yml
index d7a3fa080fed..107165a27fb6 100644
--- a/core/modules/node/tests/modules/node_test_views/test_views/views.view.test_node_bulk_form.yml
+++ b/core/modules/node/tests/modules/node_test_views/test_views/views.view.test_node_bulk_form.yml
@@ -19,6 +19,8 @@ display:
display_options:
style:
type: table
+ options:
+ class: ''
row:
type: fields
fields:
diff --git a/core/modules/node/tests/src/Kernel/NodeRequirementsStatusFilterWarningTest.php b/core/modules/node/tests/src/Kernel/NodeRequirementsStatusFilterWarningTest.php
index 6ddb8c8c987d..60ce5c7cdb04 100644
--- a/core/modules/node/tests/src/Kernel/NodeRequirementsStatusFilterWarningTest.php
+++ b/core/modules/node/tests/src/Kernel/NodeRequirementsStatusFilterWarningTest.php
@@ -197,8 +197,7 @@ class NodeRequirementsStatusFilterWarningTest extends KernelTestBase {
* The requirements raised by the Node module.
*/
private function getRequirements(): array {
- $this->container->get('module_handler')->loadInclude('node', 'install');
- return node_requirements('runtime');
+ return $this->container->get('module_handler')->invoke('node', 'runtime_requirements');
}
/**
diff --git a/core/modules/package_manager/package_manager.install b/core/modules/package_manager/package_manager.install
deleted file mode 100644
index 9c5c65773dde..000000000000
--- a/core/modules/package_manager/package_manager.install
+++ /dev/null
@@ -1,82 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains install and update functions for Package Manager.
- */
-
-declare(strict_types=1);
-
-use Drupal\Core\Site\Settings;
-use Drupal\package_manager\ComposerInspector;
-use Drupal\package_manager\Exception\FailureMarkerExistsException;
-use Drupal\package_manager\FailureMarker;
-use PhpTuf\ComposerStager\API\Exception\ExceptionInterface;
-use PhpTuf\ComposerStager\API\Finder\Service\ExecutableFinderInterface;
-
-/**
- * Implements hook_requirements().
- */
-function package_manager_requirements(string $phase): array {
- $requirements = [];
-
- if (Settings::get('testing_package_manager', FALSE) === FALSE) {
- $requirements['testing_package_manager'] = [
- 'title' => 'Package Manager',
- 'description' => t("Package Manager is available for early testing. To install the module set the value of 'testing_package_manager' to TRUE in your settings.php file."),
- 'severity' => REQUIREMENT_ERROR,
- ];
- return $requirements;
- }
-
- // If we're able to check for the presence of the failure marker at all, do it
- // irrespective of the current run phase. If the failure marker is there, the
- // site is in an indeterminate state and should be restored from backup ASAP.
- $service_id = FailureMarker::class;
- if (\Drupal::hasService($service_id)) {
- try {
- \Drupal::service($service_id)->assertNotExists(NULL);
- }
- catch (FailureMarkerExistsException $exception) {
- $requirements['package_manager_failure_marker'] = [
- 'title' => t('Failed Package Manager update detected'),
- 'description' => $exception->getMessage(),
- 'severity' => REQUIREMENT_ERROR,
- ];
- }
- }
-
- if ($phase !== 'runtime') {
- return $requirements;
- }
- /** @var \PhpTuf\ComposerStager\API\Finder\Service\ExecutableFinderInterface $executable_finder */
- $executable_finder = \Drupal::service(ExecutableFinderInterface::class);
-
- // Report the Composer version in use, as well as its path.
- $title = t('Composer version');
- try {
- $requirements['package_manager_composer'] = [
- 'title' => $title,
- 'description' => t('@version (<code>@path</code>)', [
- '@version' => \Drupal::service(ComposerInspector::class)->getVersion(),
- '@path' => $executable_finder->find('composer'),
- ]),
- 'severity' => REQUIREMENT_INFO,
- ];
- }
- catch (\Throwable $e) {
- // All Composer Stager exceptions are translatable.
- $message = $e instanceof ExceptionInterface
- ? $e->getTranslatableMessage()
- : $e->getMessage();
-
- $requirements['package_manager_composer'] = [
- 'title' => $title,
- 'description' => t('Composer was not found. The error message was: @message', [
- '@message' => $message,
- ]),
- 'severity' => REQUIREMENT_ERROR,
- ];
- }
- return $requirements;
-}
diff --git a/core/modules/package_manager/src/Hook/PackageManagerRequirementsHooks.php b/core/modules/package_manager/src/Hook/PackageManagerRequirementsHooks.php
new file mode 100644
index 000000000000..4d315a94330f
--- /dev/null
+++ b/core/modules/package_manager/src/Hook/PackageManagerRequirementsHooks.php
@@ -0,0 +1,136 @@
+<?php
+
+namespace Drupal\package_manager\Hook;
+
+use Drupal\Core\StringTranslation\StringTranslationTrait;
+use Drupal\Core\Hook\Attribute\Hook;
+use Drupal\Core\Site\Settings;
+use Drupal\package_manager\ComposerInspector;
+use Drupal\package_manager\Exception\FailureMarkerExistsException;
+use Drupal\package_manager\FailureMarker;
+use PhpTuf\ComposerStager\API\Exception\ExceptionInterface;
+use PhpTuf\ComposerStager\API\Finder\Service\ExecutableFinderInterface;
+
+/**
+ * Requirements checks for Package Manager.
+ */
+class PackageManagerRequirementsHooks {
+
+ use StringTranslationTrait;
+
+ public function __construct(
+ protected readonly ComposerInspector $composerInspector,
+ protected ExecutableFinderInterface $executableFinder,
+ ) {}
+
+ /**
+ * Implements hook_runtime_requirements().
+ */
+ #[Hook('runtime_requirements')]
+ public function runtime(): array {
+ $requirements = [];
+ $requirements = $this->checkSettings($requirements);
+ $requirements = $this->checkFailure($requirements);
+
+ // Report the Composer version in use, as well as its path.
+ $title = $this->t('Composer version');
+ try {
+ $requirements['package_manager_composer'] = [
+ 'title' => $title,
+ 'description' => $this->t('@version (<code>@path</code>)', [
+ '@version' => $this->composerInspector->getVersion(),
+ '@path' => $this->executableFinder->find('composer'),
+ ]),
+ 'severity' => REQUIREMENT_INFO,
+ ];
+ }
+ catch (\Throwable $e) {
+ // All Composer Stager exceptions are translatable.
+ $message = $e instanceof ExceptionInterface
+ ? $e->getTranslatableMessage()
+ : $e->getMessage();
+
+ $requirements['package_manager_composer'] = [
+ 'title' => $title,
+ 'description' => $this->t('Composer was not found. The error message was: @message', [
+ '@message' => $message,
+ ]),
+ 'severity' => REQUIREMENT_ERROR,
+ ];
+ }
+
+ return $requirements;
+ }
+
+ /**
+ * Implements hook_update_requirements().
+ */
+ #[Hook('update_requirements')]
+ public function update(): array {
+ $requirements = [];
+ $requirements = $this->checkSettings($requirements);
+ $requirements = $this->checkFailure($requirements);
+ return $requirements;
+ }
+
+ /**
+ * Check that package manager has an explicit setting to allow installation.
+ *
+ * @param array $requirements
+ * The requirements array that has been processed so far.
+ *
+ * @return array
+ * Requirements array.
+ *
+ * @see hook_runtime_requirements
+ * @see hook_update_requirements
+ */
+ public function checkSettings($requirements): array {
+ if (Settings::get('testing_package_manager', FALSE) === FALSE) {
+ $requirements['testing_package_manager'] = [
+ 'title' => 'Package Manager',
+ 'description' => $this->t("Package Manager is available for early testing. To install the module set the value of 'testing_package_manager' to TRUE in your settings.php file."),
+ 'severity' => REQUIREMENT_ERROR,
+ ];
+ }
+
+ return $requirements;
+ }
+
+ /**
+ * Check for a failed update.
+ *
+ * This is run during requirements to allow restoring from backup.
+ *
+ * @param array $requirements
+ * The requirements array that has been processed so far.
+ *
+ * @return array
+ * Requirements array.
+ *
+ * @see hook_runtime_requirements
+ * @see hook_update_requirements
+ */
+ public function checkFailure(array $requirements): array {
+ // If we're able to check for the presence of the failure marker at all, do
+ // it irrespective of the current run phase. If the failure marker is there,
+ // the site is in an indeterminate state and should be restored from backup
+ // ASAP.
+ $service_id = FailureMarker::class;
+ if (\Drupal::hasService($service_id)) {
+ try {
+ \Drupal::service($service_id)->assertNotExists(NULL);
+ }
+ catch (FailureMarkerExistsException $exception) {
+ $requirements['package_manager_failure_marker'] = [
+ 'title' => $this->t('Failed Package Manager update detected'),
+ 'description' => $exception->getMessage(),
+ 'severity' => REQUIREMENT_ERROR,
+ ];
+ }
+ }
+
+ return $requirements;
+ }
+
+}
diff --git a/core/modules/package_manager/src/Install/Requirements/PackageManagerRequirements.php b/core/modules/package_manager/src/Install/Requirements/PackageManagerRequirements.php
new file mode 100644
index 000000000000..45e0166ed878
--- /dev/null
+++ b/core/modules/package_manager/src/Install/Requirements/PackageManagerRequirements.php
@@ -0,0 +1,52 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Drupal\package_manager\Install\Requirements;
+
+use Drupal\Core\Extension\InstallRequirementsInterface;
+use Drupal\Core\Site\Settings;
+use Drupal\package_manager\Exception\FailureMarkerExistsException;
+use Drupal\package_manager\FailureMarker;
+
+/**
+ * Install time requirements for the package_manager module.
+ */
+class PackageManagerRequirements implements InstallRequirementsInterface {
+
+ /**
+ * {@inheritdoc}
+ */
+ public static function getRequirements(): array {
+ $requirements = [];
+
+ if (Settings::get('testing_package_manager', FALSE) === FALSE) {
+ $requirements['testing_package_manager'] = [
+ 'title' => 'Package Manager',
+ 'description' => t("Package Manager is available for early testing. To install the module set the value of 'testing_package_manager' to TRUE in your settings.php file."),
+ 'severity' => REQUIREMENT_ERROR,
+ ];
+ }
+
+ // If we're able to check for the presence of the failure marker at all, do
+ // it irrespective of the current run phase. If the failure marker is there,
+ // the site is in an indeterminate state and should be restored from backup
+ // ASAP.
+ $service_id = FailureMarker::class;
+ if (\Drupal::hasService($service_id)) {
+ try {
+ \Drupal::service($service_id)->assertNotExists(NULL);
+ }
+ catch (FailureMarkerExistsException $exception) {
+ $requirements['package_manager_failure_marker'] = [
+ 'title' => t('Failed Package Manager update detected'),
+ 'description' => $exception->getMessage(),
+ 'severity' => REQUIREMENT_ERROR,
+ ];
+ }
+ }
+
+ return $requirements;
+ }
+
+}
diff --git a/core/modules/package_manager/src/Validator/ComposerPluginsValidator.php b/core/modules/package_manager/src/Validator/ComposerPluginsValidator.php
index ba6d32f29fec..1297e6a711ed 100644
--- a/core/modules/package_manager/src/Validator/ComposerPluginsValidator.php
+++ b/core/modules/package_manager/src/Validator/ComposerPluginsValidator.php
@@ -78,6 +78,7 @@ final class ComposerPluginsValidator implements EventSubscriberInterface {
'composer/installers' => '^2.0',
'dealerdirect/phpcodesniffer-composer-installer' => '^0.7.1 || ^1.0.0',
'drupal/core-composer-scaffold' => '*',
+ 'drupal/core-recipe-unpack' => '*',
'drupal/core-project-message' => '*',
'phpstan/extension-installer' => '^1.1',
PhpTufValidator::PLUGIN_NAME => '^1',
diff --git a/core/modules/pgsql/pgsql.install b/core/modules/pgsql/pgsql.install
index 3adeb4f5dff3..682ef7d605b5 100644
--- a/core/modules/pgsql/pgsql.install
+++ b/core/modules/pgsql/pgsql.install
@@ -5,42 +5,6 @@
* Install, update and uninstall functions for the pgsql module.
*/
-use Drupal\Core\Database\Database;
-
-/**
- * Implements hook_requirements().
- */
-function pgsql_requirements(): array {
- $requirements = [];
- // Test with PostgreSQL databases for the status of the pg_trgm extension.
- if (Database::isActiveConnection()) {
- $connection = Database::getConnection();
-
- // Set the requirement just for postgres.
- if ($connection->driver() == 'pgsql') {
- $requirements['pgsql_extension_pg_trgm'] = [
- 'severity' => REQUIREMENT_OK,
- 'title' => t('PostgreSQL pg_trgm extension'),
- 'value' => t('Available'),
- 'description' => 'The pg_trgm PostgreSQL extension is present.',
- ];
-
- // If the extension is not available, set the requirement error.
- if (!$connection->schema()->extensionExists('pg_trgm')) {
- $requirements['pgsql_extension_pg_trgm']['severity'] = REQUIREMENT_ERROR;
- $requirements['pgsql_extension_pg_trgm']['value'] = t('Not created');
- $requirements['pgsql_extension_pg_trgm']['description'] = t('The <a href=":pg_trgm">pg_trgm</a> PostgreSQL extension is not present. The extension is required by Drupal 10 to improve performance when using PostgreSQL. See <a href=":requirements">Drupal database server requirements</a> for more information.', [
- ':pg_trgm' => 'https://www.postgresql.org/docs/current/pgtrgm.html',
- ':requirements' => 'https://www.drupal.org/docs/system-requirements/database-server-requirements',
- ]);
- }
-
- }
- }
-
- return $requirements;
-}
-
/**
* Implements hook_update_last_removed().
*/
diff --git a/core/modules/pgsql/src/Driver/Database/pgsql/Schema.php b/core/modules/pgsql/src/Driver/Database/pgsql/Schema.php
index a4585e15da7a..440dc7179dbf 100644
--- a/core/modules/pgsql/src/Driver/Database/pgsql/Schema.php
+++ b/core/modules/pgsql/src/Driver/Database/pgsql/Schema.php
@@ -1072,7 +1072,8 @@ EOD;
/**
* Calculates a base-64 encoded PostgreSQL-safe sha-256 hash.
*
- * The hash is modified to according to @link https://www.postgresql.org/docs/current/sql-syntax-lexical.html PostgreSQL Lexical Structure@endlink.
+ * The hash is modified to according to PostgreSQL Lexical Structure. See
+ * https://www.postgresql.org/docs/current/sql-syntax-lexical.html.
*
* @param string $data
* String to be hashed.
diff --git a/core/modules/pgsql/src/Hook/PgsqlRequirementsHooks.php b/core/modules/pgsql/src/Hook/PgsqlRequirementsHooks.php
new file mode 100644
index 000000000000..66b8e2dfea09
--- /dev/null
+++ b/core/modules/pgsql/src/Hook/PgsqlRequirementsHooks.php
@@ -0,0 +1,54 @@
+<?php
+
+namespace Drupal\pgsql\Hook;
+
+use Drupal\Core\Database\Database;
+use Drupal\Core\StringTranslation\StringTranslationTrait;
+use Drupal\Core\Hook\Attribute\Hook;
+
+/**
+ * Hook implementations for pgsql module.
+ */
+class PgsqlRequirementsHooks {
+
+ use StringTranslationTrait;
+
+ /**
+ * Implements hook_update_requirements().
+ *
+ * Implements hook_runtime_requirements().
+ */
+ #[Hook('update_requirements')]
+ #[Hook('runtime_requirements')]
+ public function checkRequirements(): array {
+ $requirements = [];
+ // Test with PostgreSQL databases for the status of the pg_trgm extension.
+ if (Database::isActiveConnection()) {
+ $connection = Database::getConnection();
+
+ // Set the requirement just for postgres.
+ if ($connection->driver() == 'pgsql') {
+ $requirements['pgsql_extension_pg_trgm'] = [
+ 'severity' => REQUIREMENT_OK,
+ 'title' => $this->t('PostgreSQL pg_trgm extension'),
+ 'value' => $this->t('Available'),
+ 'description' => $this->t('The pg_trgm PostgreSQL extension is present.'),
+ ];
+
+ // If the extension is not available, set the requirement error.
+ if (!$connection->schema()->extensionExists('pg_trgm')) {
+ $requirements['pgsql_extension_pg_trgm']['severity'] = REQUIREMENT_ERROR;
+ $requirements['pgsql_extension_pg_trgm']['value'] = $this->t('Not created');
+ $requirements['pgsql_extension_pg_trgm']['description'] = $this->t('The <a href=":pg_trgm">pg_trgm</a> PostgreSQL extension is not present. The extension is required by Drupal to improve performance when using PostgreSQL. See <a href=":requirements">Drupal database server requirements</a> for more information.', [
+ ':pg_trgm' => 'https://www.postgresql.org/docs/current/pgtrgm.html',
+ ':requirements' => 'https://www.drupal.org/docs/system-requirements/database-server-requirements',
+ ]);
+ }
+
+ }
+ }
+
+ return $requirements;
+ }
+
+}
diff --git a/core/modules/pgsql/src/Install/Requirements/PgsqlRequirements.php b/core/modules/pgsql/src/Install/Requirements/PgsqlRequirements.php
new file mode 100644
index 000000000000..a2f7771575eb
--- /dev/null
+++ b/core/modules/pgsql/src/Install/Requirements/PgsqlRequirements.php
@@ -0,0 +1,49 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Drupal\pgsql\Install\Requirements;
+
+use Drupal\Core\Database\Database;
+use Drupal\Core\Extension\InstallRequirementsInterface;
+
+/**
+ * Install time requirements for the pgsql module.
+ */
+class PgsqlRequirements implements InstallRequirementsInterface {
+
+ /**
+ * {@inheritdoc}
+ */
+ public static function getRequirements(): array {
+ $requirements = [];
+ // Test with PostgreSQL databases for the status of the pg_trgm extension.
+ if (Database::isActiveConnection()) {
+ $connection = Database::getConnection();
+
+ // Set the requirement just for postgres.
+ if ($connection->driver() == 'pgsql') {
+ $requirements['pgsql_extension_pg_trgm'] = [
+ 'severity' => REQUIREMENT_OK,
+ 'title' => t('PostgreSQL pg_trgm extension'),
+ 'value' => t('Available'),
+ 'description' => t('The pg_trgm PostgreSQL extension is present.'),
+ ];
+
+ // If the extension is not available, set the requirement error.
+ if (!$connection->schema()->extensionExists('pg_trgm')) {
+ $requirements['pgsql_extension_pg_trgm']['severity'] = REQUIREMENT_ERROR;
+ $requirements['pgsql_extension_pg_trgm']['value'] = t('Not created');
+ $requirements['pgsql_extension_pg_trgm']['description'] = t('The <a href=":pg_trgm">pg_trgm</a> PostgreSQL extension is not present. The extension is required by Drupal to improve performance when using PostgreSQL. See <a href=":requirements">Drupal database server requirements</a> for more information.', [
+ ':pg_trgm' => 'https://www.postgresql.org/docs/current/pgtrgm.html',
+ ':requirements' => 'https://www.drupal.org/docs/system-requirements/database-server-requirements',
+ ]);
+ }
+
+ }
+ }
+
+ return $requirements;
+ }
+
+}
diff --git a/core/modules/search/search.install b/core/modules/search/search.install
index 8873e1fbbf99..6e3e2691aa0f 100644
--- a/core/modules/search/search.install
+++ b/core/modules/search/search.install
@@ -122,35 +122,3 @@ function search_schema(): array {
return $schema;
}
-
-/**
- * Implements hook_requirements().
- *
- * For the Status Report, return information about search index status.
- */
-function search_requirements($phase): array {
- $requirements = [];
-
- if ($phase == 'runtime') {
- $remaining = 0;
- $total = 0;
- $search_page_repository = \Drupal::service('search.search_page_repository');
- foreach ($search_page_repository->getIndexableSearchPages() as $entity) {
- $status = $entity->getPlugin()->indexStatus();
- $remaining += $status['remaining'];
- $total += $status['total'];
- }
-
- $done = $total - $remaining;
- // Use floor() to calculate the percentage, so if it is not quite 100%, it
- // will show as 99%, to indicate "almost done".
- $percent = ($total > 0 ? floor(100 * $done / $total) : 100);
- $requirements['search_status'] = [
- 'title' => t('Search index progress'),
- 'value' => t('@percent% (@remaining remaining)', ['@percent' => $percent, '@remaining' => $remaining]),
- 'severity' => REQUIREMENT_INFO,
- ];
- }
-
- return $requirements;
-}
diff --git a/core/modules/search/src/Entity/SearchPage.php b/core/modules/search/src/Entity/SearchPage.php
index 894da73bbac0..f6c87fce2c66 100644
--- a/core/modules/search/src/Entity/SearchPage.php
+++ b/core/modules/search/src/Entity/SearchPage.php
@@ -214,7 +214,9 @@ class SearchPage extends ConfigEntityBase implements SearchPageInterface, Entity
}
/**
- * Helper callback for uasort() to sort search page entities by status, weight and label.
+ * Sorts search page entities by status, weight and label.
+ *
+ * Callback for uasort().
*/
public static function sort(ConfigEntityInterface $a, ConfigEntityInterface $b) {
/** @var \Drupal\search\SearchPageInterface $a */
diff --git a/core/modules/search/src/Hook/SearchRequirements.php b/core/modules/search/src/Hook/SearchRequirements.php
new file mode 100644
index 000000000000..4fd79e640312
--- /dev/null
+++ b/core/modules/search/src/Hook/SearchRequirements.php
@@ -0,0 +1,50 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Drupal\search\Hook;
+
+use Drupal\Core\Hook\Attribute\Hook;
+use Drupal\Core\StringTranslation\StringTranslationTrait;
+use Drupal\search\SearchPageRepositoryInterface;
+
+/**
+ * Requirements for the Search module.
+ */
+class SearchRequirements {
+
+ use StringTranslationTrait;
+
+ public function __construct(
+ protected readonly SearchPageRepositoryInterface $searchPageRepository,
+ ) {}
+
+ /**
+ * Implements hook_runtime_requirements().
+ *
+ * For the Status Report, return information about search index status.
+ */
+ #[Hook('runtime_requirements')]
+ public function runtime(): array {
+ $requirements = [];
+ $remaining = 0;
+ $total = 0;
+ foreach ($this->searchPageRepository->getIndexableSearchPages() as $entity) {
+ $status = $entity->getPlugin()->indexStatus();
+ $remaining += $status['remaining'];
+ $total += $status['total'];
+ }
+
+ $done = $total - $remaining;
+ // Use floor() to calculate the percentage, so if it is not quite 100%, it
+ // will show as 99%, to indicate "almost done".
+ $percent = ($total > 0 ? floor(100 * $done / $total) : 100);
+ $requirements['search_status'] = [
+ 'title' => $this->t('Search index progress'),
+ 'value' => $this->t('@percent% (@remaining remaining)', ['@percent' => $percent, '@remaining' => $remaining]),
+ 'severity' => REQUIREMENT_INFO,
+ ];
+ return $requirements;
+ }
+
+}
diff --git a/core/modules/sqlite/src/Driver/Database/sqlite/Schema.php b/core/modules/sqlite/src/Driver/Database/sqlite/Schema.php
index 3779e7625195..cbcdffcd2f95 100644
--- a/core/modules/sqlite/src/Driver/Database/sqlite/Schema.php
+++ b/core/modules/sqlite/src/Driver/Database/sqlite/Schema.php
@@ -149,7 +149,7 @@ class Schema extends DatabaseSchema {
}
/**
- * Create an SQL string for a field to be used in table creation or alteration.
+ * Create an SQL string for a field to be used in table create or alter.
*
* Before passing a field out of a schema definition into this function it has
* to be processed by self::processField().
@@ -647,7 +647,7 @@ class Schema extends DatabaseSchema {
}
/**
- * Utility method: rename columns in an index definition according to a new mapping.
+ * Renames columns in an index definition according to a new mapping.
*
* @param array $key_definition
* The key definition.
diff --git a/core/modules/system/src/EventSubscriber/AccessRouteAlterSubscriber.php b/core/modules/system/src/EventSubscriber/AccessRouteAlterSubscriber.php
index c9250047f1fa..777efab6b0c5 100644
--- a/core/modules/system/src/EventSubscriber/AccessRouteAlterSubscriber.php
+++ b/core/modules/system/src/EventSubscriber/AccessRouteAlterSubscriber.php
@@ -23,7 +23,7 @@ class AccessRouteAlterSubscriber implements EventSubscriberInterface {
}
/**
- * Adds _access_admin_menu_block_page requirement to routes pointing to SystemController::systemAdminMenuBlockPage.
+ * Adds requirements to some System Controller routes.
*
* @param \Drupal\Core\Routing\RouteBuildEvent $event
* The event to process.
diff --git a/core/modules/system/src/Plugin/migrate/process/d6/SystemUpdate7000.php b/core/modules/system/src/Plugin/migrate/process/d6/SystemUpdate7000.php
index ee1550984f77..2fbfd980d344 100644
--- a/core/modules/system/src/Plugin/migrate/process/d6/SystemUpdate7000.php
+++ b/core/modules/system/src/Plugin/migrate/process/d6/SystemUpdate7000.php
@@ -16,7 +16,7 @@ class SystemUpdate7000 extends ProcessPluginBase {
/**
* {@inheritdoc}
*
- * Rename blog and forum permissions to be consistent with other content types.
+ * Makes blog and forum permissions to be consistent with other content types.
*/
public function transform($value, MigrateExecutableInterface $migrate_executable, Row $row, $destination_property) {
$value = preg_replace('/(?<=^|,\ )create\ blog\ entries(?=,|$)/', 'create blog content', $value);
diff --git a/core/modules/system/system.module b/core/modules/system/system.module
index c7ed1808206f..dbda1e1b6b4b 100644
--- a/core/modules/system/system.module
+++ b/core/modules/system/system.module
@@ -223,7 +223,7 @@ function template_preprocess_entity_add_list(&$variables): void {
* Optional string to use as the page title once redirected to authorize.php.
*/
function system_authorized_init($callback, $file, $arguments = [], $page_title = NULL): void {
- @trigger_error(__METHOD__ . '() is deprecated in drupal:11.2.0 and is removed from drupal:12.0.0. There is no replacement. Use composer to manage the code for your site. See https://www.drupal.org/node/3512364', E_USER_DEPRECATED);
+ @trigger_error(__FUNCTION__ . '() is deprecated in drupal:11.2.0 and is removed from drupal:12.0.0. There is no replacement. Use composer to manage the code for your site. See https://www.drupal.org/node/3512364', E_USER_DEPRECATED);
$session = \Drupal::request()->getSession();
// First, figure out what file transfer backends the site supports, and put
@@ -255,7 +255,7 @@ function system_authorized_init($callback, $file, $arguments = [], $page_title =
* @see system_authorized_init()
*/
function system_authorized_get_url(array $options = []) {
- @trigger_error(__METHOD__ . '() is deprecated in drupal:11.2.0 and is removed from drupal:12.0.0. There is no replacement. Use composer to manage the code for your site. See https://www.drupal.org/node/3512364', E_USER_DEPRECATED);
+ @trigger_error(__FUNCTION__ . '() is deprecated in drupal:11.2.0 and is removed from drupal:12.0.0. There is no replacement. Use composer to manage the code for your site. See https://www.drupal.org/node/3512364', E_USER_DEPRECATED);
// core/authorize.php is an unrouted URL, so using the base: scheme is
// the correct usage for this case.
@@ -275,7 +275,7 @@ function system_authorized_get_url(array $options = []) {
* The full URL for the authorize.php script with batch processing options.
*/
function system_authorized_batch_processing_url(array $options = []) {
- @trigger_error(__METHOD__ . '() is deprecated in drupal:11.2.0 and is removed from drupal:12.0.0. There is no replacement. Use composer to manage the code for your site. See https://www.drupal.org/node/3512364', E_USER_DEPRECATED);
+ @trigger_error(__FUNCTION__ . '() is deprecated in drupal:11.2.0 and is removed from drupal:12.0.0. There is no replacement. Use composer to manage the code for your site. See https://www.drupal.org/node/3512364', E_USER_DEPRECATED);
$options['query'] = ['batch' => '1'];
return system_authorized_get_url($options);
@@ -287,7 +287,7 @@ function system_authorized_batch_processing_url(array $options = []) {
* @see system_authorized_init()
*/
function system_authorized_run($callback, $file, $arguments = [], $page_title = NULL) {
- @trigger_error(__METHOD__ . '() is deprecated in drupal:11.2.0 and is removed from drupal:12.0.0. There is no replacement. Use composer to manage the code for your site. See https://www.drupal.org/node/3512364', E_USER_DEPRECATED);
+ @trigger_error(__FUNCTION__ . '() is deprecated in drupal:11.2.0 and is removed from drupal:12.0.0. There is no replacement. Use composer to manage the code for your site. See https://www.drupal.org/node/3512364', E_USER_DEPRECATED);
system_authorized_init($callback, $file, $arguments, $page_title);
return new RedirectResponse(system_authorized_get_url()->toString());
@@ -299,7 +299,7 @@ function system_authorized_run($callback, $file, $arguments = [], $page_title =
* @see batch_process()
*/
function system_authorized_batch_process() {
- @trigger_error(__METHOD__ . '() is deprecated in drupal:11.2.0 and is removed from drupal:12.0.0. There is no replacement. Use composer to manage the code for your site. See https://www.drupal.org/node/3512364', E_USER_DEPRECATED);
+ @trigger_error(__FUNCTION__ . '() is deprecated in drupal:11.2.0 and is removed from drupal:12.0.0. There is no replacement. Use composer to manage the code for your site. See https://www.drupal.org/node/3512364', E_USER_DEPRECATED);
$finish_url = system_authorized_get_url();
$process_url = system_authorized_batch_processing_url();
diff --git a/core/modules/system/system.post_update.php b/core/modules/system/system.post_update.php
index b1ed3dbe019f..ce110e2c2f36 100644
--- a/core/modules/system/system.post_update.php
+++ b/core/modules/system/system.post_update.php
@@ -108,7 +108,7 @@ function system_post_update_remove_path_key(): void {
}
/**
- * Updates all entity_form_mode configuration entities to set description from empty string to null.
+ * Updates entity_form_mode descriptions from empty string to null.
*/
function system_post_update_convert_empty_description_entity_form_modes_to_null(array &$sandbox): void {
\Drupal::classResolver(ConfigEntityUpdater::class)
diff --git a/core/modules/system/tests/modules/element_info_test/src/Element/DeprecatedExtendsFormElement.php b/core/modules/system/tests/modules/element_info_test/src/Element/DeprecatedExtendsFormElement.php
index 44308c3f6a9c..33dba9b0bf6f 100644
--- a/core/modules/system/tests/modules/element_info_test/src/Element/DeprecatedExtendsFormElement.php
+++ b/core/modules/system/tests/modules/element_info_test/src/Element/DeprecatedExtendsFormElement.php
@@ -7,13 +7,14 @@ namespace Drupal\element_info_test\Element;
use Drupal\Core\Render\Attribute\FormElement;
use Drupal\Core\Render\Element\FormElement as FormElementDeprecated;
+// @phpcs:disable
/**
* Provides render element that extends deprecated FormElement for testing.
- *
- * @phpstan-ignore class.extendsDeprecatedClass
*/
#[FormElement('deprecated_extends_form')]
+// @phpstan-ignore class.extendsDeprecatedClass
class DeprecatedExtendsFormElement extends FormElementDeprecated {
+// @phpcs:enable
/**
* {@inheritdoc}
diff --git a/core/modules/system/tests/modules/element_info_test/src/Element/DeprecatedExtendsRenderElement.php b/core/modules/system/tests/modules/element_info_test/src/Element/DeprecatedExtendsRenderElement.php
index 514a3b01a4bc..72a608bfc5e1 100644
--- a/core/modules/system/tests/modules/element_info_test/src/Element/DeprecatedExtendsRenderElement.php
+++ b/core/modules/system/tests/modules/element_info_test/src/Element/DeprecatedExtendsRenderElement.php
@@ -7,13 +7,14 @@ namespace Drupal\element_info_test\Element;
use Drupal\Core\Render\Attribute\RenderElement;
use Drupal\Core\Render\Element\RenderElement as RenderElementDeprecated;
+// @phpcs:disable
/**
* Provides render element that extends deprecated RenderElement for testing.
- *
- * @phpstan-ignore class.extendsDeprecatedClass
*/
#[RenderElement('deprecated_extends_render')]
+// @phpstan-ignore class.extendsDeprecatedClass
class DeprecatedExtendsRenderElement extends RenderElementDeprecated {
+// @phpcs:enable
/**
* {@inheritdoc}
diff --git a/core/modules/system/tests/modules/entity_reference_test_views/test_views/views.view.test_entity_reference_group_by_empty_relationships.yml b/core/modules/system/tests/modules/entity_reference_test_views/test_views/views.view.test_entity_reference_group_by_empty_relationships.yml
index 39d5a0f81252..25c13b4cd4cd 100644
--- a/core/modules/system/tests/modules/entity_reference_test_views/test_views/views.view.test_entity_reference_group_by_empty_relationships.yml
+++ b/core/modules/system/tests/modules/entity_reference_test_views/test_views/views.view.test_entity_reference_group_by_empty_relationships.yml
@@ -77,6 +77,7 @@ display:
type: table
options:
grouping: { }
+ class: ''
row_class: ''
default_row_class: true
override: true
diff --git a/core/modules/system/tests/modules/render_placeholder_message_test/src/RenderPlaceholderMessageTestController.php b/core/modules/system/tests/modules/render_placeholder_message_test/src/RenderPlaceholderMessageTestController.php
index 1528a0839816..6877871e2d29 100644
--- a/core/modules/system/tests/modules/render_placeholder_message_test/src/RenderPlaceholderMessageTestController.php
+++ b/core/modules/system/tests/modules/render_placeholder_message_test/src/RenderPlaceholderMessageTestController.php
@@ -112,7 +112,9 @@ class RenderPlaceholderMessageTestController implements TrustedCallbackInterface
$reordered = [];
foreach ($placeholder_order as $placeholder) {
- $reordered[$placeholder] = $build['#attached']['placeholders'][$placeholder];
+ if (isset($build['#attached']['placeholders'][$placeholder])) {
+ $reordered[$placeholder] = $build['#attached']['placeholders'][$placeholder];
+ }
}
$build['#attached']['placeholders'] = $reordered;
diff --git a/core/modules/system/tests/modules/requirements1_test/requirements1_test.install b/core/modules/system/tests/modules/requirements1_test/requirements1_test.install
index 70767c16544d..fb84be133cd7 100644
--- a/core/modules/system/tests/modules/requirements1_test/requirements1_test.install
+++ b/core/modules/system/tests/modules/requirements1_test/requirements1_test.install
@@ -9,6 +9,8 @@ declare(strict_types=1);
/**
* Implements hook_requirements().
+ *
+ * This tests the procedural implementations for this hook.
*/
function requirements1_test_requirements($phase): array {
$requirements = [];
diff --git a/core/modules/system/tests/modules/update_script_test/src/Hook/UpdateScriptTestRequirements.php b/core/modules/system/tests/modules/update_script_test/src/Hook/UpdateScriptTestRequirements.php
new file mode 100644
index 000000000000..5927e31e460b
--- /dev/null
+++ b/core/modules/system/tests/modules/update_script_test/src/Hook/UpdateScriptTestRequirements.php
@@ -0,0 +1,59 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Drupal\update_script_test\Hook;
+
+use Drupal\Core\Config\ConfigFactoryInterface;
+use Drupal\Core\Hook\Attribute\Hook;
+
+/**
+ * Requirements for the Update Script Test module.
+ */
+class UpdateScriptTestRequirements {
+
+ public function __construct(
+ protected readonly ConfigFactoryInterface $configFactory,
+ ) {}
+
+ /**
+ * Implements hook_update_requirements().
+ */
+ #[Hook('update_requirements')]
+ public function update(): array {
+ $requirements = [];
+ // Set a requirements warning or error when the test requests it.
+ $requirement_type = $this->configFactory->get('update_script_test.settings')->get('requirement_type');
+ switch ($requirement_type) {
+ case REQUIREMENT_WARNING:
+ $requirements['update_script_test'] = [
+ 'title' => 'Update script test',
+ 'value' => 'Warning',
+ 'description' => 'This is a requirements warning provided by the update_script_test module.',
+ 'severity' => REQUIREMENT_WARNING,
+ ];
+ break;
+
+ case REQUIREMENT_ERROR:
+ $requirements['update_script_test'] = [
+ 'title' => 'Update script test',
+ 'value' => 'Error',
+ 'description' => 'This is a (buggy description fixed in update_script_test_requirements_alter()) requirements error provided by the update_script_test module.',
+ 'severity' => REQUIREMENT_ERROR,
+ ];
+ break;
+ }
+ return $requirements;
+ }
+
+ /**
+ * Implements hook_update_requirements_alter().
+ */
+ #[Hook('update_requirements_alter')]
+ public function updateAlter(array &$requirements): void {
+ if (isset($requirements['update_script_test']) && $requirements['update_script_test']['severity'] === REQUIREMENT_ERROR) {
+ $requirements['update_script_test']['description'] = 'This is a requirements error provided by the update_script_test module.';
+ }
+ }
+
+}
diff --git a/core/modules/system/tests/modules/update_script_test/update_script_test.install b/core/modules/system/tests/modules/update_script_test/update_script_test.install
index 5a641fd95644..c380aabd7ff7 100644
--- a/core/modules/system/tests/modules/update_script_test/update_script_test.install
+++ b/core/modules/system/tests/modules/update_script_test/update_script_test.install
@@ -8,48 +8,6 @@
declare(strict_types=1);
/**
- * Implements hook_requirements().
- */
-function update_script_test_requirements($phase): array {
- $requirements = [];
-
- if ($phase == 'update') {
- // Set a requirements warning or error when the test requests it.
- $requirement_type = \Drupal::config('update_script_test.settings')->get('requirement_type');
- switch ($requirement_type) {
- case REQUIREMENT_WARNING:
- $requirements['update_script_test'] = [
- 'title' => 'Update script test',
- 'value' => 'Warning',
- 'description' => 'This is a requirements warning provided by the update_script_test module.',
- 'severity' => REQUIREMENT_WARNING,
- ];
- break;
-
- case REQUIREMENT_ERROR:
- $requirements['update_script_test'] = [
- 'title' => 'Update script test',
- 'value' => 'Error',
- 'description' => 'This is a (buggy description fixed in update_script_test_requirements_alter()) requirements error provided by the update_script_test module.',
- 'severity' => REQUIREMENT_ERROR,
- ];
- break;
- }
- }
-
- return $requirements;
-}
-
-/**
- * Implements hook_requirements_alter().
- */
-function update_script_test_requirements_alter(array &$requirements): void {
- if (isset($requirements['update_script_test']) && $requirements['update_script_test']['severity'] === REQUIREMENT_ERROR) {
- $requirements['update_script_test']['description'] = 'This is a requirements error provided by the update_script_test module.';
- }
-}
-
-/**
* Implements hook_update_last_removed().
*/
function update_script_test_update_last_removed(): int {
diff --git a/core/modules/system/tests/modules/update_test_schema/src/Hook/UpdateTestSchemaRequirements.php b/core/modules/system/tests/modules/update_test_schema/src/Hook/UpdateTestSchemaRequirements.php
new file mode 100644
index 000000000000..de96ce3e36a5
--- /dev/null
+++ b/core/modules/system/tests/modules/update_test_schema/src/Hook/UpdateTestSchemaRequirements.php
@@ -0,0 +1,33 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Drupal\update_test_schema\Hook;
+
+use Drupal\Component\Render\FormattableMarkup;
+use Drupal\Core\Hook\Attribute\Hook;
+use Drupal\Core\Url;
+
+/**
+ * Requirements for the Update Test Schema module.
+ */
+class UpdateTestSchemaRequirements {
+
+ /**
+ * Implements hook_runtime_requirements().
+ */
+ #[Hook('runtime_requirements')]
+ public function runtime(): array {
+ $requirements = [];
+ $requirements['path_alias_test'] = [
+ 'title' => 'Path alias test',
+ 'value' => 'Check a path alias for the admin page',
+ 'severity' => REQUIREMENT_INFO,
+ 'description' => new FormattableMarkup('Visit <a href=":link">the structure page</a> to do many useful things.', [
+ ':link' => Url::fromRoute('system.admin_structure')->toString(),
+ ]),
+ ];
+ return $requirements;
+ }
+
+}
diff --git a/core/modules/system/tests/modules/update_test_schema/update_test_schema.install b/core/modules/system/tests/modules/update_test_schema/update_test_schema.install
index 305d2cc7913e..0ad88c618bd0 100644
--- a/core/modules/system/tests/modules/update_test_schema/update_test_schema.install
+++ b/core/modules/system/tests/modules/update_test_schema/update_test_schema.install
@@ -12,25 +12,6 @@ use Drupal\Core\Url;
use Drupal\Component\Render\FormattableMarkup;
/**
- * Implements hook_requirements().
- */
-function update_test_schema_requirements($phase): array {
- $requirements = [];
- if ($phase === 'runtime') {
- $requirements['path_alias_test'] = [
- 'title' => 'Path alias test',
- 'value' => 'Check a path alias for the admin page',
- 'severity' => REQUIREMENT_INFO,
- 'description' => new FormattableMarkup('Visit <a href=":link">the structure page</a> to do many useful things.', [
- ':link' => Url::fromRoute('system.admin_structure')->toString(),
- ]),
- ];
- }
-
- return $requirements;
-}
-
-/**
* Implements hook_schema().
*
* The schema defined here will vary on state to allow for update hook testing.
diff --git a/core/modules/system/tests/src/Functional/FileTransfer/TestFileTransfer.php b/core/modules/system/tests/src/Functional/FileTransfer/TestFileTransfer.php
index 42605c114ffa..dcf46af35c9d 100644
--- a/core/modules/system/tests/src/Functional/FileTransfer/TestFileTransfer.php
+++ b/core/modules/system/tests/src/Functional/FileTransfer/TestFileTransfer.php
@@ -50,9 +50,11 @@ class TestFileTransfer extends FileTransfer {
* Establishes a mock connection for file transfer.
*/
public function connect() {
+ // @phpstan-ignore property.deprecatedClass
$this->connection = new MockTestConnection();
// Access the connection via the property. The property used to be set via a
// magic method and this can cause problems if coded incorrectly.
+ // @phpstan-ignore property.deprecatedClass
$this->connection->connectionString = 'test://' . urlencode($this->username) . ':' . urlencode($this->password) . "@$this->host:$this->port/";
}
@@ -60,6 +62,7 @@ class TestFileTransfer extends FileTransfer {
* Copies a file within the jailed environment.
*/
public function copyFileJailed($source, $destination) {
+ // @phpstan-ignore property.deprecatedClass
$this->connection->run("copyFile $source $destination");
}
@@ -67,6 +70,7 @@ class TestFileTransfer extends FileTransfer {
* Removes a directory within the jailed environment.
*/
protected function removeDirectoryJailed($directory) {
+ // @phpstan-ignore property.deprecatedClass
$this->connection->run("rmdir $directory");
}
@@ -74,6 +78,7 @@ class TestFileTransfer extends FileTransfer {
* Creates a directory within the jailed environment.
*/
public function createDirectoryJailed($directory) {
+ // @phpstan-ignore property.deprecatedClass
$this->connection->run("mkdir $directory");
}
@@ -81,6 +86,7 @@ class TestFileTransfer extends FileTransfer {
* Removes a file within the jailed environment.
*/
public function removeFileJailed($destination) {
+ // @phpstan-ignore property.deprecatedClass
$this->connection->run("rm $destination");
}
diff --git a/core/modules/system/tests/src/Functional/Render/PlaceholderMessageTest.php b/core/modules/system/tests/src/Functional/Render/PlaceholderMessageTest.php
index 4b2779ead48a..63e325c51111 100644
--- a/core/modules/system/tests/src/Functional/Render/PlaceholderMessageTest.php
+++ b/core/modules/system/tests/src/Functional/Render/PlaceholderMessageTest.php
@@ -17,7 +17,10 @@ class PlaceholderMessageTest extends BrowserTestBase {
/**
* {@inheritdoc}
*/
- protected static $modules = ['render_placeholder_message_test'];
+ protected static $modules = [
+ 'render_placeholder_message_test',
+ 'big_pipe_messages_test',
+ ];
/**
* {@inheritdoc}
diff --git a/core/modules/update/src/Hook/UpdateHooks.php b/core/modules/update/src/Hook/UpdateHooks.php
index 49cb455a8430..2502d4bb1715 100644
--- a/core/modules/update/src/Hook/UpdateHooks.php
+++ b/core/modules/update/src/Hook/UpdateHooks.php
@@ -77,8 +77,15 @@ class UpdateHooks {
$verbose = TRUE;
break;
}
+ // This loadInclude() is to ensure that the install API is available.
+ // Since we're loading an include of type 'install', this will also
+ // include core/includes/install.inc for us, which is where the
+ // REQUIREMENTS* constants are currently defined.
+ // @todo Remove this once those constants live in a better place.
+ // @see https://www.drupal.org/project/drupal/issues/2909480
+ // @see https://www.drupal.org/project/drupal/issues/3410938
\Drupal::moduleHandler()->loadInclude('update', 'install');
- $status = update_requirements('runtime');
+ $status = \Drupal::moduleHandler()->invoke('update', 'runtime_requirements');
foreach (['core', 'contrib'] as $report_type) {
$type = 'update_' . $report_type;
// hook_requirements() supports render arrays therefore we need to
diff --git a/core/modules/update/src/Hook/UpdateRequirements.php b/core/modules/update/src/Hook/UpdateRequirements.php
new file mode 100644
index 000000000000..4aa5ccc1826f
--- /dev/null
+++ b/core/modules/update/src/Hook/UpdateRequirements.php
@@ -0,0 +1,161 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Drupal\update\Hook;
+
+use Drupal\Core\Extension\ModuleHandlerInterface;
+use Drupal\Core\Hook\Attribute\Hook;
+use Drupal\Core\Link;
+use Drupal\Core\StringTranslation\StringTranslationTrait;
+use Drupal\Core\Url;
+use Drupal\update\ProjectSecurityData;
+use Drupal\update\ProjectSecurityRequirement;
+use Drupal\update\UpdateFetcherInterface;
+use Drupal\update\UpdateManagerInterface;
+
+/**
+ * Requirements for the update module.
+ */
+class UpdateRequirements {
+
+ use StringTranslationTrait;
+
+ public function __construct(
+ protected readonly ModuleHandlerInterface $moduleHandler,
+ ) {}
+
+ /**
+ * Implements hook_runtime_requirements().
+ *
+ * Describes the status of the site regarding available updates. If
+ * there is no update data, only one record will be returned, indicating that
+ * the status of core can't be determined. If data is available, there will
+ * be two records: one for core, and another for all of contrib (assuming
+ * there are any contributed modules or themes installed on the site). In
+ * addition to the fields expected by hook_requirements ('value', 'severity',
+ * and optionally 'description'), this array will contain a 'reason'
+ * attribute, which is an integer constant to indicate why the given status
+ * is being returned (UPDATE_NOT_SECURE, UPDATE_NOT_CURRENT, or
+ * UPDATE_UNKNOWN). This is used for generating the appropriate email
+ * notification messages during update_cron(), and might be useful for other
+ * modules that invoke update_runtime_requirements() to find out if the site
+ * is up to date or not.
+ *
+ * @see _update_message_text()
+ * @see _update_cron_notify()
+ * @see \Drupal\update\UpdateManagerInterface
+ */
+ #[Hook('runtime_requirements')]
+ public function runtime(): array {
+ $requirements = [];
+ if ($available = update_get_available(FALSE)) {
+ $this->moduleHandler->loadInclude('update', 'inc', 'update.compare');
+ $data = update_calculate_project_data($available);
+ // First, populate the requirements for core:
+ $requirements['update_core'] = $this->requirementCheck($data['drupal'], 'core');
+ if (!empty($available['drupal']['releases'])) {
+ $security_data = ProjectSecurityData::createFromProjectDataAndReleases($data['drupal'], $available['drupal']['releases'])->getCoverageInfo();
+ if ($core_coverage_requirement = ProjectSecurityRequirement::createFromProjectDataAndSecurityCoverageInfo($data['drupal'], $security_data)->getRequirement()) {
+ $requirements['coverage_core'] = $core_coverage_requirement;
+ }
+ }
+
+ // We don't want to check drupal a second time.
+ unset($data['drupal']);
+ if (!empty($data)) {
+ // Now, sort our $data array based on each project's status. The
+ // status constants are numbered in the right order of precedence, so
+ // we just need to make sure the projects are sorted in ascending
+ // order of status, and we can look at the first project we find.
+ uasort($data, '_update_project_status_sort');
+ $first_project = reset($data);
+ $requirements['update_contrib'] = $this->requirementCheck($first_project, 'contrib');
+ }
+ }
+ else {
+ $requirements['update_core']['title'] = $this->t('Drupal core update status');
+ $requirements['update_core']['value'] = $this->t('No update data available');
+ $requirements['update_core']['severity'] = REQUIREMENT_WARNING;
+ $requirements['update_core']['reason'] = UpdateFetcherInterface::UNKNOWN;
+ $requirements['update_core']['description'] = _update_no_data();
+ }
+ return $requirements;
+ }
+
+ /**
+ * Fills in the requirements array.
+ *
+ * This is shared for both core and contrib to generate the right elements in
+ * the array for hook_runtime_requirements().
+ *
+ * @param array $project
+ * Array of information about the project we're testing as returned by
+ * update_calculate_project_data().
+ * @param string $type
+ * What kind of project this is ('core' or 'contrib').
+ *
+ * @return array
+ * An array to be included in the nested $requirements array.
+ *
+ * @see hook_requirements()
+ * @see update_requirements()
+ * @see update_calculate_project_data()
+ */
+ protected function requirementCheck($project, $type): array {
+ $requirement = [];
+ if ($type == 'core') {
+ $requirement['title'] = $this->t('Drupal core update status');
+ }
+ else {
+ $requirement['title'] = $this->t('Module and theme update status');
+ }
+ $status = $project['status'];
+ if ($status != UpdateManagerInterface::CURRENT) {
+ $requirement['reason'] = $status;
+ $requirement['severity'] = REQUIREMENT_ERROR;
+ // When updates are available, append the available updates link to the
+ // message from _update_message_text(), and format the two translated
+ // strings together in a single paragraph.
+ $requirement['description'][] = ['#markup' => _update_message_text($type, $status)];
+ if (!in_array($status, [UpdateFetcherInterface::UNKNOWN, UpdateFetcherInterface::NOT_CHECKED, UpdateFetcherInterface::NOT_FETCHED, UpdateFetcherInterface::FETCH_PENDING])) {
+ $requirement['description'][] = ['#prefix' => ' ', '#markup' => $this->t('See the <a href=":available_updates">available updates</a> page for more information.', [':available_updates' => Url::fromRoute('update.status')->toString()])];
+ }
+ }
+ switch ($status) {
+ case UpdateManagerInterface::NOT_SECURE:
+ $requirement_label = $this->t('Not secure!');
+ break;
+
+ case UpdateManagerInterface::REVOKED:
+ $requirement_label = $this->t('Revoked!');
+ break;
+
+ case UpdateManagerInterface::NOT_SUPPORTED:
+ $requirement_label = $this->t('Unsupported release');
+ break;
+
+ case UpdateManagerInterface::NOT_CURRENT:
+ $requirement_label = $this->t('Out of date');
+ $requirement['severity'] = REQUIREMENT_WARNING;
+ break;
+
+ case UpdateFetcherInterface::UNKNOWN:
+ case UpdateFetcherInterface::NOT_CHECKED:
+ case UpdateFetcherInterface::NOT_FETCHED:
+ case UpdateFetcherInterface::FETCH_PENDING:
+ $requirement_label = $project['reason'] ?? $this->t('Can not determine status');
+ $requirement['severity'] = REQUIREMENT_WARNING;
+ break;
+
+ default:
+ $requirement_label = $this->t('Up to date');
+ }
+ if ($status != UpdateManagerInterface::CURRENT && $type == 'core' && isset($project['recommended'])) {
+ $requirement_label .= ' ' . $this->t('(version @version available)', ['@version' => $project['recommended']]);
+ }
+ $requirement['value'] = Link::fromTextAndUrl($requirement_label, Url::fromRoute('update.status'))->toString();
+ return $requirement;
+ }
+
+}
diff --git a/core/modules/update/src/UpdateRoot.php b/core/modules/update/src/UpdateRoot.php
index abcf499d5b4b..604e9ff0f529 100644
--- a/core/modules/update/src/UpdateRoot.php
+++ b/core/modules/update/src/UpdateRoot.php
@@ -6,7 +6,7 @@ use Drupal\Core\DrupalKernelInterface;
use Symfony\Component\HttpFoundation\RequestStack;
/**
- * Gets the root path used by the legacy Update Manager to install or update projects.
+ * Gets the root path used by the legacy Update Manager.
*
* @deprecated in drupal:11.2.0 and is removed from drupal:12.0.0. There is no
* replacement. Use composer to manage the code for your site.
diff --git a/core/modules/update/update.authorize.inc b/core/modules/update/update.authorize.inc
index 59bcd97f2325..cab253303bbb 100644
--- a/core/modules/update/update.authorize.inc
+++ b/core/modules/update/update.authorize.inc
@@ -33,7 +33,7 @@ use Drupal\Core\Url;
*/
#[ProceduralHookScanStop]
function update_authorize_run_update($filetransfer, $projects) {
- @trigger_error(__METHOD__ . '() is deprecated in drupal:11.2.0 and is removed from drupal:12.0.0. There is no replacement. Use composer to manage the code for your site. See https://www.drupal.org/node/3512364', E_USER_DEPRECATED);
+ @trigger_error(__FUNCTION__ . '() is deprecated in drupal:11.2.0 and is removed from drupal:12.0.0. There is no replacement. Use composer to manage the code for your site. See https://www.drupal.org/node/3512364', E_USER_DEPRECATED);
$batch_builder = (new BatchBuilder())
->setFile(\Drupal::service('extension.list.module')->getPath('update') . '/update.authorize.inc')
@@ -79,7 +79,7 @@ function update_authorize_run_update($filetransfer, $projects) {
* Reference to an array used for Batch API storage.
*/
function update_authorize_batch_copy_project($project, $updater_name, $local_url, $filetransfer, &$context): void {
- @trigger_error(__METHOD__ . '() is deprecated in drupal:11.2.0 and is removed from drupal:12.0.0. There is no replacement. Use composer to manage the code for your site. See https://www.drupal.org/node/3512364', E_USER_DEPRECATED);
+ @trigger_error(__FUNCTION__ . '() is deprecated in drupal:11.2.0 and is removed from drupal:12.0.0. There is no replacement. Use composer to manage the code for your site. See https://www.drupal.org/node/3512364', E_USER_DEPRECATED);
// Initialize some variables in the Batch API $context array.
if (!isset($context['results']['log'])) {
@@ -100,6 +100,7 @@ function update_authorize_batch_copy_project($project, $updater_name, $local_url
// though the connection itself is now gone. So, although it's ugly, we have
// to unset the connection variable at this point so that the FileTransfer
// object will re-initiate the actual connection.
+ // @phpstan-ignore property.deprecatedClass
unset($filetransfer->connection);
if (!empty($context['results']['log'][$project]['#abort'])) {
@@ -150,7 +151,7 @@ function update_authorize_batch_copy_project($project, $updater_name, $local_url
* An associative array of results from the batch operation.
*/
function update_authorize_update_batch_finished($success, $results): void {
- @trigger_error(__METHOD__ . '() is deprecated in drupal:11.2.0 and is removed from drupal:12.0.0. There is no replacement. Use composer to manage the code for your site. See https://www.drupal.org/node/3512364', E_USER_DEPRECATED);
+ @trigger_error(__FUNCTION__ . '() is deprecated in drupal:11.2.0 and is removed from drupal:12.0.0. There is no replacement. Use composer to manage the code for your site. See https://www.drupal.org/node/3512364', E_USER_DEPRECATED);
foreach ($results['log'] as $messages) {
if (!empty($messages['#abort'])) {
@@ -234,7 +235,7 @@ function update_authorize_update_batch_finished($success, $results): void {
* if there were errors. Defaults to TRUE.
*/
function _update_batch_create_message(&$project_results, $message, $success = TRUE): void {
- @trigger_error(__METHOD__ . '() is deprecated in drupal:11.2.0 and is removed from drupal:12.0.0. There is no replacement. Use composer to manage the code for your site. See https://www.drupal.org/node/3512364', E_USER_DEPRECATED);
+ @trigger_error(__FUNCTION__ . '() is deprecated in drupal:11.2.0 and is removed from drupal:12.0.0. There is no replacement. Use composer to manage the code for your site. See https://www.drupal.org/node/3512364', E_USER_DEPRECATED);
$project_results[] = ['message' => $message, 'success' => $success];
}
@@ -253,7 +254,7 @@ function _update_batch_create_message(&$project_results, $message, $success = TR
* @see update_storage_clear()
*/
function _update_authorize_clear_update_status(): void {
- @trigger_error(__METHOD__ . '() is deprecated in drupal:11.2.0 and is removed from drupal:12.0.0. There is no replacement. Use composer to manage the code for your site. See https://www.drupal.org/node/3512364', E_USER_DEPRECATED);
+ @trigger_error(__FUNCTION__ . '() is deprecated in drupal:11.2.0 and is removed from drupal:12.0.0. There is no replacement. Use composer to manage the code for your site. See https://www.drupal.org/node/3512364', E_USER_DEPRECATED);
\Drupal::keyValueExpirable('update')->deleteAll();
\Drupal::keyValueExpirable('update_available_release')->deleteAll();
diff --git a/core/modules/update/update.fetch.inc b/core/modules/update/update.fetch.inc
index 296dfbd3d06d..a0d2a22e5624 100644
--- a/core/modules/update/update.fetch.inc
+++ b/core/modules/update/update.fetch.inc
@@ -19,8 +19,15 @@ use Drupal\update\UpdateManagerInterface;
#[ProceduralHookScanStop]
function _update_cron_notify(): void {
$update_config = \Drupal::config('update.settings');
+ // This loadInclude() is to ensure that the install API is available.
+ // Since we're loading an include of type 'install', this will also
+ // include core/includes/install.inc for us, which is where the
+ // REQUIREMENTS* constants are currently defined.
+ // @todo Remove this once those constants live in a better place.
+ // @see https://www.drupal.org/project/drupal/issues/2909480
+ // @see https://www.drupal.org/project/drupal/issues/3410938
\Drupal::moduleHandler()->loadInclude('update', 'install');
- $status = update_requirements('runtime');
+ $status = \Drupal::moduleHandler()->invoke('update', 'runtime_requirements');
$params = [];
$notify_all = ($update_config->get('notification.threshold') == 'all');
foreach (['core', 'contrib'] as $report_type) {
diff --git a/core/modules/update/update.install b/core/modules/update/update.install
index 3ad55265aab1..ccd73e993bc6 100644
--- a/core/modules/update/update.install
+++ b/core/modules/update/update.install
@@ -6,70 +6,6 @@
*/
use Drupal\Core\Hook\Attribute\ProceduralHookScanStop;
-use Drupal\Core\Link;
-use Drupal\Core\Url;
-use Drupal\update\ProjectSecurityData;
-use Drupal\update\ProjectSecurityRequirement;
-use Drupal\update\UpdateFetcherInterface;
-use Drupal\update\UpdateManagerInterface;
-
-/**
- * Implements hook_requirements().
- *
- * Describes the status of the site regarding available updates. If
- * there is no update data, only one record will be returned, indicating that
- * the status of core can't be determined. If data is available, there will be
- * two records: one for core, and another for all of contrib (assuming there
- * are any contributed modules or themes installed on the site). In addition to
- * the fields expected by hook_requirements ('value', 'severity', and
- * optionally 'description'), this array will contain a 'reason' attribute,
- * which is an integer constant to indicate why the given status is being
- * returned (UPDATE_NOT_SECURE, UPDATE_NOT_CURRENT, or UPDATE_UNKNOWN). This
- * is used for generating the appropriate email notification messages during
- * update_cron(), and might be useful for other modules that invoke
- * update_requirements() to find out if the site is up to date or not.
- *
- * @see _update_message_text()
- * @see _update_cron_notify()
- * @see \Drupal\update\UpdateManagerInterface
- */
-function update_requirements($phase): array {
- $requirements = [];
- if ($phase == 'runtime') {
- if ($available = update_get_available(FALSE)) {
- \Drupal::moduleHandler()->loadInclude('update', 'inc', 'update.compare');
- $data = update_calculate_project_data($available);
- // First, populate the requirements for core:
- $requirements['update_core'] = _update_requirement_check($data['drupal'], 'core');
- if (!empty($available['drupal']['releases'])) {
- $security_data = ProjectSecurityData::createFromProjectDataAndReleases($data['drupal'], $available['drupal']['releases'])->getCoverageInfo();
- if ($core_coverage_requirement = ProjectSecurityRequirement::createFromProjectDataAndSecurityCoverageInfo($data['drupal'], $security_data)->getRequirement()) {
- $requirements['coverage_core'] = $core_coverage_requirement;
- }
- }
-
- // We don't want to check drupal a second time.
- unset($data['drupal']);
- if (!empty($data)) {
- // Now, sort our $data array based on each project's status. The
- // status constants are numbered in the right order of precedence, so
- // we just need to make sure the projects are sorted in ascending
- // order of status, and we can look at the first project we find.
- uasort($data, '_update_project_status_sort');
- $first_project = reset($data);
- $requirements['update_contrib'] = _update_requirement_check($first_project, 'contrib');
- }
- }
- else {
- $requirements['update_core']['title'] = t('Drupal core update status');
- $requirements['update_core']['value'] = t('No update data available');
- $requirements['update_core']['severity'] = REQUIREMENT_WARNING;
- $requirements['update_core']['reason'] = UpdateFetcherInterface::UNKNOWN;
- $requirements['update_core']['description'] = _update_no_data();
- }
- }
- return $requirements;
-}
/**
* Implements hook_install().
@@ -92,81 +28,6 @@ function update_uninstall(): void {
}
/**
- * Fills in the requirements array.
- *
- * This is shared for both core and contrib to generate the right elements in
- * the array for hook_requirements().
- *
- * @param array $project
- * Array of information about the project we're testing as returned by
- * update_calculate_project_data().
- * @param string $type
- * What kind of project this is ('core' or 'contrib').
- *
- * @return array
- * An array to be included in the nested $requirements array.
- *
- * @see hook_requirements()
- * @see update_requirements()
- * @see update_calculate_project_data()
- */
-function _update_requirement_check($project, $type): array {
- $requirement = [];
- if ($type == 'core') {
- $requirement['title'] = t('Drupal core update status');
- }
- else {
- $requirement['title'] = t('Module and theme update status');
- }
- $status = $project['status'];
- if ($status != UpdateManagerInterface::CURRENT) {
- $requirement['reason'] = $status;
- $requirement['severity'] = REQUIREMENT_ERROR;
- // When updates are available, append the available updates link to the
- // message from _update_message_text(), and format the two translated
- // strings together in a single paragraph.
- $requirement['description'][] = ['#markup' => _update_message_text($type, $status)];
- if (!in_array($status, [UpdateFetcherInterface::UNKNOWN, UpdateFetcherInterface::NOT_CHECKED, UpdateFetcherInterface::NOT_FETCHED, UpdateFetcherInterface::FETCH_PENDING])) {
- $requirement['description'][] = ['#prefix' => ' ', '#markup' => t('See the <a href=":available_updates">available updates</a> page for more information.', [':available_updates' => Url::fromRoute('update.status')->toString()])];
- }
- }
- switch ($status) {
- case UpdateManagerInterface::NOT_SECURE:
- $requirement_label = t('Not secure!');
- break;
-
- case UpdateManagerInterface::REVOKED:
- $requirement_label = t('Revoked!');
- break;
-
- case UpdateManagerInterface::NOT_SUPPORTED:
- $requirement_label = t('Unsupported release');
- break;
-
- case UpdateManagerInterface::NOT_CURRENT:
- $requirement_label = t('Out of date');
- $requirement['severity'] = REQUIREMENT_WARNING;
- break;
-
- case UpdateFetcherInterface::UNKNOWN:
- case UpdateFetcherInterface::NOT_CHECKED:
- case UpdateFetcherInterface::NOT_FETCHED:
- case UpdateFetcherInterface::FETCH_PENDING:
- $requirement_label = $project['reason'] ?? t('Can not determine status');
- $requirement['severity'] = REQUIREMENT_WARNING;
- break;
-
- default:
- $requirement_label = t('Up to date');
- }
- if ($status != UpdateManagerInterface::CURRENT && $type == 'core' && isset($project['recommended'])) {
- $requirement_label .= ' ' . t('(version @version available)', ['@version' => $project['recommended']]);
- }
- $requirement['value'] = Link::fromTextAndUrl($requirement_label, Url::fromRoute('update.status'))->toString();
- return $requirement;
-}
-
-/**
* Implements hook_update_last_removed().
*/
function update_update_last_removed(): int {
diff --git a/core/modules/update/update.manager.inc b/core/modules/update/update.manager.inc
index e76854ae8de9..de29e229dd46 100644
--- a/core/modules/update/update.manager.inc
+++ b/core/modules/update/update.manager.inc
@@ -26,7 +26,7 @@ use Psr\Http\Client\ClientExceptionInterface;
*/
#[ProceduralHookScanStop]
function _update_manager_check_backends(&$form, $operation) {
- @trigger_error(__METHOD__ . '() is deprecated in drupal:11.2.0 and is removed from drupal:12.0.0. There is no replacement. Use composer to manage the code for your site. See https://www.drupal.org/node/3512364', E_USER_DEPRECATED);
+ @trigger_error(__FUNCTION__ . '() is deprecated in drupal:11.2.0 and is removed from drupal:12.0.0. There is no replacement. Use composer to manage the code for your site. See https://www.drupal.org/node/3512364', E_USER_DEPRECATED);
// If file transfers will be performed locally, we do not need to display any
// warnings or notices to the user and should automatically continue the
@@ -94,7 +94,7 @@ function _update_manager_check_backends(&$form, $operation) {
* @throws Exception
*/
function update_manager_archive_extract($file, $directory) {
- @trigger_error(__METHOD__ . '() is deprecated in drupal:11.2.0 and is removed from drupal:12.0.0. There is no replacement. Use composer to manage the code for your site. See https://www.drupal.org/node/3512364', E_USER_DEPRECATED);
+ @trigger_error(__FUNCTION__ . '() is deprecated in drupal:11.2.0 and is removed from drupal:12.0.0. There is no replacement. Use composer to manage the code for your site. See https://www.drupal.org/node/3512364', E_USER_DEPRECATED);
/** @var \Drupal\Core\Archiver\ArchiverInterface $archiver */
$archiver = \Drupal::service('plugin.manager.archiver')->getInstance([
@@ -145,7 +145,7 @@ function update_manager_archive_extract($file, $directory) {
* are no errors, it will be an empty array.
*/
function update_manager_archive_verify($project, $archive_file, $directory) {
- @trigger_error(__METHOD__ . '() is deprecated in drupal:11.2.0 and is removed from drupal:12.0.0. There is no replacement. Use composer to manage the code for your site. See https://www.drupal.org/node/3512364', E_USER_DEPRECATED);
+ @trigger_error(__FUNCTION__ . '() is deprecated in drupal:11.2.0 and is removed from drupal:12.0.0. There is no replacement. Use composer to manage the code for your site. See https://www.drupal.org/node/3512364', E_USER_DEPRECATED);
return \Drupal::moduleHandler()->invokeAllDeprecated('There is no replacement. Use composer to manage the code for your site. See https://www.drupal.org/node/3512364', 'verify_update_archive', [$project, $archive_file, $directory]);
}
@@ -162,7 +162,7 @@ function update_manager_archive_verify($project, $archive_file, $directory) {
* Path to local file, or FALSE if it could not be retrieved.
*/
function update_manager_file_get($url) {
- @trigger_error(__METHOD__ . '() is deprecated in drupal:11.2.0 and is removed from drupal:12.0.0. There is no replacement. Use composer to manage the code for your site. See https://www.drupal.org/node/3512364', E_USER_DEPRECATED);
+ @trigger_error(__FUNCTION__ . '() is deprecated in drupal:11.2.0 and is removed from drupal:12.0.0. There is no replacement. Use composer to manage the code for your site. See https://www.drupal.org/node/3512364', E_USER_DEPRECATED);
$parsed_url = parse_url($url);
$remote_schemes = ['http', 'https', 'ftp', 'ftps', 'smb', 'nfs'];
@@ -214,7 +214,7 @@ function update_manager_file_get($url) {
* @see update_manager_download_page()
*/
function update_manager_batch_project_get($project, $url, &$context): void {
- @trigger_error(__METHOD__ . '() is deprecated in drupal:11.2.0 and is removed from drupal:12.0.0. There is no replacement. Use composer to manage the code for your site. See https://www.drupal.org/node/3512364', E_USER_DEPRECATED);
+ @trigger_error(__FUNCTION__ . '() is deprecated in drupal:11.2.0 and is removed from drupal:12.0.0. There is no replacement. Use composer to manage the code for your site. See https://www.drupal.org/node/3512364', E_USER_DEPRECATED);
// This is here to show the user that we are in the process of downloading.
if (!isset($context['sandbox']['started'])) {
@@ -277,7 +277,7 @@ function update_manager_batch_project_get($project, $url, &$context): void {
* @see install_check_requirements()
*/
function update_manager_local_transfers_allowed() {
- @trigger_error(__METHOD__ . '() is deprecated in drupal:11.2.0 and is removed from drupal:12.0.0. There is no replacement. Use composer to manage the code for your site. See https://www.drupal.org/node/3512364', E_USER_DEPRECATED);
+ @trigger_error(__FUNCTION__ . '() is deprecated in drupal:11.2.0 and is removed from drupal:12.0.0. There is no replacement. Use composer to manage the code for your site. See https://www.drupal.org/node/3512364', E_USER_DEPRECATED);
$file_system = \Drupal::service('file_system');
// Compare the owner of a webserver-created temporary file to the owner of
diff --git a/core/modules/user/config/optional/views.view.user_admin_people.yml b/core/modules/user/config/optional/views.view.user_admin_people.yml
index 5f9c0667cace..001c3e20cfb9 100644
--- a/core/modules/user/config/optional/views.view.user_admin_people.yml
+++ b/core/modules/user/config/optional/views.view.user_admin_people.yml
@@ -851,6 +851,7 @@ display:
sticky: false
summary: ''
empty_table: true
+ class: ''
row:
type: fields
query:
diff --git a/core/modules/user/src/Hook/UserRequirements.php b/core/modules/user/src/Hook/UserRequirements.php
new file mode 100644
index 000000000000..186ce12285f1
--- /dev/null
+++ b/core/modules/user/src/Hook/UserRequirements.php
@@ -0,0 +1,67 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Drupal\user\Hook;
+
+use Drupal\Core\Database\Connection;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Drupal\Core\Hook\Attribute\Hook;
+use Drupal\Core\StringTranslation\StringTranslationTrait;
+
+/**
+ * Requirements for the User module.
+ */
+class UserRequirements {
+
+ use StringTranslationTrait;
+
+ public function __construct(
+ protected readonly EntityTypeManagerInterface $entityTypeManager,
+ protected readonly Connection $connection,
+ ) {}
+
+ /**
+ * Implements hook_runtime_requirements().
+ */
+ #[Hook('runtime_requirements')]
+ public function runtime(): array {
+ $requirements = [];
+
+ $result = (bool) $this->entityTypeManager->getStorage('user')->getQuery()
+ ->accessCheck(FALSE)
+ ->condition('uid', 0)
+ ->range(0, 1)
+ ->execute();
+
+ if ($result === FALSE) {
+ $requirements['anonymous user'] = [
+ 'title' => $this->t('Anonymous user'),
+ 'description' => $this->t('The anonymous user does not exist. See the <a href=":url">restore the anonymous (user ID 0) user record</a> for more information', [
+ ':url' => 'https://www.drupal.org/node/1029506',
+ ]),
+ 'severity' => REQUIREMENT_WARNING,
+ ];
+ }
+
+ $query = $this->connection->select('users_field_data');
+ $query->addExpression('LOWER(mail)', 'lower_mail');
+ $query->isNotNull('mail');
+ $query->groupBy('lower_mail');
+ $query->having('COUNT(uid) > :matches', [':matches' => 1]);
+ $conflicts = $query->countQuery()->execute()->fetchField();
+
+ if ($conflicts > 0) {
+ $requirements['conflicting emails'] = [
+ 'title' => $this->t('Conflicting user emails'),
+ 'description' => $this->t('Some user accounts have email addresses that differ only by case. For example, one account might have alice@example.com and another might have Alice@Example.com. See <a href=":url">Conflicting User Emails</a> for more information.', [
+ ':url' => 'https://www.drupal.org/node/3486109',
+ ]),
+ 'severity' => REQUIREMENT_WARNING,
+ ];
+ }
+
+ return $requirements;
+ }
+
+}
diff --git a/core/modules/user/tests/modules/user_test_views/test_views/views.view.test_user_bulk_form.yml b/core/modules/user/tests/modules/user_test_views/test_views/views.view.test_user_bulk_form.yml
index 7858370e137d..24e851e3f2a7 100644
--- a/core/modules/user/tests/modules/user_test_views/test_views/views.view.test_user_bulk_form.yml
+++ b/core/modules/user/tests/modules/user_test_views/test_views/views.view.test_user_bulk_form.yml
@@ -19,6 +19,8 @@ display:
display_options:
style:
type: table
+ options:
+ class: ''
row:
type: fields
fields:
diff --git a/core/modules/user/tests/modules/user_test_views/test_views/views.view.test_user_fields_access.yml b/core/modules/user/tests/modules/user_test_views/test_views/views.view.test_user_fields_access.yml
index ce16bacc3aae..c0b9b9d7879e 100644
--- a/core/modules/user/tests/modules/user_test_views/test_views/views.view.test_user_fields_access.yml
+++ b/core/modules/user/tests/modules/user_test_views/test_views/views.view.test_user_fields_access.yml
@@ -63,6 +63,7 @@ display:
type: table
options:
grouping: { }
+ class: ''
row_class: ''
default_row_class: true
override: true
diff --git a/core/modules/user/tests/src/Kernel/UserRequirementsTest.php b/core/modules/user/tests/src/Kernel/UserRequirementsTest.php
index 88b406bc30e4..146ab9c8b904 100644
--- a/core/modules/user/tests/src/Kernel/UserRequirementsTest.php
+++ b/core/modules/user/tests/src/Kernel/UserRequirementsTest.php
@@ -22,12 +22,20 @@ class UserRequirementsTest extends KernelTestBase {
protected static $modules = ['user'];
/**
+ * Module handler for invoking user requirements.
+ *
+ * @var \Drupal\Core\Extension\ModuleHandlerInterface
+ */
+ protected $moduleHandler;
+
+ /**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
- $this->container->get('module_handler')->loadInclude('user', 'install');
+ $this->moduleHandler = $this->container->get('module_handler');
$this->installEntitySchema('user');
+ include_once $this->root . '/core/includes/install.inc';
}
/**
@@ -37,13 +45,13 @@ class UserRequirementsTest extends KernelTestBase {
*/
public function testConflictingUserEmails(): void {
- $output = \user_requirements('runtime');
+ $output = $this->moduleHandler->invoke('user', 'runtime_requirements');
$this->assertArrayNotHasKey('conflicting emails', $output);
$this->createUser([], 'User A', FALSE, ['mail' => 'unique@example.com']);
$this->createUser([], 'User B', FALSE, ['mail' => 'UNIQUE@example.com']);
- $output = \user_requirements('runtime');
+ $output = $this->moduleHandler->invoke('user', 'runtime_requirements');
$this->assertArrayHasKey('conflicting emails', $output);
}
@@ -52,13 +60,13 @@ class UserRequirementsTest extends KernelTestBase {
*/
public function testBlankUserEmails(): void {
- $output = \user_requirements('runtime');
+ $output = $this->moduleHandler->invoke('user', 'runtime_requirements');
$this->assertArrayNotHasKey('conflicting emails', $output);
$this->createUser([], 'User A', FALSE, ['mail' => '']);
$this->createUser([], 'User B', FALSE, ['mail' => '']);
- $output = \user_requirements('runtime');
+ $output = $this->moduleHandler->invoke('user', 'runtime_requirements');
$this->assertArrayNotHasKey('conflicting emails', $output);
}
diff --git a/core/modules/user/user.install b/core/modules/user/user.install
index 99ede9e59b76..747e61253226 100644
--- a/core/modules/user/user.install
+++ b/core/modules/user/user.install
@@ -92,51 +92,6 @@ function user_install(): void {
}
/**
- * Implements hook_requirements().
- */
-function user_requirements($phase): array {
- if ($phase !== 'runtime') {
- return [];
- }
- $return = [];
-
- $result = (bool) \Drupal::entityQuery('user')
- ->accessCheck(FALSE)
- ->condition('uid', 0)
- ->range(0, 1)
- ->execute();
-
- if ($result === FALSE) {
- $return['anonymous user'] = [
- 'title' => t('Anonymous user'),
- 'description' => t('The anonymous user does not exist. See the <a href=":url">restore the anonymous (user ID 0) user record</a> for more information', [
- ':url' => 'https://www.drupal.org/node/1029506',
- ]),
- 'severity' => REQUIREMENT_WARNING,
- ];
- }
-
- $query = \Drupal::database()->select('users_field_data');
- $query->addExpression('LOWER(mail)', 'lower_mail');
- $query->isNotNull('mail');
- $query->groupBy('lower_mail');
- $query->having('COUNT(uid) > :matches', [':matches' => 1]);
- $conflicts = $query->countQuery()->execute()->fetchField();
-
- if ($conflicts > 0) {
- $return['conflicting emails'] = [
- 'title' => t('Conflicting user emails'),
- 'description' => t('Some user accounts have email addresses that differ only by case. For example, one account might have alice@example.com and another might have Alice@Example.com. See <a href=":url">Conflicting User Emails</a> for more information.', [
- ':url' => 'https://www.drupal.org/node/3486109',
- ]),
- 'severity' => REQUIREMENT_WARNING,
- ];
- }
-
- return $return;
-}
-
-/**
* Implements hook_update_last_removed().
*/
function user_update_last_removed(): int {
diff --git a/core/modules/views/config/schema/views.schema.yml b/core/modules/views/config/schema/views.schema.yml
index 8ddd73931f2c..12187c6b9465 100644
--- a/core/modules/views/config/schema/views.schema.yml
+++ b/core/modules/views/config/schema/views.schema.yml
@@ -140,16 +140,29 @@ views.view.*:
views_block:
type: block_settings
label: 'View block'
+ constraints:
+ FullyValidatable: ~
mapping:
views_label:
type: label
label: 'Title'
+ requiredKey: false
items_per_page:
- type: string
+ type: integer
label: 'Items per block'
+ constraints:
+ Range:
+ min: 1
+ # Will only be respected if the associated View is configured to allow this to be overridden.
+ # @see \Drupal\views\Plugin\views\display\Block::blockForm()
+ requiredKey: false
+ # NULL to use the default defined by the view.
+ nullable: true
block.settings.views_block:*:
type: views_block
+ constraints:
+ FullyValidatable: ~
block.settings.views_exposed_filter_block:*:
type: views_block
diff --git a/core/modules/views/src/FieldViewsDataProvider.php b/core/modules/views/src/FieldViewsDataProvider.php
index c0b9d50b2d9c..fad34ff460e2 100644
--- a/core/modules/views/src/FieldViewsDataProvider.php
+++ b/core/modules/views/src/FieldViewsDataProvider.php
@@ -139,8 +139,8 @@ class FieldViewsDataProvider {
if (!empty($translatable_configs) && empty($untranslatable_configs)) {
$translation_join_type = 'language';
}
- // If the field is translatable only on certain bundles, there will be a join
- // on langcode OR bundle name.
+ // If the field is translatable only on certain bundles, there will be a
+ // join on langcode OR bundle name.
elseif (!empty($translatable_configs) && !empty($untranslatable_configs)) {
foreach ($untranslatable_configs as $config) {
$untranslatable_config_bundles[] = $config->getTargetBundle();
@@ -268,8 +268,8 @@ class FieldViewsDataProvider {
'help' => $this->t('Appears in: @bundles.', ['@bundles' => implode(', ', $bundles_names)]),
];
- // Go through and create a list of aliases for all possible combinations of
- // entity type + name.
+ // Go through and create a list of aliases for all possible combinations
+ // of entity type + name.
$aliases = [];
$also_known = [];
foreach ($all_labels as $label_name => $true) {
@@ -296,15 +296,15 @@ class FieldViewsDataProvider {
}
if ($aliases) {
$data[$table_alias][$field_alias]['aliases'] = $aliases;
- // The $also_known variable contains markup that is HTML escaped and that
- // loses safeness when imploded. The help text is used in #description
- // and therefore XSS admin filtered by default. Escaped HTML is not
- // altered by XSS filtering, therefore it is safe to just concatenate the
- // strings. Afterwards we mark the entire string as safe, so it won't be
- // escaped, no matter where it is used.
+ // The $also_known variable contains markup that is HTML escaped and
+ // that loses safeness when imploded. The help text is used in
+ // #description and therefore XSS admin filtered by default. Escaped
+ // HTML is not altered by XSS filtering, therefore it is safe to just
+ // concatenate the strings. Afterwards we mark the entire string as
+ // safe, so it won't be escaped, no matter where it is used.
// Considering the dual use of this help data (both as metadata and as
- // help text), other patterns such as use of #markup would not be correct
- // here.
+ // help text), other patterns such as use of #markup would not be
+ // correct here.
$data[$table_alias][$field_alias]['help'] = Markup::create($data[$table_alias][$field_alias]['help'] . ' ' . $this->t('Also known as:') . ' ' . implode(', ', $also_known));
}
@@ -328,7 +328,8 @@ class FieldViewsDataProvider {
foreach ($field_columns as $column => $attributes) {
$allow_sort = TRUE;
- // Identify likely filters and arguments for each column based on field type.
+ // Identify likely filters and arguments for each column based on field
+ // type.
switch ($attributes['type']) {
case 'int':
case 'mediumint':
@@ -387,8 +388,8 @@ class FieldViewsDataProvider {
'help' => $this->t('Appears in: @bundles.', ['@bundles' => implode(', ', $bundles_names)]),
];
- // Go through and create a list of aliases for all possible combinations of
- // entity type + name.
+ // Go through and create a list of aliases for all possible combinations
+ // of entity type + name.
$aliases = [];
$also_known = [];
foreach ($all_labels as $label_name => $true) {
diff --git a/core/modules/views/src/Hook/ViewsHooks.php b/core/modules/views/src/Hook/ViewsHooks.php
index 6facc63de6de..b309887723c3 100644
--- a/core/modules/views/src/Hook/ViewsHooks.php
+++ b/core/modules/views/src/Hook/ViewsHooks.php
@@ -2,6 +2,7 @@
namespace Drupal\views\Hook;
+use Drupal\block\BlockInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\views\ViewsConfigUpdater;
use Drupal\views\ViewEntityInterface;
@@ -378,4 +379,19 @@ class ViewsHooks {
$config_updater->updateAll($view);
}
+ /**
+ * Implements hook_ENTITY_TYPE_presave() for blocks.
+ */
+ #[Hook('block_presave')]
+ public function blockPresave(BlockInterface $block): void {
+ if (str_starts_with($block->getPluginId(), 'views_block:')) {
+ $settings = $block->get('settings');
+ if (isset($settings['items_per_page']) && $settings['items_per_page'] === 'none') {
+ @trigger_error('Saving a views block with "none" items per page is deprecated in drupal:11.2.0 and removed in drupal:12.0.0. To use the items per page defined by the view, use NULL. See https://www.drupal.org/node/3522240', E_USER_DEPRECATED);
+ $settings['items_per_page'] = NULL;
+ $block->set('settings', $settings);
+ }
+ }
+ }
+
}
diff --git a/core/modules/views/src/Hook/ViewsViewsHooks.php b/core/modules/views/src/Hook/ViewsViewsHooks.php
index a54decce9e62..4f10f689646c 100644
--- a/core/modules/views/src/Hook/ViewsViewsHooks.php
+++ b/core/modules/views/src/Hook/ViewsViewsHooks.php
@@ -143,8 +143,9 @@ class ViewsViewsHooks {
}
}
// Registers an action bulk form per entity.
+ $all_actions = \Drupal::entityTypeManager()->getStorage('action')->loadMultiple();
foreach (\Drupal::entityTypeManager()->getDefinitions() as $entity_type => $entity_info) {
- $actions = array_filter(\Drupal::entityTypeManager()->getStorage('action')->loadMultiple(), function (ActionConfigEntityInterface $action) use ($entity_type) {
+ $actions = array_filter($all_actions, function (ActionConfigEntityInterface $action) use ($entity_type) {
return $action->getType() == $entity_type;
});
if (empty($actions)) {
diff --git a/core/modules/views/src/Plugin/views/argument/LanguageArgument.php b/core/modules/views/src/Plugin/views/argument/LanguageArgument.php
index 23980568f764..81a2181dd5a2 100644
--- a/core/modules/views/src/Plugin/views/argument/LanguageArgument.php
+++ b/core/modules/views/src/Plugin/views/argument/LanguageArgument.php
@@ -15,21 +15,19 @@ use Drupal\views\Attribute\ViewsArgument;
class LanguageArgument extends ArgumentPluginBase {
/**
- * Overrides \Drupal\views\Plugin\views\argument\ArgumentPluginBase::summaryName().
- *
- * Gets the user-friendly version of the language name.
+ * {@inheritdoc}
*/
public function summaryName($data) {
+ // Gets the user-friendly version of the language name.
return $this->language($data->{$this->name_alias});
}
/**
- * Overrides \Drupal\views\Plugin\views\argument\ArgumentPluginBase::title().
- *
- * Gets the user friendly version of the language name for display as a
- * title placeholder.
+ * {@inheritdoc}
*/
public function title() {
+ // Gets the user friendly version of the language name for display as a
+ // title placeholder.
return $this->language($this->argument);
}
diff --git a/core/modules/views/src/Plugin/views/display/Block.php b/core/modules/views/src/Plugin/views/display/Block.php
index 6532990b8d3e..5692ee1df013 100644
--- a/core/modules/views/src/Plugin/views/display/Block.php
+++ b/core/modules/views/src/Plugin/views/display/Block.php
@@ -120,7 +120,7 @@ class Block extends DisplayPluginBase {
* @see \Drupal\views\Plugin\Block\ViewsBlock::defaultConfiguration()
*/
public function blockSettings(array $settings) {
- $settings['items_per_page'] = 'none';
+ $settings['items_per_page'] = NULL;
return $settings;
}
@@ -315,7 +315,7 @@ class Block extends DisplayPluginBase {
40 => 40,
48 => 48,
],
- '#default_value' => $block_configuration['items_per_page'],
+ '#default_value' => $block_configuration['items_per_page'] ?? 'none',
];
break;
}
@@ -353,7 +353,7 @@ class Block extends DisplayPluginBase {
*/
public function blockSubmit(ViewsBlock $block, $form, FormStateInterface $form_state) {
if ($items_per_page = $form_state->getValue(['override', 'items_per_page'])) {
- $block->setConfigurationValue('items_per_page', $items_per_page);
+ $block->setConfigurationValue('items_per_page', $items_per_page === 'none' ? NULL : intval($items_per_page));
}
$form_state->unsetValue(['override', 'items_per_page']);
}
@@ -366,8 +366,9 @@ class Block extends DisplayPluginBase {
*/
public function preBlockBuild(ViewsBlock $block) {
$config = $block->getConfiguration();
- if ($config['items_per_page'] !== 'none') {
- $this->view->setItemsPerPage($config['items_per_page']);
+ if (is_numeric($config['items_per_page']) && $config['items_per_page'] > 0) {
+ // @todo Delete the intval() in https://www.drupal.org/project/drupal/issues/3521221
+ $this->view->setItemsPerPage(intval($config['items_per_page']));
}
}
diff --git a/core/modules/views/src/Plugin/views/field/Boolean.php b/core/modules/views/src/Plugin/views/field/Boolean.php
index f2eb8f639b87..0c91fdc59509 100644
--- a/core/modules/views/src/Plugin/views/field/Boolean.php
+++ b/core/modules/views/src/Plugin/views/field/Boolean.php
@@ -16,8 +16,9 @@ use Drupal\views\Plugin\views\display\DisplayPluginBase;
* Allows for display of true/false, yes/no, on/off, enabled/disabled.
*
* Definition terms:
- * - output formats: An array where the first entry is displayed on boolean true
- * and the second is displayed on boolean false. An example for sticky is:
+ * - output formats: An array where the first entry is displayed on boolean
+ * true and the second is displayed on boolean false. An example for sticky
+ * is:
* @code
* 'output formats' => [
* 'sticky' => [t('Sticky'), ''],
diff --git a/core/modules/views/src/Plugin/views/field/Url.php b/core/modules/views/src/Plugin/views/field/Url.php
index 18e40a61f0e9..7f5dd0a33653 100644
--- a/core/modules/views/src/Plugin/views/field/Url.php
+++ b/core/modules/views/src/Plugin/views/field/Url.php
@@ -9,7 +9,7 @@ use Drupal\views\Attribute\ViewsField;
use Drupal\views\ResultRow;
/**
- * Field handler to provide simple renderer that turns a URL into a clickable link.
+ * Field handler to provide a renderer that turns a URL into a clickable link.
*
* @ingroup views_field_handlers
*/
diff --git a/core/modules/views/src/Plugin/views/filter/FilterPluginBase.php b/core/modules/views/src/Plugin/views/filter/FilterPluginBase.php
index e3b5b87ac2ef..0f526735d6e3 100644
--- a/core/modules/views/src/Plugin/views/filter/FilterPluginBase.php
+++ b/core/modules/views/src/Plugin/views/filter/FilterPluginBase.php
@@ -889,7 +889,7 @@ abstract class FilterPluginBase extends HandlerBase implements CacheableDependen
* (optional) The form element to set any errors on.
*
* @return string
- * Returns an error message if validation fails, or NULL if validation passes.
+ * The error message if validation fails, or NULL if validation passes.
*/
protected function validateIdentifier($identifier, ?FormStateInterface $form_state = NULL, &$form_group = []) {
$error = '';
@@ -1226,9 +1226,11 @@ abstract class FilterPluginBase extends HandlerBase implements CacheableDependen
continue;
}
// Each rows contains three widgets:
- // a) The title, where users define how they identify a pair of operator | value
- // b) The operator
- // c) The value (or values) to use in the filter with the selected operator
+ // - The title, where users define how they identify a pair of
+ // operator | value.
+ // - The operator.
+ // - The value (or values) to use in the filter with the selected
+ // operator.
// In each row, we have to display the operator form and the value from
// $row acts as a fake form to render each widget in a row.
diff --git a/core/modules/views/src/Plugin/views/style/Table.php b/core/modules/views/src/Plugin/views/style/Table.php
index 48adbf427ede..561628ac6820 100644
--- a/core/modules/views/src/Plugin/views/style/Table.php
+++ b/core/modules/views/src/Plugin/views/style/Table.php
@@ -71,7 +71,7 @@ class Table extends StylePluginBase implements CacheableDependencyInterface {
$options = parent::defineOptions();
$options['columns'] = ['default' => []];
- $options['class'] = ['default' => []];
+ $options['class'] = ['default' => ''];
$options['default'] = ['default' => ''];
$options['info'] = ['default' => []];
$options['override'] = ['default' => TRUE];
diff --git a/core/modules/views/src/Plugin/views/wizard/WizardPluginBase.php b/core/modules/views/src/Plugin/views/wizard/WizardPluginBase.php
index ef8568203cd4..d53e93cecec1 100644
--- a/core/modules/views/src/Plugin/views/wizard/WizardPluginBase.php
+++ b/core/modules/views/src/Plugin/views/wizard/WizardPluginBase.php
@@ -208,7 +208,8 @@ abstract class WizardPluginBase extends PluginBase implements WizardInterface {
* Gets the availableSorts property.
*
* @return array
- * An array of available sorts, keyed by sort ID, containing sort information.
+ * An array whose keys are the available sort options and whose
+ * corresponding values are human readable labels.
*/
public function getAvailableSorts() {
return $this->availableSorts;
@@ -483,7 +484,7 @@ abstract class WizardPluginBase extends PluginBase implements WizardInterface {
}
/**
- * Gets the current value of a #select element, from within a form constructor function.
+ * Gets the current value of a #select element.
*
* This function is intended for use in highly dynamic forms (in particular
* the add view wizard) which are rebuilt in different ways depending on which
diff --git a/core/modules/views/src/ViewExecutable.php b/core/modules/views/src/ViewExecutable.php
index 55e3a8d91453..4867ba48f9a6 100644
--- a/core/modules/views/src/ViewExecutable.php
+++ b/core/modules/views/src/ViewExecutable.php
@@ -348,7 +348,7 @@ class ViewExecutable {
public $footer;
/**
- * Stores the area handlers for the empty text which are initialized on this view.
+ * The area handlers for the empty text which are initialized on this view.
*
* An array containing Drupal\views\Plugin\views\area\AreaPluginBase objects.
*
diff --git a/core/modules/views/src/ViewsConfigUpdater.php b/core/modules/views/src/ViewsConfigUpdater.php
index 3b2709cc60c2..9c5d38e10ac5 100644
--- a/core/modules/views/src/ViewsConfigUpdater.php
+++ b/core/modules/views/src/ViewsConfigUpdater.php
@@ -134,6 +134,9 @@ class ViewsConfigUpdater implements ContainerInjectionInterface {
if ($this->processRememberRolesUpdate($handler, $handler_type)) {
$changed = TRUE;
}
+ if ($this->processTableCssClassUpdate($view)) {
+ $changed = TRUE;
+ }
return $changed;
});
}
@@ -335,6 +338,7 @@ class ViewsConfigUpdater implements ContainerInjectionInterface {
if (
isset($display['display_options']['style']) &&
$display['display_options']['style']['type'] === 'table' &&
+ isset($display['display_options']['style']['options']) &&
!isset($display['display_options']['style']['options']['class'])
) {
$display['display_options']['style']['options']['class'] = '';
@@ -346,6 +350,12 @@ class ViewsConfigUpdater implements ContainerInjectionInterface {
$view->set('display', $displays);
}
+ $deprecations_triggered = &$this->triggeredDeprecations['table_css_class'][$view->id()];
+ if ($this->deprecationsEnabled && $changed && !$deprecations_triggered) {
+ $deprecations_triggered = TRUE;
+ @trigger_error(sprintf('The update to add a default table CSS class for view "%s" is deprecated in drupal:11.2.0 and is removed from drupal:12.0.0. Profile, module and theme provided configuration should be updated. See https://www.drupal.org/node/3499943', $view->id()), E_USER_DEPRECATED);
+ }
+
return $changed;
}
diff --git a/core/modules/views/tests/fixtures/update/views-block-items-per-page.php b/core/modules/views/tests/fixtures/update/views-block-items-per-page.php
new file mode 100644
index 000000000000..542992a24df2
--- /dev/null
+++ b/core/modules/views/tests/fixtures/update/views-block-items-per-page.php
@@ -0,0 +1,48 @@
+<?php
+
+/**
+ * @file
+ * Creates a Views block with an `items_per_page` setting of `none`.
+ */
+
+declare(strict_types=1);
+
+use Drupal\Core\Database\Database;
+use Drupal\Core\Serialization\Yaml;
+
+$block_data = Yaml::decode(<<<END
+uuid: ecdad54d-8165-4ed3-a678-8ad20b388282
+langcode: en
+status: true
+dependencies:
+ config:
+ - views.view.who_s_online
+ module:
+ - views
+ theme:
+ - olivero
+id: olivero_who_s_online
+theme: olivero
+region: header
+weight: 0
+provider: null
+plugin: 'views_block:who_s_online-who_s_online_block'
+settings:
+ id: 'views_block:who_s_online-who_s_online_block'
+ label: ''
+ label_display: visible
+ provider: views
+ views_label: ''
+ items_per_page: none
+visibility: { }
+END
+);
+
+Database::getConnection()
+ ->insert('config')
+ ->fields([
+ 'collection' => '',
+ 'name' => 'block.block.olivero_who_s_online',
+ 'data' => serialize($block_data),
+ ])
+ ->execute();
diff --git a/core/modules/views/tests/modules/action_bulk_test/config/install/views.view.test_bulk_form.yml b/core/modules/views/tests/modules/action_bulk_test/config/install/views.view.test_bulk_form.yml
index cba11476ae8b..573705f502da 100644
--- a/core/modules/views/tests/modules/action_bulk_test/config/install/views.view.test_bulk_form.yml
+++ b/core/modules/views/tests/modules/action_bulk_test/config/install/views.view.test_bulk_form.yml
@@ -126,6 +126,7 @@ display:
type: table
options:
grouping: { }
+ class: ''
row_class: ''
default_row_class: true
columns:
diff --git a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_click_sort.yml b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_click_sort.yml
index 3ab5ecb66894..077b4bb4ce16 100644
--- a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_click_sort.yml
+++ b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_click_sort.yml
@@ -42,6 +42,7 @@ display:
style:
type: table
options:
+ class: ''
info:
id:
sortable: true
diff --git a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_click_sort_ajax.yml b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_click_sort_ajax.yml
index bf65e0eaf379..1157a4e60bca 100644
--- a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_click_sort_ajax.yml
+++ b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_click_sort_ajax.yml
@@ -43,6 +43,15 @@ display:
style:
type: table
options:
+ grouping: { }
+ class: ''
+ row_class: ''
+ default_row_class: true
+ override: true
+ sticky: true
+ caption: ''
+ summary: ''
+ description: ''
info:
id:
sortable: true
diff --git a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_content_ajax.yml b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_content_ajax.yml
index db3fd0693bf7..2036ee77c5fb 100644
--- a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_content_ajax.yml
+++ b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_content_ajax.yml
@@ -57,6 +57,7 @@ display:
type: table
options:
grouping: { }
+ class: ''
row_class: ''
default_row_class: true
override: true
diff --git a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_distinct_click_sorting.yml b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_distinct_click_sorting.yml
index 55813bc9c49a..fe6c14f744bf 100644
--- a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_distinct_click_sorting.yml
+++ b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_distinct_click_sorting.yml
@@ -65,6 +65,7 @@ display:
type: table
options:
grouping: { }
+ class: ''
row_class: ''
default_row_class: true
override: true
diff --git a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_entity_operations.yml b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_entity_operations.yml
index a19cea01b9f3..5ed73c2a1178 100644
--- a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_entity_operations.yml
+++ b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_entity_operations.yml
@@ -133,6 +133,7 @@ display:
type: table
options:
grouping: { }
+ class: ''
row_class: ''
default_row_class: true
override: true
diff --git a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_field_header.yml b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_field_header.yml
index 091c4375635e..a8c84a7a6497 100644
--- a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_field_header.yml
+++ b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_field_header.yml
@@ -20,6 +20,8 @@ display:
element_label_type: h2
style:
type: table
+ options:
+ class: ''
display_extenders: { }
display_plugin: default
display_title: Default
diff --git a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_filter_placeholder_text.yml b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_filter_placeholder_text.yml
index 1f33e75b61fb..1217b6c86eba 100644
--- a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_filter_placeholder_text.yml
+++ b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_filter_placeholder_text.yml
@@ -52,6 +52,7 @@ display:
type: table
options:
grouping: { }
+ class: ''
row_class: ''
default_row_class: true
override: true
diff --git a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_form_multiple.yml b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_form_multiple.yml
index ddb305fb162e..dffcbe3c0b72 100644
--- a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_form_multiple.yml
+++ b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_form_multiple.yml
@@ -40,6 +40,7 @@ display:
type: table
options:
grouping: { }
+ class: ''
row_class: ''
default_row_class: true
override: true
diff --git a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_glossary.yml b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_glossary.yml
index 21ac6945808e..aa3e387ee905 100644
--- a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_glossary.yml
+++ b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_glossary.yml
@@ -286,6 +286,7 @@ display:
override: true
sticky: false
grouping: { }
+ class: ''
row_class: ''
default_row_class: true
uses_fields: false
diff --git a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_row_render_cache.yml b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_row_render_cache.yml
index b52621ee9908..a4bda56b5437 100644
--- a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_row_render_cache.yml
+++ b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_row_render_cache.yml
@@ -68,6 +68,7 @@ display:
type: table
options:
grouping: { }
+ class: ''
row_class: ''
default_row_class: true
override: true
diff --git a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_row_render_cache_none.yml b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_row_render_cache_none.yml
index aea7b9040134..91382cd03900 100644
--- a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_row_render_cache_none.yml
+++ b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_row_render_cache_none.yml
@@ -68,6 +68,7 @@ display:
type: table
options:
grouping: { }
+ class: ''
row_class: ''
default_row_class: true
override: true
diff --git a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_table.yml b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_table.yml
index 10d215d29b51..77cca5f518f3 100644
--- a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_table.yml
+++ b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_table.yml
@@ -63,6 +63,7 @@ display:
type: table
options:
grouping: { }
+ class: ''
row_class: ''
default_row_class: true
override: true
diff --git a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_view_render.yml b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_view_render.yml
index cec43f7c7dc7..98fa2836be96 100644
--- a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_view_render.yml
+++ b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_view_render.yml
@@ -48,6 +48,8 @@ display:
plugin_id: standard
style:
type: table
+ options:
+ class: ''
display_plugin: default
display_title: Default
id: default
diff --git a/core/modules/views/tests/src/Functional/Plugin/ContextualFiltersBlockContextTest.php b/core/modules/views/tests/src/Functional/Plugin/ContextualFiltersBlockContextTest.php
index 955226497f95..0384c917083e 100644
--- a/core/modules/views/tests/src/Functional/Plugin/ContextualFiltersBlockContextTest.php
+++ b/core/modules/views/tests/src/Functional/Plugin/ContextualFiltersBlockContextTest.php
@@ -122,7 +122,7 @@ class ContextualFiltersBlockContextTest extends ViewTestBase {
'provider' => 'views',
'label_display' => 'visible',
'views_label' => '',
- 'items_per_page' => 'none',
+ 'items_per_page' => NULL,
'context_mapping' => ['nid' => '@node.node_route_context:node'],
];
$this->assertEquals($expected_settings, $block->getPlugin()->getConfiguration(), 'Block settings are correct.');
diff --git a/core/modules/views/tests/src/Functional/Update/BlockItemsPerPageUpdateTest.php b/core/modules/views/tests/src/Functional/Update/BlockItemsPerPageUpdateTest.php
new file mode 100644
index 000000000000..bd250cc2736e
--- /dev/null
+++ b/core/modules/views/tests/src/Functional/Update/BlockItemsPerPageUpdateTest.php
@@ -0,0 +1,41 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Drupal\Tests\views\Functional\Update;
+
+use Drupal\block\Entity\Block;
+use Drupal\FunctionalTests\Update\UpdatePathTestBase;
+
+/**
+ * @group Update
+ * @covers views_post_update_block_items_per_page
+ */
+final class BlockItemsPerPageUpdateTest extends UpdatePathTestBase {
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function setDatabaseDumpFiles(): void {
+ $this->databaseDumpFiles = [
+ __DIR__ . '/../../../../../system/tests/fixtures/update/drupal-10.3.0.filled.standard.php.gz',
+ __DIR__ . '/../../../fixtures/update/views-block-items-per-page.php',
+ ];
+ }
+
+ /**
+ * Tests changing an `items_per_page` setting of `none` to NULL.
+ */
+ public function testUpdateItemsPerPage(): void {
+ $settings = Block::load('olivero_who_s_online')?->get('settings');
+ $this->assertIsArray($settings);
+ $this->assertSame('none', $settings['items_per_page']);
+
+ $this->runUpdates();
+
+ $settings = Block::load('olivero_who_s_online')?->get('settings');
+ $this->assertIsArray($settings);
+ $this->assertNull($settings['items_per_page']);
+ }
+
+}
diff --git a/core/modules/views/tests/src/Functional/Wizard/ItemsPerPageTest.php b/core/modules/views/tests/src/Functional/Wizard/ItemsPerPageTest.php
index 5f9cd364dac8..d290a27cd191 100644
--- a/core/modules/views/tests/src/Functional/Wizard/ItemsPerPageTest.php
+++ b/core/modules/views/tests/src/Functional/Wizard/ItemsPerPageTest.php
@@ -4,6 +4,8 @@ declare(strict_types=1);
namespace Drupal\Tests\views\Functional\Wizard;
+use Drupal\views\Entity\View;
+
/**
* Tests that the views wizard can specify the number of items per page.
*
@@ -19,6 +21,16 @@ class ItemsPerPageTest extends WizardTestBase {
/**
* {@inheritdoc}
*/
+ protected static $configSchemaCheckerExclusions = [
+ // To be able to test with the now invalid:
+ // - `items_per_page: 'none'`
+ // - `items_per_page: '5'`
+ 'block.block.views_block_items_per_page_test_with_historical_override',
+ ];
+
+ /**
+ * {@inheritdoc}
+ */
protected function setUp($import_test_views = TRUE, $modules = []): void {
parent::setUp($import_test_views, $modules);
@@ -27,6 +39,12 @@ class ItemsPerPageTest extends WizardTestBase {
/**
* Tests the number of items per page.
+ *
+ * This should be removed from the `legacy` group in
+ * https://drupal.org/i/3521221; see
+ * \Drupal\views\Hook\ViewsHooks::blockPresave().
+ *
+ * @group legacy
*/
public function testItemsPerPage(): void {
$this->drupalCreateContentType(['type' => 'article']);
@@ -83,7 +101,7 @@ class ItemsPerPageTest extends WizardTestBase {
$this->drupalGet($view['page[path]']);
$this->assertSession()->statusCodeEquals(200);
- // Make sure the page display shows the nodes we expect, and that they
+ // Make sure the page display shows the 4 nodes we expect, and that they
// appear in the expected order.
$this->assertSession()->addressEquals($view['page[path]']);
$this->assertSession()->pageTextContains($view['page[title]']);
@@ -109,21 +127,94 @@ class ItemsPerPageTest extends WizardTestBase {
// Place the block, visit a page that displays the block, and check that the
// nodes we expect appear in the correct order.
- $this->drupalPlaceBlock("views_block:{$view['id']}-block_1");
-
+ $block = $this->drupalPlaceBlock("views_block:{$view['id']}-block_1");
+
+ // Asserts that the 3 newest articles are listed, which is the configuration
+ // for the `block` display in the view. In other words: the `items_per_page`
+ // setting in the `View` config entity is respected.
+ $assert_3_newest_nodes = function () use ($node5, $node4, $node3, $node2, $node1, $page_node) {
+ $this->drupalGet('user');
+ $content = $this->getSession()->getPage()->getContent();
+ $this->assertSession()->pageTextContains($node5->label());
+ $this->assertSession()->pageTextContains($node4->label());
+ $this->assertSession()->pageTextContains($node3->label());
+ $this->assertSession()->pageTextNotContains($node2->label());
+ $this->assertSession()->pageTextNotContains($node1->label());
+ $this->assertSession()->pageTextNotContains($page_node->label());
+ $pos5 = strpos($content, $node5->label());
+ $pos4 = strpos($content, $node4->label());
+ $pos3 = strpos($content, $node3->label());
+ $this->assertGreaterThan($pos5, $pos4);
+ $this->assertGreaterThan($pos4, $pos3);
+ };
+ self::assertSame(4, View::load($view['id'])->toArray()['display']['default']['display_options']['pager']['options']['items_per_page']);
+ self::assertSame(3, View::load($view['id'])->toArray()['display']['block_1']['display_options']['pager']['options']['items_per_page']);
+ self::assertArrayNotHasKey('items_per_page', $block->get('settings'));
+ $assert_3_newest_nodes();
+ $block->delete();
+
+ // Because the `allow[items_per_page]` checkbox is checked, it is allowed to
+ // override the `items_per_page` setting for the Views's `block` display,
+ // and is actually respected. Valid values are `null` ("do not override")
+ // and a positive integer.
+ $block = $this->drupalPlaceBlock("views_block:{$view['id']}-block_1", [
+ 'items_per_page' => NULL,
+ ]);
+ self::assertSame(4, View::load($view['id'])->toArray()['display']['default']['display_options']['pager']['options']['items_per_page']);
+ self::assertSame(3, View::load($view['id'])->toArray()['display']['block_1']['display_options']['pager']['options']['items_per_page']);
+ self::assertNull($block->get('settings')['items_per_page']);
+ $assert_3_newest_nodes();
+ $block->delete();
+
+ $block = $this->drupalPlaceBlock("views_block:{$view['id']}-block_1", [
+ 'items_per_page' => 5,
+ ]);
+ self::assertSame(4, View::load($view['id'])->toArray()['display']['default']['display_options']['pager']['options']['items_per_page']);
+ self::assertSame(3, View::load($view['id'])->toArray()['display']['block_1']['display_options']['pager']['options']['items_per_page']);
+ self::assertSame(5, $block->get('settings')['items_per_page']);
$this->drupalGet('user');
- $content = $this->getSession()->getPage()->getContent();
- $this->assertSession()->pageTextContains($node5->label());
- $this->assertSession()->pageTextContains($node4->label());
- $this->assertSession()->pageTextContains($node3->label());
- $this->assertSession()->pageTextNotContains($node2->label());
- $this->assertSession()->pageTextNotContains($node1->label());
- $this->assertSession()->pageTextNotContains($page_node->label());
- $pos5 = strpos($content, $node5->label());
- $pos4 = strpos($content, $node4->label());
- $pos3 = strpos($content, $node3->label());
- $this->assertGreaterThan($pos5, $pos4);
- $this->assertGreaterThan($pos4, $pos3);
+ foreach ([$node5, $node4, $node3, $node2, $node1] as $node) {
+ $this->assertSession()->pageTextContains($node->label());
+ }
+ $block->delete();
+
+ // Finally: set `items_per_page: 'none'`, which is the predecessor of
+ // `items_per_page: null`. This must continue to work as before even if the
+ // configuration is no longer considered valid, because otherwise we risk
+ // breaking e.g. blocks placed using Layout Builder.
+ // @todo Delete in https://www.drupal.org/project/drupal/issues/3521221.
+ $block = $this->drupalPlaceBlock("views_block:{$view['id']}-block_1", [
+ 'id' => 'views_block_items_per_page_test_with_historical_override',
+ ]);
+ // Explicitly set the `items_per_page` setting to a string without casting.
+ // It should be changed to NULL by the pre-save hook.
+ // @see \Drupal\views\Hook\ViewsHooks::blockPresave()
+ $block->set('settings', [
+ 'items_per_page' => 'none',
+ ])->trustData()->save();
+ $this->expectDeprecation('Saving a views block with "none" items per page is deprecated in drupal:11.2.0 and removed in drupal:12.0.0. To use the items per page defined by the view, use NULL. See https://www.drupal.org/node/3522240');
+ self::assertNull($block->get('settings')['items_per_page']);
+ self::assertSame(4, View::load($view['id'])->toArray()['display']['default']['display_options']['pager']['options']['items_per_page']);
+ self::assertSame(3, View::load($view['id'])->toArray()['display']['block_1']['display_options']['pager']['options']['items_per_page']);
+ $assert_3_newest_nodes();
+ $block->delete();
+
+ // Truly finally: set `items_per_page: '5'`, because for the same reason as
+ // above, blocks placed using Layout Builder may still have stale settings.
+ $block = $this->drupalPlaceBlock("views_block:{$view['id']}-block_1", [
+ 'id' => 'views_block_items_per_page_test_with_historical_override',
+ ]);
+ // Explicitly set the `items_per_page` setting to a string without casting.
+ $block->set('settings', [
+ 'items_per_page' => '5',
+ ])->trustData()->save();
+ self::assertSame('5', $block->get('settings')['items_per_page']);
+ self::assertSame(4, View::load($view['id'])->toArray()['display']['default']['display_options']['pager']['options']['items_per_page']);
+ self::assertSame(3, View::load($view['id'])->toArray()['display']['block_1']['display_options']['pager']['options']['items_per_page']);
+ $this->drupalGet('user');
+ foreach ([$node5, $node4, $node3, $node2, $node1] as $node) {
+ $this->assertSession()->pageTextContains($node->label());
+ }
}
}
diff --git a/core/modules/views/tests/src/Kernel/Plugin/ViewsBlockTest.php b/core/modules/views/tests/src/Kernel/Plugin/ViewsBlockTest.php
index b336584c74d6..ebd9df73e01d 100644
--- a/core/modules/views/tests/src/Kernel/Plugin/ViewsBlockTest.php
+++ b/core/modules/views/tests/src/Kernel/Plugin/ViewsBlockTest.php
@@ -4,6 +4,8 @@ declare(strict_types=1);
namespace Drupal\Tests\views\Kernel\Plugin;
+use Drupal\Core\Extension\ThemeInstallerInterface;
+use Drupal\Tests\block\Traits\BlockCreationTrait;
use Drupal\views\Plugin\Block\ViewsBlock;
use Drupal\views\Tests\ViewTestData;
use Drupal\Tests\views\Kernel\ViewsKernelTestBase;
@@ -16,6 +18,8 @@ use Drupal\views\Views;
*/
class ViewsBlockTest extends ViewsKernelTestBase {
+ use BlockCreationTrait;
+
/**
* {@inheritdoc}
*/
@@ -142,4 +146,22 @@ class ViewsBlockTest extends ViewsKernelTestBase {
$this->assertEquals('"test_view_block::block_1" views block', $views_block->getPreviewFallbackString());
}
+ /**
+ * Tests that saving a Views block with items_per_page = `none` is deprecated.
+ *
+ * @covers \Drupal\views\Hook\ViewsHooks::blockPresave
+ *
+ * @group legacy
+ */
+ public function testSaveBlockWithDeprecatedItemsPerPageSetting(): void {
+ $this->container->get(ThemeInstallerInterface::class)->install(['stark']);
+
+ $this->expectDeprecation('Saving a views block with "none" items per page is deprecated in drupal:11.2.0 and removed in drupal:12.0.0. To use the items per page defined by the view, use NULL. See https://www.drupal.org/node/3522240');
+ $block = $this->placeBlock('views_block:test_view_block-block_1', [
+ 'items_per_page' => 'none',
+ ]);
+ $settings = $block->get('settings');
+ $this->assertNull($settings['items_per_page']);
+ }
+
}
diff --git a/core/modules/views/tests/src/Unit/Plugin/views/display/BlockTest.php b/core/modules/views/tests/src/Unit/Plugin/views/display/BlockTest.php
index 0d72bf27b4ac..6988a04d8b48 100644
--- a/core/modules/views/tests/src/Unit/Plugin/views/display/BlockTest.php
+++ b/core/modules/views/tests/src/Unit/Plugin/views/display/BlockTest.php
@@ -62,29 +62,37 @@ class BlockTest extends UnitTestCase {
/**
* Tests the build method with no overriding.
+ *
+ * @testWith [null]
+ * ["none"]
+ * [0]
+ * @todo Delete the last two cases in https://www.drupal.org/project/drupal/issues/3521221. The last one is `intval('none')`.
*/
- public function testBuildNoOverride(): void {
+ public function testBuildNoOverride($items_per_page_setting): void {
$this->executable->expects($this->never())
->method('setItemsPerPage');
$this->blockPlugin->expects($this->once())
->method('getConfiguration')
- ->willReturn(['items_per_page' => 'none']);
+ ->willReturn(['items_per_page' => $items_per_page_setting]);
$this->blockDisplay->preBlockBuild($this->blockPlugin);
}
/**
* Tests the build method with overriding items per page.
+ *
+ * @testWith [5, 5]
+ * ["5", 5]
*/
- public function testBuildOverride(): void {
+ public function testBuildOverride(mixed $input, int $expected): void {
$this->executable->expects($this->once())
->method('setItemsPerPage')
- ->with(5);
+ ->with($expected);
$this->blockPlugin->expects($this->once())
->method('getConfiguration')
- ->willReturn(['items_per_page' => 5]);
+ ->willReturn(['items_per_page' => $input]);
$this->blockDisplay->preBlockBuild($this->blockPlugin);
}
diff --git a/core/modules/views/views.api.php b/core/modules/views/views.api.php
index 4fbcad6730ea..6579f93199bb 100644
--- a/core/modules/views/views.api.php
+++ b/core/modules/views/views.api.php
@@ -532,8 +532,9 @@ function hook_views_data_alter(array &$data) {
* When collecting the views data, views_views_data() invokes this hook for each
* field storage definition, on the module that provides the field storage
* definition. If the return value is empty, the result of
- * FieldViewsDataProvider::defaultFieldImplementation() is used instead. Then the result is altered
- * by invoking hook_field_views_data_alter() on all modules.
+ * FieldViewsDataProvider::defaultFieldImplementation() is used instead. Then
+ * the result is altered by invoking hook_field_views_data_alter() on all
+ * modules.
*
* If no hook implementation exists, hook_views_data() falls back to
* FieldViewsDataProvider::defaultFieldImplementation().
diff --git a/core/modules/views/views.post_update.php b/core/modules/views/views.post_update.php
index c3f352e99a42..48623fc593e0 100644
--- a/core/modules/views/views.post_update.php
+++ b/core/modules/views/views.post_update.php
@@ -5,6 +5,7 @@
* Post update functions for Views.
*/
+use Drupal\block\BlockInterface;
use Drupal\Core\Config\Entity\ConfigEntityUpdater;
use Drupal\views\ViewEntityInterface;
use Drupal\views\ViewsConfigUpdater;
@@ -83,7 +84,29 @@ function views_post_update_update_remember_role_empty(?array &$sandbox = NULL):
function views_post_update_table_css_class(?array &$sandbox = NULL): void {
/** @var \Drupal\views\ViewsConfigUpdater $view_config_updater */
$view_config_updater = \Drupal::classResolver(ViewsConfigUpdater::class);
+ $view_config_updater->setDeprecationsEnabled(FALSE);
\Drupal::classResolver(ConfigEntityUpdater::class)->update($sandbox, 'view', function (ViewEntityInterface $view) use ($view_config_updater): bool {
return $view_config_updater->needsTableCssClassUpdate($view);
});
}
+
+/**
+ * Defaults `items_per_page` to NULL in Views blocks.
+ */
+function views_post_update_block_items_per_page(?array &$sandbox = NULL): void {
+ if (!\Drupal::moduleHandler()->moduleExists('block')) {
+ return;
+ }
+ \Drupal::classResolver(ConfigEntityUpdater::class)
+ ->update($sandbox, 'block', function (BlockInterface $block): bool {
+ if (str_starts_with($block->getPluginId(), 'views_block:')) {
+ $settings = $block->get('settings');
+ if ($settings['items_per_page'] === 'none') {
+ $settings['items_per_page'] = NULL;
+ $block->set('settings', $settings);
+ return TRUE;
+ }
+ }
+ return FALSE;
+ });
+}
diff --git a/core/modules/views_ui/admin.inc b/core/modules/views_ui/admin.inc
index b866e9ed62cc..3da4ebaed854 100644
--- a/core/modules/views_ui/admin.inc
+++ b/core/modules/views_ui/admin.inc
@@ -116,7 +116,7 @@ function views_ui_add_ajax_trigger(&$wrapping_element, $trigger_key, $refresh_pa
}
/**
- * Processes a non-JavaScript fallback submit button to limit its validation errors.
+ * Limits validation errors for a non-JavaScript fallback submit button.
*/
function views_ui_add_limited_validation($element, FormStateInterface $form_state) {
// Retrieve the AJAX triggering element so we can determine its parents. (We
@@ -148,7 +148,7 @@ function views_ui_add_limited_validation($element, FormStateInterface $form_stat
}
/**
- * After-build function that adds a wrapper to a form region (for AJAX refreshes).
+ * Adds a wrapper to a form region (for AJAX refreshes) after the build.
*
* This function inserts a wrapper around the region of the form that needs to
* be refreshed by AJAX, based on information stored in the corresponding
diff --git a/core/modules/workspaces/src/EntityQuery/Tables.php b/core/modules/workspaces/src/EntityQuery/Tables.php
index 199d5cc15597..7b397c9580c4 100644
--- a/core/modules/workspaces/src/EntityQuery/Tables.php
+++ b/core/modules/workspaces/src/EntityQuery/Tables.php
@@ -126,7 +126,7 @@ class Tables extends BaseTables {
}
/**
- * Adds a new join to the 'workspace_association' table for an entity base table.
+ * Adds a join to the 'workspace_association' table for an entity base table.
*
* This method assumes that the active workspace has already been determined
* to be a non-default workspace.
diff --git a/core/modules/workspaces/src/Install/Requirements/WorkspacesRequirements.php b/core/modules/workspaces/src/Install/Requirements/WorkspacesRequirements.php
new file mode 100644
index 000000000000..a54148215af1
--- /dev/null
+++ b/core/modules/workspaces/src/Install/Requirements/WorkspacesRequirements.php
@@ -0,0 +1,31 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Drupal\workspaces\Install\Requirements;
+
+use Drupal\Core\Extension\InstallRequirementsInterface;
+
+/**
+ * Install time requirements for the workspaces module.
+ */
+class WorkspacesRequirements implements InstallRequirementsInterface {
+
+ /**
+ * {@inheritdoc}
+ */
+ public static function getRequirements(): array {
+ $requirements = [];
+ if (\Drupal::moduleHandler()->moduleExists('workspace')) {
+ $requirements['workspace_incompatibility'] = [
+ 'severity' => REQUIREMENT_ERROR,
+ 'description' => t('Workspaces can not be installed when the contributed Workspace module is also installed. See the <a href=":link">upgrade path</a> page for more information on how to upgrade.', [
+ ':link' => 'https://www.drupal.org/node/2987783',
+ ]),
+ ];
+ }
+
+ return $requirements;
+ }
+
+}
diff --git a/core/modules/workspaces/src/Plugin/Validation/Constraint/EntityReferenceSupportedNewEntitiesConstraintValidator.php b/core/modules/workspaces/src/Plugin/Validation/Constraint/EntityReferenceSupportedNewEntitiesConstraintValidator.php
index bf2fc243f6f4..180cfa9a8a1b 100644
--- a/core/modules/workspaces/src/Plugin/Validation/Constraint/EntityReferenceSupportedNewEntitiesConstraintValidator.php
+++ b/core/modules/workspaces/src/Plugin/Validation/Constraint/EntityReferenceSupportedNewEntitiesConstraintValidator.php
@@ -36,9 +36,6 @@ class EntityReferenceSupportedNewEntitiesConstraintValidator extends ConstraintV
*/
protected $workspaceInfo;
- /**
- * Creates a new EntityReferenceSupportedNewEntitiesConstraintValidator instance.
- */
public function __construct(WorkspaceManagerInterface $workspaceManager, EntityTypeManagerInterface $entityTypeManager, WorkspaceInformationInterface $workspace_information) {
$this->workspaceManager = $workspaceManager;
$this->entityTypeManager = $entityTypeManager;
diff --git a/core/modules/workspaces/workspaces.install b/core/modules/workspaces/workspaces.install
index a798b4a2c22b..6c9c99f29a3c 100644
--- a/core/modules/workspaces/workspaces.install
+++ b/core/modules/workspaces/workspaces.install
@@ -9,25 +9,6 @@ use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\workspaces\Entity\Workspace;
/**
- * Implements hook_requirements().
- */
-function workspaces_requirements($phase): array {
- $requirements = [];
- if ($phase === 'install') {
- if (\Drupal::moduleHandler()->moduleExists('workspace')) {
- $requirements['workspace_incompatibility'] = [
- 'severity' => REQUIREMENT_ERROR,
- 'description' => t('Workspaces can not be installed when the contributed Workspace module is also installed. See the <a href=":link">upgrade path</a> page for more information on how to upgrade.', [
- ':link' => 'https://www.drupal.org/node/2987783',
- ]),
- ];
- }
- }
-
- return $requirements;
-}
-
-/**
* Implements hook_install().
*/
function workspaces_install(): void {
diff --git a/core/package.json b/core/package.json
index 126f6bc65b48..48adc5b12dd0 100644
--- a/core/package.json
+++ b/core/package.json
@@ -57,13 +57,13 @@
"@ckeditor/ckeditor5-style": "~44.0.0",
"@ckeditor/ckeditor5-table": "~44.0.0",
"@drupal/once": "^1.0.1",
- "@floating-ui/dom": "^1.6.12",
+ "@floating-ui/dom": "^1.7.0",
"@gitlab-formatters/stylelint-formatter-gitlab": "^1.0.2",
- "backbone": "^1.6.0",
- "chokidar": "^4.0.1",
+ "backbone": "^1.6.1",
+ "chokidar": "^4.0.3",
"ckeditor5": "~44.0.0",
"cspell": "^8.16.1",
- "dotenv": "^16.4.7",
+ "dotenv": "^16.5.0",
"dotenv-safe": "^9.1.0",
"eslint": "^8.57.1",
"eslint-config-airbnb-base": "^15.0.0",
@@ -73,16 +73,16 @@
"eslint-plugin-no-jquery": "^3.1.0",
"eslint-plugin-prettier": "^5.2.1",
"eslint-plugin-yml": "^1.16.0",
- "glob": "11.0.0",
+ "glob": "11.0.2",
"htmx.org": "2.0.4",
"jquery": "^4.0.0-beta.2",
"jquery-ui": "^1.14.1",
"js-cookie": "^3.0.5",
- "jsdom": "^25.0.1",
+ "jsdom": "^26.1.0",
"loadjs": "^4.3.0",
"minimist": "^1.2.8",
"mkdirp": "^3.0.1",
- "nightwatch": "^3.9.0",
+ "nightwatch": "^3.12.1",
"normalize.css": "^8.0.1",
"postcss": "^8.4.40",
"postcss-header": "^3.0.3",
@@ -98,13 +98,13 @@
"stylelint-order": "^6.0.4",
"stylelint-prettier": "^5.0.2",
"tabbable": "^6.2.0",
- "terser": "^5.37.0",
- "terser-webpack-plugin": "^5.3.10",
+ "terser": "^5.39.0",
+ "terser-webpack-plugin": "^5.3.14",
"transliteration": "^2.3.5",
"tua-body-scroll-lock": "^1.5.3",
"underscore": "~1.13.7",
- "webpack": "^5.97.1",
- "webpack-cli": "^5.1.4"
+ "webpack": "^5.99.8",
+ "webpack-cli": "^6.0.1"
},
"browserslist": [
"last 2 Chrome major versions",
@@ -117,5 +117,5 @@
"last 1 Samsung version",
"Firefox ESR"
],
- "packageManager": "yarn@4.1.1"
+ "packageManager": "yarn@4.9.1"
}
diff --git a/core/phpcs.xml.dist b/core/phpcs.xml.dist
index ae9106fbd308..623a64679581 100644
--- a/core/phpcs.xml.dist
+++ b/core/phpcs.xml.dist
@@ -100,6 +100,8 @@
<include-pattern>*/core/profiles/*</include-pattern>
<include-pattern>*/core/recipes/*</include-pattern>
<include-pattern>*/core/themes/*</include-pattern>
+ <include-pattern>*/core/modules/*</include-pattern>
+ <exclude-pattern>*/core/modules/*/tests</exclude-pattern>
</rule>
<rule ref="Drupal.Files.TxtFileLineLength"/>
<rule ref="Drupal.Formatting.MultiLineAssignment"/>
diff --git a/core/phpstan.neon.dist b/core/phpstan.neon.dist
index c666b6f95365..4cd4f9cfa591 100644
--- a/core/phpstan.neon.dist
+++ b/core/phpstan.neon.dist
@@ -2,7 +2,6 @@
# The PHPStan check is executed as a job in the CI pipeline.
includes:
- .phpstan-baseline.php
- - phar://phpstan.phar/conf/bleedingEdge.neon
parameters:
diff --git a/core/phpunit.xml.dist b/core/phpunit.xml.dist
index 8915265e148e..c858081cad26 100644
--- a/core/phpunit.xml.dist
+++ b/core/phpunit.xml.dist
@@ -100,25 +100,35 @@
<directory>modules/**/tests/src/Kernel</directory>
<directory>recipes/*/tests/src/Kernel</directory>
<directory>profiles/**/tests/src/Kernel</directory>
- <directory>profiles/tests/testing/modules/*/tests/src/Kernel</directory>
<directory>themes/**/tests/src/Kernel</directory>
<directory>../modules/**/tests/src/Kernel</directory>
<directory>../profiles/**/tests/src/Kernel</directory>
<directory>../themes/**/tests/src/Kernel</directory>
+ <!-- @todo remove line(s) below once PHPUnit 10 is no longer used; they
+ are redundant in PHPUnit 11+ that fully implements globstar (**)
+ pattern.
+ @see https://www.drupal.org/project/drupal/issues/3497116
+ -->
+ <directory>profiles/tests/testing/modules/*/tests/src/Kernel</directory>
</testsuite>
<testsuite name="functional">
<directory>tests/Drupal/FunctionalTests</directory>
<directory>modules/**/tests/src/Functional</directory>
- <directory>modules/config/tests/config_test/tests/src/Functional</directory>
- <directory>modules/system/tests/modules/entity_test/tests/src/Functional</directory>
- <directory>modules/layout_builder/modules/layout_builder_expose_all_field_blocks/tests/src/Functional</directory>
<directory>profiles/**/tests/src/Functional</directory>
- <directory>profiles/demo_umami/modules/demo_umami_content/tests/src/Functional</directory>
<directory>recipes/*/tests/src/Functional</directory>
<directory>themes/**/tests/src/Functional</directory>
<directory>../modules/**/tests/src/Functional</directory>
<directory>../profiles/**/tests/src/Functional</directory>
<directory>../themes/**/tests/src/Functional</directory>
+ <!-- @todo remove line(s) below once PHPUnit 10 is no longer used; they
+ are redundant in PHPUnit 11+ that fully implements globstar (**)
+ pattern.
+ @see https://www.drupal.org/project/drupal/issues/3497116
+ -->
+ <directory>modules/config/tests/config_test/tests/src/Functional</directory>
+ <directory>modules/system/tests/modules/entity_test/tests/src/Functional</directory>
+ <directory>modules/layout_builder/modules/layout_builder_expose_all_field_blocks/tests/src/Functional</directory>
+ <directory>profiles/demo_umami/modules/demo_umami_content/tests/src/Functional</directory>
</testsuite>
<testsuite name="functional-javascript">
<directory>tests/Drupal/FunctionalJavascriptTests</directory>
@@ -152,9 +162,6 @@
<directory>../modules/*/tests</directory>
<directory>../modules/*/*/src/Tests</directory>
<directory>../modules/*/*/tests</directory>
- <directory suffix=".api.php">./lib/**</directory>
- <directory suffix=".api.php">./modules/**</directory>
- <directory suffix=".api.php">../modules/**</directory>
</exclude>
</source>
</phpunit>
diff --git a/core/profiles/demo_umami/config/install/block.block.umami_views_block__articles_aside_block_1.yml b/core/profiles/demo_umami/config/install/block.block.umami_views_block__articles_aside_block_1.yml
index 8fc0cd3a9a96..530e031bf7e9 100644
--- a/core/profiles/demo_umami/config/install/block.block.umami_views_block__articles_aside_block_1.yml
+++ b/core/profiles/demo_umami/config/install/block.block.umami_views_block__articles_aside_block_1.yml
@@ -20,7 +20,7 @@ settings:
label_display: visible
provider: views
views_label: ''
- items_per_page: none
+ items_per_page: null
visibility:
'entity_bundle:node':
id: 'entity_bundle:node'
diff --git a/core/profiles/demo_umami/config/install/block.block.umami_views_block__promoted_items_block_1.yml b/core/profiles/demo_umami/config/install/block.block.umami_views_block__promoted_items_block_1.yml
index 21a2e54d50ea..c075bd6c7dcf 100644
--- a/core/profiles/demo_umami/config/install/block.block.umami_views_block__promoted_items_block_1.yml
+++ b/core/profiles/demo_umami/config/install/block.block.umami_views_block__promoted_items_block_1.yml
@@ -20,7 +20,7 @@ settings:
label_display: '0'
provider: views
views_label: ''
- items_per_page: none
+ items_per_page: null
visibility:
request_path:
id: request_path
diff --git a/core/profiles/demo_umami/config/install/block.block.umami_views_block__recipe_collections_block.yml b/core/profiles/demo_umami/config/install/block.block.umami_views_block__recipe_collections_block.yml
index 979c049b14b5..5eff752503e6 100644
--- a/core/profiles/demo_umami/config/install/block.block.umami_views_block__recipe_collections_block.yml
+++ b/core/profiles/demo_umami/config/install/block.block.umami_views_block__recipe_collections_block.yml
@@ -19,5 +19,5 @@ settings:
label_display: visible
provider: views
views_label: ''
- items_per_page: none
+ items_per_page: null
visibility: { }
diff --git a/core/profiles/demo_umami/config/install/core.entity_view_display.node.recipe.full.yml b/core/profiles/demo_umami/config/install/core.entity_view_display.node.recipe.full.yml
index 6eb48804da83..568bc71634d0 100644
--- a/core/profiles/demo_umami/config/install/core.entity_view_display.node.recipe.full.yml
+++ b/core/profiles/demo_umami/config/install/core.entity_view_display.node.recipe.full.yml
@@ -263,7 +263,7 @@ third_party_settings:
provider: views
context_mapping: { }
views_label: ''
- items_per_page: none
+ items_per_page: null
weight: 0
additional: { }
third_party_settings: { }
diff --git a/core/profiles/demo_umami/config/optional/views.view.media.yml b/core/profiles/demo_umami/config/optional/views.view.media.yml
index 98c7e3ee2376..5e60f311a0d3 100644
--- a/core/profiles/demo_umami/config/optional/views.view.media.yml
+++ b/core/profiles/demo_umami/config/optional/views.view.media.yml
@@ -798,6 +798,7 @@ display:
type: table
options:
grouping: { }
+ class: ''
row_class: ''
default_row_class: true
columns:
diff --git a/core/profiles/demo_umami/demo_umami.install b/core/profiles/demo_umami/demo_umami.install
index 28c696b16ceb..aad505a97dad 100644
--- a/core/profiles/demo_umami/demo_umami.install
+++ b/core/profiles/demo_umami/demo_umami.install
@@ -8,24 +8,6 @@
use Drupal\shortcut\Entity\Shortcut;
/**
- * Implements hook_requirements().
- */
-function demo_umami_requirements($phase): array {
- $requirements = [];
- if ($phase == 'runtime') {
- $profile = \Drupal::installProfile();
- $info = \Drupal::service('extension.list.profile')->getExtensionInfo($profile);
- $requirements['experimental_profile_used'] = [
- 'title' => t('Experimental installation profile used'),
- 'value' => $info['name'],
- 'description' => t('Experimental profiles are provided for testing purposes only. Use at your own risk. To start building a new site, reinstall Drupal and choose a non-experimental profile.'),
- 'severity' => REQUIREMENT_WARNING,
- ];
- }
- return $requirements;
-}
-
-/**
* Implements hook_install().
*
* Perform actions to set up the site for this profile.
diff --git a/core/profiles/demo_umami/src/Hook/DemoUmamiRequirements.php b/core/profiles/demo_umami/src/Hook/DemoUmamiRequirements.php
new file mode 100644
index 000000000000..360ad0edfe83
--- /dev/null
+++ b/core/profiles/demo_umami/src/Hook/DemoUmamiRequirements.php
@@ -0,0 +1,39 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Drupal\demo_umami\Hook;
+
+use Drupal\Core\Extension\ProfileExtensionList;
+use Drupal\Core\Hook\Attribute\Hook;
+use Drupal\Core\StringTranslation\StringTranslationTrait;
+
+/**
+ * Requirements for the Demo: Umami Food Magazine (Experimental) profile.
+ */
+class DemoUmamiRequirements {
+
+ use StringTranslationTrait;
+
+ public function __construct(
+ protected readonly ProfileExtensionList $profileExtensionList,
+ ) {}
+
+ /**
+ * Implements hook_runtime_requirements().
+ */
+ #[Hook('runtime_requirements')]
+ public function runtime(): array {
+ $requirements = [];
+ $profile = \Drupal::installProfile();
+ $info = $this->profileExtensionList->getExtensionInfo($profile);
+ $requirements['experimental_profile_used'] = [
+ 'title' => $this->t('Experimental installation profile used'),
+ 'value' => $info['name'],
+ 'description' => $this->t('Experimental profiles are provided for testing purposes only. Use at your own risk. To start building a new site, reinstall Drupal and choose a non-experimental profile.'),
+ 'severity' => REQUIREMENT_WARNING,
+ ];
+ return $requirements;
+ }
+
+}
diff --git a/core/profiles/standard/tests/src/FunctionalJavascript/StandardJavascriptTest.php b/core/profiles/standard/tests/src/FunctionalJavascript/StandardJavascriptTest.php
index f46d71442c14..9a7b1fbb93f6 100644
--- a/core/profiles/standard/tests/src/FunctionalJavascript/StandardJavascriptTest.php
+++ b/core/profiles/standard/tests/src/FunctionalJavascript/StandardJavascriptTest.php
@@ -35,13 +35,21 @@ class StandardJavascriptTest extends WebDriverTestBase {
->setPublished();
$node->save();
- // Front page: Five placeholders.
+ // Front page: Four placeholders.
$this->drupalGet('');
+ $this->assertBigPipePlaceholderReplacementCount(4);
+
+ // Front page with warm render caches: Zero placeholders.
+ $this->drupalGet('');
+ $this->assertBigPipePlaceholderReplacementCount(0);
+
+ // Node page: Five placeholders.
+ $this->drupalGet($node->toUrl());
$this->assertBigPipePlaceholderReplacementCount(5);
- // Node page: Six placeholders:
+ // Node page second request: One placeholder for the comment form.
$this->drupalGet($node->toUrl());
- $this->assertBigPipePlaceholderReplacementCount(6);
+ $this->assertBigPipePlaceholderReplacementCount(1);
}
/**
@@ -52,10 +60,17 @@ class StandardJavascriptTest extends WebDriverTestBase {
*/
protected function assertBigPipePlaceholderReplacementCount($expected_count): void {
$web_assert = $this->assertSession();
- $web_assert->waitForElement('css', 'script[data-big-pipe-event="stop"]');
+ if ($expected_count > 0) {
+ $web_assert->waitForElement('css', 'script[data-big-pipe-event="stop"]');
+ }
$page = $this->getSession()->getPage();
// Settings are removed as soon as they are processed.
- $this->assertCount(0, $this->getDrupalSettings()['bigPipePlaceholderIds']);
+ if ($expected_count === 0) {
+ $this->assertArrayNotHasKey('bigPipePlaceholderIds', $this->getDrupalSettings());
+ }
+ else {
+ $this->assertCount(0, $this->getDrupalSettings()['bigPipePlaceholderIds']);
+ }
$this->assertCount($expected_count, $page->findAll('css', 'script[data-big-pipe-replacement-for-placeholder-with-id]'));
}
diff --git a/core/profiles/standard/tests/src/FunctionalJavascript/StandardPerformanceTest.php b/core/profiles/standard/tests/src/FunctionalJavascript/StandardPerformanceTest.php
index 44bcc4f648fb..459b72d59ca2 100644
--- a/core/profiles/standard/tests/src/FunctionalJavascript/StandardPerformanceTest.php
+++ b/core/profiles/standard/tests/src/FunctionalJavascript/StandardPerformanceTest.php
@@ -363,6 +363,10 @@ class StandardPerformanceTest extends PerformanceTestBase {
$recorded_queries = $performance_data->getQueries();
$this->assertSame($expected_queries, $recorded_queries);
$expected = [
+ 'ScriptBytes' => 6500,
+ 'ScriptCount' => 1,
+ 'StylesheetBytes' => 1429,
+ 'StylesheetCount' => 1,
'QueryCount' => 17,
'CacheGetCount' => 69,
'CacheSetCount' => 1,
@@ -474,7 +478,7 @@ class StandardPerformanceTest extends PerformanceTestBase {
$this->assertSame($expected_queries, $recorded_queries);
$expected = [
'QueryCount' => 18,
- 'CacheGetCount' => 104,
+ 'CacheGetCount' => 103,
'CacheSetCount' => 1,
'CacheDeleteCount' => 1,
'CacheTagInvalidationCount' => 0,
diff --git a/core/profiles/tests/testing_requirements/src/Install/Requirements/TestingRequirementsRequirements.php b/core/profiles/tests/testing_requirements/src/Install/Requirements/TestingRequirementsRequirements.php
new file mode 100644
index 000000000000..71165b1c8dba
--- /dev/null
+++ b/core/profiles/tests/testing_requirements/src/Install/Requirements/TestingRequirementsRequirements.php
@@ -0,0 +1,28 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Drupal\testing_requirements\Install\Requirements;
+
+use Drupal\Core\Extension\InstallRequirementsInterface;
+
+/**
+ * Install time requirements for the testing_requirements module.
+ */
+class TestingRequirementsRequirements implements InstallRequirementsInterface {
+
+ /**
+ * {@inheritdoc}
+ */
+ public static function getRequirements(): array {
+ $requirements = [];
+ $requirements['testing_requirements'] = [
+ 'title' => t('Testing requirements'),
+ 'severity' => REQUIREMENT_ERROR,
+ 'description' => t('Testing requirements failed requirements.'),
+ ];
+
+ return $requirements;
+ }
+
+}
diff --git a/core/profiles/tests/testing_requirements/testing_requirements.install b/core/profiles/tests/testing_requirements/testing_requirements.install
deleted file mode 100644
index 7cdb44d9d299..000000000000
--- a/core/profiles/tests/testing_requirements/testing_requirements.install
+++ /dev/null
@@ -1,25 +0,0 @@
-<?php
-
-/**
- * @file
- * Install hooks for test profile.
- */
-
-declare(strict_types=1);
-
-/**
- * Implements hook_requirements().
- */
-function testing_requirements_requirements($phase): array {
- $requirements = [];
-
- if ($phase === 'install') {
- $requirements['testing_requirements'] = [
- 'title' => t('Testing requirements'),
- 'severity' => REQUIREMENT_ERROR,
- 'description' => t('Testing requirements failed requirements.'),
- ];
- }
-
- return $requirements;
-}
diff --git a/core/scripts/run-tests.sh b/core/scripts/run-tests.sh
index c1ca97703df8..4545a00cc3ac 100755
--- a/core/scripts/run-tests.sh
+++ b/core/scripts/run-tests.sh
@@ -173,7 +173,7 @@ if ($args['clean']) {
}
if (!Composer::upgradePHPUnitCheck(Version::id())) {
- simpletest_script_print_error("PHPUnit testing framework version 9 or greater is required when running on PHP 7.4 or greater. Run the command 'composer run-script drupal-phpunit-upgrade' in order to fix this.");
+ simpletest_script_print_error("PHPUnit testing framework version 11 or greater is required when running on PHP 8.4 or greater. Run the command 'composer run-script drupal-phpunit-upgrade' in order to fix this.");
exit(SIMPLETEST_SCRIPT_EXIT_FAILURE);
}
diff --git a/core/tests/Drupal/BuildTests/Composer/Template/ComposerProjectTemplatesTest.php b/core/tests/Drupal/BuildTests/Composer/Template/ComposerProjectTemplatesTest.php
index 806fd530a31c..6051afb1ae9b 100644
--- a/core/tests/Drupal/BuildTests/Composer/Template/ComposerProjectTemplatesTest.php
+++ b/core/tests/Drupal/BuildTests/Composer/Template/ComposerProjectTemplatesTest.php
@@ -31,7 +31,7 @@ class ComposerProjectTemplatesTest extends ComposerBuildTestBase {
*
* @see https://getcomposer.org/doc/04-schema.md#minimum-stability
*/
- protected const MINIMUM_STABILITY = 'stable';
+ protected const MINIMUM_STABILITY = 'beta';
/**
* The order of stability strings from least stable to most stable.
@@ -109,6 +109,7 @@ class ComposerProjectTemplatesTest extends ComposerBuildTestBase {
$exclude = [
'drupal/core',
+ 'drupal/core-recipe-unpack',
'drupal/core-project-message',
'drupal/core-vendor-hardening',
];
diff --git a/core/tests/Drupal/KernelTests/Core/Test/PhpUnitTestDiscoveryTest.php b/core/tests/Drupal/KernelTests/Core/Test/PhpUnitTestDiscoveryTest.php
index 6d7fcaeed9ae..705981f75079 100644
--- a/core/tests/Drupal/KernelTests/Core/Test/PhpUnitTestDiscoveryTest.php
+++ b/core/tests/Drupal/KernelTests/Core/Test/PhpUnitTestDiscoveryTest.php
@@ -6,6 +6,7 @@ namespace Drupal\KernelTests\Core\Test;
use Drupal\Core\Test\TestDiscovery;
use Drupal\KernelTests\KernelTestBase;
+use Drupal\TestTools\PhpUnitCompatibility\RunnerVersion;
use PHPUnit\TextUI\Configuration\Builder;
use PHPUnit\TextUI\Configuration\TestSuiteBuilder;
use Symfony\Component\Process\Process;
@@ -96,14 +97,28 @@ class PhpUnitTestDiscoveryTest extends KernelTestBase {
$phpUnitXmlList = new \DOMDocument();
$phpUnitXmlList->loadXML(file_get_contents($this->xmlOutputFile));
$phpUnitClientList = [];
+ // Try PHPUnit 10 format first.
+ // @todo remove once PHPUnit 10 is no longer used.
foreach ($phpUnitXmlList->getElementsByTagName('testCaseClass') as $node) {
$phpUnitClientList[] = $node->getAttribute('name');
}
+ // If empty, try PHPUnit 11+ format.
+ if (empty($phpUnitClientList)) {
+ foreach ($phpUnitXmlList->getElementsByTagName('testClass') as $node) {
+ $phpUnitClientList[] = $node->getAttribute('name');
+ }
+ }
asort($phpUnitClientList);
// Check against Drupal's discovery.
$this->assertEquals(implode("\n", $phpUnitClientList), implode("\n", $internalList), self::TEST_LIST_MISMATCH_MESSAGE);
+ // @todo once PHPUnit 10 is no longer used re-enable the rest of the test.
+ // @see https://www.drupal.org/project/drupal/issues/3497116
+ if (RunnerVersion::getMajor() >= 11) {
+ $this->markTestIncomplete('On PHPUnit 11+ the test triggers warnings due to phpunit.xml setup. Re-enable in https://www.drupal.org/project/drupal/issues/3497116.');
+ }
+
// PHPUnit's test discovery - via API.
$phpUnitConfiguration = (new Builder())->build(['--configuration', 'core']);
$phpUnitTestSuite = (new TestSuiteBuilder())->build($phpUnitConfiguration);
diff --git a/core/tests/Drupal/TestTools/Extension/DeprecationBridge/DeprecationHandler.php b/core/tests/Drupal/TestTools/Extension/DeprecationBridge/DeprecationHandler.php
index c176980bae27..f66ef7c8c947 100644
--- a/core/tests/Drupal/TestTools/Extension/DeprecationBridge/DeprecationHandler.php
+++ b/core/tests/Drupal/TestTools/Extension/DeprecationBridge/DeprecationHandler.php
@@ -76,6 +76,11 @@ final class DeprecationHandler {
$environmentVariable = "ignoreFile=$deprecationIgnoreFilename";
}
parse_str($environmentVariable, $configuration);
+
+ $environmentVariable = getenv('PHPUNIT_FAIL_ON_PHPUNIT_DEPRECATION');
+ $phpUnitDeprecationVariable = $environmentVariable !== FALSE ? $environmentVariable : TRUE;
+ $configuration['failOnPhpunitDeprecation'] = filter_var($phpUnitDeprecationVariable, \FILTER_VALIDATE_BOOLEAN);
+
return $configuration;
}
diff --git a/core/tests/Drupal/TestTools/PhpUnitCompatibility/PhpUnit11/TestCompatibilityTrait.php b/core/tests/Drupal/TestTools/PhpUnitCompatibility/PhpUnit11/TestCompatibilityTrait.php
new file mode 100644
index 000000000000..84638f9f0f57
--- /dev/null
+++ b/core/tests/Drupal/TestTools/PhpUnitCompatibility/PhpUnit11/TestCompatibilityTrait.php
@@ -0,0 +1,13 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Drupal\TestTools\PhpUnitCompatibility\PhpUnit11;
+
+/**
+ * Drupal's forward compatibility layer with multiple versions of PHPUnit.
+ *
+ * @internal
+ */
+trait TestCompatibilityTrait {
+}
diff --git a/core/tests/Drupal/Tests/Component/Datetime/DateTimePlusTest.php b/core/tests/Drupal/Tests/Component/Datetime/DateTimePlusTest.php
index 8dd4d8fb5948..ae46c3cc5a56 100644
--- a/core/tests/Drupal/Tests/Component/Datetime/DateTimePlusTest.php
+++ b/core/tests/Drupal/Tests/Component/Datetime/DateTimePlusTest.php
@@ -671,16 +671,16 @@ class DateTimePlusTest extends TestCase {
// There should be a 19 hour time interval between
// new years in Sydney and new years in LA in year 2000.
[
- 'input2' => DateTimePlus::createFromFormat('Y-m-d H:i:s', '2000-01-01 00:00:00', new \DateTimeZone('Australia/Sydney')),
- 'input1' => DateTimePlus::createFromFormat('Y-m-d H:i:s', '2000-01-01 00:00:00', new \DateTimeZone('America/Los_Angeles')),
+ 'input1' => DateTimePlus::createFromFormat('Y-m-d H:i:s', '2000-01-01 00:00:00', new \DateTimeZone('Australia/Sydney')),
+ 'input2' => DateTimePlus::createFromFormat('Y-m-d H:i:s', '2000-01-01 00:00:00', new \DateTimeZone('America/Los_Angeles')),
'absolute' => FALSE,
'expected' => $positive_19_hours,
],
// In 1970 Sydney did not observe daylight savings time
// So there is only an 18 hour time interval.
[
- 'input2' => DateTimePlus::createFromFormat('Y-m-d H:i:s', '1970-01-01 00:00:00', new \DateTimeZone('Australia/Sydney')),
- 'input1' => DateTimePlus::createFromFormat('Y-m-d H:i:s', '1970-01-01 00:00:00', new \DateTimeZone('America/Los_Angeles')),
+ 'input1' => DateTimePlus::createFromFormat('Y-m-d H:i:s', '1970-01-01 00:00:00', new \DateTimeZone('Australia/Sydney')),
+ 'input2' => DateTimePlus::createFromFormat('Y-m-d H:i:s', '1970-01-01 00:00:00', new \DateTimeZone('America/Los_Angeles')),
'absolute' => FALSE,
'expected' => $positive_18_hours,
],
diff --git a/core/tests/Drupal/Tests/Composer/Plugin/Scaffold/ExecTrait.php b/core/tests/Drupal/Tests/Composer/Plugin/ExecTrait.php
index 142ebbf52f2b..6a234499dac2 100644
--- a/core/tests/Drupal/Tests/Composer/Plugin/Scaffold/ExecTrait.php
+++ b/core/tests/Drupal/Tests/Composer/Plugin/ExecTrait.php
@@ -2,7 +2,7 @@
declare(strict_types=1);
-namespace Drupal\Tests\Composer\Plugin\Scaffold;
+namespace Drupal\Tests\Composer\Plugin;
use Symfony\Component\Process\Process;
@@ -20,17 +20,20 @@ trait ExecTrait {
* The current working directory to run the command from.
* @param array $env
* Environment variables to define for the subprocess.
+ * @param string $error_output
+ * (optional) Passed by reference to allow error output to be tested.
*
* @return string
* Standard output from the command
*/
- protected function mustExec($cmd, $cwd, array $env = []): string {
+ protected function mustExec($cmd, $cwd, array $env = [], string &$error_output = ''): string {
$process = Process::fromShellCommandline($cmd, $cwd, $env + ['PATH' => getenv('PATH'), 'HOME' => getenv('HOME')]);
$process->setTimeout(300)->setIdleTimeout(300)->run();
$exitCode = $process->getExitCode();
if (0 != $exitCode) {
throw new \RuntimeException("Exit code: {$exitCode}\n\n" . $process->getErrorOutput() . "\n\n" . $process->getOutput());
}
+ $error_output = $process->getErrorOutput();
return $process->getOutput();
}
diff --git a/core/tests/Drupal/Tests/Composer/Plugin/FixturesBase.php b/core/tests/Drupal/Tests/Composer/Plugin/FixturesBase.php
new file mode 100644
index 000000000000..0c8ea9226679
--- /dev/null
+++ b/core/tests/Drupal/Tests/Composer/Plugin/FixturesBase.php
@@ -0,0 +1,272 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Drupal\Tests\Composer\Plugin;
+
+use Composer\Composer;
+use Composer\Console\Application;
+use Composer\Factory;
+use Composer\IO\BufferIO;
+use Composer\IO\IOInterface;
+use Composer\Util\Filesystem;
+use Drupal\Composer\Plugin\Scaffold\Interpolator;
+use Symfony\Component\Console\Input\StringInput;
+use Symfony\Component\Console\Output\BufferedOutput;
+
+/**
+ * Base class for fixtures to test composer plugins.
+ */
+abstract class FixturesBase {
+
+ /**
+ * Keep a persistent prefix to help group our tmp directories together.
+ *
+ * @var string
+ */
+ protected static string $randomPrefix = '';
+
+ /**
+ * Directories to delete when we are done.
+ *
+ * @var string[]
+ */
+ protected array $tmpDirs = [];
+
+ /**
+ * A Composer IOInterface to write to.
+ *
+ * @var \Composer\IO\IOInterface|null
+ */
+ protected ?IOInterface $io;
+
+ /**
+ * The composer object.
+ *
+ * @var \Composer\Composer
+ */
+ protected Composer $composer;
+
+ /**
+ * Gets an IO fixture.
+ *
+ * @return \Composer\IO\IOInterface
+ * A Composer IOInterface to write to; output may be retrieved via
+ * Fixtures::getOutput().
+ */
+ public function io(): IOInterface {
+ if (!isset($this->io)) {
+ $this->io = new BufferIO();
+ }
+ return $this->io;
+ }
+
+ /**
+ * Gets the Composer object.
+ *
+ * @return \Composer\Composer
+ * The main Composer object, needed by the scaffold Handler, etc.
+ */
+ public function getComposer(): Composer {
+ if (!isset($this->composer)) {
+ $this->composer = Factory::create($this->io(), NULL, TRUE);
+ }
+ return $this->composer;
+ }
+
+ /**
+ * Gets the output from the io() fixture.
+ *
+ * @return string
+ * Output captured from tests that write to Fixtures::io().
+ */
+ public function getOutput(): string {
+ return $this->io()->getOutput();
+ }
+
+ /**
+ * Gets the path to Scaffold component.
+ *
+ * Used to inject the component into composer.json files.
+ *
+ * @return string
+ * Path to the root of this project.
+ */
+ abstract public function projectRoot(): string;
+
+ /**
+ * Gets the path to the project fixtures.
+ *
+ * @return string
+ * Path to project fixtures
+ */
+ abstract public function allFixturesDir(): string;
+
+ /**
+ * Gets the path to one particular project fixture.
+ *
+ * @param string $project_name
+ * The project name to get the path for.
+ *
+ * @return string
+ * Path to project fixture.
+ */
+ public function projectFixtureDir(string $project_name): string {
+ $dir = $this->allFixturesDir() . '/' . $project_name;
+ if (!is_dir($dir)) {
+ throw new \RuntimeException("Requested fixture project {$project_name} that does not exist.");
+ }
+ return $dir;
+ }
+
+ /**
+ * Gets the path to one particular bin path.
+ *
+ * @param string $bin_name
+ * The bin name to get the path for.
+ *
+ * @return string
+ * Path to project fixture.
+ */
+ public function binFixtureDir(string $bin_name): string {
+ $dir = $this->allFixturesDir() . '/scripts/' . $bin_name;
+ if (!is_dir($dir)) {
+ throw new \RuntimeException("Requested fixture bin dir {$bin_name} that does not exist.");
+ }
+ return $dir;
+ }
+
+ /**
+ * Generates a path to a temporary location, but do not create the directory.
+ *
+ * @param string $prefix
+ * A prefix for the temporary directory name.
+ *
+ * @return string
+ * Path to temporary directory
+ */
+ public function tmpDir(string $prefix): string {
+ $prefix .= static::persistentPrefix();
+ $tmpDir = sys_get_temp_dir() . '/scaffold-' . $prefix . uniqid(md5($prefix . microtime()), TRUE);
+ $this->tmpDirs[] = $tmpDir;
+ return $tmpDir;
+ }
+
+ /**
+ * Generates a persistent prefix to use with all of our temporary directories.
+ *
+ * The presumption is that this should reduce collisions in highly-parallel
+ * tests. We prepend the process id to play nicely with phpunit process
+ * isolation.
+ *
+ * @return string
+ * A random string that will remain the same for the entire process run.
+ */
+ protected static function persistentPrefix(): string {
+ if (empty(static::$randomPrefix)) {
+ static::$randomPrefix = getmypid() . md5(microtime());
+ }
+ return static::$randomPrefix;
+ }
+
+ /**
+ * Creates a temporary directory.
+ *
+ * @param string $prefix
+ * A prefix for the temporary directory name.
+ *
+ * @return string
+ * Path to temporary directory
+ */
+ public function mkTmpDir(string $prefix): string {
+ $tmpDir = $this->tmpDir($prefix);
+ $filesystem = new Filesystem();
+ $filesystem->ensureDirectoryExists($tmpDir);
+ return $tmpDir;
+ }
+
+ /**
+ * Creates an isolated cache directory for Composer.
+ */
+ public function createIsolatedComposerCacheDir(): void {
+ $cacheDir = $this->mkTmpDir('composer-cache');
+ putenv("COMPOSER_CACHE_DIR=$cacheDir");
+ }
+
+ /**
+ * Calls 'tearDown' in any test that copies fixtures to transient locations.
+ */
+ public function tearDown(): void {
+ // Remove any temporary directories that were created.
+ $filesystem = new Filesystem();
+ foreach ($this->tmpDirs as $dir) {
+ $filesystem->remove($dir);
+ }
+ // Clear out variables from the previous pass.
+ $this->tmpDirs = [];
+ $this->io = NULL;
+ // Clear the composer cache dir, if it was set
+ putenv('COMPOSER_CACHE_DIR=');
+ }
+
+ /**
+ * Creates a temporary copy of all of the fixtures projects into a temp dir.
+ *
+ * The fixtures remain dirty if they already exist. Individual tests should
+ * first delete any fixture directory that needs to remain pristine. Since all
+ * temporary directories are removed in tearDown, this is only an issue when
+ * a) the FIXTURE_DIR environment variable has been set, or b) tests are
+ * calling cloneFixtureProjects more than once per test method.
+ *
+ * @param string $fixturesDir
+ * The directory to place fixtures in.
+ * @param array $replacements
+ * Key : value mappings for placeholders to replace in composer.json
+ * templates.
+ */
+ public function cloneFixtureProjects(string $fixturesDir, array $replacements = []): void {
+ $filesystem = new Filesystem();
+ // We will replace 'SYMLINK' with the string 'true' in our composer.json
+ // fixture.
+ $replacements += ['SYMLINK' => 'true'];
+ $interpolator = new Interpolator('__', '__');
+ $interpolator->setData($replacements);
+ $filesystem->copy($this->allFixturesDir(), $fixturesDir);
+ $composer_json_templates = glob($fixturesDir . "/*/composer.json.tmpl");
+ foreach ($composer_json_templates as $composer_json_tmpl) {
+ // Inject replacements into composer.json.
+ if (file_exists($composer_json_tmpl)) {
+ $composer_json_contents = file_get_contents($composer_json_tmpl);
+ $composer_json_contents = $interpolator->interpolate($composer_json_contents, [], FALSE);
+ file_put_contents(dirname($composer_json_tmpl) . "/composer.json", $composer_json_contents);
+ @unlink($composer_json_tmpl);
+ }
+ }
+ }
+
+ /**
+ * Runs a `composer` command.
+ *
+ * @param string $cmd
+ * The Composer command to execute (escaped as required)
+ * @param string $cwd
+ * The current working directory to run the command from.
+ *
+ * @return string
+ * Standard output and standard error from the command.
+ */
+ public function runComposer(string $cmd, string $cwd): string {
+ chdir($cwd);
+ $input = new StringInput($cmd);
+ $output = new BufferedOutput();
+ $application = new Application();
+ $application->setAutoExit(FALSE);
+ $exitCode = $application->run($input, $output);
+ $output = $output->fetch();
+ if ($exitCode != 0) {
+ throw new \Exception("Fixtures::runComposer failed to set up fixtures.\n\nCommand: '{$cmd}'\nExit code: {$exitCode}\nOutput: \n\n$output");
+ }
+ return $output;
+ }
+
+}
diff --git a/core/tests/Drupal/Tests/Composer/Plugin/Scaffold/Fixtures.php b/core/tests/Drupal/Tests/Composer/Plugin/Scaffold/Fixtures.php
index 624aaa59f6cd..d92f54da5619 100644
--- a/core/tests/Drupal/Tests/Composer/Plugin/Scaffold/Fixtures.php
+++ b/core/tests/Drupal/Tests/Composer/Plugin/Scaffold/Fixtures.php
@@ -4,145 +4,33 @@ declare(strict_types=1);
namespace Drupal\Tests\Composer\Plugin\Scaffold;
-use Composer\Console\Application;
-use Composer\Factory;
-use Composer\IO\BufferIO;
-use Composer\Util\Filesystem;
+use Drupal\Tests\Composer\Plugin\FixturesBase;
use Drupal\Composer\Plugin\Scaffold\Handler;
use Drupal\Composer\Plugin\Scaffold\Interpolator;
use Drupal\Composer\Plugin\Scaffold\Operations\AppendOp;
use Drupal\Composer\Plugin\Scaffold\Operations\ReplaceOp;
use Drupal\Composer\Plugin\Scaffold\ScaffoldFilePath;
-use Symfony\Component\Console\Input\StringInput;
-use Symfony\Component\Console\Output\BufferedOutput;
/**
* Convenience class for creating fixtures.
*/
-class Fixtures {
+class Fixtures extends FixturesBase {
/**
- * Keep a persistent prefix to help group our tmp directories together.
- *
- * @var string
- */
- protected static $randomPrefix = '';
-
- /**
- * Directories to delete when we are done.
- *
- * @var string[]
- */
- protected $tmpDirs = [];
-
- /**
- * A Composer IOInterface to write to.
- *
- * @var \Composer\IO\IOInterface
- */
- protected $io;
-
- /**
- * The composer object.
- *
- * @var \Composer\Composer
+ * {@inheritdoc}
*/
- protected $composer;
-
- /**
- * Gets an IO fixture.
- *
- * @return \Composer\IO\IOInterface
- * A Composer IOInterface to write to; output may be retrieved via
- * Fixtures::getOutput().
- */
- public function io() {
- if (!$this->io) {
- $this->io = new BufferIO();
- }
- return $this->io;
- }
-
- /**
- * Gets the Composer object.
- *
- * @return \Composer\Composer
- * The main Composer object, needed by the scaffold Handler, etc.
- */
- public function getComposer() {
- if (!$this->composer) {
- $this->composer = Factory::create($this->io(), NULL, TRUE);
- }
- return $this->composer;
- }
-
- /**
- * Gets the output from the io() fixture.
- *
- * @return string
- * Output captured from tests that write to Fixtures::io().
- */
- public function getOutput() {
- return $this->io()->getOutput();
- }
-
- /**
- * Gets the path to Scaffold component.
- *
- * Used to inject the component into composer.json files.
- *
- * @return string
- * Path to the root of this project.
- */
- public function projectRoot() {
+ public function projectRoot(): string {
return realpath(__DIR__) . '/../../../../../../../composer/Plugin/Scaffold';
}
/**
- * Gets the path to the project fixtures.
- *
- * @return string
- * Path to project fixtures
+ * {@inheritdoc}
*/
- public function allFixturesDir() {
+ public function allFixturesDir(): string {
return realpath(__DIR__ . '/fixtures');
}
/**
- * Gets the path to one particular project fixture.
- *
- * @param string $project_name
- * The project name to get the path for.
- *
- * @return string
- * Path to project fixture.
- */
- public function projectFixtureDir($project_name) {
- $dir = $this->allFixturesDir() . '/' . $project_name;
- if (!is_dir($dir)) {
- throw new \RuntimeException("Requested fixture project {$project_name} that does not exist.");
- }
- return $dir;
- }
-
- /**
- * Gets the path to one particular bin path.
- *
- * @param string $bin_name
- * The bin name to get the path for.
- *
- * @return string
- * Path to project fixture.
- */
- public function binFixtureDir($bin_name) {
- $dir = $this->allFixturesDir() . '/scripts/' . $bin_name;
- if (!is_dir($dir)) {
- throw new \RuntimeException("Requested fixture bin dir {$bin_name} that does not exist.");
- }
- return $dir;
- }
-
- /**
* Gets a path to a source scaffold fixture.
*
* Use in place of ScaffoldFilePath::sourcePath().
@@ -244,114 +132,6 @@ class Fixtures {
}
/**
- * Generates a path to a temporary location, but do not create the directory.
- *
- * @param string $prefix
- * A prefix for the temporary directory name.
- *
- * @return string
- * Path to temporary directory
- */
- public function tmpDir($prefix) {
- $prefix .= static::persistentPrefix();
- $tmpDir = sys_get_temp_dir() . '/scaffold-' . $prefix . uniqid(md5($prefix . microtime()), TRUE);
- $this->tmpDirs[] = $tmpDir;
- return $tmpDir;
- }
-
- /**
- * Generates a persistent prefix to use with all of our temporary directories.
- *
- * The presumption is that this should reduce collisions in highly-parallel
- * tests. We prepend the process id to play nicely with phpunit process
- * isolation.
- *
- * @return string
- * A random string that will remain the same for the entire process run.
- */
- protected static function persistentPrefix() {
- if (empty(static::$randomPrefix)) {
- static::$randomPrefix = getmypid() . md5(microtime());
- }
- return static::$randomPrefix;
- }
-
- /**
- * Creates a temporary directory.
- *
- * @param string $prefix
- * A prefix for the temporary directory name.
- *
- * @return string
- * Path to temporary directory
- */
- public function mkTmpDir($prefix) {
- $tmpDir = $this->tmpDir($prefix);
- $filesystem = new Filesystem();
- $filesystem->ensureDirectoryExists($tmpDir);
- return $tmpDir;
- }
-
- /**
- * Create an isolated cache directory for Composer.
- */
- public function createIsolatedComposerCacheDir() {
- $cacheDir = $this->mkTmpDir('composer-cache');
- putenv("COMPOSER_CACHE_DIR=$cacheDir");
- }
-
- /**
- * Calls 'tearDown' in any test that copies fixtures to transient locations.
- */
- public function tearDown() {
- // Remove any temporary directories that were created.
- $filesystem = new Filesystem();
- foreach ($this->tmpDirs as $dir) {
- $filesystem->remove($dir);
- }
- // Clear out variables from the previous pass.
- $this->tmpDirs = [];
- $this->io = NULL;
- // Clear the composer cache dir, if it was set
- putenv('COMPOSER_CACHE_DIR=');
- }
-
- /**
- * Creates a temporary copy of all of the fixtures projects into a temp dir.
- *
- * The fixtures remain dirty if they already exist. Individual tests should
- * first delete any fixture directory that needs to remain pristine. Since all
- * temporary directories are removed in tearDown, this is only an issue when
- * a) the FIXTURE_DIR environment variable has been set, or b) tests are
- * calling cloneFixtureProjects more than once per test method.
- *
- * @param string $fixturesDir
- * The directory to place fixtures in.
- * @param array $replacements
- * Key : value mappings for placeholders to replace in composer.json
- * templates.
- */
- public function cloneFixtureProjects($fixturesDir, array $replacements = []) {
- $filesystem = new Filesystem();
- // We will replace 'SYMLINK' with the string 'true' in our composer.json
- // fixture.
- $replacements += ['SYMLINK' => 'true'];
- $interpolator = new Interpolator('__', '__');
- $interpolator->setData($replacements);
- $filesystem->copy($this->allFixturesDir(), $fixturesDir);
- $composer_json_templates = glob($fixturesDir . "/*/composer.json.tmpl");
- foreach ($composer_json_templates as $composer_json_tmpl) {
- // Inject replacements into composer.json.
- if (file_exists($composer_json_tmpl)) {
- $composer_json_contents = file_get_contents($composer_json_tmpl);
- $composer_json_contents = $interpolator->interpolate($composer_json_contents, [], FALSE);
- file_put_contents(dirname($composer_json_tmpl) . "/composer.json", $composer_json_contents);
- @unlink($composer_json_tmpl);
- }
- }
- }
-
- /**
* Runs the scaffold operation.
*
* This is equivalent to running `composer composer-scaffold`, but we do the
@@ -372,29 +152,4 @@ class Fixtures {
return $this->getOutput();
}
- /**
- * Runs a `composer` command.
- *
- * @param string $cmd
- * The Composer command to execute (escaped as required)
- * @param string $cwd
- * The current working directory to run the command from.
- *
- * @return string
- * Standard output and standard error from the command.
- */
- public function runComposer($cmd, $cwd) {
- chdir($cwd);
- $input = new StringInput($cmd);
- $output = new BufferedOutput();
- $application = new Application();
- $application->setAutoExit(FALSE);
- $exitCode = $application->run($input, $output);
- $output = $output->fetch();
- if ($exitCode != 0) {
- throw new \Exception("Fixtures::runComposer failed to set up fixtures.\n\nCommand: '{$cmd}'\nExit code: {$exitCode}\nOutput: \n\n$output");
- }
- return $output;
- }
-
}
diff --git a/core/tests/Drupal/Tests/Composer/Plugin/Scaffold/Functional/ComposerHookTest.php b/core/tests/Drupal/Tests/Composer/Plugin/Scaffold/Functional/ComposerHookTest.php
index 14d896ad1823..b87861671e8e 100644
--- a/core/tests/Drupal/Tests/Composer/Plugin/Scaffold/Functional/ComposerHookTest.php
+++ b/core/tests/Drupal/Tests/Composer/Plugin/Scaffold/Functional/ComposerHookTest.php
@@ -7,7 +7,7 @@ namespace Drupal\Tests\Composer\Plugin\Scaffold\Functional;
use Composer\Util\Filesystem;
use Drupal\BuildTests\Framework\BuildTestBase;
use Drupal\Tests\Composer\Plugin\Scaffold\AssertUtilsTrait;
-use Drupal\Tests\Composer\Plugin\Scaffold\ExecTrait;
+use Drupal\Tests\Composer\Plugin\ExecTrait;
use Drupal\Tests\Composer\Plugin\Scaffold\Fixtures;
/**
diff --git a/core/tests/Drupal/Tests/Composer/Plugin/Scaffold/Functional/ManageGitIgnoreTest.php b/core/tests/Drupal/Tests/Composer/Plugin/Scaffold/Functional/ManageGitIgnoreTest.php
index c7fd1d4d7797..8c02991aa82c 100644
--- a/core/tests/Drupal/Tests/Composer/Plugin/Scaffold/Functional/ManageGitIgnoreTest.php
+++ b/core/tests/Drupal/Tests/Composer/Plugin/Scaffold/Functional/ManageGitIgnoreTest.php
@@ -7,7 +7,7 @@ namespace Drupal\Tests\Composer\Plugin\Scaffold\Functional;
use Composer\Util\Filesystem;
use Drupal\Tests\Composer\Plugin\Scaffold\Fixtures;
use Drupal\Tests\Composer\Plugin\Scaffold\AssertUtilsTrait;
-use Drupal\Tests\Composer\Plugin\Scaffold\ExecTrait;
+use Drupal\Tests\Composer\Plugin\ExecTrait;
use PHPUnit\Framework\TestCase;
/**
diff --git a/core/tests/Drupal/Tests/Composer/Plugin/Scaffold/Functional/ScaffoldUpgradeTest.php b/core/tests/Drupal/Tests/Composer/Plugin/Scaffold/Functional/ScaffoldUpgradeTest.php
index accd108bfec6..f85732afb605 100644
--- a/core/tests/Drupal/Tests/Composer/Plugin/Scaffold/Functional/ScaffoldUpgradeTest.php
+++ b/core/tests/Drupal/Tests/Composer/Plugin/Scaffold/Functional/ScaffoldUpgradeTest.php
@@ -6,7 +6,7 @@ namespace Drupal\Tests\Composer\Plugin\Scaffold\Functional;
use Composer\Util\Filesystem;
use Drupal\Tests\Composer\Plugin\Scaffold\AssertUtilsTrait;
-use Drupal\Tests\Composer\Plugin\Scaffold\ExecTrait;
+use Drupal\Tests\Composer\Plugin\ExecTrait;
use Drupal\Tests\Composer\Plugin\Scaffold\Fixtures;
use PHPUnit\Framework\TestCase;
diff --git a/core/tests/Drupal/Tests/Composer/Plugin/Unpack/Fixtures.php b/core/tests/Drupal/Tests/Composer/Plugin/Unpack/Fixtures.php
new file mode 100644
index 000000000000..a9acd97a14d5
--- /dev/null
+++ b/core/tests/Drupal/Tests/Composer/Plugin/Unpack/Fixtures.php
@@ -0,0 +1,38 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Drupal\Tests\Composer\Plugin\Unpack;
+
+use Drupal\Tests\Composer\Plugin\FixturesBase;
+
+/**
+ * Fixture for testing the unpack composer plugin.
+ */
+class Fixtures extends FixturesBase {
+
+ /**
+ * {@inheritdoc}
+ */
+ public function projectRoot(): string {
+ return realpath(__DIR__) . '/../../../../../../../composer/Plugin/RecipeUnpack';
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function allFixturesDir(): string {
+ return realpath(__DIR__ . '/fixtures');
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function tmpDir(string $prefix): string {
+ $prefix .= static::persistentPrefix();
+ $tmpDir = sys_get_temp_dir() . '/unpack-' . $prefix . uniqid(md5($prefix . microtime()), TRUE);
+ $this->tmpDirs[] = $tmpDir;
+ return $tmpDir;
+ }
+
+}
diff --git a/core/tests/Drupal/Tests/Composer/Plugin/Unpack/Functional/UnpackRecipeTest.php b/core/tests/Drupal/Tests/Composer/Plugin/Unpack/Functional/UnpackRecipeTest.php
new file mode 100644
index 000000000000..b74349256892
--- /dev/null
+++ b/core/tests/Drupal/Tests/Composer/Plugin/Unpack/Functional/UnpackRecipeTest.php
@@ -0,0 +1,676 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Drupal\Tests\Composer\Plugin\Unpack\Functional;
+
+use Composer\InstalledVersions;
+use Composer\Util\Filesystem;
+use Drupal\Tests\Composer\Plugin\Unpack\Fixtures;
+use Drupal\BuildTests\Framework\BuildTestBase;
+use Drupal\Tests\Composer\Plugin\ExecTrait;
+
+/**
+ * Tests recipe unpacking.
+ *
+ * @group Unpack
+ */
+class UnpackRecipeTest extends BuildTestBase {
+
+ use ExecTrait;
+
+ /**
+ * Directory to perform the tests in.
+ */
+ protected string $fixturesDir;
+
+ /**
+ * The Symfony FileSystem component.
+ *
+ * @var \Composer\Util\Filesystem
+ */
+ protected Filesystem $fileSystem;
+
+ /**
+ * The Fixtures object.
+ *
+ * @var \Drupal\Tests\Composer\Plugin\Unpack\Fixtures
+ */
+ protected Fixtures $fixtures;
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function setUp(): void {
+ parent::setUp();
+
+ $this->fileSystem = new Filesystem();
+ $this->fixtures = new Fixtures();
+ $this->fixtures->createIsolatedComposerCacheDir();
+ $this->fixturesDir = $this->fixtures->tmpDir($this->name());
+ $replacements = [
+ 'PROJECT_ROOT' => $this->fixtures->projectRoot(),
+ 'COMPOSER_INSTALLERS' => InstalledVersions::getInstallPath('composer/installers'),
+ ];
+ $this->fixtures->cloneFixtureProjects($this->fixturesDir, $replacements);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function tearDown(): void {
+ // Remove any temporary directories that were created.
+ $this->fixtures->tearDown();
+ parent::tearDown();
+ }
+
+ /**
+ * Tests the dependencies unpack on install.
+ */
+ public function testAutomaticUnpack(): void {
+ $root_project_path = $this->fixturesDir . '/composer-root';
+
+ copy($root_project_path . '/composer.json', $root_project_path . '/composer.json.original');
+
+ // Run composer install and confirm the composer.lock was created.
+ $this->runComposer('install');
+
+ // Install a module in require-dev that should be moved to require
+ // by the unpacker.
+ $this->runComposer('require --dev fixtures/module-a:^1.0');
+ // Ensure we have added the dependency to require-dev.
+ $root_composer_json = $this->getFileContents($root_project_path . '/composer.json');
+ $this->assertArrayHasKey('fixtures/module-a', $root_composer_json['require-dev']);
+
+ // Install a recipe and unpack it.
+ $stdout = $this->runComposer('require fixtures/recipe-a');
+ $this->doTestRecipeAUnpacked($root_project_path, $stdout);
+ $root_composer_json = $this->getFileContents($root_project_path . '/composer.json');
+ // The more specific constraint should have been used.
+ $this->assertSame("^1.0", $root_composer_json['require']['fixtures/module-a']);
+
+ // Copy old composer.json back over and require recipe again to ensure it
+ // is still unpacked. This tests that unpacking does not rely on composer
+ // package events.
+ unlink($root_project_path . '/composer.json');
+ copy($root_project_path . '/composer.json.original', $root_project_path . '/composer.json');
+ $stdout = $this->runComposer('require fixtures/recipe-a');
+ $this->doTestRecipeAUnpacked($root_project_path, $stdout);
+ }
+
+ /**
+ * Tests recursive unpacking.
+ */
+ public function testRecursiveUnpacking(): void {
+ $root_project_path = $this->fixturesDir . '/composer-root';
+
+ // Run composer install and confirm the composer.lock was created.
+ $this->runComposer('config --merge --json sort-packages true');
+ $this->runComposer('install');
+ $stdOut = $this->runComposer('require fixtures/recipe-c fixtures/recipe-a');
+ $this->assertSame("fixtures/recipe-c unpacked.\nfixtures/recipe-a unpacked.\nfixtures/recipe-b unpacked.\n", $stdOut);
+ $root_composer_json = $this->getFileContents($root_project_path . '/composer.json');
+ $this->assertSame([
+ 'composer/installers',
+ 'drupal/core-recipe-unpack',
+ 'fixtures/module-a',
+ 'fixtures/module-b',
+ 'fixtures/theme-a',
+ ], array_keys($root_composer_json['require']));
+ // Ensure the resulting composer files are valid.
+ $this->runComposer('validate');
+ // Ensure the recipes exist.
+ $this->assertFileExists($root_project_path . '/recipes/recipe-a/recipe.yml');
+ $this->assertFileExists($root_project_path . '/recipes/recipe-b/recipe.yml');
+ $this->assertFileExists($root_project_path . '/recipes/recipe-c/recipe.yml');
+
+ // Ensure the complex constraint has been written correctly.
+ $this->assertSame('>=2.0.1.0-dev, <3.0.0.0-dev', $root_composer_json['require']['fixtures/module-b']);
+
+ // Ensure composer.lock is ordered correctly.
+ $root_composer_lock = $this->getFileContents($root_project_path . '/composer.lock');
+ $this->assertSame([
+ 'composer/installers',
+ 'drupal/core-recipe-unpack',
+ 'fixtures/module-a',
+ 'fixtures/module-b',
+ 'fixtures/theme-a',
+ ], array_column($root_composer_lock['packages'], 'name'));
+ }
+
+ /**
+ * Tests the dev dependencies do not unpack on install.
+ */
+ public function testNoAutomaticDevUnpack(): void {
+ $root_project_path = $this->fixturesDir . '/composer-root';
+
+ // Run composer install and confirm the composer.lock was created.
+ $this->runComposer('install');
+
+ // Install a module in require.
+ $this->runComposer('require fixtures/module-a');
+ $root_composer_json = $this->getFileContents($root_project_path . '/composer.json');
+ $this->assertArrayHasKey('fixtures/module-a', $root_composer_json['require']);
+
+ // Install a recipe as a dev dependency.
+ $stdout = $this->runComposer('require --dev fixtures/recipe-a');
+ $this->assertStringContainsString("Recipes required as a development dependency are not automatically unpacked.", $stdout);
+ $root_composer_json = $this->getFileContents($root_project_path . '/composer.json');
+
+ // Assert the state of the root composer.json as no unpacking has occurred.
+ $this->assertSame(['fixtures/recipe-a'], array_keys($root_composer_json['require-dev']));
+ $this->assertSame(['composer/installers', 'drupal/core-recipe-unpack', 'fixtures/module-a'], array_keys($root_composer_json['require']));
+
+ // Ensure the resulting Composer files are valid.
+ $this->runComposer('validate');
+ }
+
+ /**
+ * Tests dependency unpacking using drupal:recipe-unpack.
+ */
+ public function testUnpackCommand(): void {
+ $root_project_path = $this->fixturesDir . '/composer-root';
+
+ // Run composer install and confirm the composer.lock was created.
+ $this->runComposer('install');
+
+ // Disable automatic unpacking as it is the default behavior,
+ $this->runComposer('config --merge --json extra.drupal-recipe-unpack.on-require false');
+
+ // Install a module in require-dev.
+ $this->runComposer('require --dev fixtures/module-a');
+ // Install a module in require.
+ $this->runComposer('require fixtures/module-b:*');
+
+ // Ensure we have added the dependencies.
+ $root_composer_json = $this->getFileContents($root_project_path . '/composer.json');
+ $this->assertArrayHasKey('fixtures/module-b', $root_composer_json['require']);
+ $this->assertArrayHasKey('fixtures/module-a', $root_composer_json['require-dev']);
+
+ // Install a recipe and check it is not unpacked.
+ $stdout = $this->runComposer('require fixtures/recipe-a');
+ $root_composer_json = $this->getFileContents($root_project_path . '/composer.json');
+
+ // When the package is unpacked, the unpacked dependencies should be logged
+ // in the stdout.
+ $this->assertStringNotContainsString("unpacked.", $stdout);
+
+ $this->assertArrayHasKey('fixtures/recipe-a', $root_composer_json['require']);
+ // Ensure the resulting Composer files are valid.
+ $this->runComposer('validate');
+
+ // The package dependencies should not be in the root composer.json.
+ $this->assertArrayNotHasKey('fixtures/recipe-b', $root_composer_json['require']);
+
+ // Try unpacking a recipe that in not in the root composer.json.
+ try {
+ $this->runComposer('drupal:recipe-unpack fixtures/recipe-b');
+ $this->fail('Unpacking a non-existent dependency should fail');
+ }
+ catch (\RuntimeException $e) {
+ $this->assertStringContainsString('fixtures/recipe-b not found in the root composer.json.', $e->getMessage());
+ }
+
+ // The dev dependency has not moved.
+ $this->assertArrayHasKey('fixtures/module-a', $root_composer_json['require-dev']);
+
+ $stdout = $this->runComposer('drupal:recipe-unpack fixtures/recipe-a');
+ $this->doTestRecipeAUnpacked($root_project_path, $stdout);
+ $root_composer_json = $this->getFileContents($root_project_path . '/composer.json');
+ // The more specific constraints has been used.
+ $this->assertSame("^2.0", $root_composer_json['require']['fixtures/module-b']);
+
+ // Try unpacking something that is not a recipe.
+ try {
+ $this->runComposer('drupal:recipe-unpack fixtures/module-a');
+ $this->fail('Unpacking a module should fail');
+ }
+ catch (\RuntimeException $e) {
+ $this->assertStringContainsString('fixtures/module-a is not a recipe.', $e->getMessage());
+ }
+
+ // Try unpacking something that in not in the root composer.json.
+ try {
+ $this->runComposer('drupal:recipe-unpack fixtures/module-c');
+ $this->fail('Unpacking a non-existent dependency should fail');
+ }
+ catch (\RuntimeException $e) {
+ $this->assertStringContainsString('fixtures/module-c not found in the root composer.json.', $e->getMessage());
+ }
+ }
+
+ /**
+ * Tests dependency unpacking using drupal:recipe-unpack with multiple args.
+ */
+ public function testUnpackCommandWithMultipleRecipes(): void {
+ $root_project_path = $this->fixturesDir . '/composer-root';
+ $this->runComposer('install');
+
+ // Disable automatic unpacking as it is the default behavior,
+ $this->runComposer('config --merge --json extra.drupal-recipe-unpack.on-require false');
+
+ // Install a recipe and check it is not unpacked.
+ $stdOut = $this->runComposer('require fixtures/recipe-a fixtures/recipe-d');
+ $root_composer_json = $this->getFileContents($root_project_path . '/composer.json');
+
+ // When the package is unpacked, the unpacked dependencies should be logged
+ // in the stdout.
+ $this->assertStringNotContainsString("unpacked.", $stdOut);
+
+ $this->assertArrayHasKey('fixtures/recipe-a', $root_composer_json['require']);
+ $this->assertArrayHasKey('fixtures/recipe-d', $root_composer_json['require']);
+
+ $stdOut = $this->runComposer('drupal:recipe-unpack fixtures/recipe-a fixtures/recipe-d');
+ $this->assertStringContainsString("fixtures/recipe-a unpacked.", $stdOut);
+ $this->assertStringContainsString("fixtures/recipe-d unpacked.", $stdOut);
+
+ $root_composer_json = $this->getFileContents($root_project_path . '/composer.json');
+ $this->assertArrayNotHasKey('fixtures/recipe-a', $root_composer_json['require']);
+ $this->assertArrayNotHasKey('fixtures/recipe-d', $root_composer_json['require']);
+ // Ensure the resulting Composer files are valid.
+ $this->runComposer('validate');
+ }
+
+ /**
+ * Tests dependency unpacking using drupal:recipe-unpack with no arguments.
+ */
+ public function testUnpackCommandWithoutRecipesArgument(): void {
+ $root_project_path = $this->fixturesDir . '/composer-root';
+ $this->runComposer('install');
+
+ // Tests unpack command with no arguments and no recipes in the root
+ // composer package.
+ $stdOut = $this->runComposer('drupal:recipe-unpack');
+ $this->assertSame("No recipes to unpack.\n", $stdOut);
+
+ // Disable automatic unpacking as it is the default behavior,
+ $this->runComposer('config --merge --json extra.drupal-recipe-unpack.on-require false');
+
+ // Install a recipe and check it is not unpacked.
+ $stdOut = $this->runComposer('require fixtures/recipe-a fixtures/recipe-d');
+ $root_composer_json = $this->getFileContents($root_project_path . '/composer.json');
+
+ // When the package is unpacked, the unpacked dependencies should be logged
+ // in the stdout.
+ $this->assertStringNotContainsString("unpacked.", $stdOut);
+
+ $this->assertArrayHasKey('fixtures/recipe-a', $root_composer_json['require']);
+ $this->assertArrayHasKey('fixtures/recipe-d', $root_composer_json['require']);
+
+ $stdOut = $this->runComposer('drupal:recipe-unpack');
+ $this->assertStringContainsString("fixtures/recipe-a unpacked.", $stdOut);
+ $this->assertStringContainsString("fixtures/recipe-d unpacked.", $stdOut);
+
+ $root_composer_json = $this->getFileContents($root_project_path . '/composer.json');
+ $this->assertArrayNotHasKey('fixtures/recipe-a', $root_composer_json['require']);
+ $this->assertArrayNotHasKey('fixtures/recipe-d', $root_composer_json['require']);
+ // Ensure the resulting Composer files are valid.
+ $this->runComposer('validate');
+ }
+
+ /**
+ * Tests unpacking a recipe in require-dev using drupal:recipe-unpack.
+ */
+ public function testUnpackCommandOnDevRecipe(): void {
+ $root_project_path = $this->fixturesDir . '/composer-root';
+
+ // Run composer install and confirm the composer.lock was created.
+ $this->runComposer('install');
+
+ // Disable automatic unpacking, which is the default behavior.
+ $this->runComposer('config --merge --json extra.drupal-recipe-unpack.on-require false');
+
+ $this->runComposer('require fixtures/recipe-b');
+
+ // Install a recipe and check it is not unpacked.
+ $this->runComposer('require --dev fixtures/recipe-a');
+ $root_composer_json = $this->getFileContents($root_project_path . '/composer.json');
+
+ $this->assertArrayHasKey('fixtures/recipe-a', $root_composer_json['require-dev']);
+ $this->assertArrayHasKey('fixtures/recipe-b', $root_composer_json['require']);
+
+ $error_output = '';
+ $stdout = $this->runComposer('drupal:recipe-unpack fixtures/recipe-a', error_output: $error_output);
+ $this->assertStringContainsString("fixtures/recipe-a is present in the require-dev key. Unpacking will move the recipe's dependencies to the require key.", $error_output);
+ $root_composer_json = $this->getFileContents($root_project_path . '/composer.json');
+
+ // Ensure recipe A's dependencies are moved to require.
+ $this->doTestRecipeAUnpacked($root_project_path, $stdout);
+
+ // Ensure recipe B's dependencies are in require and the recipe has been
+ // unpacked.
+ $this->assertArrayNotHasKey('fixtures/recipe-b', $root_composer_json['require']);
+ $this->assertArrayHasKey('fixtures/module-a', $root_composer_json['require']);
+ $this->assertArrayHasKey('fixtures/theme-a', $root_composer_json['require']);
+
+ // Ensure installed.json and installed.php are correct.
+ $installed_json = $this->getFileContents($root_project_path . '/vendor/composer/installed.json');
+ $installed_packages = array_column($installed_json['packages'], 'name');
+ $this->assertContains('fixtures/module-b', $installed_packages);
+ $this->assertNotContains('fixtures/recipe-a', $installed_packages);
+ $this->assertSame([], $installed_json['dev-package-names']);
+ $installed_php = include_once $root_project_path . '/vendor/composer/installed.php';
+ $this->assertArrayHasKey('fixtures/module-b', $installed_php['versions']);
+ $this->assertFalse($installed_php['versions']['fixtures/module-b']['dev_requirement']);
+ $this->assertArrayNotHasKey('fixtures/recipe-a', $installed_php['versions']);
+ }
+
+ /**
+ * Tests the unpacking a recipe that is an indirect dev dependency.
+ */
+ public function testUnpackCommandOnIndirectDevDependencyRecipe(): void {
+ $root_project_path = $this->fixturesDir . '/composer-root';
+
+ // Run composer install and confirm the composer.lock was created.
+ $this->runComposer('install');
+ // Disable automatic unpacking as it is the default behavior,
+ $this->runComposer('config --merge --json extra.drupal-recipe-unpack.on-require false');
+
+ $this->runComposer('require --dev fixtures/recipe-b');
+
+ // Install a recipe and ensure it is not unpacked.
+ $this->runComposer('require fixtures/recipe-a');
+ $root_composer_json = $this->getFileContents($root_project_path . '/composer.json');
+
+ $this->assertArrayHasKey('fixtures/recipe-a', $root_composer_json['require']);
+ $this->assertArrayHasKey('fixtures/recipe-b', $root_composer_json['require-dev']);
+
+ // Ensure the resulting Composer files are valid.
+ $this->runComposer('validate');
+
+ $this->runComposer('drupal:recipe-unpack fixtures/recipe-a');
+ $root_composer_json = $this->getFileContents($root_project_path . '/composer.json');
+
+ // Ensure recipe A's dependencies are in require.
+ $this->assertArrayNotHasKey('fixtures/recipe-a', $root_composer_json['require']);
+ $this->assertArrayHasKey('fixtures/module-b', $root_composer_json['require']);
+ $this->assertArrayHasKey('fixtures/module-a', $root_composer_json['require']);
+ $this->assertArrayHasKey('fixtures/theme-a', $root_composer_json['require']);
+
+ // Ensure recipe B is still in require-dev even though all it's dependencies
+ // have been unpacked to require due to unpacking recipe A.
+ $this->assertSame(['fixtures/recipe-b'], array_keys($root_composer_json['require-dev']));
+
+ // Ensure recipe B is still list in installed.json.
+ $installed_json = $this->getFileContents($root_project_path . '/vendor/composer/installed.json');
+ $installed_packages = array_column($installed_json['packages'], 'name');
+ $this->assertContains('fixtures/recipe-b', $installed_packages);
+ $this->assertContains('fixtures/recipe-b', $installed_json['dev-package-names']);
+
+ // Ensure the resulting Composer files are valid.
+ $this->runComposer('validate');
+ }
+
+ /**
+ * Tests a recipe can be removed and the unpack plugin does not interfere.
+ */
+ public function testRemoveRecipe(): void {
+ $root_project_path = $this->fixturesDir . '/composer-root';
+
+ // Disable automatic unpacking, which is the default behavior,
+ $this->runComposer('config --merge --json extra.drupal-recipe-unpack.on-require false');
+
+ $this->runComposer('install');
+
+ // Install a recipe and ensure it is not unpacked.
+ $this->runComposer('require fixtures/recipe-a');
+ $root_composer_json = $this->getFileContents($root_project_path . '/composer.json');
+ $this->assertSame([
+ 'composer/installers',
+ 'drupal/core-recipe-unpack',
+ 'fixtures/recipe-a',
+ ], array_keys($root_composer_json['require']));
+
+ // Removing the recipe should work as normal.
+ $this->runComposer('remove fixtures/recipe-a');
+ $root_composer_json = $this->getFileContents($root_project_path . '/composer.json');
+ $this->assertSame([
+ 'composer/installers',
+ 'drupal/core-recipe-unpack',
+ ], array_keys($root_composer_json['require']));
+
+ // Ensure the resulting Composer files are valid.
+ $this->runComposer('validate');
+ }
+
+ /**
+ * Tests a recipe can be ignored and not unpacked.
+ */
+ public function testIgnoreRecipe(): void {
+ $root_project_path = $this->fixturesDir . '/composer-root';
+
+ // Disable automatic unpacking as it is the default behavior,
+ $this->runComposer('config --merge --json extra.drupal-recipe-unpack.ignore \'["fixtures/recipe-a"]\'');
+
+ $this->runComposer('install');
+
+ // Install a recipe and ensure it does not get unpacked.
+ $stdOut = $this->runComposer('require --verbose fixtures/recipe-a');
+ $root_composer_json = $this->getFileContents($root_project_path . '/composer.json');
+ $this->assertSame("fixtures/recipe-a not unpacked because it is ignored.", trim($stdOut));
+
+ $this->assertSame([
+ 'composer/installers',
+ 'drupal/core-recipe-unpack',
+ 'fixtures/recipe-a',
+ ], array_keys($root_composer_json['require']));
+
+ // Ensure the resulting Composer files are valid.
+ $this->runComposer('validate');
+
+ // Try using the unpack command on an ignored recipe.
+ try {
+ $this->runComposer('drupal:recipe-unpack fixtures/recipe-a');
+ $this->fail('Ignored recipes should not be unpacked.');
+ }
+ catch (\RuntimeException $e) {
+ $this->assertStringContainsString('fixtures/recipe-a is in the extra.drupal-recipe-unpack.ignore list.', $e->getMessage());
+ }
+ }
+
+ /**
+ * Tests a dependent recipe can be ignored and not unpacked.
+ */
+ public function testIgnoreDependentRecipe(): void {
+ $root_project_path = $this->fixturesDir . '/composer-root';
+
+ // Disable automatic unpacking, which is the default behavior,
+ $this->runComposer('config --merge --json extra.drupal-recipe-unpack.ignore \'["fixtures/recipe-b"]\'');
+ $this->runComposer('config sort-packages true');
+
+ $this->runComposer('install');
+
+ // Install a recipe and check it is not packed but not removed.
+ $stdOut = $this->runComposer('require --verbose fixtures/recipe-a');
+ $root_composer_json = $this->getFileContents($root_project_path . '/composer.json');
+ $this->assertStringContainsString("fixtures/recipe-b not unpacked because it is ignored.", $stdOut);
+ $this->assertStringContainsString("fixtures/recipe-a unpacked.", $stdOut);
+
+ $this->assertSame([
+ 'composer/installers',
+ 'drupal/core-recipe-unpack',
+ 'fixtures/module-b',
+ 'fixtures/recipe-b',
+ ], array_keys($root_composer_json['require']));
+
+ // Ensure the resulting Composer files are valid.
+ $this->runComposer('validate');
+ }
+
+ /**
+ * Tests that recipes stick around after being unpacked.
+ */
+ public function testRecipeIsPhysicallyPresentAfterUnpack(): void {
+ $root_project_dir = 'composer-root';
+ $root_project_path = $this->fixturesDir . '/' . $root_project_dir;
+
+ $this->runComposer('install');
+
+ // Install a recipe, which should unpack it.
+ $stdOut = $this->runComposer('require --verbose fixtures/recipe-b');
+ $this->assertStringContainsString("fixtures/recipe-b unpacked.", $stdOut);
+ $this->assertFileExists($root_project_path . '/recipes/recipe-b/recipe.yml');
+
+ // Require another dependency.
+ $this->runComposer('require --verbose fixtures/module-b');
+
+ // The recipe should still be physically installed...
+ $this->assertFileExists($root_project_path . '/recipes/recipe-b/recipe.yml');
+
+ // ...but it should NOT be in installed.json or installed.php.
+ $installed_json = $this->getFileContents($root_project_path . '/vendor/composer/installed.json');
+ $installed_packages = array_column($installed_json['packages'], 'name');
+ $this->assertContains('fixtures/module-b', $installed_packages);
+ $this->assertNotContains('fixtures/recipe-b', $installed_packages);
+ $installed_php = include_once $root_project_path . '/vendor/composer/installed.php';
+ $this->assertArrayHasKey('fixtures/module-b', $installed_php['versions']);
+ $this->assertArrayNotHasKey('fixtures/recipe-b', $installed_php['versions']);
+ }
+
+ /**
+ * Tests a recipe can be required using --no-install and installed later.
+ */
+ public function testRecipeNotUnpackedIfInstallIsDeferred(): void {
+ $root_project_path = $this->fixturesDir . '/composer-root';
+
+ $this->runComposer('install');
+
+ // Install a recipe and check it is in `composer.json` but not unpacked or
+ // physically installed.
+ $stdOut = $this->runComposer('require --verbose --no-install fixtures/recipe-a');
+ $root_composer_json = $this->getFileContents($root_project_path . '/composer.json');
+ $this->assertSame("Recipes are not unpacked when the --no-install option is used.", trim($stdOut));
+ $this->assertSame([
+ 'composer/installers',
+ 'drupal/core-recipe-unpack',
+ 'fixtures/recipe-a',
+ ], array_keys($root_composer_json['require']));
+ $this->assertFileDoesNotExist($root_project_path . '/recipes/recipe-a/recipe.yml');
+
+ // After installing dependencies, the recipe should be installed, but still
+ // not unpacked.
+ $this->runComposer('install');
+ $root_composer_json = $this->getFileContents($root_project_path . '/composer.json');
+ $this->assertSame([
+ 'composer/installers',
+ 'drupal/core-recipe-unpack',
+ 'fixtures/recipe-a',
+ ], array_keys($root_composer_json['require']));
+ $this->assertFileExists($root_project_path . '/recipes/recipe-a/recipe.yml');
+
+ // Ensure the resulting Composer files are valid.
+ $this->runComposer('validate');
+ }
+
+ /**
+ * Tests that recipes are unpacked when using `composer create-project`.
+ */
+ public function testComposerCreateProject(): void {
+ // Prepare the project to use for create-project.
+ $root_project_path = $this->fixturesDir . '/composer-root';
+ $this->runComposer('require --verbose --no-install fixtures/recipe-a');
+
+ $stdOut = $this->runComposer('create-project --repository=\'{"type": "path","url": "' . $root_project_path . '","options": {"symlink": false}}\' fixtures/root composer-root2 -s dev', $this->fixturesDir);
+ // The recipes depended upon by the project, even indirectly, should all
+ // have been unpacked.
+ $this->assertSame("fixtures/recipe-b unpacked.\nfixtures/recipe-a unpacked.\n", $stdOut);
+ $this->doTestRecipeAUnpacked($this->fixturesDir . '/composer-root2', $stdOut);
+ }
+
+ /**
+ * Tests Recipe A is unpacked correctly.
+ *
+ * @param string $root_project_path
+ * Path to the composer project under test.
+ * @param string $stdout
+ * The standard out from the composer command unpacks the recipe.
+ */
+ private function doTestRecipeAUnpacked(string $root_project_path, string $stdout): void {
+ $root_composer_json = $this->getFileContents($root_project_path . '/composer.json');
+
+ // @see core/tests/Drupal/Tests/Composer/Plugin/Unpack/fixtures/recipes/composer-recipe-a/composer.json
+ // @see core/tests/Drupal/Tests/Composer/Plugin/Unpack/fixtures/recipes/composer-recipe-b/composer.json
+ $expected_unpacked = [
+ 'fixtures/recipe-a' => [
+ 'fixtures/module-b',
+ ],
+ 'fixtures/recipe-b' => [
+ 'fixtures/module-a',
+ 'fixtures/theme-a',
+ ],
+ ];
+ foreach ($expected_unpacked as $package => $dependencies) {
+ // When the package is unpacked, the unpacked dependencies should be logged
+ // in the stdout.
+ $this->assertStringContainsString("$package unpacked.", $stdout);
+
+ // After being unpacked, the package should be removed from the root
+ // composer.json and composer.lock.
+ $this->assertArrayNotHasKey($package, $root_composer_json['require']);
+
+ foreach ($dependencies as $dependency) {
+ // The package dependencies should be in the root composer.json.
+ $this->assertArrayHasKey($dependency, $root_composer_json['require']);
+ }
+ }
+
+ // Ensure the resulting Composer files are valid.
+ $this->runComposer('validate', $root_project_path);
+
+ // The dev dependency has moved.
+ $this->assertArrayNotHasKey('require-dev', $root_composer_json);
+
+ // Ensure recipe files exist.
+ $this->assertFileExists($root_project_path . '/recipes/recipe-a/recipe.yml');
+ $this->assertFileExists($root_project_path . '/recipes/recipe-b/recipe.yml');
+
+ // Ensure composer.lock is ordered correctly.
+ $root_composer_lock = $this->getFileContents($root_project_path . '/composer.lock');
+ $this->assertSame([
+ 'composer/installers',
+ 'drupal/core-recipe-unpack',
+ 'fixtures/module-a',
+ 'fixtures/module-b',
+ 'fixtures/theme-a',
+ ], array_column($root_composer_lock['packages'], 'name'));
+ }
+
+ /**
+ * Executes a Composer command with standard options.
+ *
+ * @param string $command
+ * The composer command to execute.
+ * @param string $cwd
+ * The current working directory to run the command from.
+ * @param string $error_output
+ * Passed by reference to allow error output to be tested.
+ *
+ * @return string
+ * Standard output from the command.
+ */
+ private function runComposer(string $command, ?string $cwd = NULL, string &$error_output = ''): string {
+ $cwd ??= $this->fixturesDir . '/composer-root';
+
+ // Always add --no-interaction and --no-ansi to Composer commands.
+ $output = $this->mustExec("composer $command --no-interaction --no-ansi", $cwd, [], $error_output);
+ if ($command === 'install') {
+ $this->assertFileExists($cwd . '/composer.lock');
+ }
+ return $output;
+ }
+
+ /**
+ * Gets the contents of a file as an array.
+ *
+ * @param string $path
+ * The path to the file.
+ *
+ * @return array
+ * The contents of the file as an array.
+ */
+ private function getFileContents(string $path): array {
+ $file = file_get_contents($path);
+ return json_decode($file, TRUE, flags: JSON_THROW_ON_ERROR);
+ }
+
+}
diff --git a/core/tests/Drupal/Tests/Composer/Plugin/Unpack/SemVerTest.php b/core/tests/Drupal/Tests/Composer/Plugin/Unpack/SemVerTest.php
new file mode 100644
index 000000000000..acaec43ae5f7
--- /dev/null
+++ b/core/tests/Drupal/Tests/Composer/Plugin/Unpack/SemVerTest.php
@@ -0,0 +1,51 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Drupal\Tests\Composer\Plugin\Unpack;
+
+use Composer\Semver\VersionParser;
+use Drupal\Composer\Plugin\RecipeUnpack\SemVer;
+use PHPUnit\Framework\TestCase;
+
+/**
+ * @coversDefaultClass \Drupal\Composer\Plugin\RecipeUnpack\SemVer
+ *
+ * @group Unpack
+ */
+class SemVerTest extends TestCase {
+
+ /**
+ * @testWith ["^6.1", "^6.3", "^6.3"]
+ * ["*", "^6.3", "^6.3"]
+ * ["^6@dev", "^6.3", "^6.3"]
+ *
+ * @covers ::minimizeConstraints
+ */
+ public function testMinimizeConstraints(string $constraint_a, string $constraint_b, string $expected): void {
+ $version_parser = new VersionParser();
+ $this->assertSame($expected, SemVer::minimizeConstraints($version_parser, $constraint_a, $constraint_b));
+ $this->assertSame($expected, SemVer::minimizeConstraints($version_parser, $constraint_b, $constraint_a));
+ }
+
+ /**
+ * @testWith ["^6.1 || ^4.0", "^6.3 || ^7.4", ">=6.3.0.0-dev, <7.0.0.0-dev"]
+ *
+ * @covers ::minimizeConstraints
+ */
+ public function testMinimizeConstraintsWhichAreNotSubsets(string $constraint_a, string $constraint_b, string $expected): void {
+ $this->assertSame($expected, SemVer::minimizeConstraints(new VersionParser(), $constraint_a, $constraint_b));
+ }
+
+ /**
+ * @testWith ["^6.1", "^5.1", ">=6.3.0.0-dev, <7.0.0.0-dev"]
+ *
+ * @covers ::minimizeConstraints
+ */
+ public function testMinimizeConstraintsWhichDoNotIntersect(string $constraint_a, string $constraint_b, string $expected): void {
+ $this->expectException(\LogicException::class);
+ $this->expectExceptionMessage('The constraints "^6.1" and "^5.1" do not intersect and cannot be minimized.');
+ $this->assertSame($expected, SemVer::minimizeConstraints(new VersionParser(), $constraint_a, $constraint_b));
+ }
+
+}
diff --git a/core/tests/Drupal/Tests/Composer/Plugin/Unpack/fixtures/composer-root/composer.json.tmpl b/core/tests/Drupal/Tests/Composer/Plugin/Unpack/fixtures/composer-root/composer.json.tmpl
new file mode 100644
index 000000000000..a5b81535f77b
--- /dev/null
+++ b/core/tests/Drupal/Tests/Composer/Plugin/Unpack/fixtures/composer-root/composer.json.tmpl
@@ -0,0 +1,97 @@
+{
+ "name": "fixtures/root",
+ "description": "Test recipe unpacking",
+ "type": "project",
+ "minimum-stability": "dev",
+ "prefer-stable": true,
+ "repositories": {
+ "packagist.org": false,
+ "core-recipe-unpack": {
+ "type": "path",
+ "url": "__PROJECT_ROOT__",
+ "options": {
+ "symlink": true
+ }
+ },
+ "composer/installers": {
+ "type": "path",
+ "url": "__COMPOSER_INSTALLERS__",
+ "options": {
+ "symlink": true
+ }
+ },
+ "recipes/recipe-a": {
+ "type": "path",
+ "url": "../recipes/composer-recipe-a",
+ "options": {
+ "symlink": true
+ }
+ },
+ "recipes/recipe-b": {
+ "type": "path",
+ "url": "../recipes/composer-recipe-b",
+ "options": {
+ "symlink": true
+ }
+ },
+ "recipes/recipe-c": {
+ "type": "path",
+ "url": "../recipes/composer-recipe-c",
+ "options": {
+ "symlink": true
+ }
+ },
+ "recipes/recipe-d": {
+ "type": "path",
+ "url": "../recipes/composer-recipe-d",
+ "options": {
+ "symlink": true
+ }
+ },
+ "modules/module-a": {
+ "type": "path",
+ "url": "../modules/composer-module-a",
+ "options": {
+ "symlink": true
+ }
+ },
+ "modules/module-b": {
+ "type": "path",
+ "url": "../modules/composer-module-b",
+ "options": {
+ "symlink": true
+ }
+ },
+ "themes/theme-a": {
+ "type": "path",
+ "url": "../themes/composer-theme-a",
+ "options": {
+ "symlink": true
+ }
+ }
+ },
+ "require": {
+ "composer/installers": "*",
+ "drupal/core-recipe-unpack": "*"
+ },
+ "extra": {
+ "installer-paths": {
+ "core": ["type:drupal-core"],
+ "modules/contrib/{$name}": ["type:drupal-module"],
+ "modules/custom/{$name}": ["type:drupal-custom-module"],
+ "themes/contrib/{$name}": ["type:drupal-theme"],
+ "themes/custom/{$name}": ["type:drupal-custom-theme"],
+ "recipes/{$name}": ["type:drupal-recipe"],
+ "profiles/contrib/{$name}": ["type:drupal-profile"],
+ "profiles/custom/{$name}": ["type:drupal-custom-profile"],
+ "libraries/{$name}": ["type:drupal-library"],
+ "drush/Commands/contrib/{$name}": ["type:drupal-drush"]
+ }
+ },
+ "config": {
+ "allow-plugins": {
+ "composer/installers": true,
+ "drupal/core-recipe-unpack": true
+ }
+ }
+}
diff --git a/core/tests/Drupal/Tests/Composer/Plugin/Unpack/fixtures/modules/composer-module-a/composer.json b/core/tests/Drupal/Tests/Composer/Plugin/Unpack/fixtures/modules/composer-module-a/composer.json
new file mode 100644
index 000000000000..0bc34f28fdc2
--- /dev/null
+++ b/core/tests/Drupal/Tests/Composer/Plugin/Unpack/fixtures/modules/composer-module-a/composer.json
@@ -0,0 +1,6 @@
+{
+ "name": "fixtures/module-a",
+ "version": "1.0.4",
+ "type": "drupal-module",
+ "description": "A Drupal module's composer for testing."
+}
diff --git a/core/tests/Drupal/Tests/Composer/Plugin/Unpack/fixtures/modules/composer-module-b/composer.json b/core/tests/Drupal/Tests/Composer/Plugin/Unpack/fixtures/modules/composer-module-b/composer.json
new file mode 100644
index 000000000000..1085192e4a84
--- /dev/null
+++ b/core/tests/Drupal/Tests/Composer/Plugin/Unpack/fixtures/modules/composer-module-b/composer.json
@@ -0,0 +1,6 @@
+{
+ "name": "fixtures/module-b",
+ "version": "2.0.1",
+ "type": "drupal-module",
+ "description": "A Drupal module's composer for testing."
+}
diff --git a/core/tests/Drupal/Tests/Composer/Plugin/Unpack/fixtures/recipes/composer-recipe-a/composer.json b/core/tests/Drupal/Tests/Composer/Plugin/Unpack/fixtures/recipes/composer-recipe-a/composer.json
new file mode 100644
index 000000000000..240f7848638d
--- /dev/null
+++ b/core/tests/Drupal/Tests/Composer/Plugin/Unpack/fixtures/recipes/composer-recipe-a/composer.json
@@ -0,0 +1,9 @@
+{
+ "name": "fixtures/recipe-a",
+ "type": "drupal-recipe",
+ "description": "A Drupal recipe's composer for testing.",
+ "require": {
+ "fixtures/recipe-b": "*",
+ "fixtures/module-b": "^2.0"
+ }
+}
diff --git a/core/tests/Drupal/Tests/Composer/Plugin/Unpack/fixtures/recipes/composer-recipe-a/recipe.yml b/core/tests/Drupal/Tests/Composer/Plugin/Unpack/fixtures/recipes/composer-recipe-a/recipe.yml
new file mode 100644
index 000000000000..94a93c054ef1
--- /dev/null
+++ b/core/tests/Drupal/Tests/Composer/Plugin/Unpack/fixtures/recipes/composer-recipe-a/recipe.yml
@@ -0,0 +1,3 @@
+name: 'RecipeA'
+description: 'Recipe A.'
+type: 'test recipe'
diff --git a/core/tests/Drupal/Tests/Composer/Plugin/Unpack/fixtures/recipes/composer-recipe-b/composer.json b/core/tests/Drupal/Tests/Composer/Plugin/Unpack/fixtures/recipes/composer-recipe-b/composer.json
new file mode 100644
index 000000000000..673d8ea86c46
--- /dev/null
+++ b/core/tests/Drupal/Tests/Composer/Plugin/Unpack/fixtures/recipes/composer-recipe-b/composer.json
@@ -0,0 +1,9 @@
+{
+ "name": "fixtures/recipe-b",
+ "type": "drupal-recipe",
+ "description": "A Drupal recipe's composer for testing.",
+ "require": {
+ "fixtures/module-a": "*",
+ "fixtures/theme-a": "*"
+ }
+}
diff --git a/core/tests/Drupal/Tests/Composer/Plugin/Unpack/fixtures/recipes/composer-recipe-b/recipe.yml b/core/tests/Drupal/Tests/Composer/Plugin/Unpack/fixtures/recipes/composer-recipe-b/recipe.yml
new file mode 100644
index 000000000000..8eb3f822c3fd
--- /dev/null
+++ b/core/tests/Drupal/Tests/Composer/Plugin/Unpack/fixtures/recipes/composer-recipe-b/recipe.yml
@@ -0,0 +1,3 @@
+name: 'RecipeB'
+description: 'Recipe B.'
+type: 'test recipe'
diff --git a/core/tests/Drupal/Tests/Composer/Plugin/Unpack/fixtures/recipes/composer-recipe-c/composer.json b/core/tests/Drupal/Tests/Composer/Plugin/Unpack/fixtures/recipes/composer-recipe-c/composer.json
new file mode 100644
index 000000000000..692a54431643
--- /dev/null
+++ b/core/tests/Drupal/Tests/Composer/Plugin/Unpack/fixtures/recipes/composer-recipe-c/composer.json
@@ -0,0 +1,9 @@
+{
+ "name": "fixtures/recipe-c",
+ "type": "drupal-recipe",
+ "description": "A Drupal recipe's composer for testing.",
+ "require": {
+ "fixtures/recipe-b": "*",
+ "fixtures/module-b": ">=2.0.1 || 1.1.1"
+ }
+}
diff --git a/core/tests/Drupal/Tests/Composer/Plugin/Unpack/fixtures/recipes/composer-recipe-c/recipe.yml b/core/tests/Drupal/Tests/Composer/Plugin/Unpack/fixtures/recipes/composer-recipe-c/recipe.yml
new file mode 100644
index 000000000000..d4ce9e245f88
--- /dev/null
+++ b/core/tests/Drupal/Tests/Composer/Plugin/Unpack/fixtures/recipes/composer-recipe-c/recipe.yml
@@ -0,0 +1,3 @@
+name: 'RecipeC'
+description: 'Recipe C.'
+type: 'test recipe'
diff --git a/core/tests/Drupal/Tests/Composer/Plugin/Unpack/fixtures/recipes/composer-recipe-d/composer.json b/core/tests/Drupal/Tests/Composer/Plugin/Unpack/fixtures/recipes/composer-recipe-d/composer.json
new file mode 100644
index 000000000000..9c349b32cb1b
--- /dev/null
+++ b/core/tests/Drupal/Tests/Composer/Plugin/Unpack/fixtures/recipes/composer-recipe-d/composer.json
@@ -0,0 +1,5 @@
+{
+ "name": "fixtures/recipe-d",
+ "type": "drupal-recipe",
+ "description": "A Drupal recipe's composer for testing."
+}
diff --git a/core/tests/Drupal/Tests/Composer/Plugin/Unpack/fixtures/recipes/composer-recipe-d/recipe.yml b/core/tests/Drupal/Tests/Composer/Plugin/Unpack/fixtures/recipes/composer-recipe-d/recipe.yml
new file mode 100644
index 000000000000..bd7037d00c10
--- /dev/null
+++ b/core/tests/Drupal/Tests/Composer/Plugin/Unpack/fixtures/recipes/composer-recipe-d/recipe.yml
@@ -0,0 +1,3 @@
+name: 'RecipeD'
+description: 'Recipe D.'
+type: 'test recipe'
diff --git a/core/tests/Drupal/Tests/Composer/Plugin/Unpack/fixtures/themes/composer-theme-a/composer.json b/core/tests/Drupal/Tests/Composer/Plugin/Unpack/fixtures/themes/composer-theme-a/composer.json
new file mode 100644
index 000000000000..ad45c1227cc5
--- /dev/null
+++ b/core/tests/Drupal/Tests/Composer/Plugin/Unpack/fixtures/themes/composer-theme-a/composer.json
@@ -0,0 +1,5 @@
+{
+ "name": "fixtures/theme-a",
+ "type": "drupal-theme",
+ "description": "A Drupal theme's composer for testing."
+}
diff --git a/core/tests/Drupal/Tests/Core/Config/Entity/ConfigEntityStorageTest.php b/core/tests/Drupal/Tests/Core/Config/Entity/ConfigEntityStorageTest.php
index 2b4d2990d52e..76f5cc118ae6 100644
--- a/core/tests/Drupal/Tests/Core/Config/Entity/ConfigEntityStorageTest.php
+++ b/core/tests/Drupal/Tests/Core/Config/Entity/ConfigEntityStorageTest.php
@@ -172,25 +172,17 @@ class ConfigEntityStorageTest extends UnitTestCase {
*/
public function testCreateWithPredefinedUuid(): void {
$this->cacheTagsInvalidator->invalidateTags(Argument::cetera())->shouldNotBeCalled();
-
- $entity = $this->getMockEntity();
- $entity->set('id', 'foo');
- $entity->set('langcode', 'hu');
- $entity->set('uuid', 'baz');
- $entity->setOriginalId('foo');
- $entity->enforceIsNew();
-
- $this->moduleHandler->invokeAll('test_entity_type_create', [$entity])
- ->shouldBeCalled();
- $this->moduleHandler->invokeAll('entity_create', [$entity, 'test_entity_type'])
- ->shouldBeCalled();
-
$this->uuidService->generate()->shouldNotBeCalled();
$entity = $this->entityStorage->create(['id' => 'foo', 'uuid' => 'baz']);
$this->assertInstanceOf(EntityInterface::class, $entity);
$this->assertSame('foo', $entity->id());
$this->assertSame('baz', $entity->uuid());
+
+ $this->moduleHandler->invokeAll('test_entity_type_create', [$entity])
+ ->shouldBeCalled();
+ $this->moduleHandler->invokeAll('entity_create', [$entity, 'test_entity_type'])
+ ->shouldBeCalled();
}
/**
@@ -202,25 +194,18 @@ class ConfigEntityStorageTest extends UnitTestCase {
*/
public function testCreate() {
$this->cacheTagsInvalidator->invalidateTags(Argument::cetera())->shouldNotBeCalled();
+ $this->uuidService->generate()->willReturn('bar');
- $entity = $this->getMockEntity();
- $entity->set('id', 'foo');
- $entity->set('langcode', 'hu');
- $entity->set('uuid', 'bar');
- $entity->setOriginalId('foo');
- $entity->enforceIsNew();
+ $entity = $this->entityStorage->create(['id' => 'foo']);
+ $this->assertInstanceOf(EntityInterface::class, $entity);
+ $this->assertSame('foo', $entity->id());
+ $this->assertSame('bar', $entity->uuid());
$this->moduleHandler->invokeAll('test_entity_type_create', [$entity])
->shouldBeCalled();
$this->moduleHandler->invokeAll('entity_create', [$entity, 'test_entity_type'])
->shouldBeCalled();
- $this->uuidService->generate()->willReturn('bar');
-
- $entity = $this->entityStorage->create(['id' => 'foo']);
- $this->assertInstanceOf(EntityInterface::class, $entity);
- $this->assertSame('foo', $entity->id());
- $this->assertSame('bar', $entity->uuid());
return $entity;
}
diff --git a/core/tests/Drupal/Tests/Core/Datetime/DrupalDateTimeTest.php b/core/tests/Drupal/Tests/Core/Datetime/DrupalDateTimeTest.php
index e1ec84395f9d..40b94bd046bf 100644
--- a/core/tests/Drupal/Tests/Core/Datetime/DrupalDateTimeTest.php
+++ b/core/tests/Drupal/Tests/Core/Datetime/DrupalDateTimeTest.php
@@ -84,16 +84,16 @@ class DrupalDateTimeTest extends UnitTestCase {
// There should be a 19 hour time interval between
// new years in Sydney and new years in LA in year 2000.
[
- 'input2' => DrupalDateTime::createFromFormat('Y-m-d H:i:s', '2000-01-01 00:00:00', new \DateTimeZone('Australia/Sydney'), $settings),
- 'input1' => DrupalDateTime::createFromFormat('Y-m-d H:i:s', '2000-01-01 00:00:00', new \DateTimeZone('America/Los_Angeles'), $settings),
+ 'input1' => DrupalDateTime::createFromFormat('Y-m-d H:i:s', '2000-01-01 00:00:00', new \DateTimeZone('Australia/Sydney'), $settings),
+ 'input2' => DrupalDateTime::createFromFormat('Y-m-d H:i:s', '2000-01-01 00:00:00', new \DateTimeZone('America/Los_Angeles'), $settings),
'absolute' => FALSE,
'expected' => $positive_19_hours,
],
// In 1970 Sydney did not observe daylight savings time
// So there is only an 18 hour time interval.
[
- 'input2' => DrupalDateTime::createFromFormat('Y-m-d H:i:s', '1970-01-01 00:00:00', new \DateTimeZone('Australia/Sydney'), $settings),
- 'input1' => DrupalDateTime::createFromFormat('Y-m-d H:i:s', '1970-01-01 00:00:00', new \DateTimeZone('America/Los_Angeles'), $settings),
+ 'input1' => DrupalDateTime::createFromFormat('Y-m-d H:i:s', '1970-01-01 00:00:00', new \DateTimeZone('Australia/Sydney'), $settings),
+ 'input2' => DrupalDateTime::createFromFormat('Y-m-d H:i:s', '1970-01-01 00:00:00', new \DateTimeZone('America/Los_Angeles'), $settings),
'absolute' => FALSE,
'expected' => $positive_18_hours,
],
diff --git a/core/tests/Drupal/Tests/Core/DependencyInjection/YamlFileLoaderTest.php b/core/tests/Drupal/Tests/Core/DependencyInjection/YamlFileLoaderTest.php
index 717026fe3f46..cbc46da7860f 100644
--- a/core/tests/Drupal/Tests/Core/DependencyInjection/YamlFileLoaderTest.php
+++ b/core/tests/Drupal/Tests/Core/DependencyInjection/YamlFileLoaderTest.php
@@ -40,10 +40,10 @@ services:
Drupal\Core\ExampleClass: ~
example_tagged_iterator:
class: \Drupal\Core\ExampleClass
- arguments: [!tagged_iterator foo.bar]"
+ arguments: [!tagged_iterator foo.bar]
example_service_closure:
class: \Drupal\Core\ExampleClass
- arguments: [!service_closure '@example_service_1']"
+ arguments: [!service_closure '@example_service_1']
YAML;
vfsStream::setup('drupal', NULL, [
diff --git a/core/tests/Drupal/Tests/Core/Entity/KeyValueStore/KeyValueEntityStorageTest.php b/core/tests/Drupal/Tests/Core/Entity/KeyValueStore/KeyValueEntityStorageTest.php
index f57a80d1393c..2381b64b83a5 100644
--- a/core/tests/Drupal/Tests/Core/Entity/KeyValueStore/KeyValueEntityStorageTest.php
+++ b/core/tests/Drupal/Tests/Core/Entity/KeyValueStore/KeyValueEntityStorageTest.php
@@ -206,11 +206,8 @@ class KeyValueEntityStorageTest extends UnitTestCase {
/**
* @covers ::create
* @covers ::doCreate
- *
- * @return \Drupal\Core\Entity\EntityInterface
- * The newly created entity instance with the specified ID and generated UUID.
*/
- public function testCreate() {
+ public function testCreate(): void {
$entity = $this->getMockEntity(EntityBaseTest::class, [], ['toArray']);
$this->entityType->expects($this->once())
->method('getClass')
@@ -231,24 +228,18 @@ class KeyValueEntityStorageTest extends UnitTestCase {
$this->assertInstanceOf('Drupal\Core\Entity\EntityInterface', $entity);
$this->assertSame('foo', $entity->id());
$this->assertSame('bar', $entity->uuid());
- return $entity;
}
/**
* @covers ::save
* @covers ::doSave
- *
- * @param \Drupal\Core\Entity\EntityInterface $entity
- * The entity.
- *
- * @return \Drupal\Core\Entity\EntityInterface
- * The saved entity instance after insertion.
- *
- * @depends testCreate
*/
- public function testSaveInsert(EntityInterface $entity) {
+ public function testSaveInsert(): EntityInterface&MockObject {
$this->setUpKeyValueEntityStorage();
+ $entity = $this->getMockEntity(EntityBaseTest::class, [['id' => 'foo']], ['toArray']);
+ $entity->enforceIsNew();
+
$expected = ['id' => 'foo'];
$this->keyValueStore->expects($this->exactly(2))
->method('has')
@@ -285,12 +276,9 @@ class KeyValueEntityStorageTest extends UnitTestCase {
* @param \Drupal\Core\Entity\EntityInterface $entity
* The entity.
*
- * @return \Drupal\Core\Entity\EntityInterface
- * The updated entity instance after saving.
- *
* @depends testSaveInsert
*/
- public function testSaveUpdate(EntityInterface $entity) {
+ public function testSaveUpdate(EntityInterface $entity): void {
$this->entityType->expects($this->once())
->method('getClass')
->willReturn(get_class($entity));
@@ -320,7 +308,6 @@ class KeyValueEntityStorageTest extends UnitTestCase {
->with('foo', $expected);
$return = $this->entityStorage->save($entity);
$this->assertSame(SAVED_UPDATED, $return);
- return $entity;
}
/**
diff --git a/core/tests/Drupal/Tests/Core/Entity/Sql/SqlContentEntityStorageTest.php b/core/tests/Drupal/Tests/Core/Entity/Sql/SqlContentEntityStorageTest.php
index 37cede409b88..5653e8a356b0 100644
--- a/core/tests/Drupal/Tests/Core/Entity/Sql/SqlContentEntityStorageTest.php
+++ b/core/tests/Drupal/Tests/Core/Entity/Sql/SqlContentEntityStorageTest.php
@@ -1105,12 +1105,7 @@ class SqlContentEntityStorageTest extends UnitTestCase {
$this->setUpEntityStorage();
$entity = $this->entityStorage->create();
- $entity->expects($this->atLeastOnce())
- ->method('id')
- ->willReturn('foo');
-
$this->assertInstanceOf(EntityInterface::class, $entity);
- $this->assertSame('foo', $entity->id());
$this->assertTrue($entity->isNew());
}
diff --git a/core/tests/Drupal/Tests/Core/Render/Placeholder/ChainedPlaceholderStrategyTest.php b/core/tests/Drupal/Tests/Core/Render/Placeholder/ChainedPlaceholderStrategyTest.php
index f1344b0ce8e2..396d0d5db8fb 100644
--- a/core/tests/Drupal/Tests/Core/Render/Placeholder/ChainedPlaceholderStrategyTest.php
+++ b/core/tests/Drupal/Tests/Core/Render/Placeholder/ChainedPlaceholderStrategyTest.php
@@ -40,20 +40,6 @@ class ChainedPlaceholderStrategyTest extends UnitTestCase {
$prophet = new Prophet();
$data = [];
- // Empty placeholders.
- $data['empty placeholders'] = [[], [], []];
-
- // Placeholder removing strategy.
- $placeholders = [
- 'remove-me' => ['#markup' => 'I-am-a-llama-that-will-be-removed-sad-face.'],
- ];
-
- $prophecy = $prophet->prophesize('\Drupal\Core\Render\Placeholder\PlaceholderStrategyInterface');
- $prophecy->processPlaceholders($placeholders)->willReturn([]);
- $dev_null_strategy = $prophecy->reveal();
-
- $data['placeholder removing strategy'] = [[$dev_null_strategy], $placeholders, []];
-
// Fake Single Flush strategy.
$placeholders = [
'67890' => ['#markup' => 'special-placeholder'],
diff --git a/core/tests/PHPStan/composer.json b/core/tests/PHPStan/composer.json
index ab10409fcbff..62e1876dea7a 100644
--- a/core/tests/PHPStan/composer.json
+++ b/core/tests/PHPStan/composer.json
@@ -2,8 +2,8 @@
"name": "drupal/phpstan-testing",
"description": "Tests Drupal core's PHPStan rules",
"require-dev": {
- "phpunit/phpunit": "^10",
- "phpstan/phpstan": "2.1.12"
+ "phpunit/phpunit": "^11",
+ "phpstan/phpstan": "2.1.14"
},
"license": "GPL-2.0-or-later",
"autoload": {
diff --git a/core/tests/fixtures/config_install/multilingual/views.view.content.yml b/core/tests/fixtures/config_install/multilingual/views.view.content.yml
index 20afac251c06..5f7a02fde704 100644
--- a/core/tests/fixtures/config_install/multilingual/views.view.content.yml
+++ b/core/tests/fixtures/config_install/multilingual/views.view.content.yml
@@ -562,6 +562,7 @@ display:
empty_table: true
caption: ''
description: ''
+ class: ''
row:
type: fields
query:
diff --git a/core/tests/fixtures/config_install/multilingual/views.view.files.yml b/core/tests/fixtures/config_install/multilingual/views.view.files.yml
index d7261ea7b7cf..b74df66290c1 100644
--- a/core/tests/fixtures/config_install/multilingual/views.view.files.yml
+++ b/core/tests/fixtures/config_install/multilingual/views.view.files.yml
@@ -713,6 +713,7 @@ display:
empty_table: true
caption: ''
description: ''
+ class: ''
row:
type: fields
query:
@@ -1104,6 +1105,7 @@ display:
empty_table: true
caption: ''
description: ''
+ class: ''
row:
type: fields
options: { }
diff --git a/core/tests/fixtures/config_install/multilingual/views.view.glossary.yml b/core/tests/fixtures/config_install/multilingual/views.view.glossary.yml
index cc63d8642feb..37af1f766bbd 100644
--- a/core/tests/fixtures/config_install/multilingual/views.view.glossary.yml
+++ b/core/tests/fixtures/config_install/multilingual/views.view.glossary.yml
@@ -346,6 +346,7 @@ display:
summary: ''
order: asc
empty_table: false
+ class: ''
row:
type: fields
options:
diff --git a/core/tests/fixtures/config_install/multilingual/views.view.user_admin_people.yml b/core/tests/fixtures/config_install/multilingual/views.view.user_admin_people.yml
index 4c48d1332245..8c0aa01004ee 100644
--- a/core/tests/fixtures/config_install/multilingual/views.view.user_admin_people.yml
+++ b/core/tests/fixtures/config_install/multilingual/views.view.user_admin_people.yml
@@ -854,6 +854,7 @@ display:
sticky: false
summary: ''
empty_table: true
+ class: ''
row:
type: fields
query:
diff --git a/core/tests/fixtures/config_install/multilingual/views.view.watchdog.yml b/core/tests/fixtures/config_install/multilingual/views.view.watchdog.yml
index 00ef2451f3aa..7776386d9907 100644
--- a/core/tests/fixtures/config_install/multilingual/views.view.watchdog.yml
+++ b/core/tests/fixtures/config_install/multilingual/views.view.watchdog.yml
@@ -657,6 +657,7 @@ display:
empty_table: false
caption: ''
description: ''
+ class: ''
row:
type: fields
query:
diff --git a/core/yarn.lock b/core/yarn.lock
index a8a1ccd6483d..01c8a24805d9 100644
--- a/core/yarn.lock
+++ b/core/yarn.lock
@@ -12,6 +12,19 @@ __metadata:
languageName: node
linkType: hard
+"@asamuzakjp/css-color@npm:^3.1.2":
+ version: 3.1.7
+ resolution: "@asamuzakjp/css-color@npm:3.1.7"
+ dependencies:
+ "@csstools/css-calc": "npm:^2.1.3"
+ "@csstools/css-color-parser": "npm:^3.0.9"
+ "@csstools/css-parser-algorithms": "npm:^3.0.4"
+ "@csstools/css-tokenizer": "npm:^3.0.3"
+ lru-cache: "npm:^10.4.3"
+ checksum: 10c0/ac70af38c9b9d972941fe9dbed5221118077bc8f217617edbd534d9c7c281fd2c4023d0f0daa349d57b895cb73430d67d509b722226294bad2910a81d9439e91
+ languageName: node
+ linkType: hard
+
"@babel/code-frame@npm:^7.0.0":
version: 7.23.5
resolution: "@babel/code-frame@npm:7.23.5"
@@ -1358,6 +1371,13 @@ __metadata:
languageName: node
linkType: hard
+"@csstools/color-helpers@npm:^5.0.2":
+ version: 5.0.2
+ resolution: "@csstools/color-helpers@npm:5.0.2"
+ checksum: 10c0/bebaddb28b9eb58b0449edd5d0c0318fa88f3cb079602ee27e88c9118070d666dcc4e09a5aa936aba2fde6ba419922ade07b7b506af97dd7051abd08dfb2959b
+ languageName: node
+ linkType: hard
+
"@csstools/css-calc@npm:^1.2.4":
version: 1.2.4
resolution: "@csstools/css-calc@npm:1.2.4"
@@ -1368,6 +1388,16 @@ __metadata:
languageName: node
linkType: hard
+"@csstools/css-calc@npm:^2.1.3":
+ version: 2.1.3
+ resolution: "@csstools/css-calc@npm:2.1.3"
+ peerDependencies:
+ "@csstools/css-parser-algorithms": ^3.0.4
+ "@csstools/css-tokenizer": ^3.0.3
+ checksum: 10c0/85f5b4f96d60f395d5f0108056b0ddee037b22d6deba448d74324b50f1c554de284f84715ebfac7b2888b78e09d20d02a7cd213ee7bdaa71011ea9b4eee3a251
+ languageName: node
+ linkType: hard
+
"@csstools/css-color-parser@npm:^2.0.4":
version: 2.0.4
resolution: "@csstools/css-color-parser@npm:2.0.4"
@@ -1381,6 +1411,19 @@ __metadata:
languageName: node
linkType: hard
+"@csstools/css-color-parser@npm:^3.0.9":
+ version: 3.0.9
+ resolution: "@csstools/css-color-parser@npm:3.0.9"
+ dependencies:
+ "@csstools/color-helpers": "npm:^5.0.2"
+ "@csstools/css-calc": "npm:^2.1.3"
+ peerDependencies:
+ "@csstools/css-parser-algorithms": ^3.0.4
+ "@csstools/css-tokenizer": ^3.0.3
+ checksum: 10c0/acc026a6bd6d8c4c641fa5f9b4d77cd5dfa54c57c3278ae52329d96b5837723428dcb93c34db4062bbea2f45a98451119df06eaf39fd196aaf6368c59d799f20
+ languageName: node
+ linkType: hard
+
"@csstools/css-parser-algorithms@npm:^2.7.1":
version: 2.7.1
resolution: "@csstools/css-parser-algorithms@npm:2.7.1"
@@ -1840,10 +1883,10 @@ __metadata:
languageName: node
linkType: hard
-"@discoveryjs/json-ext@npm:^0.5.0":
- version: 0.5.7
- resolution: "@discoveryjs/json-ext@npm:0.5.7"
- checksum: 10c0/e10f1b02b78e4812646ddf289b7d9f2cb567d336c363b266bd50cd223cf3de7c2c74018d91cd2613041568397ef3a4a2b500aba588c6e5bd78c38374ba68f38c
+"@discoveryjs/json-ext@npm:^0.6.1":
+ version: 0.6.3
+ resolution: "@discoveryjs/json-ext@npm:0.6.3"
+ checksum: 10c0/778a9f9d5c3696da3c1f9fa4186613db95a1090abbfb6c2601430645c0d0158cd5e4ba4f32c05904e2dd2747d57710f6aab22bd2f8aa3c4e8feab9b247c65d85
languageName: node
linkType: hard
@@ -1903,36 +1946,29 @@ __metadata:
languageName: node
linkType: hard
-"@floating-ui/core@npm:^1.6.0":
- version: 1.6.4
- resolution: "@floating-ui/core@npm:1.6.4"
+"@floating-ui/core@npm:^1.7.0":
+ version: 1.7.0
+ resolution: "@floating-ui/core@npm:1.7.0"
dependencies:
- "@floating-ui/utils": "npm:^0.2.4"
- checksum: 10c0/545684b6f76cda7579b6049bafb9903542d3f9c177300192fe83db19d99b1df285bc33aba3b8ec2978d021151c4168356876e8181002dd2ff4fb93d9e4b7bf71
+ "@floating-ui/utils": "npm:^0.2.9"
+ checksum: 10c0/f7e66a650ad8c73765edb39a7530d81fa990c08c172f03b6129030234d32bccd4401c29ded9c8a4e4135e9beac349c5608d94962fa08c2a2ae2dab7a6530550c
languageName: node
linkType: hard
-"@floating-ui/dom@npm:^1.6.12":
- version: 1.6.12
- resolution: "@floating-ui/dom@npm:1.6.12"
+"@floating-ui/dom@npm:^1.7.0":
+ version: 1.7.0
+ resolution: "@floating-ui/dom@npm:1.7.0"
dependencies:
- "@floating-ui/core": "npm:^1.6.0"
- "@floating-ui/utils": "npm:^0.2.8"
- checksum: 10c0/c67b39862175b175c6ac299ea970f17a22c7482cfdf3b1bc79313407bf0880188b022b878953fa69d3ce166ff2bd9ae57c86043e5dd800c262b470d877591b7d
+ "@floating-ui/core": "npm:^1.7.0"
+ "@floating-ui/utils": "npm:^0.2.9"
+ checksum: 10c0/49a7f0fbef82ba2c2f0bde7bb4812b276ae431b59e6a81427283a55cfb36c0af9cc459cbeb0bb1a5cc3efca1a332f584e123e7b1a8f0a9c94a21989b09b8c060
languageName: node
linkType: hard
-"@floating-ui/utils@npm:^0.2.4":
- version: 0.2.4
- resolution: "@floating-ui/utils@npm:0.2.4"
- checksum: 10c0/154924b01157cb45cf305f4835d7f603e931dda8b00bbe52666729bccc5e7b99630e8b951333725e526d4e53d9b342976434ad5750b8b1da58728e3698bdcc2b
- languageName: node
- linkType: hard
-
-"@floating-ui/utils@npm:^0.2.8":
- version: 0.2.8
- resolution: "@floating-ui/utils@npm:0.2.8"
- checksum: 10c0/a8cee5f17406c900e1c3ef63e3ca89b35e7a2ed645418459a73627b93b7377477fc888081011c6cd177cac45ec2b92a6cab018c14ea140519465498dddd2d3f9
+"@floating-ui/utils@npm:^0.2.9":
+ version: 0.2.9
+ resolution: "@floating-ui/utils@npm:0.2.9"
+ checksum: 10c0/48bbed10f91cb7863a796cc0d0e917c78d11aeb89f98d03fc38d79e7eb792224a79f538ed8a2d5d5584511d4ca6354ef35f1712659fd569868e342df4398ad6f
languageName: node
linkType: hard
@@ -2026,7 +2062,7 @@ __metadata:
languageName: node
linkType: hard
-"@jridgewell/trace-mapping@npm:^0.3.20":
+"@jridgewell/trace-mapping@npm:^0.3.25":
version: 0.3.25
resolution: "@jridgewell/trace-mapping@npm:0.3.25"
dependencies:
@@ -2187,7 +2223,7 @@ __metadata:
languageName: node
linkType: hard
-"@types/json-schema@npm:*, @types/json-schema@npm:^7.0.8":
+"@types/json-schema@npm:*, @types/json-schema@npm:^7.0.15, @types/json-schema@npm:^7.0.9":
version: 7.0.15
resolution: "@types/json-schema@npm:7.0.15"
checksum: 10c0/a996a745e6c5d60292f36731dd41341339d4eeed8180bb09226e5c8d23759067692b1d88e5d91d72ee83dfc00d3aca8e7bd43ea120516c17922cbcb7c3e252db
@@ -2387,36 +2423,36 @@ __metadata:
languageName: node
linkType: hard
-"@webpack-cli/configtest@npm:^2.1.1":
- version: 2.1.1
- resolution: "@webpack-cli/configtest@npm:2.1.1"
+"@webpack-cli/configtest@npm:^3.0.1":
+ version: 3.0.1
+ resolution: "@webpack-cli/configtest@npm:3.0.1"
peerDependencies:
- webpack: 5.x.x
- webpack-cli: 5.x.x
- checksum: 10c0/a8da1f15702cb289807da99235ed95326ed7dabeb1a36ca59bd3a5dbe6adcc946a9a2767936050fc4d5ed14efab0e5b5a641dfe8e3d862c36caa5791ac12759d
+ webpack: ^5.82.0
+ webpack-cli: 6.x.x
+ checksum: 10c0/edd24ecfc429298fe86446f7d7daedfe82d72e7f6236c81420605484fdadade5d59c6bcef3d76bd724e11d9727f74e75de183223ae62d3a568b2d54199688cbe
languageName: node
linkType: hard
-"@webpack-cli/info@npm:^2.0.2":
- version: 2.0.2
- resolution: "@webpack-cli/info@npm:2.0.2"
+"@webpack-cli/info@npm:^3.0.1":
+ version: 3.0.1
+ resolution: "@webpack-cli/info@npm:3.0.1"
peerDependencies:
- webpack: 5.x.x
- webpack-cli: 5.x.x
- checksum: 10c0/ca88a35604dc9aedac7c26e8f6793c5039dc1eea2b12a85fbfd669a5f21ecf9cf169d7fd157ea366a62666e3fa05b776306a96742ac61a9868f44fdce6b40f7d
+ webpack: ^5.82.0
+ webpack-cli: 6.x.x
+ checksum: 10c0/b23b94e7dc8c93e79248f20d5f1bd0fbb7b9ba4b012803e2fdc5440b8f2ee1f3eca7f4933bbca346c8168673bf572b1858169a3cb2c17d9b8bcd833d480c2170
languageName: node
linkType: hard
-"@webpack-cli/serve@npm:^2.0.5":
- version: 2.0.5
- resolution: "@webpack-cli/serve@npm:2.0.5"
+"@webpack-cli/serve@npm:^3.0.1":
+ version: 3.0.1
+ resolution: "@webpack-cli/serve@npm:3.0.1"
peerDependencies:
- webpack: 5.x.x
- webpack-cli: 5.x.x
+ webpack: ^5.82.0
+ webpack-cli: 6.x.x
peerDependenciesMeta:
webpack-dev-server:
optional: true
- checksum: 10c0/36079d34971ff99a58b66b13f4184dcdd8617853c48cccdbc3f9ab7ea9e5d4fcf504e873c298ea7aa15e0b51ad2c4aee4d7a70bd7d9364e60f57b0eb93ca15fc
+ checksum: 10c0/65245e45bfa35e11a5b30631b99cfed0c1b39b2cc8320fa2d2a4185264535618827d349ec032c58af4201d6236cbc43bec894fcb840fdd06314611537a80e210
languageName: node
linkType: hard
@@ -2462,13 +2498,13 @@ __metadata:
"@ckeditor/ckeditor5-style": "npm:~44.0.0"
"@ckeditor/ckeditor5-table": "npm:~44.0.0"
"@drupal/once": "npm:^1.0.1"
- "@floating-ui/dom": "npm:^1.6.12"
+ "@floating-ui/dom": "npm:^1.7.0"
"@gitlab-formatters/stylelint-formatter-gitlab": "npm:^1.0.2"
- backbone: "npm:^1.6.0"
- chokidar: "npm:^4.0.1"
+ backbone: "npm:^1.6.1"
+ chokidar: "npm:^4.0.3"
ckeditor5: "npm:~44.0.0"
cspell: "npm:^8.16.1"
- dotenv: "npm:^16.4.7"
+ dotenv: "npm:^16.5.0"
dotenv-safe: "npm:^9.1.0"
eslint: "npm:^8.57.1"
eslint-config-airbnb-base: "npm:^15.0.0"
@@ -2478,16 +2514,16 @@ __metadata:
eslint-plugin-no-jquery: "npm:^3.1.0"
eslint-plugin-prettier: "npm:^5.2.1"
eslint-plugin-yml: "npm:^1.16.0"
- glob: "npm:11.0.0"
+ glob: "npm:11.0.2"
htmx.org: "npm:2.0.4"
jquery: "npm:^4.0.0-beta.2"
jquery-ui: "npm:^1.14.1"
js-cookie: "npm:^3.0.5"
- jsdom: "npm:^25.0.1"
+ jsdom: "npm:^26.1.0"
loadjs: "npm:^4.3.0"
minimist: "npm:^1.2.8"
mkdirp: "npm:^3.0.1"
- nightwatch: "npm:^3.9.0"
+ nightwatch: "npm:^3.12.1"
normalize.css: "npm:^8.0.1"
postcss: "npm:^8.4.40"
postcss-header: "npm:^3.0.3"
@@ -2503,13 +2539,13 @@ __metadata:
stylelint-order: "npm:^6.0.4"
stylelint-prettier: "npm:^5.0.2"
tabbable: "npm:^6.2.0"
- terser: "npm:^5.37.0"
- terser-webpack-plugin: "npm:^5.3.10"
+ terser: "npm:^5.39.0"
+ terser-webpack-plugin: "npm:^5.3.14"
transliteration: "npm:^2.3.5"
tua-body-scroll-lock: "npm:^1.5.3"
underscore: "npm:~1.13.7"
- webpack: "npm:^5.97.1"
- webpack-cli: "npm:^5.1.4"
+ webpack: "npm:^5.99.8"
+ webpack-cli: "npm:^6.0.1"
languageName: unknown
linkType: soft
@@ -2556,6 +2592,13 @@ __metadata:
languageName: node
linkType: hard
+"agent-base@npm:^7.1.2":
+ version: 7.1.3
+ resolution: "agent-base@npm:7.1.3"
+ checksum: 10c0/6192b580c5b1d8fb399b9c62bf8343d76654c2dd62afcb9a52b2cf44a8b6ace1e3b704d3fe3547d91555c857d3df02603341ff2cb961b9cfe2b12f9f3c38ee11
+ languageName: node
+ linkType: hard
+
"aggregate-error@npm:^3.0.0":
version: 3.1.0
resolution: "aggregate-error@npm:3.1.0"
@@ -2566,16 +2609,32 @@ __metadata:
languageName: node
linkType: hard
-"ajv-keywords@npm:^3.5.2":
- version: 3.5.2
- resolution: "ajv-keywords@npm:3.5.2"
+"ajv-formats@npm:^2.1.1":
+ version: 2.1.1
+ resolution: "ajv-formats@npm:2.1.1"
+ dependencies:
+ ajv: "npm:^8.0.0"
+ peerDependencies:
+ ajv: ^8.0.0
+ peerDependenciesMeta:
+ ajv:
+ optional: true
+ checksum: 10c0/e43ba22e91b6a48d96224b83d260d3a3a561b42d391f8d3c6d2c1559f9aa5b253bfb306bc94bbeca1d967c014e15a6efe9a207309e95b3eaae07fcbcdc2af662
+ languageName: node
+ linkType: hard
+
+"ajv-keywords@npm:^5.1.0":
+ version: 5.1.0
+ resolution: "ajv-keywords@npm:5.1.0"
+ dependencies:
+ fast-deep-equal: "npm:^3.1.3"
peerDependencies:
- ajv: ^6.9.1
- checksum: 10c0/0c57a47cbd656e8cdfd99d7c2264de5868918ffa207c8d7a72a7f63379d4333254b2ba03d69e3c035e996a3fd3eb6d5725d7a1597cca10694296e32510546360
+ ajv: ^8.8.2
+ checksum: 10c0/18bec51f0171b83123ba1d8883c126e60c6f420cef885250898bf77a8d3e65e3bfb9e8564f497e30bdbe762a83e0d144a36931328616a973ee669dc74d4a9590
languageName: node
linkType: hard
-"ajv@npm:^6.12.4, ajv@npm:^6.12.5":
+"ajv@npm:^6.12.4":
version: 6.12.6
resolution: "ajv@npm:6.12.6"
dependencies:
@@ -2587,6 +2646,18 @@ __metadata:
languageName: node
linkType: hard
+"ajv@npm:^8.0.0, ajv@npm:^8.9.0":
+ version: 8.17.1
+ resolution: "ajv@npm:8.17.1"
+ dependencies:
+ fast-deep-equal: "npm:^3.1.3"
+ fast-uri: "npm:^3.0.1"
+ json-schema-traverse: "npm:^1.0.0"
+ require-from-string: "npm:^2.0.2"
+ checksum: 10c0/ec3ba10a573c6b60f94639ffc53526275917a2df6810e4ab5a6b959d87459f9ef3f00d5e7865b82677cb7d21590355b34da14d1d0b9c32d75f95a187e76fff35
+ languageName: node
+ linkType: hard
+
"ajv@npm:^8.0.1":
version: 8.12.0
resolution: "ajv@npm:8.12.0"
@@ -2928,12 +2999,12 @@ __metadata:
languageName: node
linkType: hard
-"backbone@npm:^1.6.0":
- version: 1.6.0
- resolution: "backbone@npm:1.6.0"
+"backbone@npm:^1.6.1":
+ version: 1.6.1
+ resolution: "backbone@npm:1.6.1"
dependencies:
underscore: "npm:>=1.8.3"
- checksum: 10c0/1aa3823e431abdbd3b7d5576ff69deea65554596714869ba55eb26d53f9bc68bffc5b42f8f58020a85a4eeed87820a8491c63033bc2363a99aacfa3b5a019b3c
+ checksum: 10c0/de59ea1f81c72f18cebecf9bd9ecfc30ea5dcd7633e18aea820c5236cbdd1d807777a0d4f76d63d38c3c47647eddcbe1f739d797c3043de473c2469aa13f304a
languageName: node
linkType: hard
@@ -3259,12 +3330,12 @@ __metadata:
languageName: node
linkType: hard
-"chokidar@npm:^4.0.1":
- version: 4.0.1
- resolution: "chokidar@npm:4.0.1"
+"chokidar@npm:^4.0.3":
+ version: 4.0.3
+ resolution: "chokidar@npm:4.0.3"
dependencies:
readdirp: "npm:^4.0.1"
- checksum: 10c0/4bb7a3adc304059810bb6c420c43261a15bb44f610d77c35547addc84faa0374265c3adc67f25d06f363d9a4571962b02679268c40de07676d260de1986efea9
+ checksum: 10c0/a58b9df05bb452f7d105d9e7229ac82fa873741c0c40ddcc7bb82f8a909fbe3f7814c9ebe9bc9a2bef9b737c0ec6e2d699d179048ef06ad3ec46315df0ebe6ad
languageName: node
linkType: hard
@@ -3512,13 +3583,6 @@ __metadata:
languageName: node
linkType: hard
-"commander@npm:^10.0.1":
- version: 10.0.1
- resolution: "commander@npm:10.0.1"
- checksum: 10c0/53f33d8927758a911094adadda4b2cbac111a5b377d8706700587650fd8f45b0bbe336de4b5c3fe47fd61f420a3d9bd452b6e0e6e5600a7e74d7bf0174f6efe3
- languageName: node
- linkType: hard
-
"commander@npm:^12.1.0":
version: 12.1.0
resolution: "commander@npm:12.1.0"
@@ -3842,12 +3906,13 @@ __metadata:
languageName: node
linkType: hard
-"cssstyle@npm:^4.1.0":
- version: 4.1.0
- resolution: "cssstyle@npm:4.1.0"
+"cssstyle@npm:^4.2.1":
+ version: 4.3.1
+ resolution: "cssstyle@npm:4.3.1"
dependencies:
- rrweb-cssom: "npm:^0.7.1"
- checksum: 10c0/05c6597e5d3e0ec6b15221f2c0ce9a0443a46cc50a6089a3ba9ee1ac27f83ff86a445a8f95435137dadd859f091fc61b6d342abaf396d3c910471b5b33cfcbfa
+ "@asamuzakjp/css-color": "npm:^3.1.2"
+ rrweb-cssom: "npm:^0.8.0"
+ checksum: 10c0/89d73252d5f9930cf67f5c576de8030a9d960aae4c8bdd42d60464b2f67c8d809601fb7e620b43d4c84e03472016da77528df9a21a21393387ed256610ca0ab4
languageName: node
linkType: hard
@@ -3948,6 +4013,13 @@ __metadata:
languageName: node
linkType: hard
+"decimal.js@npm:^10.5.0":
+ version: 10.5.0
+ resolution: "decimal.js@npm:10.5.0"
+ checksum: 10c0/785c35279df32762143914668df35948920b6c1c259b933e0519a69b7003fc0a5ed2a766b1e1dda02574450c566b21738a45f15e274b47c2ac02072c0d1f3ac3
+ languageName: node
+ linkType: hard
+
"deep-eql@npm:4.0.1":
version: 4.0.1
resolution: "deep-eql@npm:4.0.1"
@@ -4110,10 +4182,10 @@ __metadata:
languageName: node
linkType: hard
-"dotenv@npm:^16.4.7":
- version: 16.4.7
- resolution: "dotenv@npm:16.4.7"
- checksum: 10c0/be9f597e36a8daf834452daa1f4cc30e5375a5968f98f46d89b16b983c567398a330580c88395069a77473943c06b877d1ca25b4afafcdd6d4adb549e8293462
+"dotenv@npm:^16.5.0":
+ version: 16.5.0
+ resolution: "dotenv@npm:16.5.0"
+ checksum: 10c0/5bc94c919fbd955bf0ba44d33922a1e93d1078e64a1db5c30faeded1d996e7a83c55332cb8ea4fae5a9ca4d0be44cbceb95c5811e70f9f095298df09d1997dd9
languageName: node
linkType: hard
@@ -4223,6 +4295,13 @@ __metadata:
languageName: node
linkType: hard
+"entities@npm:^6.0.0":
+ version: 6.0.0
+ resolution: "entities@npm:6.0.0"
+ checksum: 10c0/b82a7bd5de282860f3c36a91e815e41e874fd036c83956a568b82729678492eb088359d6f7e0a4f5c00776427263fcba04959b8340fefa430c39b9bce770427e
+ languageName: node
+ linkType: hard
+
"env-paths@npm:^2.2.0, env-paths@npm:^2.2.1":
version: 2.2.1
resolution: "env-paths@npm:2.2.1"
@@ -4237,7 +4316,7 @@ __metadata:
languageName: node
linkType: hard
-"envinfo@npm:7.11.0, envinfo@npm:^7.7.3":
+"envinfo@npm:7.11.0":
version: 7.11.0
resolution: "envinfo@npm:7.11.0"
bin:
@@ -4246,6 +4325,15 @@ __metadata:
languageName: node
linkType: hard
+"envinfo@npm:^7.14.0":
+ version: 7.14.0
+ resolution: "envinfo@npm:7.14.0"
+ bin:
+ envinfo: dist/cli.js
+ checksum: 10c0/059a031eee101e056bd9cc5cbfe25c2fab433fe1780e86cf0a82d24a000c6931e327da6a8ffb3dce528a24f83f256e7efc0b36813113eff8fdc6839018efe327
+ languageName: node
+ linkType: hard
+
"err-code@npm:^2.0.2":
version: 2.0.3
resolution: "err-code@npm:2.0.3"
@@ -4838,6 +4926,13 @@ __metadata:
languageName: node
linkType: hard
+"fast-uri@npm:^3.0.1":
+ version: 3.0.6
+ resolution: "fast-uri@npm:3.0.6"
+ checksum: 10c0/74a513c2af0584448aee71ce56005185f81239eab7a2343110e5bad50c39ad4fb19c5a6f99783ead1cac7ccaf3461a6034fda89fffa2b30b6d99b9f21c2f9d29
+ languageName: node
+ linkType: hard
+
"fastest-levenshtein@npm:^1.0.12, fastest-levenshtein@npm:^1.0.16":
version: 1.0.16
resolution: "fastest-levenshtein@npm:1.0.16"
@@ -5202,9 +5297,9 @@ __metadata:
languageName: node
linkType: hard
-"glob@npm:11.0.0":
- version: 11.0.0
- resolution: "glob@npm:11.0.0"
+"glob@npm:11.0.2":
+ version: 11.0.2
+ resolution: "glob@npm:11.0.2"
dependencies:
foreground-child: "npm:^3.1.0"
jackspeak: "npm:^4.0.1"
@@ -5214,7 +5309,7 @@ __metadata:
path-scurry: "npm:^2.0.0"
bin:
glob: dist/esm/bin.mjs
- checksum: 10c0/419866015d8795258a8ac51de5b9d1a99c72634fc3ead93338e4da388e89773ab21681e494eac0fbc4250b003451ca3110bb4f1c9393d15d14466270094fdb4e
+ checksum: 10c0/49f91c64ca882d5e3a72397bd45a146ca91fd3ca53dafb5254daf6c0e83fc510d39ea66f136f9ac7ca075cdd11fbe9aaa235b28f743bd477622e472f4fdc0240
languageName: node
linkType: hard
@@ -5559,6 +5654,16 @@ __metadata:
languageName: node
linkType: hard
+"https-proxy-agent@npm:^7.0.6":
+ version: 7.0.6
+ resolution: "https-proxy-agent@npm:7.0.6"
+ dependencies:
+ agent-base: "npm:^7.1.2"
+ debug: "npm:4"
+ checksum: 10c0/f729219bc735edb621fa30e6e84e60ee5d00802b8247aac0d7b79b0bd6d4b3294737a337b93b86a0bd9e68099d031858a39260c976dc14cdbba238ba1f8779ac
+ languageName: node
+ linkType: hard
+
"iconv-lite@npm:0.6.3, iconv-lite@npm:^0.6.2":
version: 0.6.3
resolution: "iconv-lite@npm:0.6.3"
@@ -6253,37 +6358,36 @@ __metadata:
languageName: node
linkType: hard
-"jsdom@npm:^25.0.1":
- version: 25.0.1
- resolution: "jsdom@npm:25.0.1"
+"jsdom@npm:^26.1.0":
+ version: 26.1.0
+ resolution: "jsdom@npm:26.1.0"
dependencies:
- cssstyle: "npm:^4.1.0"
+ cssstyle: "npm:^4.2.1"
data-urls: "npm:^5.0.0"
- decimal.js: "npm:^10.4.3"
- form-data: "npm:^4.0.0"
+ decimal.js: "npm:^10.5.0"
html-encoding-sniffer: "npm:^4.0.0"
http-proxy-agent: "npm:^7.0.2"
- https-proxy-agent: "npm:^7.0.5"
+ https-proxy-agent: "npm:^7.0.6"
is-potential-custom-element-name: "npm:^1.0.1"
- nwsapi: "npm:^2.2.12"
- parse5: "npm:^7.1.2"
- rrweb-cssom: "npm:^0.7.1"
+ nwsapi: "npm:^2.2.16"
+ parse5: "npm:^7.2.1"
+ rrweb-cssom: "npm:^0.8.0"
saxes: "npm:^6.0.0"
symbol-tree: "npm:^3.2.4"
- tough-cookie: "npm:^5.0.0"
+ tough-cookie: "npm:^5.1.1"
w3c-xmlserializer: "npm:^5.0.0"
webidl-conversions: "npm:^7.0.0"
whatwg-encoding: "npm:^3.1.1"
whatwg-mimetype: "npm:^4.0.0"
- whatwg-url: "npm:^14.0.0"
+ whatwg-url: "npm:^14.1.1"
ws: "npm:^8.18.0"
xml-name-validator: "npm:^5.0.0"
peerDependencies:
- canvas: ^2.11.2
+ canvas: ^3.0.0
peerDependenciesMeta:
canvas:
optional: true
- checksum: 10c0/6bda32a6dfe4e37a30568bf51136bdb3ba9c0b72aadd6356280404275a34c9e097c8c25b5eb3c742e602623741e172da977ff456684befd77c9042ed9bf8c2b4
+ checksum: 10c0/5b14a5bc32ce077a06fb42d1ab95b1191afa5cbbce8859e3b96831c5143becbbcbf0511d4d4934e922d2901443ced2cdc3b734c1cf30b5f73b3e067ce457d0f4
languageName: node
linkType: hard
@@ -6524,6 +6628,13 @@ __metadata:
languageName: node
linkType: hard
+"lru-cache@npm:^10.4.3":
+ version: 10.4.3
+ resolution: "lru-cache@npm:10.4.3"
+ checksum: 10c0/ebd04fbca961e6c1d6c0af3799adcc966a1babe798f685bb84e6599266599cd95d94630b10262f5424539bc4640107e8a33aa28585374abf561d30d16f4b39fb
+ languageName: node
+ linkType: hard
+
"lru-cache@npm:^11.0.0":
version: 11.0.0
resolution: "lru-cache@npm:11.0.0"
@@ -6943,9 +7054,9 @@ __metadata:
languageName: node
linkType: hard
-"nightwatch@npm:^3.9.0":
- version: 3.9.0
- resolution: "nightwatch@npm:3.9.0"
+"nightwatch@npm:^3.12.1":
+ version: 3.12.1
+ resolution: "nightwatch@npm:3.12.1"
dependencies:
"@nightwatch/chai": "npm:5.0.3"
"@nightwatch/html-reporter-template": "npm:^0.3.0"
@@ -6975,7 +7086,7 @@ __metadata:
open: "npm:8.4.2"
ora: "npm:5.4.1"
piscina: "npm:^4.3.1"
- selenium-webdriver: "npm:4.26.0"
+ selenium-webdriver: "npm:4.27.0"
semver: "npm:7.5.4"
stacktrace-parser: "npm:0.1.10"
strip-ansi: "npm:6.0.1"
@@ -6992,7 +7103,7 @@ __metadata:
optional: true
bin:
nightwatch: bin/nightwatch
- checksum: 10c0/c7cad273fe63478e6862d7a831455b44d0e1837b826d9ad9208438b5699964bf47d8d0222ed530874c41234bda00076af6266dccaa794023565ca1384508b427
+ checksum: 10c0/8c42206b19a135602728cdecd57c19d47d8f45f468c73a14281bf4c4f0a67dab413a13f334744d80c3b53aa1ef5c6d8fb19c3ebec968fde47ed35f2991e741aa
languageName: node
linkType: hard
@@ -7089,6 +7200,13 @@ __metadata:
languageName: node
linkType: hard
+"nwsapi@npm:^2.2.16":
+ version: 2.2.20
+ resolution: "nwsapi@npm:2.2.20"
+ checksum: 10c0/07f4dafa3186aef7c007863e90acd4342a34ba9d44b22f14f644fdb311f6086887e21c2fc15efaa826c2bc39ab2bc841364a1a630e7c87e0cb723ba59d729297
+ languageName: node
+ linkType: hard
+
"object-inspect@npm:^1.13.1, object-inspect@npm:^1.9.0":
version: 1.13.1
resolution: "object-inspect@npm:1.13.1"
@@ -7342,6 +7460,15 @@ __metadata:
languageName: node
linkType: hard
+"parse5@npm:^7.2.1":
+ version: 7.3.0
+ resolution: "parse5@npm:7.3.0"
+ dependencies:
+ entities: "npm:^6.0.0"
+ checksum: 10c0/7fd2e4e247e85241d6f2a464d0085eed599a26d7b0a5233790c49f53473232eb85350e8133344d9b3fd58b89339e7ad7270fe1f89d28abe50674ec97b87f80b5
+ languageName: node
+ linkType: hard
+
"path-exists@npm:^4.0.0":
version: 4.0.0
resolution: "path-exists@npm:4.0.0"
@@ -8295,6 +8422,13 @@ __metadata:
languageName: node
linkType: hard
+"rrweb-cssom@npm:^0.8.0":
+ version: 0.8.0
+ resolution: "rrweb-cssom@npm:0.8.0"
+ checksum: 10c0/56f2bfd56733adb92c0b56e274c43f864b8dd48784d6fe946ef5ff8d438234015e59ad837fc2ad54714b6421384141c1add4eb569e72054e350d1f8a50b8ac7b
+ languageName: node
+ linkType: hard
+
"run-parallel@npm:^1.1.9":
version: 1.2.0
resolution: "run-parallel@npm:1.2.0"
@@ -8380,26 +8514,27 @@ __metadata:
languageName: node
linkType: hard
-"schema-utils@npm:^3.1.1, schema-utils@npm:^3.2.0":
- version: 3.3.0
- resolution: "schema-utils@npm:3.3.0"
+"schema-utils@npm:^4.3.0, schema-utils@npm:^4.3.2":
+ version: 4.3.2
+ resolution: "schema-utils@npm:4.3.2"
dependencies:
- "@types/json-schema": "npm:^7.0.8"
- ajv: "npm:^6.12.5"
- ajv-keywords: "npm:^3.5.2"
- checksum: 10c0/fafdbde91ad8aa1316bc543d4b61e65ea86970aebbfb750bfb6d8a6c287a23e415e0e926c2498696b242f63af1aab8e585252637fabe811fd37b604351da6500
+ "@types/json-schema": "npm:^7.0.9"
+ ajv: "npm:^8.9.0"
+ ajv-formats: "npm:^2.1.1"
+ ajv-keywords: "npm:^5.1.0"
+ checksum: 10c0/981632f9bf59f35b15a9bcdac671dd183f4946fe4b055ae71a301e66a9797b95e5dd450de581eb6cca56fb6583ce8f24d67b2d9f8e1b2936612209697f6c277e
languageName: node
linkType: hard
-"selenium-webdriver@npm:4.26.0":
- version: 4.26.0
- resolution: "selenium-webdriver@npm:4.26.0"
+"selenium-webdriver@npm:4.27.0":
+ version: 4.27.0
+ resolution: "selenium-webdriver@npm:4.27.0"
dependencies:
"@bazel/runfiles": "npm:^6.3.1"
jszip: "npm:^3.10.1"
tmp: "npm:^0.2.3"
ws: "npm:^8.18.0"
- checksum: 10c0/af34a108fe2f9e717aab0ee3130ad101bbfc84be988729bce7eb7cd101403cd0ac2bdc5fe134e3e2b6c21bd4f00d9e9790062317877ff43ad370c2274fa57b7b
+ checksum: 10c0/fe6aedae92e9d6675326b72ee00c9f9c3022f14746672cacbf7ca285816cc512ce3e0f23a77a2976995192099e6bfdf0011d2cc19efd974cd5683d9709f90d3e
languageName: node
linkType: hard
@@ -8452,12 +8587,12 @@ __metadata:
languageName: node
linkType: hard
-"serialize-javascript@npm:^6.0.1":
- version: 6.0.1
- resolution: "serialize-javascript@npm:6.0.1"
+"serialize-javascript@npm:^6.0.2":
+ version: 6.0.2
+ resolution: "serialize-javascript@npm:6.0.2"
dependencies:
randombytes: "npm:^2.1.0"
- checksum: 10c0/1af427f4fee3fee051f54ffe15f77068cff78a3c96d20f5c1178d20630d3ab122d8350e639d5e13cde8111ef9db9439b871305ffb185e24be0a2149cec230988
+ checksum: 10c0/2dd09ef4b65a1289ba24a788b1423a035581bef60817bea1f01eda8e3bda623f86357665fe7ac1b50f6d4f583f97db9615b3f07b2a2e8cbcb75033965f771dd2
languageName: node
linkType: hard
@@ -9054,15 +9189,15 @@ __metadata:
languageName: node
linkType: hard
-"terser-webpack-plugin@npm:^5.3.10":
- version: 5.3.10
- resolution: "terser-webpack-plugin@npm:5.3.10"
+"terser-webpack-plugin@npm:^5.3.11, terser-webpack-plugin@npm:^5.3.14":
+ version: 5.3.14
+ resolution: "terser-webpack-plugin@npm:5.3.14"
dependencies:
- "@jridgewell/trace-mapping": "npm:^0.3.20"
+ "@jridgewell/trace-mapping": "npm:^0.3.25"
jest-worker: "npm:^27.4.5"
- schema-utils: "npm:^3.1.1"
- serialize-javascript: "npm:^6.0.1"
- terser: "npm:^5.26.0"
+ schema-utils: "npm:^4.3.0"
+ serialize-javascript: "npm:^6.0.2"
+ terser: "npm:^5.31.1"
peerDependencies:
webpack: ^5.1.0
peerDependenciesMeta:
@@ -9072,27 +9207,13 @@ __metadata:
optional: true
uglify-js:
optional: true
- checksum: 10c0/66d1ed3174542560911cf96f4716aeea8d60e7caab212291705d50072b6ba844c7391442541b13c848684044042bea9ec87512b8506528c12854943da05faf91
- languageName: node
- linkType: hard
-
-"terser@npm:^5.26.0":
- version: 5.31.0
- resolution: "terser@npm:5.31.0"
- dependencies:
- "@jridgewell/source-map": "npm:^0.3.3"
- acorn: "npm:^8.8.2"
- commander: "npm:^2.20.0"
- source-map-support: "npm:~0.5.20"
- bin:
- terser: bin/terser
- checksum: 10c0/cb127a579b03fb9dcee0d293ff24814deedcd430f447933b618e8593b7454f615b5c8493c68e86a4b0188769d5ea2af5251b5d507edb208114f7e8aebdc7c850
+ checksum: 10c0/9b060947241af43bd6fd728456f60e646186aef492163672a35ad49be6fbc7f63b54a7356c3f6ff40a8f83f00a977edc26f044b8e106cc611c053c8c0eaf8569
languageName: node
linkType: hard
-"terser@npm:^5.37.0":
- version: 5.37.0
- resolution: "terser@npm:5.37.0"
+"terser@npm:^5.31.1, terser@npm:^5.39.0":
+ version: 5.39.0
+ resolution: "terser@npm:5.39.0"
dependencies:
"@jridgewell/source-map": "npm:^0.3.3"
acorn: "npm:^8.8.2"
@@ -9100,7 +9221,7 @@ __metadata:
source-map-support: "npm:~0.5.20"
bin:
terser: bin/terser
- checksum: 10c0/ff0dc79b0a0da821e7f5bf7a047eab6d04e70e88b62339a0f1d71117db3310e255f5c00738fa3b391f56c3571f800a00047720261ba04ced0241c1f9922199f4
+ checksum: 10c0/83326545ea1aecd6261030568b6191ccfa4cb6aa61d9ea41746a52479f50017a78b77e4725fbbc207c5df841ffa66a773c5ac33636e95c7ab94fe7e0379ae5c7
languageName: node
linkType: hard
@@ -9167,12 +9288,12 @@ __metadata:
languageName: node
linkType: hard
-"tough-cookie@npm:^5.0.0":
- version: 5.0.0
- resolution: "tough-cookie@npm:5.0.0"
+"tough-cookie@npm:^5.1.1":
+ version: 5.1.2
+ resolution: "tough-cookie@npm:5.1.2"
dependencies:
tldts: "npm:^6.1.32"
- checksum: 10c0/4a69c885bf6f45c5a64e60262af99e8c0d58a33bd3d0ce5da62121eeb9c00996d0128a72df8fc4614cbde59cc8b70aa3e21e4c3c98c2bbde137d7aba7fa00124
+ checksum: 10c0/5f95023a47de0f30a902bba951664b359725597d8adeabc66a0b93a931c3af801e1e697dae4b8c21a012056c0ea88bd2bf4dfe66b2adcf8e2f42cd9796fe0626
languageName: node
linkType: hard
@@ -9185,6 +9306,15 @@ __metadata:
languageName: node
linkType: hard
+"tr46@npm:^5.1.0":
+ version: 5.1.1
+ resolution: "tr46@npm:5.1.1"
+ dependencies:
+ punycode: "npm:^2.3.1"
+ checksum: 10c0/ae270e194d52ec67ebd695c1a42876e0f19b96e4aca2ab464ab1d9d17dc3acd3e18764f5034c93897db73421563be27c70c98359c4501136a497e46deda5d5ec
+ languageName: node
+ linkType: hard
+
"transliteration@npm:^2.3.5":
version: 2.3.5
resolution: "transliteration@npm:2.3.5"
@@ -9567,46 +9697,44 @@ __metadata:
languageName: node
linkType: hard
-"webpack-cli@npm:^5.1.4":
- version: 5.1.4
- resolution: "webpack-cli@npm:5.1.4"
+"webpack-cli@npm:^6.0.1":
+ version: 6.0.1
+ resolution: "webpack-cli@npm:6.0.1"
dependencies:
- "@discoveryjs/json-ext": "npm:^0.5.0"
- "@webpack-cli/configtest": "npm:^2.1.1"
- "@webpack-cli/info": "npm:^2.0.2"
- "@webpack-cli/serve": "npm:^2.0.5"
+ "@discoveryjs/json-ext": "npm:^0.6.1"
+ "@webpack-cli/configtest": "npm:^3.0.1"
+ "@webpack-cli/info": "npm:^3.0.1"
+ "@webpack-cli/serve": "npm:^3.0.1"
colorette: "npm:^2.0.14"
- commander: "npm:^10.0.1"
+ commander: "npm:^12.1.0"
cross-spawn: "npm:^7.0.3"
- envinfo: "npm:^7.7.3"
+ envinfo: "npm:^7.14.0"
fastest-levenshtein: "npm:^1.0.12"
import-local: "npm:^3.0.2"
interpret: "npm:^3.1.1"
rechoir: "npm:^0.8.0"
- webpack-merge: "npm:^5.7.3"
+ webpack-merge: "npm:^6.0.1"
peerDependencies:
- webpack: 5.x.x
+ webpack: ^5.82.0
peerDependenciesMeta:
- "@webpack-cli/generators":
- optional: true
webpack-bundle-analyzer:
optional: true
webpack-dev-server:
optional: true
bin:
- webpack-cli: bin/cli.js
- checksum: 10c0/4266909ae5e2e662c8790ac286e965b2c7fd5a4a2f07f48e28576234c9a5f631847ccddc18e1b3281c7b4be04a7ff4717d2636033a322dde13ac995fd0d9de10
+ webpack-cli: ./bin/cli.js
+ checksum: 10c0/2aaca78e277427f03f528602abd707d224696048fb46286ea636c7975592409c4381ca94d68bbbb3900f195ca97f256e619583e8feb34a80da531461323bf3e2
languageName: node
linkType: hard
-"webpack-merge@npm:^5.7.3":
- version: 5.10.0
- resolution: "webpack-merge@npm:5.10.0"
+"webpack-merge@npm:^6.0.1":
+ version: 6.0.1
+ resolution: "webpack-merge@npm:6.0.1"
dependencies:
clone-deep: "npm:^4.0.1"
flat: "npm:^5.0.2"
- wildcard: "npm:^2.0.0"
- checksum: 10c0/b607c84cabaf74689f965420051a55a08722d897bdd6c29cb0b2263b451c090f962d41ecf8c9bf56b0ab3de56e65476ace0a8ecda4f4a4663684243d90e0512b
+ wildcard: "npm:^2.0.1"
+ checksum: 10c0/bf1429567858b353641801b8a2696ca0aac270fc8c55d4de8a7b586fe07d27fdcfc83099a98ab47e6162383db8dd63bb8cc25b1beb2ec82150422eec843b0dc0
languageName: node
linkType: hard
@@ -9617,12 +9745,13 @@ __metadata:
languageName: node
linkType: hard
-"webpack@npm:^5.97.1":
- version: 5.97.1
- resolution: "webpack@npm:5.97.1"
+"webpack@npm:^5.99.8":
+ version: 5.99.8
+ resolution: "webpack@npm:5.99.8"
dependencies:
"@types/eslint-scope": "npm:^3.7.7"
"@types/estree": "npm:^1.0.6"
+ "@types/json-schema": "npm:^7.0.15"
"@webassemblyjs/ast": "npm:^1.14.1"
"@webassemblyjs/wasm-edit": "npm:^1.14.1"
"@webassemblyjs/wasm-parser": "npm:^1.14.1"
@@ -9639,9 +9768,9 @@ __metadata:
loader-runner: "npm:^4.2.0"
mime-types: "npm:^2.1.27"
neo-async: "npm:^2.6.2"
- schema-utils: "npm:^3.2.0"
+ schema-utils: "npm:^4.3.2"
tapable: "npm:^2.1.1"
- terser-webpack-plugin: "npm:^5.3.10"
+ terser-webpack-plugin: "npm:^5.3.11"
watchpack: "npm:^2.4.1"
webpack-sources: "npm:^3.2.3"
peerDependenciesMeta:
@@ -9649,7 +9778,7 @@ __metadata:
optional: true
bin:
webpack: bin/webpack.js
- checksum: 10c0/a12d3dc882ca582075f2c4bd88840be8307427245c90a8a0e0b372d73560df13fcf25a61625c9e7edc964981d16b5a8323640562eb48347cf9dd2f8bd1b39d35
+ checksum: 10c0/c4852c3b795ed3fba799d2925802a4e259b2de7c2c597f0aaf0e228acfdc6755389ed8c29f1dad86610a9c6ad968c0b57c702b93891d60f09d302af63b2debe0
languageName: node
linkType: hard
@@ -9679,6 +9808,16 @@ __metadata:
languageName: node
linkType: hard
+"whatwg-url@npm:^14.1.1":
+ version: 14.2.0
+ resolution: "whatwg-url@npm:14.2.0"
+ dependencies:
+ tr46: "npm:^5.1.0"
+ webidl-conversions: "npm:^7.0.0"
+ checksum: 10c0/f746fc2f4c906607d09537de1227b13f9494c171141e5427ed7d2c0dd0b6a48b43d8e71abaae57d368d0c06b673fd8ec63550b32ad5ed64990c7b0266c2b4272
+ languageName: node
+ linkType: hard
+
"which-boxed-primitive@npm:^1.0.2":
version: 1.0.2
resolution: "which-boxed-primitive@npm:1.0.2"
@@ -9793,7 +9932,7 @@ __metadata:
languageName: node
linkType: hard
-"wildcard@npm:^2.0.0":
+"wildcard@npm:^2.0.1":
version: 2.0.1
resolution: "wildcard@npm:2.0.1"
checksum: 10c0/08f70cd97dd9a20aea280847a1fe8148e17cae7d231640e41eb26d2388697cbe65b67fd9e68715251c39b080c5ae4f76d71a9a69fa101d897273efdfb1b58bf7