From 45837b1d8814bb46242509685c9aea8af70c6d80 Mon Sep 17 00:00:00 2001
From: "lee.5151" <lee.5151@osu.edu>
Date: Wed, 15 Mar 2023 14:09:44 -0400
Subject: [PATCH] Upgrading drupal/core (9.5.3 => 9.5.5)

---
 composer.json                                 |   2 +-
 composer.lock                                 |  28 ++---
 scripts/tmux-parallel-push.sh                 |   2 +-
 vendor/composer/installed.json                |  30 ++---
 vendor/composer/installed.php                 |  66 +++++------
 web/core/MAINTAINERS.txt                      |   1 -
 web/core/core.api.php                         |  22 ++++
 web/core/includes/install.core.inc            |   2 +-
 web/core/lib/Drupal.php                       |   2 +-
 .../Component/Utility/DeprecatedArray.php     |   2 +
 .../Core/Asset/LibraryDiscoveryParser.php     |  12 ++
 .../Drupal/Core/Block/BlockPluginTrait.php    |   2 +-
 web/core/lib/Drupal/Core/DrupalKernel.php     |   9 +-
 .../lib/Drupal/Core/DrupalKernelInterface.php |   8 +-
 ...EntityDefinitionUpdateManagerInterface.php |   5 +-
 .../lib/Drupal/Core/Entity/EntityType.php     |  10 +-
 .../lib/Drupal/Core/Extension/module.api.php  |  19 ++--
 .../Drupal/Core/Mail/Plugin/Mail/PhpMail.php  |  45 ++++++--
 .../Core/ParamConverter/EntityConverter.php   |  11 +-
 .../Core/Routing/UrlGeneratorInterface.php    |   2 +
 web/core/misc/states.es6.js                   |   6 +-
 web/core/misc/states.js                       |   5 +-
 .../src/Controller/AggregatorController.php   |   2 +-
 .../Functional/AggregatorRenderingTest.php    |   2 +
 web/core/modules/book/book.views.inc          |   2 +-
 .../modules/book/src/BookManagerInterface.php |  16 ++-
 .../ckeditor5/src/SmartDefaultSettings.php    |  31 ++---
 .../src/Kernel/SmartDefaultSettingsTest.php   |   5 +-
 .../src/Entity/ContentModerationState.php     |   7 +-
 .../content_moderation_test_resave.info.yml   |   7 ++
 .../content_moderation_test_resave.install    |  15 +++
 .../content_moderation_test_resave.module     |  30 +++++
 .../Kernel/ContentModerationResaveTest.php    | 107 ++++++++++++++++++
 .../modules/contextual/js/contextual.es6.js   |   2 +-
 web/core/modules/contextual/js/contextual.js  |   2 +-
 .../ContextualLinksTest.php                   |  17 +++
 .../tests/src/Kernel/ContextualUnitTest.php   |  91 ++++++++-------
 .../src/Functional/Views/FilterDateTest.php   |  51 +++++++++
 web/core/modules/file/src/FileRepository.php  |   6 +-
 .../file/tests/src/Kernel/CopyTest.php        |   1 +
 .../tests/src/Kernel/FileRepositoryTest.php   |   1 +
 .../file/tests/src/Kernel/MoveTest.php        |   1 +
 .../tests/src/Functional/ResourceTestBase.php |  13 +--
 .../src/ConfigurableLanguageManager.php       |  16 +++
 .../src/Plugin/Block/LanguageBlock.php        |   3 +-
 .../LanguageNegotiationContentEntity.php      |   2 +-
 .../LanguageNegotiationSession.php            |   3 +-
 .../LanguageNegotiationUrl.php                |   3 +-
 .../layout_builder/layout_builder.module      |   2 +-
 .../layout_builder/src/Form/MoveBlockForm.php |   2 +
 .../MoveBlockFormTest.php                     |  42 +++++++
 .../tests/src/Functional/LinkFieldTest.php    |  26 ++++-
 .../MediaThumbnailFormatter.php               |   8 ++
 .../src/Plugin/migrate/source/MenuLink.php    |   6 +-
 .../Kernel/Migrate/d7/MigrateMenuLinkTest.php |   5 +
 .../Plugin/migrate/source/MenuLinkTest.php    |  31 +++++
 web/core/modules/node/node.api.php            |   7 +-
 .../responsive_image/responsive_image.module  |   4 +-
 .../src/Controller/SystemInfoController.php   |   2 +-
 .../system/src/Form/ModulesUninstallForm.php  |  18 ++-
 .../src/EntityTestAccessControlHandler.php    |   3 +
 .../src/Form/JavascriptStatesForm.php         |  61 ++++++++++
 .../src/Functional/Module/UninstallTest.php   |  14 +++
 .../system/tests/src/Kernel/Mail/MailTest.php |  21 +++-
 .../tests/src/Kernel/System/FloodTest.php     |  41 -------
 web/core/modules/tour/src/TourViewBuilder.php |   2 +-
 .../tour/tests/src/Functional/TourTest.php    |   4 +-
 .../user/config/schema/user.schema.yml        |   1 +
 web/core/modules/user/src/Entity/Role.php     |   6 -
 .../user/src/Plugin/views/field/Roles.php     |   2 +-
 .../Update/UserUpdateRoleDependenciesTest.php |   2 +-
 .../tests/src/Kernel/UserMailNotifyTest.php   |   2 +
 .../tests/src/Kernel/UserRoleEntityTest.php   |  12 ++
 .../tests/src/Kernel/Views/UserRoleTest.php   |  41 +++++++
 web/core/modules/user/user.module             |   4 +-
 web/core/modules/user/user.post_update.php    |  11 ++
 .../views/src/Plugin/views/PluginBase.php     |   2 +-
 .../views/display/DisplayPluginBase.php       |   2 +-
 .../src/Plugin/views/field/TimeInterval.php   |   5 +-
 .../Handler/FieldTimeIntervalTest.php         |  97 ++++++++++++++++
 .../PaginationAJAXTest.php                    |   2 +-
 .../workspaces/src/WorkspaceRepository.php    |   1 +
 .../tests/src/Kernel/WorkspaceCRUDTest.php    |   8 ++
 .../config/install/user.role.author.yml       |   8 ++
 .../config/install/user.role.editor.yml       |   8 ++
 .../umami/css/components/tour/tour.theme.css  |   1 +
 web/core/scripts/dev/commit-code-check.sh     |  14 ++-
 web/core/scripts/run-tests.sh                 |   7 +-
 .../Ajax/MultiFormTest.php                    |  17 +--
 .../Core/Form/JavascriptStatesTest.php        |  27 +++++
 .../FunctionalTests/AssertLegacyTrait.php     |   3 +-
 ...llerExistingConfigExistingSettingsTest.php |  40 +++++++
 .../Core/Action/EmailActionTest.php           |   2 +
 .../EntityConverterLatestRevisionTest.php     |  62 ++++++++++
 .../Drupal/Nightwatch/Tests/statesTest.js     |  24 ++++
 .../Tests/Core/Command/GenerateThemeTest.php  |  25 ++--
 .../Tests/Core/Flood/MemoryBackendTest.php    | 106 +++++++++++++++++
 .../Core/Mail/Plugin/Mail/PhpMailTest.php     |  96 ++++++++++++++++
 web/core/themes/claro/claro.theme             |   4 +-
 99 files changed, 1372 insertions(+), 296 deletions(-)
 create mode 100644 web/core/modules/content_moderation/tests/modules/content_moderation_test_resave/content_moderation_test_resave.info.yml
 create mode 100644 web/core/modules/content_moderation/tests/modules/content_moderation_test_resave/content_moderation_test_resave.install
 create mode 100644 web/core/modules/content_moderation/tests/modules/content_moderation_test_resave/content_moderation_test_resave.module
 create mode 100644 web/core/modules/content_moderation/tests/src/Kernel/ContentModerationResaveTest.php
 create mode 100644 web/core/modules/user/tests/src/Kernel/Views/UserRoleTest.php
 create mode 100644 web/core/modules/views/tests/src/Functional/Handler/FieldTimeIntervalTest.php
 create mode 100644 web/core/tests/Drupal/FunctionalTests/Installer/InstallerExistingConfigExistingSettingsTest.php
 create mode 100644 web/core/tests/Drupal/Tests/Core/Flood/MemoryBackendTest.php
 create mode 100644 web/core/tests/Drupal/Tests/Core/Mail/Plugin/Mail/PhpMailTest.php

diff --git a/composer.json b/composer.json
index da32471557..1d2f895d81 100644
--- a/composer.json
+++ b/composer.json
@@ -107,7 +107,7 @@
         "drupal/console": "1.9.7",
         "drupal/content_access": "1.0-alpha3",
         "drupal/core-composer-scaffold": "^9.0",
-        "drupal/core-recommended": "9.5.3",
+        "drupal/core-recommended": "9.5.5",
         "drupal/crop": "2.3",
         "drupal/ctools": "3.13",
         "drupal/dropzonejs": "2.7",
diff --git a/composer.lock b/composer.lock
index 73bbde26a6..f9ca9a4d82 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
         "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
         "This file is @generated automatically"
     ],
-    "content-hash": "87628b41be55de273fcabd1a1179e831",
+    "content-hash": "34d6f8d40b4e8320b4d8293429038bea",
     "packages": [
         {
             "name": "alchemy/zippy",
@@ -3014,16 +3014,16 @@
         },
         {
             "name": "drupal/core",
-            "version": "9.5.3",
+            "version": "9.5.5",
             "source": {
                 "type": "git",
                 "url": "https://github.com/drupal/core.git",
-                "reference": "67e34a5e8f48cafdd5c26e778a9570860e2d44a5"
+                "reference": "eae5e76a8b403cbd42b3465f567313b52d78b0dc"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/drupal/core/zipball/67e34a5e8f48cafdd5c26e778a9570860e2d44a5",
-                "reference": "67e34a5e8f48cafdd5c26e778a9570860e2d44a5",
+                "url": "https://api.github.com/repos/drupal/core/zipball/eae5e76a8b403cbd42b3465f567313b52d78b0dc",
+                "reference": "eae5e76a8b403cbd42b3465f567313b52d78b0dc",
                 "shasum": ""
             },
             "require": {
@@ -3175,9 +3175,9 @@
             ],
             "description": "Drupal is an open source content management platform powering millions of websites and applications.",
             "support": {
-                "source": "https://github.com/drupal/core/tree/9.5.3"
+                "source": "https://github.com/drupal/core/tree/9.5.5"
             },
-            "time": "2023-02-01T19:47:31+00:00"
+            "time": "2023-03-15T14:30:25+00:00"
         },
         {
             "name": "drupal/core-composer-scaffold",
@@ -3231,16 +3231,16 @@
         },
         {
             "name": "drupal/core-recommended",
-            "version": "9.5.3",
+            "version": "9.5.5",
             "source": {
                 "type": "git",
                 "url": "https://github.com/drupal/core-recommended.git",
-                "reference": "3dc8d9757c6c4a0457d32dd58a755532504ad959"
+                "reference": "3c1d205349407e706cc89f56aa34448742fe81b4"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/drupal/core-recommended/zipball/3dc8d9757c6c4a0457d32dd58a755532504ad959",
-                "reference": "3dc8d9757c6c4a0457d32dd58a755532504ad959",
+                "url": "https://api.github.com/repos/drupal/core-recommended/zipball/3c1d205349407e706cc89f56aa34448742fe81b4",
+                "reference": "3c1d205349407e706cc89f56aa34448742fe81b4",
                 "shasum": ""
             },
             "require": {
@@ -3249,7 +3249,7 @@
                 "doctrine/annotations": "~1.13.3",
                 "doctrine/lexer": "~1.2.3",
                 "doctrine/reflection": "~1.2.3",
-                "drupal/core": "9.5.3",
+                "drupal/core": "9.5.5",
                 "egulias/email-validator": "~3.2.1",
                 "guzzlehttp/guzzle": "~6.5.8",
                 "guzzlehttp/promises": "~1.5.2",
@@ -3311,9 +3311,9 @@
             ],
             "description": "Core and its dependencies with known-compatible minor versions. Require this project INSTEAD OF drupal/core.",
             "support": {
-                "source": "https://github.com/drupal/core-recommended/tree/9.5.3"
+                "source": "https://github.com/drupal/core-recommended/tree/9.5.5"
             },
-            "time": "2023-02-01T19:47:31+00:00"
+            "time": "2023-03-15T14:30:25+00:00"
         },
         {
             "name": "drupal/crop",
diff --git a/scripts/tmux-parallel-push.sh b/scripts/tmux-parallel-push.sh
index df7193fb7e..a89b1781c9 100755
--- a/scripts/tmux-parallel-push.sh
+++ b/scripts/tmux-parallel-push.sh
@@ -40,7 +40,7 @@
 
 terminus org:site:list ohio-state-arts-and-sciences --upstream=5161e51a-0e4a-414c-974e-8565094b76b1 --tag=D8 --fields=name --format=string | sort | tee $LOG_DIR/site_list.txt;
 
-parallel --delay 0.2 --tmuxpane --fg -j 36 -a $LOG_DIR/site_list.txt "scripts/deploy-site-env.sh {}.$ENV $DEPLOY_MSG 2>&1 | tee $LOG_DIR/{}.log";
+parallel --delay 0.2 -j 36 -a $LOG_DIR/site_list.txt "scripts/deploy-site-env.sh {}.$ENV $DEPLOY_MSG 2>&1 | tee $LOG_DIR/{}.log";
 
 echo; echo; echo "Sleeping for 11 seconds..."; echo; echo;
 sleep 11;
diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json
index d3ae0b61ec..7a4aaa4c7f 100644
--- a/vendor/composer/installed.json
+++ b/vendor/composer/installed.json
@@ -3117,17 +3117,17 @@
         },
         {
             "name": "drupal/core",
-            "version": "9.5.3",
-            "version_normalized": "9.5.3.0",
+            "version": "9.5.5",
+            "version_normalized": "9.5.5.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/drupal/core.git",
-                "reference": "67e34a5e8f48cafdd5c26e778a9570860e2d44a5"
+                "reference": "eae5e76a8b403cbd42b3465f567313b52d78b0dc"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/drupal/core/zipball/67e34a5e8f48cafdd5c26e778a9570860e2d44a5",
-                "reference": "67e34a5e8f48cafdd5c26e778a9570860e2d44a5",
+                "url": "https://api.github.com/repos/drupal/core/zipball/eae5e76a8b403cbd42b3465f567313b52d78b0dc",
+                "reference": "eae5e76a8b403cbd42b3465f567313b52d78b0dc",
                 "shasum": ""
             },
             "require": {
@@ -3207,7 +3207,7 @@
                 "drupal/core-uuid": "self.version",
                 "drupal/core-version": "self.version"
             },
-            "time": "2023-02-01T19:47:31+00:00",
+            "time": "2023-03-15T14:30:25+00:00",
             "type": "drupal-core",
             "extra": {
                 "drupal-scaffold": {
@@ -3286,7 +3286,7 @@
             ],
             "description": "Drupal is an open source content management platform powering millions of websites and applications.",
             "support": {
-                "source": "https://github.com/drupal/core/tree/9.5.3"
+                "source": "https://github.com/drupal/core/tree/9.5.5"
             },
             "install-path": "../../web/core"
         },
@@ -3342,17 +3342,17 @@
         },
         {
             "name": "drupal/core-recommended",
-            "version": "9.5.3",
-            "version_normalized": "9.5.3.0",
+            "version": "9.5.5",
+            "version_normalized": "9.5.5.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/drupal/core-recommended.git",
-                "reference": "3dc8d9757c6c4a0457d32dd58a755532504ad959"
+                "reference": "3c1d205349407e706cc89f56aa34448742fe81b4"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/drupal/core-recommended/zipball/3dc8d9757c6c4a0457d32dd58a755532504ad959",
-                "reference": "3dc8d9757c6c4a0457d32dd58a755532504ad959",
+                "url": "https://api.github.com/repos/drupal/core-recommended/zipball/3c1d205349407e706cc89f56aa34448742fe81b4",
+                "reference": "3c1d205349407e706cc89f56aa34448742fe81b4",
                 "shasum": ""
             },
             "require": {
@@ -3361,7 +3361,7 @@
                 "doctrine/annotations": "~1.13.3",
                 "doctrine/lexer": "~1.2.3",
                 "doctrine/reflection": "~1.2.3",
-                "drupal/core": "9.5.3",
+                "drupal/core": "9.5.5",
                 "egulias/email-validator": "~3.2.1",
                 "guzzlehttp/guzzle": "~6.5.8",
                 "guzzlehttp/promises": "~1.5.2",
@@ -3416,7 +3416,7 @@
             "conflict": {
                 "webflo/drupal-core-strict": "*"
             },
-            "time": "2023-02-01T19:47:31+00:00",
+            "time": "2023-03-15T14:30:25+00:00",
             "type": "metapackage",
             "notification-url": "https://packagist.org/downloads/",
             "license": [
@@ -3424,7 +3424,7 @@
             ],
             "description": "Core and its dependencies with known-compatible minor versions. Require this project INSTEAD OF drupal/core.",
             "support": {
-                "source": "https://github.com/drupal/core-recommended/tree/9.5.3"
+                "source": "https://github.com/drupal/core-recommended/tree/9.5.5"
             },
             "install-path": null
         },
diff --git a/vendor/composer/installed.php b/vendor/composer/installed.php
index 5296a79fcf..ac30584a02 100644
--- a/vendor/composer/installed.php
+++ b/vendor/composer/installed.php
@@ -3,7 +3,7 @@
         'name' => 'osu-asc-webservices/d8-upstream',
         'pretty_version' => 'dev-master',
         'version' => 'dev-master',
-        'reference' => '0a423db0646b509ef70824e7c1183e79aa41052b',
+        'reference' => '1c4ba03b2165c82f1bbded8bf2f748a63f1121f7',
         'type' => 'project',
         'install_path' => __DIR__ . '/../../',
         'aliases' => array(),
@@ -476,9 +476,9 @@
             'dev_requirement' => false,
         ),
         'drupal/core' => array(
-            'pretty_version' => '9.5.3',
-            'version' => '9.5.3.0',
-            'reference' => '67e34a5e8f48cafdd5c26e778a9570860e2d44a5',
+            'pretty_version' => '9.5.5',
+            'version' => '9.5.5.0',
+            'reference' => 'eae5e76a8b403cbd42b3465f567313b52d78b0dc',
             'type' => 'drupal-core',
             'install_path' => __DIR__ . '/../../web/core',
             'aliases' => array(),
@@ -487,25 +487,25 @@
         'drupal/core-annotation' => array(
             'dev_requirement' => false,
             'replaced' => array(
-                0 => '9.5.3',
+                0 => '9.5.5',
             ),
         ),
         'drupal/core-assertion' => array(
             'dev_requirement' => false,
             'replaced' => array(
-                0 => '9.5.3',
+                0 => '9.5.5',
             ),
         ),
         'drupal/core-bridge' => array(
             'dev_requirement' => false,
             'replaced' => array(
-                0 => '9.5.3',
+                0 => '9.5.5',
             ),
         ),
         'drupal/core-class-finder' => array(
             'dev_requirement' => false,
             'replaced' => array(
-                0 => '9.5.3',
+                0 => '9.5.5',
             ),
         ),
         'drupal/core-composer-scaffold' => array(
@@ -520,97 +520,97 @@
         'drupal/core-datetime' => array(
             'dev_requirement' => false,
             'replaced' => array(
-                0 => '9.5.3',
+                0 => '9.5.5',
             ),
         ),
         'drupal/core-dependency-injection' => array(
             'dev_requirement' => false,
             'replaced' => array(
-                0 => '9.5.3',
+                0 => '9.5.5',
             ),
         ),
         'drupal/core-diff' => array(
             'dev_requirement' => false,
             'replaced' => array(
-                0 => '9.5.3',
+                0 => '9.5.5',
             ),
         ),
         'drupal/core-discovery' => array(
             'dev_requirement' => false,
             'replaced' => array(
-                0 => '9.5.3',
+                0 => '9.5.5',
             ),
         ),
         'drupal/core-event-dispatcher' => array(
             'dev_requirement' => false,
             'replaced' => array(
-                0 => '9.5.3',
+                0 => '9.5.5',
             ),
         ),
         'drupal/core-file-cache' => array(
             'dev_requirement' => false,
             'replaced' => array(
-                0 => '9.5.3',
+                0 => '9.5.5',
             ),
         ),
         'drupal/core-file-security' => array(
             'dev_requirement' => false,
             'replaced' => array(
-                0 => '9.5.3',
+                0 => '9.5.5',
             ),
         ),
         'drupal/core-filesystem' => array(
             'dev_requirement' => false,
             'replaced' => array(
-                0 => '9.5.3',
+                0 => '9.5.5',
             ),
         ),
         'drupal/core-front-matter' => array(
             'dev_requirement' => false,
             'replaced' => array(
-                0 => '9.5.3',
+                0 => '9.5.5',
             ),
         ),
         'drupal/core-gettext' => array(
             'dev_requirement' => false,
             'replaced' => array(
-                0 => '9.5.3',
+                0 => '9.5.5',
             ),
         ),
         'drupal/core-graph' => array(
             'dev_requirement' => false,
             'replaced' => array(
-                0 => '9.5.3',
+                0 => '9.5.5',
             ),
         ),
         'drupal/core-http-foundation' => array(
             'dev_requirement' => false,
             'replaced' => array(
-                0 => '9.5.3',
+                0 => '9.5.5',
             ),
         ),
         'drupal/core-php-storage' => array(
             'dev_requirement' => false,
             'replaced' => array(
-                0 => '9.5.3',
+                0 => '9.5.5',
             ),
         ),
         'drupal/core-plugin' => array(
             'dev_requirement' => false,
             'replaced' => array(
-                0 => '9.5.3',
+                0 => '9.5.5',
             ),
         ),
         'drupal/core-proxy-builder' => array(
             'dev_requirement' => false,
             'replaced' => array(
-                0 => '9.5.3',
+                0 => '9.5.5',
             ),
         ),
         'drupal/core-recommended' => array(
-            'pretty_version' => '9.5.3',
-            'version' => '9.5.3.0',
-            'reference' => '3dc8d9757c6c4a0457d32dd58a755532504ad959',
+            'pretty_version' => '9.5.5',
+            'version' => '9.5.5.0',
+            'reference' => '3c1d205349407e706cc89f56aa34448742fe81b4',
             'type' => 'metapackage',
             'install_path' => NULL,
             'aliases' => array(),
@@ -619,37 +619,37 @@
         'drupal/core-render' => array(
             'dev_requirement' => false,
             'replaced' => array(
-                0 => '9.5.3',
+                0 => '9.5.5',
             ),
         ),
         'drupal/core-serialization' => array(
             'dev_requirement' => false,
             'replaced' => array(
-                0 => '9.5.3',
+                0 => '9.5.5',
             ),
         ),
         'drupal/core-transliteration' => array(
             'dev_requirement' => false,
             'replaced' => array(
-                0 => '9.5.3',
+                0 => '9.5.5',
             ),
         ),
         'drupal/core-utility' => array(
             'dev_requirement' => false,
             'replaced' => array(
-                0 => '9.5.3',
+                0 => '9.5.5',
             ),
         ),
         'drupal/core-uuid' => array(
             'dev_requirement' => false,
             'replaced' => array(
-                0 => '9.5.3',
+                0 => '9.5.5',
             ),
         ),
         'drupal/core-version' => array(
             'dev_requirement' => false,
             'replaced' => array(
-                0 => '9.5.3',
+                0 => '9.5.5',
             ),
         ),
         'drupal/crop' => array(
@@ -1588,7 +1588,7 @@
         'osu-asc-webservices/d8-upstream' => array(
             'pretty_version' => 'dev-master',
             'version' => 'dev-master',
-            'reference' => '0a423db0646b509ef70824e7c1183e79aa41052b',
+            'reference' => '1c4ba03b2165c82f1bbded8bf2f748a63f1121f7',
             'type' => 'project',
             'install_path' => __DIR__ . '/../../',
             'aliases' => array(),
diff --git a/web/core/MAINTAINERS.txt b/web/core/MAINTAINERS.txt
index 717ed7e502..3aaa40b15a 100644
--- a/web/core/MAINTAINERS.txt
+++ b/web/core/MAINTAINERS.txt
@@ -143,7 +143,6 @@ Comment
 
 Configuration API
 - Alex Pott 'alexpott' https://www.drupal.org/u/alexpott
-- Matthew Tift 'mtift' https://www.drupal.org/u/mtift
 
 Configuration Entity API
 - Alex Pott 'alexpott' https://www.drupal.org/u/alexpott
diff --git a/web/core/core.api.php b/web/core/core.api.php
index 312af5e12c..d42b5c776f 100644
--- a/web/core/core.api.php
+++ b/web/core/core.api.php
@@ -2448,6 +2448,28 @@ function hook_validation_constraint_alter(array &$definitions) {
  *   autocomplete, as a \Symfony\Component\HttpFoundation\JsonResponse object.
  *   See the @link menu Routing topic @endlink for more information about
  *   routing.
+ *
+ * @section sec_query Query parameters in Ajax requests
+ * If a form uses an Ajax field, all the query parameters in the current request
+ * will be also added to the Ajax POST requests along with an additional
+ * 'ajax_form=1' parameter (See \Drupal\Core\Render\Element\RenderElement).
+ * @code
+ * $settings['options']['query'] += \Drupal::request()->query->all();
+ * $settings['options']['query'][FormBuilderInterface::AJAX_FORM_REQUEST] = TRUE;
+ * @endcode
+ *
+ * Form elements of type 'managed_file' will have an additional
+ * 'element_parents' query parameter in Ajax POST requests. This parameter will
+ * include the name of the element and its parents as per the render array.
+ * This helps to identify the position of the element in the form (See
+ * \Drupal\file\Element\ManagedFile).
+ * @code
+ * 'options' => [
+ *   'query' => [
+ *     'element_parents' => implode('/', $element['#array_parents']),
+ *   ],
+ * ],
+ * @endcode
  */
 
 /**
diff --git a/web/core/includes/install.core.inc b/web/core/includes/install.core.inc
index b4956350ab..fcbf23c6d6 100644
--- a/web/core/includes/install.core.inc
+++ b/web/core/includes/install.core.inc
@@ -388,7 +388,7 @@ function install_begin_request($class_loader, &$install_state) {
   $install_state['database_verified'] = install_verify_database_settings($site_path);
   // A valid settings.php has database settings and a hash_salt value. Other
   // settings will be checked by system_requirements().
-  $install_state['settings_verified'] = $install_state['database_verified'] && (bool) Settings::get('hash_salt', FALSE);
+  $install_state['settings_verified'] = $install_state['config_verified'] && $install_state['database_verified'] && (bool) Settings::get('hash_salt', FALSE);
 
   if ($install_state['settings_verified']) {
     try {
diff --git a/web/core/lib/Drupal.php b/web/core/lib/Drupal.php
index f327e72f11..91d7cfd1e6 100644
--- a/web/core/lib/Drupal.php
+++ b/web/core/lib/Drupal.php
@@ -75,7 +75,7 @@ class Drupal {
   /**
    * The current system version.
    */
-  const VERSION = '9.5.3';
+  const VERSION = '9.5.5';
 
   /**
    * Core API compatibility.
diff --git a/web/core/lib/Drupal/Component/Utility/DeprecatedArray.php b/web/core/lib/Drupal/Component/Utility/DeprecatedArray.php
index 63b5b7f133..f3ef310ce4 100644
--- a/web/core/lib/Drupal/Component/Utility/DeprecatedArray.php
+++ b/web/core/lib/Drupal/Component/Utility/DeprecatedArray.php
@@ -75,6 +75,7 @@ public function getIterator() {
   /**
    * {@inheritdoc}
    */
+  #[\ReturnTypeWillChange]
   public function unserialize($serialized) {
     @trigger_error($this->message, E_USER_DEPRECATED);
     parent::unserialize($serialized);
@@ -83,6 +84,7 @@ public function unserialize($serialized) {
   /**
    * {@inheritdoc}
    */
+  #[\ReturnTypeWillChange]
   public function serialize() {
     @trigger_error($this->message, E_USER_DEPRECATED);
     return parent::serialize();
diff --git a/web/core/lib/Drupal/Core/Asset/LibraryDiscoveryParser.php b/web/core/lib/Drupal/Core/Asset/LibraryDiscoveryParser.php
index 09bdc01d3d..9ab9a1dde1 100644
--- a/web/core/lib/Drupal/Core/Asset/LibraryDiscoveryParser.php
+++ b/web/core/lib/Drupal/Core/Asset/LibraryDiscoveryParser.php
@@ -107,6 +107,18 @@ public function __construct($root, ModuleHandlerInterface $module_handler, Theme
    *   Thrown when a library has no js/css/setting.
    * @throws \UnexpectedValueException
    *   Thrown when a js file defines a positive weight.
+   * @throws \UnknownExtensionTypeException
+   *   Thrown when the extension type is unknown.
+   * @throws \UnknownExtensionException
+   *   Thrown when the extension is unknown.
+   * @throws \InvalidLibraryFileException
+   *   Thrown when the library file is invalid.
+   * @throws \InvalidLibrariesOverrideSpecificationException
+   *   Thrown when a definition refers to a non-existent library.
+   * @throws \Drupal\Core\Asset\Exception\LibraryDefinitionMissingLicenseException
+   *   Thrown when a library definition has no license information.
+   * @throws \LogicException
+   *   Thrown when a header key in a library definition is invalid.
    */
   public function buildByExtension($extension) {
     if ($extension === 'core') {
diff --git a/web/core/lib/Drupal/Core/Block/BlockPluginTrait.php b/web/core/lib/Drupal/Core/Block/BlockPluginTrait.php
index 401ab2bb15..acb850ed01 100644
--- a/web/core/lib/Drupal/Core/Block/BlockPluginTrait.php
+++ b/web/core/lib/Drupal/Core/Block/BlockPluginTrait.php
@@ -138,7 +138,7 @@ public function access(AccountInterface $account, $return_as_object = FALSE) {
    * @param \Drupal\Core\Session\AccountInterface $account
    *   The user session for which to check access.
    *
-   * @return \Drupal\Core\Access\AccessResult
+   * @return \Drupal\Core\Access\AccessResultInterface
    *   The access result.
    *
    * @see self::access()
diff --git a/web/core/lib/Drupal/Core/DrupalKernel.php b/web/core/lib/Drupal/Core/DrupalKernel.php
index c70ccdc61b..98a1969e39 100644
--- a/web/core/lib/Drupal/Core/DrupalKernel.php
+++ b/web/core/lib/Drupal/Core/DrupalKernel.php
@@ -230,7 +230,12 @@ class DrupalKernel implements DrupalKernelInterface, TerminableInterface {
   protected static $isEnvironmentInitialized = FALSE;
 
   /**
-   * The site directory.
+   * The site path directory.
+   *
+   * Site path is relative to the app root directory.
+   * Usually defined as "sites/default".
+   *
+   * By default, Drupal uses sites/default.
    *
    * @var string
    */
@@ -1584,7 +1589,7 @@ public static function validateHostname(Request $request) {
    * @see \Drupal\Core\Http\TrustedHostsRequestFactory
    */
   protected static function setupTrustedHosts(Request $request, $host_patterns) {
-    $request->setTrustedHosts($host_patterns);
+    Request::setTrustedHosts($host_patterns);
 
     // Get the host, which will validate the current request.
     try {
diff --git a/web/core/lib/Drupal/Core/DrupalKernelInterface.php b/web/core/lib/Drupal/Core/DrupalKernelInterface.php
index dfd00f2017..f42e8eec6f 100644
--- a/web/core/lib/Drupal/Core/DrupalKernelInterface.php
+++ b/web/core/lib/Drupal/Core/DrupalKernelInterface.php
@@ -74,7 +74,9 @@ public function getContainer();
   public function getCachedContainerDefinition();
 
   /**
-   * Set the current site path.
+   * Set the current site path directory.
+   *
+   * Format: "folder-name/child-folder" usually uses "sites/default".
    *
    * @param string $path
    *   The current site path.
@@ -85,10 +87,10 @@ public function getCachedContainerDefinition();
   public function setSitePath($path);
 
   /**
-   * Get the site path.
+   * Gets the site path directory.
    *
    * @return string
-   *   The current site path.
+   *   The current site path directory.
    */
   public function getSitePath();
 
diff --git a/web/core/lib/Drupal/Core/Entity/EntityDefinitionUpdateManagerInterface.php b/web/core/lib/Drupal/Core/Entity/EntityDefinitionUpdateManagerInterface.php
index 0a6dc5d6b2..edae57c9e4 100644
--- a/web/core/lib/Drupal/Core/Entity/EntityDefinitionUpdateManagerInterface.php
+++ b/web/core/lib/Drupal/Core/Entity/EntityDefinitionUpdateManagerInterface.php
@@ -184,8 +184,9 @@ public function updateFieldableEntityType(EntityTypeInterface $entity_type, arra
    * @param string $entity_type_id
    *   The entity type identifier.
    *
-   * @return \Drupal\Core\Field\FieldStorageDefinitionInterface
-   *   The field storage definition.
+   * @return \Drupal\Core\Field\FieldStorageDefinitionInterface|null
+   *   The field storage definition or NULL if there is none for the given field
+   *   name and entity type.
    *
    * @todo Make this return a mutable storage definition interface when we have
    *   one. See https://www.drupal.org/node/2346329.
diff --git a/web/core/lib/Drupal/Core/Entity/EntityType.php b/web/core/lib/Drupal/Core/Entity/EntityType.php
index 6fbbbe950d..13fc14775e 100644
--- a/web/core/lib/Drupal/Core/Entity/EntityType.php
+++ b/web/core/lib/Drupal/Core/Entity/EntityType.php
@@ -200,9 +200,15 @@ class EntityType extends PluginDefinition implements EntityTypeInterface {
   /**
    * A definite singular/plural name of the type.
    *
-   * Needed keys: "singular" and "plural".
+   * Needed keys: "singular" and "plural". Can also have key: "context".
+   * @code
+   * [
+   *    'singular' => '@count entity',
+   *    'plural' => '@count entities',
+   *    'context' => 'Entity context',
+   * ]
    *
-   * @var string|\Drupal\Core\StringTranslation\TranslatableMarkup
+   * @var string[]
    *
    * @see \Drupal\Core\Entity\EntityTypeInterface::getCountLabel()
    */
diff --git a/web/core/lib/Drupal/Core/Extension/module.api.php b/web/core/lib/Drupal/Core/Extension/module.api.php
index f19b5ca667..9cd4a03366 100644
--- a/web/core/lib/Drupal/Core/Extension/module.api.php
+++ b/web/core/lib/Drupal/Core/Extension/module.api.php
@@ -183,7 +183,9 @@ function hook_module_preinstall($module) {
  *   TRUE if the module is being installed as part of a configuration import. In
  *   these cases, your hook implementation needs to carefully consider what
  *   changes, if any, it should make. For example, it should not make any
- *   changes to configuration objects or entities.
+ *   changes to configuration objects or configuration entities. Those changes
+ *   should be made earlier and exported so during import there's no need to
+ *   do them again.
  *
  * @see \Drupal\Core\Extension\ModuleInstaller::install()
  * @see hook_install()
@@ -230,8 +232,9 @@ function hook_modules_installed($modules, $is_syncing) {
  * @param bool $is_syncing
  *   TRUE if the module is being installed as part of a configuration import. In
  *   these cases, your hook implementation needs to carefully consider what
- *   changes, if any, it should make. For example, it should not make any
- *   changes to configuration objects or entities.
+ *   changes to configuration objects or configuration entities. Those changes
+ *   should be made earlier and exported so during import there's no need to
+ *   do them again.
  *
  * @see \Drupal\Core\Config\ConfigInstallerInterface::isSyncing
  * @see hook_schema()
@@ -269,8 +272,9 @@ function hook_module_preuninstall($module) {
  * @param bool $is_syncing
  *   TRUE if the module is being uninstalled as part of a configuration import.
  *   In these cases, your hook implementation needs to carefully consider what
- *   changes, if any, it should make. For example, it should not make any
- *   changes to configuration objects or entities.
+ *   changes to configuration objects or configuration entities. Those changes
+ *   should be made earlier and exported so during import there's no need to
+ *   do them again.
  *
  * @see hook_uninstall()
  */
@@ -307,8 +311,9 @@ function hook_modules_uninstalled($modules, $is_syncing) {
  * @param bool $is_syncing
  *   TRUE if the module is being uninstalled as part of a configuration import.
  *   In these cases, your hook implementation needs to carefully consider what
- *   changes, if any, it should make. For example, it should not make any
- *   changes to configuration objects or entities.
+ *   changes to configuration objects or configuration entities. Those changes
+ *   should be made earlier and exported so during import there's no need to
+ *   do them again.
  *
  * @see hook_install()
  * @see hook_schema()
diff --git a/web/core/lib/Drupal/Core/Mail/Plugin/Mail/PhpMail.php b/web/core/lib/Drupal/Core/Mail/Plugin/Mail/PhpMail.php
index 138257b8da..cae954c49e 100644
--- a/web/core/lib/Drupal/Core/Mail/Plugin/Mail/PhpMail.php
+++ b/web/core/lib/Drupal/Core/Mail/Plugin/Mail/PhpMail.php
@@ -87,7 +87,9 @@ public function mail(array $message) {
     $headers = new Headers();
     foreach ($message['headers'] as $name => $value) {
       if (in_array(strtolower($name), self::MAILBOX_LIST_HEADERS, TRUE)) {
-        $value = explode(',', $value);
+        // Split values by comma, but ignore commas encapsulated in double
+        // quotes.
+        $value = str_getcsv($value, ',');
       }
       $headers->addHeader($name, $value);
     }
@@ -104,12 +106,7 @@ public function mail(array $message) {
     $mail_headers = str_replace("\r\n", "\n", $headers->toString());
     $mail_subject = str_replace("\r\n", "\n", $mail_subject);
 
-    $request = \Drupal::request();
-
-    // We suppress warnings and notices from mail() because of issues on some
-    // hosts. The return value of this method will still indicate whether mail
-    // was sent successfully.
-    if (!$request->server->has('WINDIR') && strpos($request->server->get('SERVER_SOFTWARE'), 'Win32') === FALSE) {
+    if (substr(PHP_OS, 0, 3) != 'WIN') {
       // On most non-Windows systems, the "-f" option to the sendmail command
       // is used to set the Return-Path. There is no space between -f and
       // the value of the return path.
@@ -117,7 +114,7 @@ public function mail(array $message) {
       // we assume to be safe.
       $site_mail = $this->configFactory->get('system.site')->get('mail');
       $additional_headers = isset($message['Return-Path']) && ($site_mail === $message['Return-Path'] || static::_isShellSafe($message['Return-Path'])) ? '-f' . $message['Return-Path'] : '';
-      $mail_result = @mail(
+      $mail_result = $this->doMail(
         $message['to'],
         $mail_subject,
         $mail_body,
@@ -130,7 +127,7 @@ public function mail(array $message) {
       // Return-Path header.
       $old_from = ini_get('sendmail_from');
       ini_set('sendmail_from', $message['Return-Path']);
-      $mail_result = @mail(
+      $mail_result = $this->doMail(
         $message['to'],
         $mail_subject,
         $mail_body,
@@ -142,6 +139,36 @@ public function mail(array $message) {
     return $mail_result;
   }
 
+  /**
+   * Wrapper around PHP's mail() function.
+   *
+   * We suppress warnings and notices from mail() because of issues on some
+   * hosts. The return value of this method will still indicate whether mail was
+   * sent successfully.
+   *
+   * @param string $to
+   *   Receiver, or receivers of the mail.
+   * @param string $subject
+   *   Subject of the email to be sent.
+   * @param string $message
+   *   Message to be sent.
+   * @param array $additional_headers
+   *   (optional) Array to be inserted at the end of the email header.
+   * @param string $additional_params
+   *   (optional) Can be used to pass additional flags as command line options.
+   *
+   * @see mail()
+   */
+  protected function doMail(string $to, string $subject, string $message, $additional_headers = [], string $additional_params = ''): bool {
+    return @mail(
+      $to,
+      $subject,
+      $message,
+      $additional_headers,
+      $additional_params
+    );
+  }
+
   /**
    * Disallows potentially unsafe shell characters.
    *
diff --git a/web/core/lib/Drupal/Core/ParamConverter/EntityConverter.php b/web/core/lib/Drupal/Core/ParamConverter/EntityConverter.php
index 823dcdaa1e..c3820baaca 100644
--- a/web/core/lib/Drupal/Core/ParamConverter/EntityConverter.php
+++ b/web/core/lib/Drupal/Core/ParamConverter/EntityConverter.php
@@ -120,7 +120,16 @@ public function convert($value, $definition, $name, array $defaults) {
     // If the entity type is revisionable and the parameter has the
     // "load_latest_revision" flag, load the active variant.
     if (!empty($definition['load_latest_revision'])) {
-      return $this->entityRepository->getActive($entity_type_id, $value);
+      $entity = $this->entityRepository->getActive($entity_type_id, $value);
+
+      if (
+        !empty($definition['bundle']) &&
+        $entity instanceof EntityInterface &&
+        !in_array($entity->bundle(), $definition['bundle'], TRUE)
+      ) {
+        return NULL;
+      }
+      return $entity;
     }
 
     // Do not inject the context repository as it is not an actual dependency:
diff --git a/web/core/lib/Drupal/Core/Routing/UrlGeneratorInterface.php b/web/core/lib/Drupal/Core/Routing/UrlGeneratorInterface.php
index 891b035413..a69ffb39a1 100644
--- a/web/core/lib/Drupal/Core/Routing/UrlGeneratorInterface.php
+++ b/web/core/lib/Drupal/Core/Routing/UrlGeneratorInterface.php
@@ -53,6 +53,8 @@ public function getPathFromRoute($name, $parameters = []);
    *   - 'https': Whether this URL should point to a secure location. If not
    *     defined, the current scheme is used, so the user stays on HTTP or HTTPS
    *     respectively. TRUE enforces HTTPS and FALSE enforces HTTP.
+   *   - 'path_processing': Defaults to TRUE. Whether to pass the path to a
+   *     processor manager to allow alterations.
    *   - 'base_url': Only used internally by a path processor, for example, to
    *     modify the base URL when a language dependent URL requires so.
    *   - 'prefix': Only used internally, to modify the path when a language
diff --git a/web/core/misc/states.es6.js b/web/core/misc/states.es6.js
index b45e1e0504..0a9071ef5c 100644
--- a/web/core/misc/states.es6.js
+++ b/web/core/misc/states.es6.js
@@ -526,6 +526,10 @@
         // the state.
         return this.val() === '';
       },
+      // Listen to 'change' for number native "spinner" widgets.
+      change() {
+        return this.val() === '';
+      },
     },
 
     checked: {
@@ -722,7 +726,7 @@
 
   $document.on('state:checked', (e) => {
     if (e.trigger) {
-      $(e.target).prop('checked', e.value);
+      $(e.target).prop('checked', e.value).trigger('change');
     }
   });
 
diff --git a/web/core/misc/states.js b/web/core/misc/states.js
index e827a7baa8..d42bb2cd1d 100644
--- a/web/core/misc/states.js
+++ b/web/core/misc/states.js
@@ -217,6 +217,9 @@
     empty: {
       keyup: function keyup() {
         return this.val() === '';
+      },
+      change: function change() {
+        return this.val() === '';
       }
     },
     checked: {
@@ -320,7 +323,7 @@
   });
   $document.on('state:checked', function (e) {
     if (e.trigger) {
-      $(e.target).prop('checked', e.value);
+      $(e.target).prop('checked', e.value).trigger('change');
     }
   });
   $document.on('state:collapsed', function (e) {
diff --git a/web/core/modules/aggregator/src/Controller/AggregatorController.php b/web/core/modules/aggregator/src/Controller/AggregatorController.php
index 0d2a329a9e..118a107c29 100644
--- a/web/core/modules/aggregator/src/Controller/AggregatorController.php
+++ b/web/core/modules/aggregator/src/Controller/AggregatorController.php
@@ -73,8 +73,8 @@ protected function buildPageList(array $items, $feed_source = '') {
     if ($items) {
       $build['items'] = $this->entityTypeManager()->getViewBuilder('aggregator_item')
         ->viewMultiple($items, 'default');
-      $build['pager'] = ['#type' => 'pager'];
     }
+    $build['pager'] = ['#type' => 'pager'];
     return $build;
   }
 
diff --git a/web/core/modules/aggregator/tests/src/Functional/AggregatorRenderingTest.php b/web/core/modules/aggregator/tests/src/Functional/AggregatorRenderingTest.php
index ca27af1d56..7ea19b9858 100644
--- a/web/core/modules/aggregator/tests/src/Functional/AggregatorRenderingTest.php
+++ b/web/core/modules/aggregator/tests/src/Functional/AggregatorRenderingTest.php
@@ -103,6 +103,8 @@ public function testFeedPage() {
     $feed = $this->createFeed();
     $this->updateFeedItems($feed, 30);
 
+    // Request page with no feed items to ensure cache context is set correctly.
+    $this->drupalGet('aggregator', ['query' => ['page' => 2]]);
     // Check for presence of an aggregator pager.
     $this->drupalGet('aggregator');
     $this->assertSession()->elementExists('xpath', '//ul[contains(@class, "pager__items")]');
diff --git a/web/core/modules/book/book.views.inc b/web/core/modules/book/book.views.inc
index 63dfb38aec..9e10ccb2e8 100644
--- a/web/core/modules/book/book.views.inc
+++ b/web/core/modules/book/book.views.inc
@@ -84,7 +84,7 @@ function book_views_data() {
 
   $data['book']['depth'] = [
     'title' => t('Depth'),
-    'help' => t('The depth of the book page in the hierarchy; top level books have a depth of 1.'),
+    'help' => t('The depth of the book page in the hierarchy; top level book pages have a depth of 1.'),
     'field' => [
       'id' => 'numeric',
     ],
diff --git a/web/core/modules/book/src/BookManagerInterface.php b/web/core/modules/book/src/BookManagerInterface.php
index 959c45cb9a..f787971655 100644
--- a/web/core/modules/book/src/BookManagerInterface.php
+++ b/web/core/modules/book/src/BookManagerInterface.php
@@ -183,15 +183,23 @@ public function getAllBooks();
   public function updateOutline(NodeInterface $node);
 
   /**
-   * Saves a single book entry.
+   * Saves a link for a single book entry to the book.
    *
    * @param array $link
-   *   The link data to save.
+   *   The link data to save. $link['nid'] must be set. Other keys in this array
+   *   get default values from
+   *   \Drupal\book\BookManagerInterface::getLinkDefaults(). The array keys
+   *   available to be set are documented in
+   *   \Drupal\book\BookOutlineStorageInterface::loadMultiple().
    * @param bool $new
-   *   Is this a new book.
+   *   Whether this is a link to a new book entry.
    *
    * @return array
-   *   The book data of that node.
+   *   The book entry link information. This is $link with values added or
+   *   updated.
+   *
+   * @see \Drupal\book\BookManagerInterface::getLinkDefaults()
+   * @see \Drupal\book\BookOutlineStorageInterface::loadMultiple()
    */
   public function saveBookLink(array $link, $new);
 
diff --git a/web/core/modules/ckeditor5/src/SmartDefaultSettings.php b/web/core/modules/ckeditor5/src/SmartDefaultSettings.php
index a10a64b213..9da23d1aba 100644
--- a/web/core/modules/ckeditor5/src/SmartDefaultSettings.php
+++ b/web/core/modules/ckeditor5/src/SmartDefaultSettings.php
@@ -178,39 +178,40 @@ public function computeSmartDefaultSettings(?EditorInterface $text_editor, Filte
       $unsupported = $missing->diff($missing_attributes);
 
       if ($enabling_message_content) {
-        $this->logger->info(new FormattableMarkup('The CKEditor 5 migration enabled the following plugins to support tags that are allowed by the %text_format text format: %enabling_message_content. The text format must be saved to make these changes active.',
+        $this->logger->info('The CKEditor 5 migration enabled the following plugins to support tags that are allowed by the %text_format text format: %enabling_message_content. The text format must be saved to make these changes active.',
           [
             '%text_format' => $editor->getFilterFormat()->get('name'),
             '%enabling_message_content' => $enabling_message_content,
-          ],
-        ));
+          ]
+        );
       }
+
       // Warn user about unsupported tags.
       if (!$unsupported->allowsNothing()) {
         $this->addTagsToSourceEditing($editor, $unsupported);
         $source_editing_additions = $source_editing_additions->merge($unsupported);
-        $this->logger->info(new FormattableMarkup("The following tags were permitted by the %text_format text format's filter configuration, but no plugin was available that supports them. To ensure the tags remain supported by this text format, the following were added to the Source Editing plugin's <em>Manually editable HTML tags</em>: @unsupported_string. The text format must be saved to make these changes active.", [
+        $this->logger->info("The following tags were permitted by the %text_format text format's filter configuration, but no plugin was available that supports them. To ensure the tags remain supported by this text format, the following were added to the Source Editing plugin's <em>Manually editable HTML tags</em>: @unsupported_string. The text format must be saved to make these changes active.", [
           '%text_format' => $editor->getFilterFormat()->get('name'),
           '@unsupported_string' => $unsupported->toFilterHtmlAllowedTagsString(),
-        ]));
+        ]);
       }
 
       if ($enabled_for_attributes_message_content) {
-        $this->logger->info(new FormattableMarkup('The CKEditor 5 migration process enabled the following plugins to support specific attributes that are allowed by the %text_format text format: %enabled_for_attributes_message_content.',
+        $this->logger->info('The CKEditor 5 migration process enabled the following plugins to support specific attributes that are allowed by the %text_format text format: %enabled_for_attributes_message_content.',
           [
             '%text_format' => $editor->getFilterFormat()->get('name'),
             '%enabled_for_attributes_message_content' => $enabled_for_attributes_message_content,
           ],
-        ));
+        );
       }
       // Warn user about supported tags but missing attributes.
       if (!$missing_attributes->allowsNothing()) {
         $this->addTagsToSourceEditing($editor, $missing_attributes);
         $source_editing_additions = $source_editing_additions->merge($missing_attributes);
-        $this->logger->info(new FormattableMarkup("As part of migrating to CKEditor 5, it was found that the %text_format text format's HTML filters includes plugins that support the following tags, but not some of their attributes. To ensure these attributes remain supported, the following were added to the Source Editing plugin's <em>Manually editable HTML tags</em>: @missing_attributes. The text format must be saved to make these changes active.", [
+        $this->logger->info("As part of migrating to CKEditor 5, it was found that the %text_format text format's HTML filters includes plugins that support the following tags, but not some of their attributes. To ensure these attributes remain supported, the following were added to the Source Editing plugin's <em>Manually editable HTML tags</em>: @missing_attributes. The text format must be saved to make these changes active.", [
           '%text_format' => $editor->getFilterFormat()->get('name'),
           '@missing_attributes' => $missing_attributes->toFilterHtmlAllowedTagsString(),
-        ]));
+        ]);
       }
     }
 
@@ -225,10 +226,10 @@ public function computeSmartDefaultSettings(?EditorInterface $text_editor, Filte
       $missing_fundamental_tags = $fundamental->diff($filter_html_restrictions);
       if (!$missing_fundamental_tags->allowsNothing()) {
         $editor->getFilterFormat()->setFilterConfig('filter_html', $filter_html_restrictions->merge($fundamental)->getAllowedElements());
-        $this->logger->warning(new FormattableMarkup("As part of migrating the %text_format text format to CKEditor 5, the following tag(s) were added to <em>Limit allowed HTML tags and correct faulty HTML</em>, because they are needed to provide fundamental CKEditor 5 functionality : @missing_tags. The text format must be saved to make these changes active.", [
+        $this->logger->warning("As part of migrating the %text_format text format to CKEditor 5, the following tag(s) were added to <em>Limit allowed HTML tags and correct faulty HTML</em>, because they are needed to provide fundamental CKEditor 5 functionality : @missing_tags. The text format must be saved to make these changes active.", [
           '%text_format' => $editor->getFilterFormat()->get('name'),
           '@missing_tags' => $missing_fundamental_tags->toFilterHtmlAllowedTagsString(),
-        ]));
+        ]);
       }
     }
 
@@ -448,9 +449,9 @@ private function createSettingsFromCKEditor4(array $ckeditor4_settings, HTMLRest
             $equivalent = $this->upgradePluginManager->mapCKEditor4ToolbarButtonToCKEditor5ToolbarItem($cke4_button, $text_format_html_restrictions);
           }
           catch (\OutOfBoundsException $e) {
-            $this->logger->warning(new FormattableMarkup('The CKEditor 4 button %button does not have a known upgrade path. If it allowed editing markup, then you can do so now through the Source Editing functionality.', [
+            $this->logger->warning('The CKEditor 4 button %button does not have a known upgrade path. If it allowed editing markup, then you can do so now through the Source Editing functionality.', [
               '%button' => $cke4_button,
-            ]));
+            ]);
             $messages[MessengerInterface::TYPE_WARNING][] = $this->t('The CKEditor 4 button %button does not have a known upgrade path. If it allowed editing markup, then you can do so now through the Source Editing functionality.', [
               '%button' => $cke4_button,
             ]);
@@ -489,9 +490,9 @@ private function createSettingsFromCKEditor4(array $ckeditor4_settings, HTMLRest
         $settings['plugins'] += $cke5_plugin_settings;
       }
       catch (\OutOfBoundsException $e) {
-        $this->logger->warning(new FormattableMarkup('The %cke4_plugin_id plugin settings do not have a known upgrade path.', [
+        $this->logger->warning('The %cke4_plugin_id plugin settings do not have a known upgrade path.', [
           '%cke4_plugin_id' => $cke4_plugin_id,
-        ]));
+        ]);
         $messages[MessengerInterface::TYPE_WARNING][] = $this->t('The %cke4_plugin_id plugin settings do not have a known upgrade path.', [
           '%cke4_plugin_id' => $cke4_plugin_id,
         ]);
diff --git a/web/core/modules/ckeditor5/tests/src/Kernel/SmartDefaultSettingsTest.php b/web/core/modules/ckeditor5/tests/src/Kernel/SmartDefaultSettingsTest.php
index 1903d3b008..65c64f3485 100644
--- a/web/core/modules/ckeditor5/tests/src/Kernel/SmartDefaultSettingsTest.php
+++ b/web/core/modules/ckeditor5/tests/src/Kernel/SmartDefaultSettingsTest.php
@@ -5,6 +5,7 @@
 namespace Drupal\Tests\ckeditor5\Kernel;
 
 use Drupal\ckeditor5\HTMLRestrictions;
+use Drupal\Component\Render\FormattableMarkup;
 use Drupal\Component\Utility\NestedArray;
 use Drupal\Core\Entity\Entity\EntityViewMode;
 use Drupal\editor\Entity\Editor;
@@ -572,7 +573,9 @@ public function test(string $format_id, array $filters_to_drop, array $expected_
     ];
     $db_logs = [];
     foreach ($db_logged as $log) {
-      $db_logs[$type_to_status[$log->severity]][] = $log->message;
+      $variables = unserialize($log->variables);
+      $message = new FormattableMarkup($log->message, $variables);
+      $db_logs[$type_to_status[$log->severity]][] = (string) $message;
     }
 
     // Transforms TranslatableMarkup objects to string.
diff --git a/web/core/modules/content_moderation/src/Entity/ContentModerationState.php b/web/core/modules/content_moderation/src/Entity/ContentModerationState.php
index 6d0cb0abd5..fabce0090f 100644
--- a/web/core/modules/content_moderation/src/Entity/ContentModerationState.php
+++ b/web/core/modules/content_moderation/src/Entity/ContentModerationState.php
@@ -134,12 +134,17 @@ public static function loadFromModeratedEntity(EntityInterface $entity) {
       /** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
       $storage = \Drupal::entityTypeManager()->getStorage('content_moderation_state');
 
+      // New entities may not have a loaded revision ID at this point, but the
+      // creation of a content moderation state entity may have already been
+      // triggered elsewhere. In this case we have to match on the revision ID
+      // (instead of the loaded revision ID).
+      $revision_id = $entity->getLoadedRevisionId() ?: $entity->getRevisionId();
       $ids = $storage->getQuery()
         ->accessCheck(FALSE)
         ->condition('content_entity_type_id', $entity->getEntityTypeId())
         ->condition('content_entity_id', $entity->id())
         ->condition('workflow', $moderation_info->getWorkflowForEntity($entity)->id())
-        ->condition('content_entity_revision_id', $entity->getLoadedRevisionId())
+        ->condition('content_entity_revision_id', $revision_id)
         ->allRevisions()
         ->execute();
 
diff --git a/web/core/modules/content_moderation/tests/modules/content_moderation_test_resave/content_moderation_test_resave.info.yml b/web/core/modules/content_moderation/tests/modules/content_moderation_test_resave/content_moderation_test_resave.info.yml
new file mode 100644
index 0000000000..25d8618bbf
--- /dev/null
+++ b/web/core/modules/content_moderation/tests/modules/content_moderation_test_resave/content_moderation_test_resave.info.yml
@@ -0,0 +1,7 @@
+name: 'Content moderation test re-save'
+type: module
+description: 'Re-saves moderated entities for testing purposes.'
+package: Testing
+version: VERSION
+dependencies:
+  - drupal:content_moderation
diff --git a/web/core/modules/content_moderation/tests/modules/content_moderation_test_resave/content_moderation_test_resave.install b/web/core/modules/content_moderation/tests/modules/content_moderation_test_resave/content_moderation_test_resave.install
new file mode 100644
index 0000000000..383e59ee8c
--- /dev/null
+++ b/web/core/modules/content_moderation/tests/modules/content_moderation_test_resave/content_moderation_test_resave.install
@@ -0,0 +1,15 @@
+<?php
+
+/**
+ * @file
+ * Contains install functions for the Content moderation test re-save module.
+ */
+
+/**
+ * Implements hook_install().
+ */
+function content_moderation_test_resave_install() {
+  // Make sure that this module's hooks are run before Content Moderation's
+  // hooks.
+  module_set_weight('content_moderation_test_resave', -10);
+}
diff --git a/web/core/modules/content_moderation/tests/modules/content_moderation_test_resave/content_moderation_test_resave.module b/web/core/modules/content_moderation/tests/modules/content_moderation_test_resave/content_moderation_test_resave.module
new file mode 100644
index 0000000000..74292920d7
--- /dev/null
+++ b/web/core/modules/content_moderation/tests/modules/content_moderation_test_resave/content_moderation_test_resave.module
@@ -0,0 +1,30 @@
+<?php
+
+/**
+ * @file
+ * Contains hook implementations for the Content moderation test re-save module.
+ */
+
+use Drupal\Core\Entity\EntityInterface;
+
+/**
+ * Implements hook_entity_insert().
+ */
+function content_moderation_test_resave_entity_insert(EntityInterface $entity) {
+  /** @var \Drupal\content_moderation\ModerationInformationInterface $content_moderation */
+  $content_moderation = \Drupal::service('content_moderation.moderation_information');
+  if ($content_moderation->isModeratedEntity($entity)) {
+    /** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
+    // Saving the passed entity object would populate its loaded revision ID,
+    // which we want to avoid. Thus, save a clone of the original object.
+    $cloned_entity = clone $entity;
+    // Set the entity's syncing status, as we do not want Content Moderation to
+    // create new revisions for the re-saving. Without this call Content
+    // Moderation would end up creating two separate content moderation state
+    // entities: one for the re-save revision and one for the initial revision.
+    $cloned_entity->setSyncing(TRUE)->save();
+
+    // Record the fact that a re-save happened.
+    \Drupal::state()->set('content_moderation_test_resave', TRUE);
+  }
+}
diff --git a/web/core/modules/content_moderation/tests/src/Kernel/ContentModerationResaveTest.php b/web/core/modules/content_moderation/tests/src/Kernel/ContentModerationResaveTest.php
new file mode 100644
index 0000000000..15f9aaf03a
--- /dev/null
+++ b/web/core/modules/content_moderation/tests/src/Kernel/ContentModerationResaveTest.php
@@ -0,0 +1,107 @@
+<?php
+
+namespace Drupal\Tests\content_moderation\Kernel;
+
+use Drupal\content_moderation\Entity\ContentModerationState;
+use Drupal\KernelTests\KernelTestBase;
+use Drupal\Tests\content_moderation\Traits\ContentModerationTestTrait;
+
+/**
+ * Tests Content Moderation with entities that get re-saved automatically.
+ *
+ * @group content_moderation
+ */
+class ContentModerationResaveTest extends KernelTestBase {
+
+  use ContentModerationTestTrait;
+
+  /**
+   * {@inheritdoc}
+   */
+  protected static $modules = [
+    // Make sure the test module is listed first as module weights do not apply
+    // for kernel tests.
+    /* @see \content_moderation_test_resave_install() */
+    'content_moderation_test_resave',
+    'content_moderation',
+    'entity_test',
+    'user',
+    'workflows',
+  ];
+
+  /**
+   * The content moderation state entity storage.
+   *
+   * @var \Drupal\Core\Entity\EntityStorageInterface
+   */
+  protected $contentModerationStateStorage;
+
+  /**
+   * The entity storage for the entity type used in the test.
+   *
+   * @var \Drupal\Core\Entity\EntityStorageInterface
+   */
+  protected $entityStorage;
+
+  /**
+   * The state service.
+   *
+   * @var \Drupal\Core\State\StateInterface
+   */
+  protected $state;
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp(): void {
+    parent::setUp();
+
+    $entity_type_id = 'entity_test_rev';
+
+    $this->installEntitySchema('content_moderation_state');
+    $this->installEntitySchema($entity_type_id);
+
+    $workflow = $this->createEditorialWorkflow();
+    $this->addEntityTypeAndBundleToWorkflow($workflow, $entity_type_id, $entity_type_id);
+
+    /** @var \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager */
+    $entity_type_manager = $this->container->get('entity_type.manager');
+    $this->contentModerationStateStorage = $entity_type_manager->getStorage('content_moderation_state');
+    $this->entityStorage = $entity_type_manager->getStorage($entity_type_id);
+    $this->state = $this->container->get('state');
+  }
+
+  /**
+   * Tests that Content Moderation works with entities being resaved.
+   */
+  public function testContentModerationResave() {
+    $entity = $this->entityStorage->create();
+    $this->assertSame('draft', $entity->get('moderation_state')->value);
+    $this->assertNull(\Drupal::state()->get('content_moderation_test_resave'));
+    $this->assertNull(ContentModerationState::loadFromModeratedEntity($entity));
+    $content_moderation_state_query = $this->contentModerationStateStorage
+      ->getQuery()
+      ->accessCheck(FALSE)
+      ->count();
+    $this->assertSame(0, (int) $content_moderation_state_query->execute());
+    $content_moderation_state_revision_query = $this->contentModerationStateStorage
+      ->getQuery()
+      ->accessCheck(FALSE)
+      ->allRevisions()
+      ->count();
+    $this->assertSame(0, (int) $content_moderation_state_revision_query->execute());
+
+    // The test module will re-save the entity in its hook_insert()
+    // implementation creating the content moderation state entity before
+    // Content Moderation's hook_insert() has run for the initial save
+    // operation.
+    $entity->save();
+    $this->assertSame('draft', $entity->get('moderation_state')->value);
+    $this->assertTrue(\Drupal::state()->get('content_moderation_test_resave'));
+    $content_moderation_state = ContentModerationState::loadFromModeratedEntity($entity);
+    $this->assertInstanceOf(ContentModerationState::class, $content_moderation_state);
+    $this->assertSame(1, (int) $content_moderation_state_query->execute());
+    $this->assertSame(1, (int) $content_moderation_state_revision_query->execute());
+  }
+
+}
diff --git a/web/core/modules/contextual/js/contextual.es6.js b/web/core/modules/contextual/js/contextual.es6.js
index b5fe7d094f..90b0d9056a 100644
--- a/web/core/modules/contextual/js/contextual.es6.js
+++ b/web/core/modules/contextual/js/contextual.es6.js
@@ -99,7 +99,7 @@
 
     // Set the destination parameter on each of the contextual links.
     const destination = `destination=${Drupal.encodePath(
-      Drupal.url(drupalSettings.path.currentPath),
+      Drupal.url(drupalSettings.path.currentPath + window.location.search),
     )}`;
     $contextual.find('.contextual-links a').each(function () {
       const url = this.getAttribute('href');
diff --git a/web/core/modules/contextual/js/contextual.js b/web/core/modules/contextual/js/contextual.js
index 65e5dfb3d7..c473ca8119 100644
--- a/web/core/modules/contextual/js/contextual.js
+++ b/web/core/modules/contextual/js/contextual.js
@@ -46,7 +46,7 @@
     var $region = $contextual.closest('.contextual-region');
     var contextual = Drupal.contextual;
     $contextual.html(html).addClass('contextual').prepend(Drupal.theme('contextualTrigger'));
-    var destination = "destination=".concat(Drupal.encodePath(Drupal.url(drupalSettings.path.currentPath)));
+    var destination = "destination=".concat(Drupal.encodePath(Drupal.url(drupalSettings.path.currentPath + window.location.search)));
     $contextual.find('.contextual-links a').each(function () {
       var url = this.getAttribute('href');
       var glue = url.indexOf('?') === -1 ? '?' : '&';
diff --git a/web/core/modules/contextual/tests/src/FunctionalJavascript/ContextualLinksTest.php b/web/core/modules/contextual/tests/src/FunctionalJavascript/ContextualLinksTest.php
index 1d16440cbf..0a16d616b8 100644
--- a/web/core/modules/contextual/tests/src/FunctionalJavascript/ContextualLinksTest.php
+++ b/web/core/modules/contextual/tests/src/FunctionalJavascript/ContextualLinksTest.php
@@ -2,6 +2,7 @@
 
 namespace Drupal\Tests\contextual\FunctionalJavascript;
 
+use Drupal\Core\Url;
 use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
 use Drupal\user\Entity\Role;
 
@@ -114,4 +115,20 @@ public function testContextualLinksDestination() {
     $this->assertEquals("destination=$expected_destination_value", $contextual_link_url_parsed['query']);
   }
 
+  /**
+   * Tests the contextual links destination with query.
+   */
+  public function testContextualLinksDestinationWithQuery() {
+    $this->grantPermissions(Role::load(Role::AUTHENTICATED_ID), [
+      'access contextual links',
+      'administer blocks',
+    ]);
+
+    $this->drupalGet('admin/structure/block', ['query' => ['foo' => 'bar']]);
+    $this->assertSession()->waitForElement('css', '.contextual button');
+    $expected_destination_value = Url::fromRoute('block.admin_display')->toString();
+    $contextual_link_url_parsed = parse_url($this->getSession()->getPage()->findLink('Configure block')->getAttribute('href'));
+    $this->assertEquals("destination=$expected_destination_value%3Ffoo%3Dbar", $contextual_link_url_parsed['query']);
+  }
+
 }
diff --git a/web/core/modules/contextual/tests/src/Kernel/ContextualUnitTest.php b/web/core/modules/contextual/tests/src/Kernel/ContextualUnitTest.php
index 9e088aefb7..dc46bef32a 100644
--- a/web/core/modules/contextual/tests/src/Kernel/ContextualUnitTest.php
+++ b/web/core/modules/contextual/tests/src/Kernel/ContextualUnitTest.php
@@ -5,8 +5,7 @@
 use Drupal\KernelTests\KernelTestBase;
 
 /**
- * Tests all edge cases of converting from #contextual_links to ids and vice
- * versa.
+ * Tests edge cases for converting between contextual links and IDs.
  *
  * @group contextual
  */
@@ -23,14 +22,13 @@ class ContextualUnitTest extends KernelTestBase {
    * Provides testcases for both test functions.
    *
    * Used in testContextualLinksToId() and testContextualIdToLinks().
+   *
+   * @return array[]
+   *   Test cases.
    */
-  public function _contextual_links_id_testcases() {
-    // Test branch conditions:
-    // - one group.
-    // - one dynamic path argument.
-    // - no metadata.
-    $tests[] = [
-      'links' => [
+  public function contextualLinksDataProvider(): array {
+    $tests['one group, one dynamic path argument, no metadata'] = [
+      [
         'node' => [
           'route_parameters' => [
             'node' => '14031991',
@@ -38,33 +36,25 @@ public function _contextual_links_id_testcases() {
           'metadata' => ['langcode' => 'en'],
         ],
       ],
-      'id' => 'node:node=14031991:langcode=en',
+      'node:node=14031991:langcode=en',
     ];
 
-    // Test branch conditions:
-    // - one group.
-    // - multiple dynamic path arguments.
-    // - no metadata.
-    $tests[] = [
-      'links' => [
+    $tests['one group, multiple dynamic path arguments, no metadata'] = [
+      [
         'foo' => [
           'route_parameters' => [
-            'bar',
+            0 => 'bar',
             'key' => 'baz',
-            'qux',
+            1 => 'qux',
           ],
           'metadata' => ['langcode' => 'en'],
         ],
       ],
-      'id' => 'foo:0=bar&key=baz&1=qux:langcode=en',
+      'foo:0=bar&key=baz&1=qux:langcode=en',
     ];
 
-    // Test branch conditions:
-    // - one group.
-    // - one dynamic path argument.
-    // - metadata.
-    $tests[] = [
-      'links' => [
+    $tests['one group, one dynamic path argument, metadata'] = [
+      [
         'views_ui_edit' => [
           'route_parameters' => [
             'view' => 'frontpage',
@@ -76,14 +66,11 @@ public function _contextual_links_id_testcases() {
           ],
         ],
       ],
-      'id' => 'views_ui_edit:view=frontpage:location=page&display=page_1&langcode=en',
+      'views_ui_edit:view=frontpage:location=page&display=page_1&langcode=en',
     ];
 
-    // Test branch conditions:
-    // - multiple groups.
-    // - multiple dynamic path arguments.
-    $tests[] = [
-      'links' => [
+    $tests['multiple groups, multiple dynamic path arguments'] = [
+      [
         'node' => [
           'route_parameters' => [
             'node' => '14031991',
@@ -92,9 +79,9 @@ public function _contextual_links_id_testcases() {
         ],
         'foo' => [
           'route_parameters' => [
-            'bar',
+            0 => 'bar',
             'key' => 'baz',
-            'qux',
+            1 => 'qux',
           ],
           'metadata' => ['langcode' => 'en'],
         ],
@@ -103,30 +90,42 @@ public function _contextual_links_id_testcases() {
           'metadata' => ['langcode' => 'en'],
         ],
       ],
-      'id' => 'node:node=14031991:langcode=en|foo:0=bar&key=baz&1=qux:langcode=en|edge:0=20011988:langcode=en',
+      'node:node=14031991:langcode=en|foo:0=bar&key=baz&1=qux:langcode=en|edge:0=20011988:langcode=en',
     ];
 
     return $tests;
   }
 
   /**
-   * Tests _contextual_links_to_id().
+   * Tests the conversion from contextual links to IDs.
+   *
+   * @param array $links
+   *   The #contextual_links property value array.
+   * @param string $id
+   *   The serialized representation of the passed links.
+   *
+   * @covers ::_contextual_links_to_id
+   *
+   * @dataProvider contextualLinksDataProvider
    */
-  public function testContextualLinksToId() {
-    $tests = $this->_contextual_links_id_testcases();
-    foreach ($tests as $test) {
-      $this->assertSame($test['id'], _contextual_links_to_id($test['links']));
-    }
+  public function testContextualLinksToId(array $links, string $id) {
+    $this->assertSame($id, _contextual_links_to_id($links));
   }
 
   /**
-   * Tests _contextual_id_to_links().
+   * Tests the conversion from contextual ID to links.
+   *
+   * @param array $links
+   *   The #contextual_links property value array.
+   * @param string $id
+   *   The serialized representation of the passed links.
+   *
+   * @covers ::_contextual_id_to_links
+   *
+   * @dataProvider contextualLinksDataProvider
    */
-  public function testContextualIdToLinks() {
-    $tests = $this->_contextual_links_id_testcases();
-    foreach ($tests as $test) {
-      $this->assertSame($test['links'], _contextual_id_to_links($test['id']));
-    }
+  public function testContextualIdToLinks(array $links, string $id) {
+    $this->assertSame($links, _contextual_id_to_links($id));
   }
 
 }
diff --git a/web/core/modules/datetime/tests/src/Functional/Views/FilterDateTest.php b/web/core/modules/datetime/tests/src/Functional/Views/FilterDateTest.php
index 7d0bb6c455..ce3f430145 100644
--- a/web/core/modules/datetime/tests/src/Functional/Views/FilterDateTest.php
+++ b/web/core/modules/datetime/tests/src/Functional/Views/FilterDateTest.php
@@ -198,4 +198,55 @@ protected function assertIds(array $expected_ids = []): void {
     $this->assertEquals($expected_ids, $actual_ids);
   }
 
+  /**
+   * Tests exposed date filters with a pager.
+   */
+  public function testExposedFilterWithPager() {
+    // Expose the empty and not empty operators in a grouped filter.
+    $this->drupalGet('admin/structure/views/nojs/handler/test_filter_datetime/default/filter/' . $this->fieldName . '_value');
+    $this->submitForm([], t('Expose filter'));
+
+    $edit = [];
+    $edit['options[operator]'] = '>';
+
+    $this->submitForm($edit, 'Apply');
+
+    // Expose the view and set the pager to 2 items.
+    $path = 'test_filter_datetime-path';
+    $this->drupalGet('admin/structure/views/view/test_filter_datetime/edit');
+    $this->submitForm([], 'Add Page');
+    $this->drupalGet('admin/structure/views/nojs/display/test_filter_datetime/page_1/path');
+    $this->submitForm(['path' => $path], 'Apply');
+    $this->drupalGet('admin/structure/views/nojs/display/test_filter_datetime/default/pager_options');
+    $this->submitForm(['pager_options[items_per_page]' => 2], 'Apply');
+    $this->submitForm([], t('Save'));
+
+    // Assert the page without filters.
+    $this->drupalGet($path);
+    $results = $this->cssSelect('.views-row');
+    $this->assertCount(2, $results);
+    $this->assertSession()->pageTextContains('Next');
+
+    // Assert the page with filter in the future, one results without pager.
+    $page = $this->getSession()->getPage();
+    $now = \Drupal::time()->getRequestTime();
+    $page->fillField($this->fieldName . '_value', DrupalDateTime::createFromTimestamp($now + 1)->format('Y-m-d H:i:s'));
+    $page->pressButton('Apply');
+
+    $results = $this->cssSelect('.views-row');
+    $this->assertCount(1, $results);
+    $this->assertSession()->pageTextNotContains('Next');
+
+    // Assert the page with filter in the past, 3 results with pager.
+    $page->fillField($this->fieldName . '_value', DrupalDateTime::createFromTimestamp($now - 1000000)->format('Y-m-d H:i:s'));
+    $this->getSession()->getPage()->pressButton('Apply');
+    $results = $this->cssSelect('.views-row');
+    $this->assertCount(2, $results);
+    $this->assertSession()->pageTextContains('Next');
+    $page->clickLink('2');
+    $results = $this->cssSelect('.views-row');
+    $this->assertCount(1, $results);
+
+  }
+
 }
diff --git a/web/core/modules/file/src/FileRepository.php b/web/core/modules/file/src/FileRepository.php
index 3ec359c61d..1a2d08d75a 100644
--- a/web/core/modules/file/src/FileRepository.php
+++ b/web/core/modules/file/src/FileRepository.php
@@ -88,7 +88,7 @@ public function __construct(FileSystemInterface $fileSystem, StreamWrapperManage
    */
   public function writeData(string $data, string $destination, int $replace = FileSystemInterface::EXISTS_RENAME): FileInterface {
     if (!$this->streamWrapperManager->isValidUri($destination)) {
-      throw new InvalidStreamWrapperException(sprintf('Invalid stream wrapper: %destination', ['%destination' => $destination]));
+      throw new InvalidStreamWrapperException("Invalid stream wrapper: {$destination}");
     }
     $uri = $this->fileSystem->saveData($data, $destination, $replace);
     return $this->createOrUpdate($uri, $destination, $replace === FileSystemInterface::EXISTS_RENAME);
@@ -132,7 +132,7 @@ protected function createOrUpdate(string $uri, string $destination, bool $rename
    */
   public function copy(FileInterface $source, string $destination, int $replace = FileSystemInterface::EXISTS_RENAME): FileInterface {
     if (!$this->streamWrapperManager->isValidUri($destination)) {
-      throw new InvalidStreamWrapperException(sprintf('Invalid stream wrapper: %destination', ['%destination' => $destination]));
+      throw new InvalidStreamWrapperException("Invalid stream wrapper: {$destination}");
     }
     $uri = $this->fileSystem->copy($source->getFileUri(), $destination, $replace);
 
@@ -166,7 +166,7 @@ public function copy(FileInterface $source, string $destination, int $replace =
    */
   public function move(FileInterface $source, string $destination, int $replace = FileSystemInterface::EXISTS_RENAME): FileInterface {
     if (!$this->streamWrapperManager->isValidUri($destination)) {
-      throw new InvalidStreamWrapperException(sprintf('Invalid stream wrapper: %destination', ['%destination' => $destination]));
+      throw new InvalidStreamWrapperException("Invalid stream wrapper: {$destination}");
     }
     $uri = $this->fileSystem->move($source->getFileUri(), $destination, $replace);
     $delete_source = FALSE;
diff --git a/web/core/modules/file/tests/src/Kernel/CopyTest.php b/web/core/modules/file/tests/src/Kernel/CopyTest.php
index 668f99ae40..9c8e79766b 100644
--- a/web/core/modules/file/tests/src/Kernel/CopyTest.php
+++ b/web/core/modules/file/tests/src/Kernel/CopyTest.php
@@ -185,6 +185,7 @@ public function testExistingError() {
    */
   public function testInvalidStreamWrapper() {
     $this->expectException(InvalidStreamWrapperException::class);
+    $this->expectExceptionMessage('Invalid stream wrapper: foo://');
     $source = $this->createFile();
     $this->fileRepository->copy($source, 'foo://');
   }
diff --git a/web/core/modules/file/tests/src/Kernel/FileRepositoryTest.php b/web/core/modules/file/tests/src/Kernel/FileRepositoryTest.php
index 7db4dd08b9..b7790a659c 100644
--- a/web/core/modules/file/tests/src/Kernel/FileRepositoryTest.php
+++ b/web/core/modules/file/tests/src/Kernel/FileRepositoryTest.php
@@ -170,6 +170,7 @@ public function testExistingError() {
    */
   public function testInvalidStreamWrapper() {
     $this->expectException(InvalidStreamWrapperException::class);
+    $this->expectExceptionMessage('Invalid stream wrapper: foo://');
     $this->fileRepository->writeData('asdf', 'foo://');
   }
 
diff --git a/web/core/modules/file/tests/src/Kernel/MoveTest.php b/web/core/modules/file/tests/src/Kernel/MoveTest.php
index 389b86210f..c48ed721cd 100644
--- a/web/core/modules/file/tests/src/Kernel/MoveTest.php
+++ b/web/core/modules/file/tests/src/Kernel/MoveTest.php
@@ -209,6 +209,7 @@ public function testExistingError() {
    */
   public function testInvalidStreamWrapper() {
     $this->expectException(InvalidStreamWrapperException::class);
+    $this->expectExceptionMessage('Invalid stream wrapper: foo://');
     $source = $this->createFile();
     $this->fileRepository->move($source, 'foo://');
   }
diff --git a/web/core/modules/jsonapi/tests/src/Functional/ResourceTestBase.php b/web/core/modules/jsonapi/tests/src/Functional/ResourceTestBase.php
index 2f358158f5..0099573062 100644
--- a/web/core/modules/jsonapi/tests/src/Functional/ResourceTestBase.php
+++ b/web/core/modules/jsonapi/tests/src/Functional/ResourceTestBase.php
@@ -607,21 +607,14 @@ protected static function getExpectedCollectionCacheability(AccountInterface $ac
   /**
    * Sets up the necessary authorization.
    *
-   * In case of a test verifying publicly accessible REST resources: grant
-   * permissions to the anonymous user role.
-   *
-   * In case of a test verifying behavior when using a particular authentication
-   * provider: create a user with a particular set of permissions.
-   *
    * Because of the $method parameter, it's possible to first set up
-   * authentication for only GET, then add POST, et cetera. This then also
+   * authorization for only GET, then add POST, et cetera. This then also
    * allows for verifying a 403 in case of missing authorization.
    *
    * @param string $method
-   *   The HTTP method for which to set up authentication.
+   *   The HTTP method for which to set up authorization.
    *
-   * @see ::grantPermissionsToAnonymousRole()
-   * @see ::grantPermissionsToAuthenticatedRole()
+   * @see ::grantPermissionsToTestedRole()
    */
   abstract protected function setUpAuthorization($method);
 
diff --git a/web/core/modules/language/src/ConfigurableLanguageManager.php b/web/core/modules/language/src/ConfigurableLanguageManager.php
index 9dd65ff495..b72f9b3709 100644
--- a/web/core/modules/language/src/ConfigurableLanguageManager.php
+++ b/web/core/modules/language/src/ConfigurableLanguageManager.php
@@ -408,7 +408,23 @@ public function getLanguageSwitchLinks($type, Url $url) {
         $reflector = new \ReflectionClass($method['class']);
 
         if ($reflector->implementsInterface('\Drupal\language\LanguageSwitcherInterface')) {
+          $original_languages = $this->negotiatedLanguages;
           $result = $this->negotiator->getNegotiationMethodInstance($method_id)->getLanguageSwitchLinks($this->requestStack->getCurrentRequest(), $type, $url);
+          $result = array_filter($result, function (array $link): bool {
+            $url = $link['url'] ?? NULL;
+            $language = $link['language'] ?? NULL;
+            if ($language instanceof LanguageInterface) {
+              $this->negotiatedLanguages[LanguageInterface::TYPE_CONTENT] = $language;
+              $this->negotiatedLanguages[LanguageInterface::TYPE_INTERFACE] = $language;
+            }
+            try {
+              return $url instanceof Url && $url->access();
+            }
+            catch (\Exception $e) {
+              return FALSE;
+            }
+          });
+          $this->negotiatedLanguages = $original_languages;
 
           if (!empty($result)) {
             // Allow modules to provide translations for specific links.
diff --git a/web/core/modules/language/src/Plugin/Block/LanguageBlock.php b/web/core/modules/language/src/Plugin/Block/LanguageBlock.php
index 2765971b8e..5830758348 100644
--- a/web/core/modules/language/src/Plugin/Block/LanguageBlock.php
+++ b/web/core/modules/language/src/Plugin/Block/LanguageBlock.php
@@ -83,9 +83,8 @@ protected function blockAccess(AccountInterface $account) {
    */
   public function build() {
     $build = [];
-    $route_name = $this->pathMatcher->isFrontPage() ? '<front>' : '<current>';
     $type = $this->getDerivativeId();
-    $links = $this->languageManager->getLanguageSwitchLinks($type, Url::fromRoute($route_name));
+    $links = $this->languageManager->getLanguageSwitchLinks($type, Url::fromRouteMatch(\Drupal::routeMatch()));
 
     if (isset($links->links)) {
       $build = [
diff --git a/web/core/modules/language/src/Plugin/LanguageNegotiation/LanguageNegotiationContentEntity.php b/web/core/modules/language/src/Plugin/LanguageNegotiation/LanguageNegotiationContentEntity.php
index 11c59adb4a..2c5157d20d 100644
--- a/web/core/modules/language/src/Plugin/LanguageNegotiation/LanguageNegotiationContentEntity.php
+++ b/web/core/modules/language/src/Plugin/LanguageNegotiation/LanguageNegotiationContentEntity.php
@@ -139,7 +139,7 @@ public function processOutbound($path, &$options = [], Request $request = NULL,
   public function getLanguageSwitchLinks(Request $request, $type, Url $url) {
     $links = [];
     $query = [];
-    parse_str($request->getQueryString(), $query);
+    parse_str($request->getQueryString() ?? '', $query);
 
     foreach ($this->languageManager->getNativeLanguages() as $language) {
       $langcode = $language->getId();
diff --git a/web/core/modules/language/src/Plugin/LanguageNegotiation/LanguageNegotiationSession.php b/web/core/modules/language/src/Plugin/LanguageNegotiation/LanguageNegotiationSession.php
index f36d43e946..77df05538d 100644
--- a/web/core/modules/language/src/Plugin/LanguageNegotiation/LanguageNegotiationSession.php
+++ b/web/core/modules/language/src/Plugin/LanguageNegotiation/LanguageNegotiationSession.php
@@ -125,10 +125,11 @@ public function processOutbound($path, &$options = [], Request $request = NULL,
    */
   public function getLanguageSwitchLinks(Request $request, $type, Url $url) {
     $links = [];
+    $query = [];
+    parse_str($request->getQueryString() ?? '', $query);
     $config = $this->config->get('language.negotiation')->get('session');
     $param = $config['parameter'];
     $language_query = $_SESSION[$param] ?? $this->languageManager->getCurrentLanguage($type)->getId();
-    $query = $request->query->all();
 
     foreach ($this->languageManager->getNativeLanguages() as $language) {
       $langcode = $language->getId();
diff --git a/web/core/modules/language/src/Plugin/LanguageNegotiation/LanguageNegotiationUrl.php b/web/core/modules/language/src/Plugin/LanguageNegotiation/LanguageNegotiationUrl.php
index 48ee5e028b..f894f52e80 100644
--- a/web/core/modules/language/src/Plugin/LanguageNegotiation/LanguageNegotiationUrl.php
+++ b/web/core/modules/language/src/Plugin/LanguageNegotiation/LanguageNegotiationUrl.php
@@ -195,7 +195,8 @@ public function processOutbound($path, &$options = [], Request $request = NULL,
    */
   public function getLanguageSwitchLinks(Request $request, $type, Url $url) {
     $links = [];
-    $query = $request->query->all();
+    $query = [];
+    parse_str($request->getQueryString() ?? '', $query);
 
     foreach ($this->languageManager->getNativeLanguages() as $language) {
       $links[$language->getId()] = [
diff --git a/web/core/modules/layout_builder/layout_builder.module b/web/core/modules/layout_builder/layout_builder.module
index 1646139aa7..a6e9981fe0 100644
--- a/web/core/modules/layout_builder/layout_builder.module
+++ b/web/core/modules/layout_builder/layout_builder.module
@@ -158,7 +158,7 @@ function layout_builder_entity_view_alter(array &$build, EntityInterface $entity
   // If the entity is displayed within a Layout Builder block and the current
   // route is in the Layout Builder UI, then remove all contextual link
   // placeholders.
-  if ($display instanceof LayoutBuilderEntityViewDisplay && strpos($route_name, 'layout_builder.') === 0) {
+  if ($route_name && $display instanceof LayoutBuilderEntityViewDisplay && strpos($route_name, 'layout_builder.') === 0) {
     unset($build['#contextual_links']);
   }
 }
diff --git a/web/core/modules/layout_builder/src/Form/MoveBlockForm.php b/web/core/modules/layout_builder/src/Form/MoveBlockForm.php
index d051de47e8..beb3aa9757 100644
--- a/web/core/modules/layout_builder/src/Form/MoveBlockForm.php
+++ b/web/core/modules/layout_builder/src/Form/MoveBlockForm.php
@@ -190,6 +190,7 @@ public function buildForm(array $form, FormStateInterface $form_state, SectionSt
     if (!isset($components[$uuid])) {
       $components[$uuid] = $sections[$delta]->getComponent($uuid);
     }
+    $state_weight_delta = round(count($components) / 2);
     foreach ($components as $component_uuid => $component) {
       /** @var \Drupal\Core\Block\BlockPluginInterface $plugin */
       $plugin = $component->getPlugin();
@@ -222,6 +223,7 @@ public function buildForm(array $form, FormStateInterface $form_state, SectionSt
           '#attributes' => [
             'class' => ['table-sort-weight'],
           ],
+          '#delta' => $state_weight_delta,
         ],
       ];
     }
diff --git a/web/core/modules/layout_builder/tests/src/FunctionalJavascript/MoveBlockFormTest.php b/web/core/modules/layout_builder/tests/src/FunctionalJavascript/MoveBlockFormTest.php
index d6b0093271..4f73970306 100644
--- a/web/core/modules/layout_builder/tests/src/FunctionalJavascript/MoveBlockFormTest.php
+++ b/web/core/modules/layout_builder/tests/src/FunctionalJavascript/MoveBlockFormTest.php
@@ -96,6 +96,7 @@ protected function setUp(): void {
    */
   public function testMoveBlock() {
     $page = $this->getSession()->getPage();
+    $assert_session = $this->assertSession();
 
     // Reorder body field in current region.
     $this->openBodyMoveForm(1, 'content', ['Links', 'Body (current)']);
@@ -135,6 +136,47 @@ public function testMoveBlock() {
     $this->assertBlockTable(['Body (current)']);
     $page->pressButton('Move');
     $this->assertRegionBlocksOrder(0, 'second', ['.block-field-blocknodebundle-with-section-fieldbody']);
+
+    // The weight element uses -10 to 10 by default, which can cause bugs.
+    // Add 25 'Powered by Drupal' blocks to a new section.
+    $page->clickLink('Add section');
+    $assert_session->waitForElementVisible('css', '#drupal-off-canvas');
+    $assert_session->assertWaitOnAjaxRequest();
+    $page->clickLink('One column');
+    $assert_session->assertWaitOnAjaxRequest();
+    $this->assertNotEmpty($assert_session->waitForElementVisible('css', 'input[value="Add section"]'));
+    $page->pressButton('Add section');
+    $assert_session->assertNoElementAfterWait('css', '#drupal-off-canvas');
+    $large_block_number = 25;
+    for ($i = 0; $i < $large_block_number; $i++) {
+      $assert_session->elementExists('css', '[data-layout-delta="0"].layout--onecol [data-region="content"] .layout-builder__add-block')->click();
+      $this->assertNotEmpty($assert_session->waitForElementVisible('css', '#drupal-off-canvas a:contains("Powered by Drupal")'));
+      $assert_session->assertWaitOnAjaxRequest();
+      $page->clickLink('Powered by Drupal');
+      $this->assertNotEmpty($assert_session->waitForElementVisible('css', 'input[value="Add block"]'));
+      $assert_session->assertWaitOnAjaxRequest();
+      $page->pressButton('Add block');
+      $assert_session->assertNoElementAfterWait('css', '#drupal-off-canvas');
+    }
+    $first_region_block_locator = '[data-layout-delta="0"].layout--onecol [data-region="content"] [data-layout-block-uuid]';
+    $assert_session->elementsCount('css', $first_region_block_locator, $large_block_number);
+
+    // Move the Body block to the end of the new section.
+    $this->openBodyMoveForm(1, 'second', ['Body (current)']);
+    $page->selectFieldOption('Region', '0:content');
+    $expected_block_table = array_fill(0, $large_block_number, 'Powered by Drupal');
+    $expected_block_table[] = 'Body (current)';
+    $this->assertBlockTable($expected_block_table);
+    $expected_block_table = array_fill(0, $large_block_number - 1, 'Powered by Drupal');
+    $expected_block_table[] = 'Body (current)*';
+    $expected_block_table[] = 'Powered by Drupal';
+    $this->moveBlockWithKeyboard('up', 'Body', $expected_block_table);
+    $page->pressButton('Move');
+    $assert_session->assertNoElementAfterWait('css', '#drupal-off-canvas');
+    // Get all blocks currently in the region.
+    $blocks = $page->findAll('css', $first_region_block_locator);
+    // The second to last $block should be the body.
+    $this->assertTrue($blocks[count($blocks) - 2]->hasClass('block-field-blocknodebundle-with-section-fieldbody'));
   }
 
   /**
diff --git a/web/core/modules/link/tests/src/Functional/LinkFieldTest.php b/web/core/modules/link/tests/src/Functional/LinkFieldTest.php
index 47ddf484d3..9f1255bce7 100644
--- a/web/core/modules/link/tests/src/Functional/LinkFieldTest.php
+++ b/web/core/modules/link/tests/src/Functional/LinkFieldTest.php
@@ -68,10 +68,24 @@ protected function setUp(): void {
     ]));
   }
 
+  /**
+   * Tests the functionality and rendering of the link field.
+   *
+   * This is being as one to avoid multiple Drupal install.
+   */
+  public function testLinkField() {
+    $this->doTestURLValidation();
+    $this->doTestLinkTitle();
+    $this->doTestLinkFormatter();
+    $this->doTestLinkSeparateFormatter();
+    $this->doTestEditNonNodeEntityLink();
+    $this->doTestLinkTypeOnLinkWidget();
+  }
+
   /**
    * Tests link field URL validation.
    */
-  public function testURLValidation() {
+  protected function doTestURLValidation() {
     $field_name = mb_strtolower($this->randomMachineName());
     // Create a field with settings to validate.
     $this->fieldStorage = FieldStorageConfig::create([
@@ -255,7 +269,7 @@ protected function assertInvalidEntries(string $field_name, array $invalid_entri
   /**
    * Tests the link title settings of a link field.
    */
-  public function testLinkTitle() {
+  protected function doTestLinkTitle() {
     $field_name = mb_strtolower($this->randomMachineName());
     // Create a field with settings to validate.
     $this->fieldStorage = FieldStorageConfig::create([
@@ -380,7 +394,7 @@ public function testLinkTitle() {
   /**
    * Tests the default 'link' formatter.
    */
-  public function testLinkFormatter() {
+  protected function doTestLinkFormatter() {
     $field_name = mb_strtolower($this->randomMachineName());
     // Create a field with settings to validate.
     $this->fieldStorage = FieldStorageConfig::create([
@@ -537,7 +551,7 @@ public function testLinkFormatter() {
    * This test is mostly the same as testLinkFormatter(), but they cannot be
    * merged, since they involve different configuration and output.
    */
-  public function testLinkSeparateFormatter() {
+  protected function doTestLinkSeparateFormatter() {
     $field_name = mb_strtolower($this->randomMachineName());
     // Create a field with settings to validate.
     $this->fieldStorage = FieldStorageConfig::create([
@@ -664,7 +678,7 @@ public function testLinkSeparateFormatter() {
    * a link and also which LinkItemInterface::LINK_* is (EXTERNAL, GENERIC,
    * INTERNAL).
    */
-  public function testLinkTypeOnLinkWidget() {
+  protected function doTestLinkTypeOnLinkWidget() {
 
     $link_type = LinkItemInterface::LINK_EXTERNAL;
     $field_name = mb_strtolower($this->randomMachineName());
@@ -702,7 +716,7 @@ public function testLinkTypeOnLinkWidget() {
   /**
    * Tests editing a link to a non-node entity.
    */
-  public function testEditNonNodeEntityLink() {
+  protected function doTestEditNonNodeEntityLink() {
 
     $entity_type_manager = \Drupal::entityTypeManager();
     $entity_test_storage = $entity_type_manager->getStorage('entity_test');
diff --git a/web/core/modules/media/src/Plugin/Field/FieldFormatter/MediaThumbnailFormatter.php b/web/core/modules/media/src/Plugin/Field/FieldFormatter/MediaThumbnailFormatter.php
index dc77b17888..83a506c65e 100644
--- a/web/core/modules/media/src/Plugin/Field/FieldFormatter/MediaThumbnailFormatter.php
+++ b/web/core/modules/media/src/Plugin/Field/FieldFormatter/MediaThumbnailFormatter.php
@@ -183,6 +183,14 @@ public static function isApplicable(FieldDefinitionInterface $field_definition)
     return ($field_definition->getFieldStorageDefinition()->getSetting('target_type') == 'media');
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  protected function checkAccess(EntityInterface $entity) {
+    return $entity->access('view', NULL, TRUE)
+      ->andIf(parent::checkAccess($entity));
+  }
+
   /**
    * Get the URL for the media thumbnail.
    *
diff --git a/web/core/modules/menu_link_content/src/Plugin/migrate/source/MenuLink.php b/web/core/modules/menu_link_content/src/Plugin/migrate/source/MenuLink.php
index b998b6eb1e..0f4676b62b 100644
--- a/web/core/modules/menu_link_content/src/Plugin/migrate/source/MenuLink.php
+++ b/web/core/modules/menu_link_content/src/Plugin/migrate/source/MenuLink.php
@@ -50,7 +50,11 @@ class MenuLink extends DrupalSqlBase {
    */
   public function query() {
     $query = $this->select('menu_links', 'ml')
-      ->fields('ml');
+      ->fields('ml')
+      // Shortcut set links are migrated by the d7_shortcut migration.
+      // Shortcuts are not used in Drupal 6.
+      // @see Drupal\shortcut\Plugin\migrate\source\d7\Shortcut::query()
+      ->condition('ml.menu_name', 'shortcut-set-%', 'NOT LIKE');
     $and = $query->andConditionGroup()
       ->condition('ml.module', 'menu')
       ->condition('ml.router_path', ['admin/build/menu-customize/%', 'admin/structure/menu/manage/%'], 'NOT IN');
diff --git a/web/core/modules/menu_link_content/tests/src/Kernel/Migrate/d7/MigrateMenuLinkTest.php b/web/core/modules/menu_link_content/tests/src/Kernel/Migrate/d7/MigrateMenuLinkTest.php
index cd271f89d5..dbe7d0f137 100644
--- a/web/core/modules/menu_link_content/tests/src/Kernel/Migrate/d7/MigrateMenuLinkTest.php
+++ b/web/core/modules/menu_link_content/tests/src/Kernel/Migrate/d7/MigrateMenuLinkTest.php
@@ -112,6 +112,11 @@ public function testMenuLinks() {
     $this->assertEntity(485, 'en', 'is - The thing about Deep Space 9', 'tools', NULL, TRUE, FALSE, ['attributes' => ['title' => '']], 'entity:node/2', 10);
     $this->assertEntity(486, 'und', 'is - The thing about Firefly', 'tools', NULL, TRUE, FALSE, ['attributes' => ['title' => '']], 'entity:node/4', 11);
     $this->assertEntity(487, 'en', 'en - The thing about Firefly', 'tools', NULL, TRUE, FALSE, ['attributes' => ['title' => '']], 'entity:node/4', 12);
+
+    // Test there have been no attempts to stub a shortcut in a MigrationLookup
+    // process.
+    $messages = $this->getMigration('d7_menu')->getIdMap()->getMessages()->fetchAll();
+    $this->assertCount(0, $messages);
   }
 
 }
diff --git a/web/core/modules/menu_link_content/tests/src/Kernel/Plugin/migrate/source/MenuLinkTest.php b/web/core/modules/menu_link_content/tests/src/Kernel/Plugin/migrate/source/MenuLinkTest.php
index b8d80c1531..2f6d19a3e1 100644
--- a/web/core/modules/menu_link_content/tests/src/Kernel/Plugin/migrate/source/MenuLinkTest.php
+++ b/web/core/modules/menu_link_content/tests/src/Kernel/Plugin/migrate/source/MenuLinkTest.php
@@ -241,6 +241,37 @@ public function providerSource() {
         'i18n_tsid' => '1',
         'skip_translation' => FALSE,
       ],
+      [
+        // D7 shortcut set link.
+        'menu_name' => 'shortcut-set-1',
+        'mlid' => 301,
+        'plid' => 0,
+        'link_path' => 'node/add',
+        'router_path' => 'node/add',
+        'link_title' => 'Add Content',
+        'options' => [],
+        'module' => 'menu',
+        'hidden' => 0,
+        'external' => 0,
+        'has_children' => 0,
+        'expanded' => 0,
+        'weight' => 0,
+        'depth' => 1,
+        'customized' => 0,
+        'p1' => '301',
+        'p2' => '0',
+        'p3' => '0',
+        'p4' => '0',
+        'p5' => '0',
+        'p6' => '0',
+        'p7' => '0',
+        'p8' => '0',
+        'p9' => '0',
+        'updated' => '0',
+        'language' => 'und',
+        'i18n_tsid' => '0',
+        'skip_translation' => TRUE,
+      ],
     ];
 
     // Add long link title attributes to source data.
diff --git a/web/core/modules/node/node.api.php b/web/core/modules/node/node.api.php
index face0558d2..8b01c66323 100644
--- a/web/core/modules/node/node.api.php
+++ b/web/core/modules/node/node.api.php
@@ -22,11 +22,8 @@
  * "realms". In hook_node_access_records(), the realms and grant IDs are
  * associated with permission to view, edit, and delete individual nodes.
  *
- * The realms and grant IDs can be arbitrarily defined by your node access
- * module; it is common to use role IDs as grant IDs, but that is not required.
- * Your module could instead maintain its own list of users, where each list has
- * an ID. In that case, the return value of this hook would be an array of the
- * list IDs that this user is a member of.
+ * Grant IDs can be arbitrarily defined by a node access module using a list of
+ * integer IDs associated with users.
  *
  * A node access module may implement as many realms as necessary to properly
  * define the access privileges for the nodes. Note that the system makes no
diff --git a/web/core/modules/responsive_image/responsive_image.module b/web/core/modules/responsive_image/responsive_image.module
index 6bd0e51f28..9a56204589 100644
--- a/web/core/modules/responsive_image/responsive_image.module
+++ b/web/core/modules/responsive_image/responsive_image.module
@@ -348,8 +348,8 @@ function template_preprocess_responsive_image(&$variables) {
  * @param array $multipliers
  *   An array with multipliers as keys and image style mappings as values.
  *
- * @return \Drupal\Core\Template\Attribute[]
- *   An array of attributes for the source tag.
+ * @return \Drupal\Core\Template\Attribute
+ *   An object of attributes for the source tag.
  */
 function _responsive_image_build_source_attributes(array $variables, BreakpointInterface $breakpoint, array $multipliers) {
   if ((empty($variables['width']) || empty($variables['height']))) {
diff --git a/web/core/modules/system/src/Controller/SystemInfoController.php b/web/core/modules/system/src/Controller/SystemInfoController.php
index 76c2c7a377..3cbfa68652 100644
--- a/web/core/modules/system/src/Controller/SystemInfoController.php
+++ b/web/core/modules/system/src/Controller/SystemInfoController.php
@@ -59,7 +59,7 @@ public function status() {
   public function php() {
     if (function_exists('phpinfo')) {
       ob_start();
-      phpinfo();
+      phpinfo(~ (INFO_VARIABLES | INFO_ENVIRONMENT));
       $output = ob_get_clean();
     }
     else {
diff --git a/web/core/modules/system/src/Form/ModulesUninstallForm.php b/web/core/modules/system/src/Form/ModulesUninstallForm.php
index 82c014c230..483959410c 100644
--- a/web/core/modules/system/src/Form/ModulesUninstallForm.php
+++ b/web/core/modules/system/src/Form/ModulesUninstallForm.php
@@ -146,8 +146,22 @@ public function buildForm(array $form, FormStateInterface $form_state) {
       return $form;
     }
 
-    // Sort all modules by their name.
-    uasort($uninstallable, [ModuleExtensionList::class, 'sortByName']);
+    // Deprecated and obsolete modules should appear at the top of the
+    // uninstallation list.
+    $unstable_lifecycle = array_flip([
+      ExtensionLifecycle::DEPRECATED,
+      ExtensionLifecycle::OBSOLETE,
+    ]);
+
+    // Sort all modules by their lifecycle identifier and name.
+    uasort($uninstallable, function ($a, $b) use ($unstable_lifecycle) {
+      $lifecycle_a = isset($unstable_lifecycle[$a->info[ExtensionLifecycle::LIFECYCLE_IDENTIFIER]]) ? -1 : 1;
+      $lifecycle_b = isset($unstable_lifecycle[$b->info[ExtensionLifecycle::LIFECYCLE_IDENTIFIER]]) ? -1 : 1;
+      if ($lifecycle_a === $lifecycle_b) {
+        return ModuleExtensionList::sortByName($a, $b);
+      }
+      return $lifecycle_a <=> $lifecycle_b;
+    });
     $validation_reasons = $this->moduleInstaller->validateUninstall(array_keys($uninstallable));
 
     $form['uninstall'] = ['#tree' => TRUE];
diff --git a/web/core/modules/system/tests/modules/entity_test/src/EntityTestAccessControlHandler.php b/web/core/modules/system/tests/modules/entity_test/src/EntityTestAccessControlHandler.php
index 4e7b4dc2a0..7aab7849b5 100644
--- a/web/core/modules/system/tests/modules/entity_test/src/EntityTestAccessControlHandler.php
+++ b/web/core/modules/system/tests/modules/entity_test/src/EntityTestAccessControlHandler.php
@@ -55,6 +55,9 @@ protected function checkAccess(EntityInterface $entity, $operation, AccountInter
           return AccessResult::allowedIfHasPermission($account, 'view test entity translations');
         }
       }
+      if ($entity instanceof EntityPublishedInterface && !$entity->isPublished()) {
+        return AccessResult::neutral('Unpublished entity');
+      }
       return AccessResult::allowedIfHasPermission($account, 'view test entity');
     }
     elseif (in_array($operation, ['update', 'delete'])) {
diff --git a/web/core/modules/system/tests/modules/form_test/src/Form/JavascriptStatesForm.php b/web/core/modules/system/tests/modules/form_test/src/Form/JavascriptStatesForm.php
index 4d589d349d..851c55e2fc 100644
--- a/web/core/modules/system/tests/modules/form_test/src/Form/JavascriptStatesForm.php
+++ b/web/core/modules/system/tests/modules/form_test/src/Form/JavascriptStatesForm.php
@@ -31,6 +31,52 @@ public function buildForm(array $form, FormStateInterface $form_state) {
       '#type' => 'textfield',
       '#title' => 'Textfield trigger',
     ];
+    $form['radios_opposite1'] = [
+      '#type' => 'radios',
+      '#title' => 'Radios opposite 1',
+      '#options' => [
+        0 => 'zero',
+        1 => 'one',
+      ],
+      '#default_value' => 0,
+      0 => [
+        '#states' => [
+          'checked' => [
+            ':input[name="radios_opposite2"]' => ['value' => 1],
+          ],
+        ],
+      ],
+      1 => [
+        '#states' => [
+          'checked' => [
+            ':input[name="radios_opposite2"]' => ['value' => 0],
+          ],
+        ],
+      ],
+    ];
+    $form['radios_opposite2'] = [
+      '#type' => 'radios',
+      '#title' => 'Radios opposite 2',
+      '#options' => [
+        0 => 'zero',
+        1 => 'one',
+      ],
+      '#default_value' => 1,
+      0 => [
+        '#states' => [
+          'checked' => [
+            ':input[name="radios_opposite1"]' => ['value' => 1],
+          ],
+        ],
+      ],
+      1 => [
+        '#states' => [
+          'checked' => [
+            ':input[name="radios_opposite1"]' => ['value' => 0],
+          ],
+        ],
+      ],
+    ];
     $form['radios_trigger'] = [
       '#type' => 'radios',
       '#title' => 'Radios trigger',
@@ -60,6 +106,10 @@ public function buildForm(array $form, FormStateInterface $form_state) {
       '#empty_value' => '_none',
       '#empty_option' => '- None -',
     ];
+    $form['number_trigger'] = [
+      '#type' => 'number',
+      '#title' => 'Number trigger',
+    ];
 
     // Tested fields.
     // Checkbox trigger.
@@ -328,6 +378,17 @@ public function buildForm(array $form, FormStateInterface $form_state) {
       ],
     ];
 
+    // Number triggers.
+    $form['item_visible_when_number_trigger_filled_by_spinner'] = [
+      '#type' => 'item',
+      '#title' => 'Item visible when number trigger filled by spinner widget',
+      '#states' => [
+        'visible' => [
+          ':input[name="number_trigger"]' => ['filled' => TRUE],
+        ],
+      ],
+    ];
+
     $form['select'] = [
       '#type' => 'select',
       '#title' => 'select 1',
diff --git a/web/core/modules/system/tests/src/Functional/Module/UninstallTest.php b/web/core/modules/system/tests/src/Functional/Module/UninstallTest.php
index a5ecb9c242..1fb8c5234d 100644
--- a/web/core/modules/system/tests/src/Functional/Module/UninstallTest.php
+++ b/web/core/modules/system/tests/src/Functional/Module/UninstallTest.php
@@ -82,6 +82,20 @@ public function testUninstallPage() {
     $this->assertSession()->elementExists('xpath', "//a[contains(@aria-label, 'View information on the Obsolete status of the module System obsolete status test')]");
     $this->assertSession()->elementExists('xpath', "//a[contains(@href, 'https://example.com/obsolete')]");
 
+    $form = $this->assertSession()->elementExists('xpath', "//form[@id='system-modules-uninstall']");
+    $form_html = $form->getOuterHtml();
+
+    // Select the first stable module on the uninstall list.
+    $module_stable = $this->assertSession()->elementExists('xpath', "//label[contains(@class, 'module-name') and not(./a[contains(@class, 'module-link--non-stable')])]")->getOuterHtml();
+
+    // Select the unstable modules (deprecated, and obsolete).
+    $module_unstable_1 = $this->assertSession()->elementExists('xpath', "//label[./a[contains(@aria-label, 'View information on the Deprecated status of the module Deprecated module')]]")->getOuterHtml();
+    $module_unstable_2 = $this->assertSession()->elementExists('xpath', "//label[./a[contains(@aria-label, 'View information on the Obsolete status of the module System obsolete status test')]]")->getOuterHtml();
+
+    // Check that all unstable modules appear before the first stable module.
+    $this->assertGreaterThan(strpos($form_html, $module_unstable_1), strpos($form_html, $module_stable));
+    $this->assertGreaterThan(strpos($form_html, $module_unstable_2), strpos($form_html, $module_stable));
+
     foreach (\Drupal::service('extension.list.module')->getAllInstalledInfo() as $module => $info) {
       $field_name = "uninstall[$module]";
       if (!empty($info['required'])) {
diff --git a/web/core/modules/system/tests/src/Kernel/Mail/MailTest.php b/web/core/modules/system/tests/src/Kernel/Mail/MailTest.php
index 1eed7c5007..76167feaa3 100644
--- a/web/core/modules/system/tests/src/Kernel/Mail/MailTest.php
+++ b/web/core/modules/system/tests/src/Kernel/Mail/MailTest.php
@@ -40,6 +40,12 @@ protected function setUp(): void {
     parent::setUp();
     $this->installEntitySchema('user');
     $this->installEntitySchema('file');
+
+    // Set required site configuration.
+    $this->config('system.site')
+      ->set('mail', 'mailtest@example.com')
+      ->set('name', 'Drupal')
+      ->save();
   }
 
   /**
@@ -115,12 +121,6 @@ public function testCancelMessage() {
   public function testFromAndReplyToHeader() {
     $language = \Drupal::languageManager()->getCurrentLanguage();
 
-    // Set required site configuration.
-    $this->config('system.site')
-      ->set('mail', 'mailtest@example.com')
-      ->set('name', 'Drupal')
-      ->save();
-
     // Reset the state variable that holds sent messages.
     \Drupal::state()->set('system.test_mail_collector', []);
     // Send an email with a reply-to address specified.
@@ -153,6 +153,15 @@ public function testFromAndReplyToHeader() {
     // Errors-to header must not be set, it is deprecated.
     $this->assertFalse(isset($sent_message['headers']['Errors-To']));
 
+    // Test that From names containing commas work as expected.
+    $this->config('system.site')->set('name', 'Foo, Bar, and Baz')->save();
+    // Send an email and check that the From-header contains the site name.
+    \Drupal::service('plugin.manager.mail')->mail('mail_cancel_test', 'from_test', 'from_test@example.com', $language);
+    $captured_emails = \Drupal::state()->get('system.test_mail_collector');
+    $sent_message = end($captured_emails);
+    // From header contains the quoted site name with commas.
+    $this->assertEquals('"Foo, Bar, and Baz" <mailtest@example.com>', $sent_message['headers']['From']);
+
     // Test RFC-2822 rules are respected for 'display-name' component of
     // 'From:' header. Specials characters are not allowed, so randomly add one
     // of them to the site name and check the string is wrapped in quotes. Also
diff --git a/web/core/modules/system/tests/src/Kernel/System/FloodTest.php b/web/core/modules/system/tests/src/Kernel/System/FloodTest.php
index 97b78aa974..cc7b04bb4d 100644
--- a/web/core/modules/system/tests/src/Kernel/System/FloodTest.php
+++ b/web/core/modules/system/tests/src/Kernel/System/FloodTest.php
@@ -3,7 +3,6 @@
 namespace Drupal\Tests\system\Kernel\System;
 
 use Drupal\Core\Flood\DatabaseBackend;
-use Drupal\Core\Flood\MemoryBackend;
 use Drupal\KernelTests\KernelTestBase;
 
 /**
@@ -46,46 +45,6 @@ public function testCleanUp() {
     $this->assertFalse($flood->isAllowed($name, $threshold));
   }
 
-  /**
-   * Tests flood control memory backend.
-   */
-  public function testMemoryBackend() {
-    $threshold = 1;
-    $window_expired = -1;
-    $name = 'flood_test_cleanup';
-
-    $request_stack = \Drupal::service('request_stack');
-    $flood = new MemoryBackend($request_stack);
-    $this->assertTrue($flood->isAllowed($name, $threshold));
-    // Register expired event.
-    $flood->register($name, $window_expired);
-    // Verify event is not allowed.
-    $this->assertFalse($flood->isAllowed($name, $threshold));
-    // Run cron and verify event is now allowed.
-    $flood->garbageCollection();
-    $this->assertTrue($flood->isAllowed($name, $threshold));
-
-    // Register unexpired event.
-    $flood->register($name);
-    // Verify event is not allowed.
-    $this->assertFalse($flood->isAllowed($name, $threshold));
-    // Run cron and verify event is still not allowed.
-    $flood->garbageCollection();
-    $this->assertFalse($flood->isAllowed($name, $threshold));
-  }
-
-  /**
-   * Tests memory backend records events to the nearest microsecond.
-   */
-  public function testMemoryBackendThreshold() {
-    $request_stack = \Drupal::service('request_stack');
-    $flood = new MemoryBackend($request_stack);
-    $flood->register('new event');
-    $this->assertTrue($flood->isAllowed('new event', '2'));
-    $flood->register('new event');
-    $this->assertFalse($flood->isAllowed('new event', '2'));
-  }
-
   /**
    * Tests flood control database backend.
    */
diff --git a/web/core/modules/tour/src/TourViewBuilder.php b/web/core/modules/tour/src/TourViewBuilder.php
index 61e17a732e..105563ad5f 100644
--- a/web/core/modules/tour/src/TourViewBuilder.php
+++ b/web/core/modules/tour/src/TourViewBuilder.php
@@ -60,7 +60,7 @@ public function viewMultiple(array $entities = [], $view_mode = 'full', $langcod
           $body = (string) \Drupal::service('renderer')->renderPlain($body_render_array);
           $output = [
             'body' => $body,
-            'title' => Html::escape($tip->getLabel()),
+            'title' => $tip->getLabel(),
           ];
 
           $selector = $tip->getSelector();
diff --git a/web/core/modules/tour/tests/src/Functional/TourTest.php b/web/core/modules/tour/tests/src/Functional/TourTest.php
index 32e958dbbd..aaf2a781a2 100644
--- a/web/core/modules/tour/tests/src/Functional/TourTest.php
+++ b/web/core/modules/tour/tests/src/Functional/TourTest.php
@@ -158,7 +158,7 @@ public function testTourFunctionality() {
         'tour-test-1' => [
           'id' => 'tour-code-test-1',
           'plugin' => 'text',
-          'label' => 'The rain in spain',
+          'label' => 'The rain in spain is <strong>strong</strong>',
           'body' => 'Falls mostly on the plain.',
           'weight' => '100',
           'selector' => '#tour-code-test-1',
@@ -194,7 +194,7 @@ public function testTourFunctionality() {
 
     $elements = $this->findTip([
       'id' => 'tour-code-test-1',
-      'title' => 'The rain in spain',
+      'title' => 'The rain in spain is <strong>strong</strong>',
     ]);
     $this->assertCount(1, $elements, 'Found the required tip markup for tip 4');
 
diff --git a/web/core/modules/user/config/schema/user.schema.yml b/web/core/modules/user/config/schema/user.schema.yml
index 99285373c3..b778163bf5 100644
--- a/web/core/modules/user/config/schema/user.schema.yml
+++ b/web/core/modules/user/config/schema/user.schema.yml
@@ -122,6 +122,7 @@ user.role.*:
     permissions:
       type: sequence
       label: 'Permissions'
+      orderby: value
       sequence:
         type: string
         label: 'Permission'
diff --git a/web/core/modules/user/src/Entity/Role.php b/web/core/modules/user/src/Entity/Role.php
index 97b0155719..50258f5aca 100644
--- a/web/core/modules/user/src/Entity/Role.php
+++ b/web/core/modules/user/src/Entity/Role.php
@@ -185,12 +185,6 @@ public function preSave(EntityStorageInterface $storage) {
       });
       $this->weight = $max + 1;
     }
-
-    if (!$this->isSyncing()) {
-      // Permissions are always ordered alphabetically to avoid conflicts in the
-      // exported configuration.
-      sort($this->permissions);
-    }
   }
 
   /**
diff --git a/web/core/modules/user/src/Plugin/views/field/Roles.php b/web/core/modules/user/src/Plugin/views/field/Roles.php
index ad504200ba..cf36ba7f4e 100644
--- a/web/core/modules/user/src/Plugin/views/field/Roles.php
+++ b/web/core/modules/user/src/Plugin/views/field/Roles.php
@@ -85,7 +85,7 @@ public function preRender(&$values) {
         $sorted_keys = array_intersect_key($ordered_roles, $user_roles);
         // Merge with the unsorted array of role information which has the
         // effect of sorting it.
-        $user_roles = array_merge($sorted_keys, $user_roles);
+        $user_roles = array_replace($sorted_keys, $user_roles);
       }
     }
   }
diff --git a/web/core/modules/user/tests/src/Functional/Update/UserUpdateRoleDependenciesTest.php b/web/core/modules/user/tests/src/Functional/Update/UserUpdateRoleDependenciesTest.php
index c865a05b29..9140a06b9a 100644
--- a/web/core/modules/user/tests/src/Functional/Update/UserUpdateRoleDependenciesTest.php
+++ b/web/core/modules/user/tests/src/Functional/Update/UserUpdateRoleDependenciesTest.php
@@ -47,7 +47,7 @@ public function testRolePermissions() {
     $this->drupalLogin($this->createUser(['access site reports']));
     $this->drupalGet('admin/reports/dblog', ['query' => ['type[]' => 'update']]);
     $this->clickLink('The role Authenticated user has had the following non-…');
-    $this->assertSession()->pageTextContains('The role Authenticated user has had the following non-existent permission(s) removed: use text format plain_text, does_not_exist.');
+    $this->assertSession()->pageTextContains('The role Authenticated user has had the following non-existent permission(s) removed: does_not_exist, use text format plain_text.');
     $this->getSession()->back();
     $this->clickLink('The role Anonymous user has had the following non-…');
     $this->assertSession()->pageTextContains('The role Anonymous user has had the following non-existent permission(s) removed: use text format plain_text.');
diff --git a/web/core/modules/user/tests/src/Kernel/UserMailNotifyTest.php b/web/core/modules/user/tests/src/Kernel/UserMailNotifyTest.php
index 2038d18c0e..a8ac682484 100644
--- a/web/core/modules/user/tests/src/Kernel/UserMailNotifyTest.php
+++ b/web/core/modules/user/tests/src/Kernel/UserMailNotifyTest.php
@@ -82,6 +82,7 @@ public function userMailsProvider() {
    */
   public function testUserMailsSent($op, array $mail_keys) {
     $this->installConfig('user');
+    $this->config('system.site')->set('mail', 'test@example.com')->save();
     $this->config('user.settings')->set('notify.' . $op, TRUE)->save();
     $return = _user_mail_notify($op, $this->createUser());
     $this->assertTrue($return);
@@ -173,6 +174,7 @@ public function testUserRecoveryMailLanguage() {
 
     // Recovery email should respect user preferred langcode by default if
     // langcode not set.
+    $this->config('system.site')->set('mail', 'test@example.com')->save();
     $params['account'] = $user;
     $default_email = \Drupal::service('plugin.manager.mail')->mail('user', 'password_reset', $user->getEmail(), $preferredLangcode, $params);
     $this->assertTrue($default_email['result']);
diff --git a/web/core/modules/user/tests/src/Kernel/UserRoleEntityTest.php b/web/core/modules/user/tests/src/Kernel/UserRoleEntityTest.php
index 5d85147ca1..aa6f9c4acf 100644
--- a/web/core/modules/user/tests/src/Kernel/UserRoleEntityTest.php
+++ b/web/core/modules/user/tests/src/Kernel/UserRoleEntityTest.php
@@ -46,4 +46,16 @@ public function testGrantingNonExistentPermission() {
       ->save();
   }
 
+  public function testPermissionRevokeAndConfigSync() {
+    $role = Role::create(['id' => 'test_role', 'label' => 'Test role']);
+    $role->setSyncing(TRUE);
+    $role->grantPermission('a')
+      ->grantPermission('b')
+      ->grantPermission('c')
+      ->save();
+    $this->assertSame(['a', 'b', 'c'], $role->getPermissions());
+    $role->revokePermission('b')->save();
+    $this->assertSame(['a', 'c'], $role->getPermissions());
+  }
+
 }
diff --git a/web/core/modules/user/tests/src/Kernel/Views/UserRoleTest.php b/web/core/modules/user/tests/src/Kernel/Views/UserRoleTest.php
new file mode 100644
index 0000000000..28b9e7fae0
--- /dev/null
+++ b/web/core/modules/user/tests/src/Kernel/Views/UserRoleTest.php
@@ -0,0 +1,41 @@
+<?php
+
+namespace Drupal\Tests\user\Kernel\Views;
+
+use Drupal\Tests\views\Kernel\ViewsKernelTestBase;
+use Drupal\user\Entity\Role;
+use Drupal\user\Entity\User;
+use Drupal\views\Views;
+
+/**
+ * Tests rendering when the role is numeric.
+ *
+ * @group user
+ */
+class UserRoleTest extends ViewsKernelTestBase {
+
+  /**
+   * Tests numeric role.
+   */
+  public function testNumericRole() {
+    $this->installEntitySchema('user');
+    $this->installSchema('user', ['users_data']);
+
+    Role::create(['id' => 123, 'label' => 'Numeric'])
+      ->save();
+
+    $user = User::create([
+      'uid' => 2,
+      'name' => 'foo',
+      'roles' => 123,
+    ]);
+    $user->save();
+
+    $view = Views::getView('user_admin_people');
+    $this->executeView($view);
+    $view->render('user_admin_people');
+    $output = $view->field['roles_target_id']->render($view->result[0]);
+    $this->assertEquals(2, $output);
+  }
+
+}
diff --git a/web/core/modules/user/user.module b/web/core/modules/user/user.module
index 5eb8c52188..8312a1efae 100644
--- a/web/core/modules/user/user.module
+++ b/web/core/modules/user/user.module
@@ -167,7 +167,7 @@ function user_user_presave(UserInterface $account) {
  * @param string $mail
  *   String with the account's email address.
  *
- * @return object|bool
+ * @return \Drupal\user\UserInterface|false
  *   A fully-loaded $user object upon successful user load or FALSE if user
  *   cannot be loaded.
  *
@@ -185,7 +185,7 @@ function user_load_by_mail($mail) {
  * @param string $name
  *   String with the account's user name.
  *
- * @return object|bool
+ * @return \Drupal\user\UserInterface|false
  *   A fully-loaded $user object upon successful user load or FALSE if user
  *   cannot be loaded.
  *
diff --git a/web/core/modules/user/user.post_update.php b/web/core/modules/user/user.post_update.php
index 14662df8ef..7bb42b2d56 100644
--- a/web/core/modules/user/user.post_update.php
+++ b/web/core/modules/user/user.post_update.php
@@ -47,3 +47,14 @@ function user_post_update_update_roles(&$sandbox = NULL) {
     );
   }
 }
+
+/**
+ * Ensure permissions stored in role configuration are sorted using the schema.
+ */
+function user_post_update_sort_permissions(&$sandbox = NULL) {
+  \Drupal::classResolver(ConfigEntityUpdater::class)->update($sandbox, 'user_role', function (Role $role) {
+    $permissions = $role->getPermissions();
+    sort($permissions);
+    return $permissions !== $role->getPermissions();
+  });
+}
diff --git a/web/core/modules/views/src/Plugin/views/PluginBase.php b/web/core/modules/views/src/Plugin/views/PluginBase.php
index 0f588c531c..9e6d3c0567 100644
--- a/web/core/modules/views/src/Plugin/views/PluginBase.php
+++ b/web/core/modules/views/src/Plugin/views/PluginBase.php
@@ -89,7 +89,7 @@ abstract class PluginBase extends ComponentPluginBase implements ContainerFactor
   public $displayHandler;
 
   /**
-   * Plugins's definition.
+   * Plugins' definition.
    *
    * @var array
    */
diff --git a/web/core/modules/views/src/Plugin/views/display/DisplayPluginBase.php b/web/core/modules/views/src/Plugin/views/display/DisplayPluginBase.php
index 303cba292a..c404d1c863 100644
--- a/web/core/modules/views/src/Plugin/views/display/DisplayPluginBase.php
+++ b/web/core/modules/views/src/Plugin/views/display/DisplayPluginBase.php
@@ -2231,7 +2231,7 @@ public function elementPreRender(array $element) {
     $element['#feed_icons'] = !empty($view->feedIcons) ? $view->feedIcons : [];
 
     if ($view->display_handler->renderPager()) {
-      $exposed_input = $view->exposed_raw_input ?? NULL;
+      $exposed_input = $view->getExposedInput();
       $element['#pager'] = $view->renderPager($exposed_input);
     }
 
diff --git a/web/core/modules/views/src/Plugin/views/field/TimeInterval.php b/web/core/modules/views/src/Plugin/views/field/TimeInterval.php
index 3c288fe61e..c3eba54387 100644
--- a/web/core/modules/views/src/Plugin/views/field/TimeInterval.php
+++ b/web/core/modules/views/src/Plugin/views/field/TimeInterval.php
@@ -82,7 +82,10 @@ public function buildOptionsForm(&$form, FormStateInterface $form_state) {
    */
   public function render(ResultRow $values) {
     $value = $values->{$this->field_alias};
-    return $this->dateFormatter->formatInterval((int) $value, $this->options['granularity'] ?? 2);
+    if ($value != NULL) {
+      return $this->dateFormatter->formatInterval((int) $value, $this->options['granularity'] ?? 2);
+    }
+    return '';
   }
 
 }
diff --git a/web/core/modules/views/tests/src/Functional/Handler/FieldTimeIntervalTest.php b/web/core/modules/views/tests/src/Functional/Handler/FieldTimeIntervalTest.php
new file mode 100644
index 0000000000..2344984fb6
--- /dev/null
+++ b/web/core/modules/views/tests/src/Functional/Handler/FieldTimeIntervalTest.php
@@ -0,0 +1,97 @@
+<?php
+
+namespace Drupal\Tests\views\Functional\Handler;
+
+use Drupal\Tests\views\Functional\ViewTestBase;
+use Drupal\views\Views;
+use Drupal\Core\StringTranslation\StringTranslationTrait;
+
+/**
+ * Tests the time interval handler.
+ *
+ * @group views
+ * @see \Drupal\views\Plugin\views\field\TimeInterval
+ */
+class FieldTimeIntervalTest extends ViewTestBase {
+
+  use StringTranslationTrait;
+
+  /**
+   * Views used by this test.
+   *
+   * @var array
+   */
+  public static $testViews = ['test_view'];
+
+  /**
+   * {@inheritdoc}
+   */
+  protected $defaultTheme = 'stark';
+
+  /**
+   * Ages dataset.
+   *
+   * @var array
+   */
+  protected $ages = [
+    [0, '0 sec', 2],
+    [1000, '16 min', 1],
+    [1000000, '1 week 4 days 13 hours 46 min', 4],
+    // DateFormatter::formatInterval will output 2 because there are no weeks.
+    [100000000, '3 years 2 months', 5],
+    [NULL, '', 3],
+  ];
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp($import_test_views = TRUE, $modules = ['views_test_config']): void {
+    parent::setUp($import_test_views, $modules);
+
+    $this->enableViewsTestModule();
+  }
+
+  /**
+   * Test TimeInterval handler.
+   */
+  public function testFieldTimeInterval() {
+    $view = Views::getView('test_view');
+    $view->setDisplay();
+    $this->executeView($view);
+    foreach ($view->result as $delta => $row) {
+      [$value, $formatted_value, $granularity] = $this->ages[$delta];
+      $view->field['age']->options['granularity'] = $granularity;
+      $this->assertEquals($formatted_value, $view->field['age']->advancedRender($row));
+    }
+  }
+
+  /**
+   * Overrides \Drupal\views\Tests\ViewUnitTestBase::schemaDefinition().
+   */
+  protected function schemaDefinition() {
+    $schema_definition = parent::schemaDefinition();
+    $schema_definition['views_test_data']['fields']['age']['not null'] = FALSE;
+    return $schema_definition;
+  }
+
+  /**
+   * Overrides \Drupal\views\Tests\ViewUnitTestBase::viewsData().
+   */
+  protected function viewsData() {
+    $data = parent::viewsData();
+    $data['views_test_data']['age']['field']['id'] = 'time_interval';
+    return $data;
+  }
+
+  /**
+   * Overrides \Drupal\views\Tests\ViewUnitTestBase::dataSet().
+   */
+  protected function dataSet() {
+    $data_set = parent::dataSet();
+    foreach ($data_set as $delta => $person) {
+      $data_set[$delta]['age'] = $this->ages[$delta][0];
+    }
+    return $data_set;
+  }
+
+}
diff --git a/web/core/modules/views/tests/src/FunctionalJavascript/PaginationAJAXTest.php b/web/core/modules/views/tests/src/FunctionalJavascript/PaginationAJAXTest.php
index f96b468e3c..00454912af 100644
--- a/web/core/modules/views/tests/src/FunctionalJavascript/PaginationAJAXTest.php
+++ b/web/core/modules/views/tests/src/FunctionalJavascript/PaginationAJAXTest.php
@@ -99,7 +99,7 @@ public function testBasicPagination() {
     $this->assertStringContainsString('Node 6 content', $rows[0]->getHtml());
     $link = $page->findLink('Go to page 3');
     // Test that no unwanted parameters are added to the URL.
-    $this->assertEquals('?status=All&type=All&langcode=All&items_per_page=5&order=changed&sort=asc&title=&page=2', $link->getAttribute('href'));
+    $this->assertEquals('?status=All&type=All&langcode=All&items_per_page=5&order=changed&sort=asc&page=2', $link->getAttribute('href'));
     $this->assertNoDuplicateAssetsOnPage();
 
     $this->clickLink('Go to page 3');
diff --git a/web/core/modules/workspaces/src/WorkspaceRepository.php b/web/core/modules/workspaces/src/WorkspaceRepository.php
index 535c83366b..d939cafdf5 100644
--- a/web/core/modules/workspaces/src/WorkspaceRepository.php
+++ b/web/core/modules/workspaces/src/WorkspaceRepository.php
@@ -116,6 +116,7 @@ public function loadTree() {
       }
       $graph = (new Graph($graph))->searchAndSort();
 
+      $this->tree = [];
       foreach (array_keys($tree) as $workspace_id) {
         $this->tree[$workspace_id] = [
           'depth' => count($graph[$workspace_id]['paths']),
diff --git a/web/core/modules/workspaces/tests/src/Kernel/WorkspaceCRUDTest.php b/web/core/modules/workspaces/tests/src/Kernel/WorkspaceCRUDTest.php
index 19dc0e1661..71803e50d9 100644
--- a/web/core/modules/workspaces/tests/src/Kernel/WorkspaceCRUDTest.php
+++ b/web/core/modules/workspaces/tests/src/Kernel/WorkspaceCRUDTest.php
@@ -323,4 +323,12 @@ public function testDeletingWorkspaceWithChildren() {
     $this->assertNull(Workspace::load('stage'));
   }
 
+  /**
+   * Tests loading the workspace tree when there are no workspaces available.
+   */
+  public function testEmptyWorkspaceTree() {
+    $tree = \Drupal::service('workspaces.repository')->loadTree();
+    $this->assertSame([], $tree);
+  }
+
 }
diff --git a/web/core/profiles/demo_umami/config/install/user.role.author.yml b/web/core/profiles/demo_umami/config/install/user.role.author.yml
index c9d34695e6..53860ff036 100644
--- a/web/core/profiles/demo_umami/config/install/user.role.author.yml
+++ b/web/core/profiles/demo_umami/config/install/user.role.author.yml
@@ -2,6 +2,9 @@ langcode: en
 status: true
 dependencies:
   config:
+    - core.entity_view_display.node.article.full
+    - core.entity_view_display.node.page.full
+    - core.entity_view_display.node.recipe.full
     - node.type.article
     - node.type.page
     - node.type.recipe
@@ -12,6 +15,7 @@ dependencies:
     - content_moderation
     - contextual
     - file
+    - layout_builder
     - node
     - path
     - system
@@ -28,6 +32,10 @@ permissions:
   - 'access toolbar'
   - 'cancel account'
   - 'change own username'
+  - 'configure editable article node layout overrides'
+  - 'configure editable page node layout overrides'
+  - 'configure editable recipe node layout overrides'
+  - 'create and edit custom blocks'
   - 'create article content'
   - 'create page content'
   - 'create recipe content'
diff --git a/web/core/profiles/demo_umami/config/install/user.role.editor.yml b/web/core/profiles/demo_umami/config/install/user.role.editor.yml
index 40d5f651ee..0a0558c768 100644
--- a/web/core/profiles/demo_umami/config/install/user.role.editor.yml
+++ b/web/core/profiles/demo_umami/config/install/user.role.editor.yml
@@ -2,6 +2,9 @@ langcode: en
 status: true
 dependencies:
   config:
+    - core.entity_view_display.node.article.full
+    - core.entity_view_display.node.page.full
+    - core.entity_view_display.node.recipe.full
     - node.type.article
     - node.type.page
     - node.type.recipe
@@ -13,6 +16,7 @@ dependencies:
     - content_translation
     - contextual
     - file
+    - layout_builder
     - node
     - path
     - shortcut
@@ -33,6 +37,10 @@ permissions:
   - 'administer shortcuts'
   - 'cancel account'
   - 'change own username'
+  - 'configure editable article node layout overrides'
+  - 'configure editable page node layout overrides'
+  - 'configure editable recipe node layout overrides'
+  - 'create and edit custom blocks'
   - 'create content translations'
   - 'create terms in recipe_category'
   - 'create terms in tags'
diff --git a/web/core/profiles/demo_umami/themes/umami/css/components/tour/tour.theme.css b/web/core/profiles/demo_umami/themes/umami/css/components/tour/tour.theme.css
index 64ee4f64f3..484cf8ed56 100644
--- a/web/core/profiles/demo_umami/themes/umami/css/components/tour/tour.theme.css
+++ b/web/core/profiles/demo_umami/themes/umami/css/components/tour/tour.theme.css
@@ -7,6 +7,7 @@
 }
 
 .shepherd-cancel-icon {
+  border: solid 2px transparent;
   line-height: 1;
 }
 
diff --git a/web/core/scripts/dev/commit-code-check.sh b/web/core/scripts/dev/commit-code-check.sh
index cbabd8efa8..46b90c11f0 100755
--- a/web/core/scripts/dev/commit-code-check.sh
+++ b/web/core/scripts/dev/commit-code-check.sh
@@ -72,29 +72,31 @@
   green=""
   reset=""
   DRUPAL_VERSION=$(php -r "include 'vendor/autoload.php'; print preg_replace('#\.[0-9]+-dev#', '.x', \Drupal::VERSION);")
+  GIT="sudo -u www-data git"
 else
   red=$(tput setaf 1 && tput bold)
   green=$(tput setaf 2)
   reset=$(tput sgr0)
+  GIT="git"
 fi
 
 # Gets list of files to check.
 if [[ "$BRANCH" != "" ]]; then
-  FILES=$(git diff --name-only $BRANCH HEAD);
+  FILES=$($GIT diff --name-only $BRANCH HEAD);
 elif [[ "$CACHED" == "0" ]]; then
   # For DrupalCI patch testing or when running without --cached or --branch,
   # list of all changes in the working directory.
-  FILES=$(git ls-files --other --modified --exclude-standard --exclude=vendor)
+  FILES=$($GIT ls-files --other --modified --exclude-standard --exclude=vendor)
 else
   # Check staged files only.
-  if git rev-parse --verify HEAD >/dev/null 2>&1
+  if $GIT rev-parse --verify HEAD >/dev/null 2>&1
   then
     AGAINST=HEAD
   else
     # Initial commit: diff against an empty tree object
     AGAINST=4b825dc642cb6eb9a060e54bf8d69288fbee4904
   fi
-  FILES=$(git diff --cached --name-only $AGAINST);
+  FILES=$($GIT diff --cached --name-only $AGAINST);
 fi
 
 if [[ "$FILES" == "" ]] && [[ "$DRUPALCI" == "1" ]]; then
@@ -102,10 +104,10 @@
   # need to diff against the Drupal branch or tag related to the Drupal version.
   printf "Creating list of files to check by comparing branch to %s\n" "$DRUPAL_VERSION"
   # On DrupalCI there's a merge commit so we can compare to HEAD~1.
-  FILES=$(git diff --name-only HEAD~1 HEAD);
+  FILES=$($GIT diff --name-only HEAD~1 HEAD);
 fi
 
-TOP_LEVEL=$(git rev-parse --show-toplevel)
+TOP_LEVEL=$($GIT rev-parse --show-toplevel)
 
 # This variable will be set to one when the file core/phpcs.xml.dist is changed.
 PHPCS_XML_DIST_FILE_CHANGED=0
diff --git a/web/core/scripts/run-tests.sh b/web/core/scripts/run-tests.sh
index 1ce883352d..59149f587c 100755
--- a/web/core/scripts/run-tests.sh
+++ b/web/core/scripts/run-tests.sh
@@ -2,7 +2,12 @@
 
 /**
  * @file
- * This script runs Drupal tests from command line.
+ * Script for running tests on DrupalCI.
+ *
+ * This script is intended for use only by drupal.org's testing. In general,
+ * tests should be run directly with phpunit.
+ *
+ * @internal
  */
 
 use Drupal\Component\FileSystem\FileSystem;
diff --git a/web/core/tests/Drupal/FunctionalJavascriptTests/Ajax/MultiFormTest.php b/web/core/tests/Drupal/FunctionalJavascriptTests/Ajax/MultiFormTest.php
index fbd979c4e3..83c26e96e1 100644
--- a/web/core/tests/Drupal/FunctionalJavascriptTests/Ajax/MultiFormTest.php
+++ b/web/core/tests/Drupal/FunctionalJavascriptTests/Ajax/MultiFormTest.php
@@ -92,20 +92,21 @@ public function testMultiForm() {
     // page update, ensure the same as above.
 
     for ($i = 0; $i < 2; $i++) {
-      $forms = $page->find('xpath', $form_xpath);
+      $forms = $page->findAll('xpath', $form_xpath);
       foreach ($forms as $offset => $form) {
         $button = $form->findButton('Add another item');
         $this->assertNotNull($button, 'Add Another Item button exists');
         $button->press();
 
-        // Wait for page update.
-        $this->assertSession()->assertWaitOnAjaxRequest();
+        // Wait for field to be added with ajax.
+        $this->assertNotEmpty($page->waitFor(10, function () use ($form, $i) {
+          return $form->findField('field_ajax_test[' . ($i + 1) . '][value]');
+        }));
 
-        // After AJAX request and response page will update.
-        $page_updated = $session->getPage();
-        $field = $page_updated->findAll('xpath', '.' . $field_xpath);
-        $this->assertCount($i + 2, $field[0]->find('xpath', '.' . $field_items_xpath_suffix), 'Found the correct number of field items after an AJAX submission.');
-        $this->assertNotNull($field[0]->find('xpath', '.' . $button_xpath_suffix), 'Found the "add more" button after an AJAX submission.');
+        // After AJAX request and response verify the correct number of text
+        // fields (including title), as well as the "Add another item" button.
+        $this->assertCount($i + 3, $form->findAll('css', 'input[type="text"]'), 'Found the correct number of field items after an AJAX submission.');
+        $this->assertNotEmpty($form->findButton('Add another item'), 'Found the "add more" button after an AJAX submission.');
         $this->assertSession()->pageContainsNoDuplicateId();
       }
     }
diff --git a/web/core/tests/Drupal/FunctionalJavascriptTests/Core/Form/JavascriptStatesTest.php b/web/core/tests/Drupal/FunctionalJavascriptTests/Core/Form/JavascriptStatesTest.php
index 259a7e146f..80790d48f3 100644
--- a/web/core/tests/Drupal/FunctionalJavascriptTests/Core/Form/JavascriptStatesTest.php
+++ b/web/core/tests/Drupal/FunctionalJavascriptTests/Core/Form/JavascriptStatesTest.php
@@ -63,6 +63,7 @@ public function testJavascriptStates() {
     $this->doRadiosTriggerTests();
     $this->doSelectTriggerTests();
     $this->doMultipleTriggerTests();
+    $this->doNestedTriggerTests();
   }
 
   /**
@@ -322,4 +323,30 @@ protected function doMultipleTriggerTests() {
     $this->assertTrue($item_visible_value2_and_textfield->isVisible());
   }
 
+  /**
+   * Tests states of radios element triggered by other radios element.
+   */
+  protected function doNestedTriggerTests() {
+    $this->drupalGet('form-test/javascript-states-form');
+    $page = $this->getSession()->getPage();
+
+    // Find trigger and target elements.
+    $radios_opposite1 = $page->findField('radios_opposite1');
+    $this->assertNotEmpty($radios_opposite1);
+    $radios_opposite2 = $page->findField('radios_opposite2');
+    $this->assertNotEmpty($radios_opposite2);
+
+    // Verify initial state.
+    $this->assertEquals('0', $radios_opposite1->getValue());
+    $this->assertEquals('1', $radios_opposite2->getValue());
+
+    // Set $radios_opposite2 value to 0, $radios_opposite1 value should be 1.
+    $radios_opposite2->setValue('0');
+    $this->assertEquals('1', $radios_opposite1->getValue());
+
+    // Set $radios_opposite1 value to 1, $radios_opposite2 value should be 0.
+    $radios_opposite1->setValue('0');
+    $this->assertEquals('1', $radios_opposite2->getValue());
+  }
+
 }
diff --git a/web/core/tests/Drupal/FunctionalTests/AssertLegacyTrait.php b/web/core/tests/Drupal/FunctionalTests/AssertLegacyTrait.php
index 25bf18878b..6ac0ffaf37 100644
--- a/web/core/tests/Drupal/FunctionalTests/AssertLegacyTrait.php
+++ b/web/core/tests/Drupal/FunctionalTests/AssertLegacyTrait.php
@@ -4,7 +4,6 @@
 
 use Behat\Mink\Element\NodeElement;
 use Behat\Mink\Exception\ExpectationException;
-use Behat\Mink\Selector\Xpath\Escaper;
 use Drupal\Component\Render\FormattableMarkup;
 use Drupal\Component\Utility\Xss;
 use Drupal\KernelTests\AssertLegacyTrait as BaseAssertLegacyTrait;
@@ -759,7 +758,7 @@ protected function assertFieldsByValue($fields, $value = NULL, $message = '') {
             // Input element with correct value.
             $found = TRUE;
           }
-          elseif ($field->find('xpath', '//option[@value = ' . (new Escaper())->escapeLiteral($value) . ' and @selected = "selected"]')) {
+          elseif ($field->find('xpath', '//option[@value = ' . $value . ' and @selected = "selected"]')) {
             // Select element with an option.
             $found = TRUE;
           }
diff --git a/web/core/tests/Drupal/FunctionalTests/Installer/InstallerExistingConfigExistingSettingsTest.php b/web/core/tests/Drupal/FunctionalTests/Installer/InstallerExistingConfigExistingSettingsTest.php
new file mode 100644
index 0000000000..fe762f113a
--- /dev/null
+++ b/web/core/tests/Drupal/FunctionalTests/Installer/InstallerExistingConfigExistingSettingsTest.php
@@ -0,0 +1,40 @@
+<?php
+
+namespace Drupal\FunctionalTests\Installer;
+
+use Drupal\Core\Database\Database;
+
+/**
+ * Verifies that installing from existing configuration works.
+ *
+ * @group Installer
+ */
+class InstallerExistingConfigExistingSettingsTest extends InstallerExistingConfigTest {
+
+  /**
+   * {@inheritdoc}
+   *
+   * Partially configures a preexisting settings.php file before invoking the
+   * interactive installer.
+   */
+  protected function prepareEnvironment() {
+    parent::prepareEnvironment();
+    // Pre-configure hash salt.
+    // Any string is valid, so simply use the class name of this test.
+    $this->settings['settings']['hash_salt'] = (object) [
+      'value' => __CLASS__,
+      'required' => TRUE,
+    ];
+
+    // Pre-configure database credentials.
+    $connection_info = Database::getConnectionInfo();
+    unset($connection_info['default']['pdo']);
+    unset($connection_info['default']['init_commands']);
+
+    $this->settings['databases']['default'] = (object) [
+      'value' => $connection_info,
+      'required' => TRUE,
+    ];
+  }
+
+}
diff --git a/web/core/tests/Drupal/KernelTests/Core/Action/EmailActionTest.php b/web/core/tests/Drupal/KernelTests/Core/Action/EmailActionTest.php
index 81d8aad895..f71fcd34a7 100644
--- a/web/core/tests/Drupal/KernelTests/Core/Action/EmailActionTest.php
+++ b/web/core/tests/Drupal/KernelTests/Core/Action/EmailActionTest.php
@@ -31,6 +31,8 @@ protected function setUp(): void {
    * Tests the email action plugin.
    */
   public function testEmailAction() {
+    $this->config('system.site')->set('mail', 'test@example.com')->save();
+
     /** @var \Drupal\Core\Action\ActionManager $plugin_manager */
     $plugin_manager = $this->container->get('plugin.manager.action');
     $configuration = [
diff --git a/web/core/tests/Drupal/KernelTests/Core/ParamConverter/EntityConverterLatestRevisionTest.php b/web/core/tests/Drupal/KernelTests/Core/ParamConverter/EntityConverterLatestRevisionTest.php
index 9e06a63b20..739c680124 100644
--- a/web/core/tests/Drupal/KernelTests/Core/ParamConverter/EntityConverterLatestRevisionTest.php
+++ b/web/core/tests/Drupal/KernelTests/Core/ParamConverter/EntityConverterLatestRevisionTest.php
@@ -189,4 +189,66 @@ public function testConvertNonRevisionableEntityType() {
     $this->assertEquals($entity->id(), $converted->id());
   }
 
+  /**
+   * Tests an entity route parameter having 'bundle' definition property.
+   *
+   * @covers ::convert
+   */
+  public function testRouteParamWithBundleDefinition(): void {
+    $entity1 = EntityTestMulRev::create([
+      'name' => $this->randomString(),
+      'type' => 'foo',
+    ]);
+    $entity1->save();
+    $entity2 = EntityTestMulRev::create([
+      'name' => $this->randomString(),
+      'type' => 'bar',
+    ]);
+    $entity2->save();
+    $entity3 = EntityTestMulRev::create([
+      'name' => $this->randomString(),
+      'type' => 'baz',
+    ]);
+    $entity3->save();
+
+    $definition = [
+      'type' => 'entity:entity_test_mulrev',
+      'bundle' => [
+        'foo',
+        'bar',
+      ],
+      'load_latest_revision' => TRUE,
+    ];
+
+    // An entity whose bundle is in the definition list is converted.
+    $converted = $this->converter->convert($entity1->id(), $definition, 'qux', []);
+    $this->assertSame($entity1->id(), $converted->id());
+
+    // An entity whose bundle is in the definition list is converted.
+    $converted = $this->converter->convert($entity2->id(), $definition, 'qux', []);
+    $this->assertSame($entity2->id(), $converted->id());
+
+    // An entity whose bundle is missed from definition is not converted.
+    $converted = $this->converter->convert($entity3->id(), $definition, 'qux', []);
+    $this->assertNull($converted);
+
+    // A non-existing entity returns NULL.
+    $converted = $this->converter->convert('some-non-existing-entity-id', $definition, 'qux', []);
+    $this->assertNull($converted);
+
+    $definition = [
+      'type' => 'entity:entity_test_mulrev',
+    ];
+
+    // Check that all entities are returned when 'bundle' is not defined.
+    $converted = $this->converter->convert($entity1->id(), $definition, 'qux', []);
+    $this->assertSame($entity1->id(), $converted->id());
+    $converted = $this->converter->convert($entity2->id(), $definition, 'qux', []);
+    $this->assertSame($entity2->id(), $converted->id());
+    $converted = $this->converter->convert($entity3->id(), $definition, 'qux', []);
+    $this->assertSame($entity3->id(), $converted->id());
+    $converted = $this->converter->convert('some-non-existing-entity-id', $definition, 'qux', []);
+    $this->assertNull($converted);
+  }
+
 }
diff --git a/web/core/tests/Drupal/Nightwatch/Tests/statesTest.js b/web/core/tests/Drupal/Nightwatch/Tests/statesTest.js
index 2ae6d41de4..622005ebe3 100644
--- a/web/core/tests/Drupal/Nightwatch/Tests/statesTest.js
+++ b/web/core/tests/Drupal/Nightwatch/Tests/statesTest.js
@@ -21,4 +21,28 @@ module.exports = {
       .waitForElementNotVisible('input[name="textfield"]', 1000)
       .assert.noDeprecationErrors();
   },
+  'Test number trigger with spinner widget': (browser) => {
+    browser
+      .drupalRelativeURL('/form-test/javascript-states-form')
+      .waitForElementVisible('body', 1000)
+      .waitForElementNotVisible(
+        '#edit-item-visible-when-number-trigger-filled-by-spinner',
+        1000,
+      )
+      .execute(
+        // eslint-disable-next-line func-names, prefer-arrow-callback, no-shadow
+        function () {
+          // Emulate usage of the spinner browser widget on number inputs
+          // on modern browsers.
+          const numberTrigger = document.getElementById('edit-number-trigger');
+          numberTrigger.value = 1;
+          numberTrigger.dispatchEvent(new Event('change'));
+        },
+      );
+
+    browser.waitForElementVisible(
+      '#edit-item-visible-when-number-trigger-filled-by-spinner',
+      1000,
+    );
+  },
 };
diff --git a/web/core/tests/Drupal/Tests/Core/Command/GenerateThemeTest.php b/web/core/tests/Drupal/Tests/Core/Command/GenerateThemeTest.php
index 14dfdda5db..d5dba42a32 100644
--- a/web/core/tests/Drupal/Tests/Core/Command/GenerateThemeTest.php
+++ b/web/core/tests/Drupal/Tests/Core/Command/GenerateThemeTest.php
@@ -45,7 +45,7 @@ public function setUp(): void {
    * @return \Symfony\Component\Process\Process
    *   The PHP process
    */
-  private function generateThemeFromStarterkit() : Process {
+  private function generateThemeFromStarterkit($env = NULL) : Process {
     $install_command = [
       $this->php,
       'core/scripts/drupal',
@@ -54,7 +54,7 @@ private function generateThemeFromStarterkit() : Process {
       '--name="Test custom starterkit theme"',
       '--description="Custom theme generated from a starterkit theme"',
     ];
-    $process = new Process($install_command, NULL);
+    $process = new Process($install_command, NULL, $env);
     $process->setTimeout(60);
     return $process;
   }
@@ -262,20 +262,23 @@ public function testContribStarterkitDevSnapshotWithGitNotInstalled(): void {
 SH;
     file_put_contents($unavailableGitPath . '/git', $bash);
     chmod($unavailableGitPath . '/git', 0755);
-    $oldPath = getenv('PATH');
-    putenv('PATH=' . $unavailableGitPath . ':' . getenv('PATH'));
     // Confirm that 'git' is no longer available.
-    $output = [];
-    exec('git --help', $output, $status);
-    $this->assertEquals(127, $status);
-
-    $process = $this->generateThemeFromStarterkit();
+    $env = [
+      'PATH' => $unavailableGitPath . ':' . getenv('PATH'),
+      'COLUMNS' => 80,
+    ];
+    $process = new Process([
+      'git',
+      '--help',
+    ], NULL, $env);
+    $process->run();
+    $this->assertEquals(127, $process->getExitCode(), 'Fake git used by process.');
+
+    $process = $this->generateThemeFromStarterkit($env);
     $result = $process->run();
     $this->assertEquals("[ERROR] The source theme starterkit_theme has a development version number     \n         (7.x-dev). Determining a specific commit is not possible because git is\n         not installed. Either install git or use a tagged release to generate a\n         theme.", trim($process->getOutput()), $process->getErrorOutput());
     $this->assertSame(1, $result);
     $this->assertFileDoesNotExist($this->getWorkspaceDirectory() . "/themes/test_custom_theme");
-
-    putenv('PATH=' . $oldPath . ':' . getenv('PATH'));
   }
 
   /**
diff --git a/web/core/tests/Drupal/Tests/Core/Flood/MemoryBackendTest.php b/web/core/tests/Drupal/Tests/Core/Flood/MemoryBackendTest.php
new file mode 100644
index 0000000000..6be982db83
--- /dev/null
+++ b/web/core/tests/Drupal/Tests/Core/Flood/MemoryBackendTest.php
@@ -0,0 +1,106 @@
+<?php
+
+namespace Drupal\Tests\Core\Flood;
+
+use Drupal\Core\Flood\MemoryBackend;
+use Symfony\Component\HttpFoundation\RequestStack;
+use Symfony\Component\HttpFoundation\Request;
+use Drupal\Tests\UnitTestCase;
+
+/**
+ * Tests the memory flood implementation.
+ *
+ * @group flood
+ * @coversDefaultClass \Drupal\Core\Flood\MemoryBackend
+ */
+class MemoryBackendTest extends UnitTestCase {
+
+  /**
+   * The tested memory flood backend.
+   *
+   * @var \Drupal\Core\Flood\MemoryBackend
+   */
+  protected $flood;
+
+  protected function setUp(): void {
+    $request = new RequestStack();
+    $request_mock = $this->getMockBuilder(Request::class)
+      ->onlyMethods(['getClientIp'])
+      ->getMock();
+    $request->push($request_mock);
+    $this->flood = new MemoryBackend($request);
+  }
+
+  /**
+   * Tests an allowed flood event.
+   */
+  public function testAllowedProceeding() {
+    $threshold = 2;
+    $window_expired = -1;
+
+    $this->flood->register('test_event', $window_expired);
+    $this->assertTrue($this->flood->isAllowed('test_event', $threshold));
+  }
+
+  /**
+   * Tests a flood event with more than the allowed calls.
+   */
+  public function testNotAllowedProceeding() {
+    $threshold = 1;
+    $window_expired = -1;
+
+    // Register the event twice, so it is not allowed to proceed.
+    $this->flood->register('test_event', $window_expired);
+    $this->flood->register('test_event', $window_expired, 1);
+
+    $this->assertFalse($this->flood->isAllowed('test_event', $threshold));
+  }
+
+  /**
+   * Tests a flood event with expiring, so cron will allow to proceed.
+   *
+   * @medium
+   */
+  public function testExpiring() {
+    $threshold = 1;
+    $window_expired = -1;
+
+    $this->flood->register('test_event', $window_expired);
+    usleep(2);
+    $this->flood->register('test_event', $window_expired);
+
+    $this->assertFalse($this->flood->isAllowed('test_event', $threshold));
+
+    // "Run cron", which clears the flood data and verify event is now allowed.
+    $this->flood->garbageCollection();
+    $this->assertTrue($this->flood->isAllowed('test_event', $threshold));
+  }
+
+  /**
+   * Tests a flood event with no expiring, so cron will not allow to proceed.
+   */
+  public function testNotExpiring() {
+    $threshold = 2;
+
+    $this->flood->register('test_event', 1);
+    usleep(3);
+    $this->flood->register('test_event', 1);
+
+    $this->assertFalse($this->flood->isAllowed('test_event', $threshold));
+
+    // "Run cron", which clears the flood data and verify event is not allowed.
+    $this->flood->garbageCollection();
+    $this->assertFalse($this->flood->isAllowed('test_event', $threshold));
+  }
+
+  /**
+   * Tests memory backend records events to the nearest microsecond.
+   */
+  public function testMemoryBackendThreshold() {
+    $this->flood->register('new event');
+    $this->assertTrue($this->flood->isAllowed('new event', '2'));
+    $this->flood->register('new event');
+    $this->assertFalse($this->flood->isAllowed('new event', '2'));
+  }
+
+}
diff --git a/web/core/tests/Drupal/Tests/Core/Mail/Plugin/Mail/PhpMailTest.php b/web/core/tests/Drupal/Tests/Core/Mail/Plugin/Mail/PhpMailTest.php
new file mode 100644
index 0000000000..bdf97280cc
--- /dev/null
+++ b/web/core/tests/Drupal/Tests/Core/Mail/Plugin/Mail/PhpMailTest.php
@@ -0,0 +1,96 @@
+<?php
+
+namespace Drupal\Tests\Core\Mail\Plugin\Mail;
+
+use Drupal\Core\DependencyInjection\ContainerBuilder;
+use Drupal\Core\Mail\Plugin\Mail\PhpMail;
+use Drupal\Tests\UnitTestCase;
+
+/**
+ * @coversDefaultClass \Drupal\Core\Mail\Plugin\Mail\PhpMail
+ * @group Mail
+ */
+class PhpMailTest extends UnitTestCase {
+
+  /**
+   * The configuration factory.
+   *
+   * @var \Drupal\Core\Config\ConfigFactoryInterface|\PHPUnit\Framework\MockObject\MockObject
+   */
+  protected $configFactory;
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp(): void {
+    parent::setUp();
+
+    // Use the provided config for system.mail.interface settings.
+    $this->configFactory = $this->getConfigFactoryStub([
+      'system.mail' => [
+        'interface' => [],
+      ],
+      'system.site' => [
+        'mail' => 'test@example.com',
+      ],
+    ]);
+
+    $container = new ContainerBuilder();
+    $container->set('config.factory', $this->configFactory);
+    \Drupal::setContainer($container);
+  }
+
+  /**
+   * Creates a mocked PhpMail object.
+   *
+   * The method "doMail()" gets overridden to avoid a mail() call in tests.
+   *
+   * @return \Drupal\Core\Mail\Plugin\Mail\PhpMail|\PHPUnit\Framework\MockObject\MockObject
+   *   A PhpMail instance.
+   */
+  protected function createPhpMailInstance(): PhpMail {
+    $mailer = $this->getMockBuilder(PhpMail::class)
+      ->onlyMethods(['doMail'])
+      ->getMock();
+
+    $mailer->expects($this->once())->method('doMail')
+      ->willReturn(TRUE);
+
+    return $mailer;
+  }
+
+  /**
+   * Tests sending a mail using a From address with a comma in it.
+   *
+   * @covers ::testMail
+   */
+  public function testMail() {
+    // Setup a mail message.
+    $message = [
+      'id' => 'example_key',
+      'module' => 'example',
+      'key' => 'key',
+      'to' => 'to@example.org',
+      'from' => 'from@example.org',
+      'reply-to' => 'from@example.org',
+      'langcode' => 'en',
+      'params' => [],
+      'send' => TRUE,
+      'subject' => '',
+      'body' => '',
+      'headers' => [
+        'MIME-Version' => '1.0',
+        'Content-Type' => 'text/plain; charset=UTF-8; format=flowed; delsp=yes',
+        'Content-Transfer-Encoding' => '8Bit',
+        'X-Mailer' => 'Drupal',
+        'Return-Path' => 'from@example.org',
+        'From' => '"Foo, Bar, and Baz" <from@example.org>',
+        'Reply-to' => 'from@example.org',
+      ],
+    ];
+
+    $mailer = $this->createPhpMailInstance();
+    $this->assertTrue($mailer->mail($message));
+  }
+
+}
diff --git a/web/core/themes/claro/claro.theme b/web/core/themes/claro/claro.theme
index 3447ff8619..14540dd208 100644
--- a/web/core/themes/claro/claro.theme
+++ b/web/core/themes/claro/claro.theme
@@ -1553,7 +1553,9 @@ function claro_form_views_ui_config_item_form_alter(array &$form, FormStateInter
     // needed or require refactoring in https://drupal.org/node/3164890
     unset($form['options']['clear_markup_start']);
     unset($form['options']['clear_markup_end']);
-    $form['options']['expose_button']['#prefix'] = str_replace('clearfix', '', $form['options']['expose_button']['#prefix']);
+    if (isset($form['options']['expose_button']['#prefix'])) {
+      $form['options']['expose_button']['#prefix'] = str_replace('clearfix', '', $form['options']['expose_button']['#prefix']);
+    }
     if (isset($form['options']['group_button']['#prefix'])) {
       $form['options']['group_button']['#prefix'] = str_replace('clearfix', '', $form['options']['group_button']['#prefix']);
     }
-- 
GitLab