Browse Source

updated core to 8.6.3

Bachir Soussi Chiadmi 5 years ago
parent
commit
c92c348eee
100 changed files with 881 additions and 333 deletions
  1. 6 6
      composer.lock
  2. 4 1
      core/.stylelintrc.json
  3. 1 1
      core/MAINTAINERS.txt
  4. 123 25
      core/UPDATE.txt
  5. 48 18
      core/assets/vendor/ckeditor/CHANGES.md
  6. 0 0
      core/assets/vendor/ckeditor/ckeditor.js
  7. 0 0
      core/assets/vendor/ckeditor/lang/af.js
  8. 0 0
      core/assets/vendor/ckeditor/lang/fa.js
  9. 0 0
      core/assets/vendor/ckeditor/lang/fr.js
  10. 0 0
      core/assets/vendor/ckeditor/lang/lv.js
  11. 0 0
      core/assets/vendor/ckeditor/lang/ro.js
  12. 0 0
      core/assets/vendor/ckeditor/lang/sk.js
  13. BIN
      core/assets/vendor/ckeditor/plugins/icons.png
  14. BIN
      core/assets/vendor/ckeditor/plugins/icons_hidpi.png
  15. 0 0
      core/assets/vendor/ckeditor/plugins/image2/dialogs/image2.js
  16. 0 0
      core/assets/vendor/ckeditor/skins/moono-lisa/editor.css
  17. 0 0
      core/assets/vendor/ckeditor/skins/moono-lisa/editor_gecko.css
  18. 0 0
      core/assets/vendor/ckeditor/skins/moono-lisa/editor_ie.css
  19. 0 0
      core/assets/vendor/ckeditor/skins/moono-lisa/editor_ie8.css
  20. 0 0
      core/assets/vendor/ckeditor/skins/moono-lisa/editor_iequirks.css
  21. BIN
      core/assets/vendor/ckeditor/skins/moono-lisa/icons.png
  22. BIN
      core/assets/vendor/ckeditor/skins/moono-lisa/icons_hidpi.png
  23. 1 1
      core/authorize.php
  24. 1 1
      core/composer.json
  25. 2 2
      core/core.libraries.yml
  26. 1 3
      core/core.services.yml
  27. 10 6
      core/includes/bootstrap.inc
  28. 16 7
      core/includes/install.core.inc
  29. 17 0
      core/includes/install.inc
  30. 2 2
      core/lib/Drupal.php
  31. 1 1
      core/lib/Drupal/Component/DependencyInjection/PhpArrayContainer.php
  32. 1 1
      core/lib/Drupal/Component/Gettext/PoStreamReader.php
  33. 1 1
      core/lib/Drupal/Component/HttpFoundation/SecuredRedirectResponse.php
  34. 4 1
      core/lib/Drupal/Component/Plugin/PluginManagerBase.php
  35. 3 3
      core/lib/Drupal/Core/Archiver/ArchiveTar.php
  36. 1 1
      core/lib/Drupal/Core/Cache/CacheBackendInterface.php
  37. 1 3
      core/lib/Drupal/Core/Cache/MemoryBackend.php
  38. 16 5
      core/lib/Drupal/Core/Config/ConfigInstaller.php
  39. 2 1
      core/lib/Drupal/Core/Config/ConfigInstallerInterface.php
  40. 1 1
      core/lib/Drupal/Core/Config/Entity/ConfigEntityInterface.php
  41. 6 2
      core/lib/Drupal/Core/Config/FileStorage.php
  42. 1 1
      core/lib/Drupal/Core/Database/Driver/mysql/Install/Tasks.php
  43. 1 1
      core/lib/Drupal/Core/Database/Driver/pgsql/Connection.php
  44. 7 0
      core/lib/Drupal/Core/Database/Query/Select.php
  45. 6 6
      core/lib/Drupal/Core/Database/StatementPrefetch.php
  46. 1 1
      core/lib/Drupal/Core/DependencyInjection/Compiler/CorsCompilerPass.php
  47. 6 2
      core/lib/Drupal/Core/DrupalKernel.php
  48. 2 1
      core/lib/Drupal/Core/Entity/ContentEntityDeleteForm.php
  49. 2 2
      core/lib/Drupal/Core/Entity/Element/EntityAutocomplete.php
  50. 1 0
      core/lib/Drupal/Core/Entity/EntityBundleListener.php
  51. 1 1
      core/lib/Drupal/Core/Entity/EntityFieldManager.php
  52. 1 1
      core/lib/Drupal/Core/Entity/EntityStorageBase.php
  53. 1 1
      core/lib/Drupal/Core/Entity/Plugin/DataType/EntityReference.php
  54. 1 1
      core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorage.php
  55. 21 9
      core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorageSchema.php
  56. 1 4
      core/lib/Drupal/Core/EventSubscriber/KernelDestructionSubscriber.php
  57. 17 7
      core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/TimestampAgoFormatter.php
  58. 4 4
      core/lib/Drupal/Core/Form/ConfirmFormInterface.php
  59. 1 1
      core/lib/Drupal/Core/Form/FormBuilder.php
  60. 2 2
      core/lib/Drupal/Core/Form/FormState.php
  61. 1 1
      core/lib/Drupal/Core/Http/HandlerStackConfigurator.php
  62. 1 1
      core/lib/Drupal/Core/Http/TrustedHostsRequestFactory.php
  63. 1 1
      core/lib/Drupal/Core/Installer/Exception/InstallProfileMismatchException.php
  64. 10 0
      core/lib/Drupal/Core/Mail/MailManager.php
  65. 15 12
      core/lib/Drupal/Core/Menu/LocalActionDefault.php
  66. 3 3
      core/lib/Drupal/Core/Menu/LocalActionManager.php
  67. 15 13
      core/lib/Drupal/Core/Menu/LocalTaskDefault.php
  68. 1 1
      core/lib/Drupal/Core/Menu/MenuLinkTreeInterface.php
  69. 1 1
      core/lib/Drupal/Core/PageCache/RequestPolicy/NoSessionOpen.php
  70. 8 1
      core/lib/Drupal/Core/PathProcessor/InboundPathProcessorInterface.php
  71. 2 0
      core/lib/Drupal/Core/Plugin/Context/Context.php
  72. 1 1
      core/lib/Drupal/Core/ProxyBuilder/ProxyBuilder.php
  73. 2 0
      core/lib/Drupal/Core/Render/Element/Email.php
  74. 2 0
      core/lib/Drupal/Core/Render/Element/Password.php
  75. 1 1
      core/lib/Drupal/Core/Render/Element/RenderElement.php
  76. 2 0
      core/lib/Drupal/Core/Render/Element/Tel.php
  77. 3 1
      core/lib/Drupal/Core/Render/Element/Textfield.php
  78. 2 0
      core/lib/Drupal/Core/Render/Element/Url.php
  79. 1 1
      core/lib/Drupal/Core/Session/SessionConfiguration.php
  80. 2 4
      core/lib/Drupal/Core/Session/WriteSafeSessionHandlerInterface.php
  81. 41 29
      core/lib/Drupal/Core/State/State.php
  82. 1 1
      core/lib/Drupal/Core/StreamWrapper/StreamWrapperInterface.php
  83. 29 1
      core/lib/Drupal/Core/TempStore/PrivateTempStore.php
  84. 1 1
      core/lib/Drupal/Core/TypedData/TypedDataManager.php
  85. 1 1
      core/lib/Drupal/Core/Update/UpdateRegistry.php
  86. 1 1
      core/lib/Drupal/Core/Updater/Module.php
  87. 1 1
      core/lib/Drupal/Core/Updater/Theme.php
  88. 0 1
      core/lib/Drupal/Core/Utility/UnroutedUrlAssembler.php
  89. 1 1
      core/misc/announce.es6.js
  90. 1 1
      core/misc/autocomplete.es6.js
  91. 1 1
      core/misc/normalize-fixes.css
  92. 38 12
      core/misc/states.es6.js
  93. 21 8
      core/misc/states.js
  94. 1 1
      core/modules/action/src/Plugin/Action/GotoAction.php
  95. 1 1
      core/modules/aggregator/src/ItemsImporter.php
  96. 1 1
      core/modules/ban/src/Plugin/migrate/destination/BlockedIp.php
  97. 5 91
      core/modules/block_content/src/Plugin/migrate/source/d6/BoxTranslation.php
  98. 99 0
      core/modules/block_content/src/Plugin/migrate/source/d7/BlockCustomTranslation.php
  99. 69 0
      core/modules/block_content/tests/src/Kernel/Migrate/d7/MigrateCustomBlockContentTranslationTest.php
  100. 148 0
      core/modules/block_content/tests/src/Kernel/Plugin/migrate/source/d7/BlockCustomTranslationTest.php

+ 6 - 6
composer.lock

@@ -1639,16 +1639,16 @@
         },
         },
         {
         {
             "name": "drupal/core",
             "name": "drupal/core",
-            "version": "8.6.2",
+            "version": "8.6.3",
             "source": {
             "source": {
                 "type": "git",
                 "type": "git",
                 "url": "https://github.com/drupal/core.git",
                 "url": "https://github.com/drupal/core.git",
-                "reference": "356292934802bb1aecc478e88a3cba77442d7c62"
+                "reference": "9e9a1dd9e280ebaf10622217e54448b529167965"
             },
             },
             "dist": {
             "dist": {
                 "type": "zip",
                 "type": "zip",
-                "url": "https://api.github.com/repos/drupal/core/zipball/356292934802bb1aecc478e88a3cba77442d7c62",
-                "reference": "356292934802bb1aecc478e88a3cba77442d7c62",
+                "url": "https://api.github.com/repos/drupal/core/zipball/9e9a1dd9e280ebaf10622217e54448b529167965",
+                "reference": "9e9a1dd9e280ebaf10622217e54448b529167965",
                 "shasum": ""
                 "shasum": ""
             },
             },
             "require": {
             "require": {
@@ -1814,7 +1814,7 @@
                 "jcalderonzumba/gastonjs": "^1.0.2",
                 "jcalderonzumba/gastonjs": "^1.0.2",
                 "jcalderonzumba/mink-phantomjs-driver": "^0.3.1",
                 "jcalderonzumba/mink-phantomjs-driver": "^0.3.1",
                 "mikey179/vfsstream": "^1.2",
                 "mikey179/vfsstream": "^1.2",
-                "phpspec/prophecy": "^1.4",
+                "phpspec/prophecy": "^1.7",
                 "phpunit/phpunit": "^4.8.35 || ^6.5",
                 "phpunit/phpunit": "^4.8.35 || ^6.5",
                 "symfony/css-selector": "^3.4.0",
                 "symfony/css-selector": "^3.4.0",
                 "symfony/debug": "^3.4.0",
                 "symfony/debug": "^3.4.0",
@@ -1873,7 +1873,7 @@
                 "GPL-2.0-or-later"
                 "GPL-2.0-or-later"
             ],
             ],
             "description": "Drupal is an open source content management platform powering millions of websites and applications.",
             "description": "Drupal is an open source content management platform powering millions of websites and applications.",
-            "time": "2018-10-17T22:19:50+00:00"
+            "time": "2018-11-07T14:45:40+00:00"
         },
         },
         {
         {
             "name": "drush/drush",
             "name": "drush/drush",

+ 4 - 1
core/.stylelintrc.json

@@ -11,7 +11,10 @@
     "no-duplicate-selectors": null,
     "no-duplicate-selectors": null,
     "no-unknown-animations": true,
     "no-unknown-animations": true,
     "media-feature-name-no-unknown": [true, {
     "media-feature-name-no-unknown": [true, {
-      "ignoreMediaFeatureNames": ["prefers-reduced-motion"]
+      "ignoreMediaFeatureNames": [
+        "prefers-reduced-motion",
+        "min--moz-device-pixel-ratio"
+      ]
     }],
     }],
     "number-leading-zero": "always",
     "number-leading-zero": "always",
     "plugin/no-browser-hacks": [true, {
     "plugin/no-browser-hacks": [true, {

+ 1 - 1
core/MAINTAINERS.txt

@@ -113,7 +113,6 @@ CKEditor
 
 
 Classy
 Classy
 - David Hernandez 'davidhernandez' https://www.drupal.org/u/davidhernandez
 - David Hernandez 'davidhernandez' https://www.drupal.org/u/davidhernandez
-- Morten Birch Heide-Jørgensen 'mortendk' https://www.drupal.org/u/mortendk
 
 
 Color
 Color
 - ?
 - ?
@@ -475,6 +474,7 @@ their responsibilities. The initiative coordinators for Drupal 8 are:
 API-first Initiative
 API-first Initiative
 - Wim Leers 'Wim Leers' https://www.drupal.org/u/wim-leers
 - Wim Leers 'Wim Leers' https://www.drupal.org/u/wim-leers
 - Mateu Aguiló Bosch 'e0ipso' https://www.drupal.org/u/e0ipso
 - Mateu Aguiló Bosch 'e0ipso' https://www.drupal.org/u/e0ipso
+- Gabe Sullice 'gabesullice' https://www.drupal.org/u/gabesullice
 
 
 Admin UI & JavaScript Modernisation Initiative
 Admin UI & JavaScript Modernisation Initiative
 - Angela Byron 'webchick' https://www.drupal.org/u/webchick
 - Angela Byron 'webchick' https://www.drupal.org/u/webchick

+ 123 - 25
core/UPDATE.txt

@@ -64,15 +64,51 @@ following the instructions in the INTRODUCTION section at the top of this file:
    Enable the "Put site into maintenance mode" checkbox and save the
    Enable the "Put site into maintenance mode" checkbox and save the
    configuration.
    configuration.
 
 
-3. Remove the 'core' and 'vendor' directories. Also remove all of the files
-   in the top-level directory, except any that you added manually.
+3. Determine if your project is managed by Composer.
 
 
-   If you made modifications to files like .htaccess, composer.json, or
-   robots.txt you will need to re-apply them from your backup, after the new
-   files are in place.
+   On a typical Unix/Linux command line, this can be determined by running the
+   following command (replace /PATH/TO/composer with the appropriate location
+   for your system):
+
+     /PATH/TO/composer info drupal/core
+
+   If this is successful, your project is managed by Composer.
+
+   If you don't have Composer installed or access to the command line, you can
+   check the contents of composer.json. If "drupal/core" is present in the
+   "require" section of your composer.json file, then the project is managed by
+   Composer.
+
+   If the project is not managed by Composer, follow the steps under "UPDATING
+   CODE WITHOUT COMPOSER", otherwise go to "UPDATING CODE WITH COMPOSER".
+
+UPDATING CODE WITH COMPOSER
+---------------------------
+1. On a typical Unix/Linux command line, run the following command from the root
+   directory (replace /PATH/TO/composer with the appropriate location for your
+   system):
+
+     /PATH/TO/composer update
+
+   Note, if Composer is not installed you will need to install it in order to
+   update Drupal.
+
+   Note, if you want to only update drupal/core the following command will
+   probably work:
+
+     /PATH/TO/composer update drupal/core symfony/* --with-all-dependencies
+
+2. Check the release notes for the updated version of Drupal to find out if
+   there is a change to default.settings.php.
+
+   You can find the release notes for your version at
+   https://www.drupal.org/project/drupal. At bottom of the project page under
+   "Downloads" use the link for your version of Drupal to view the release
+   notes. If your version is not listed, use the 'View all releases' link. From
+   this page you can scroll down or use the filter to find your version and its
+   release notes.
 
 
-   Sometimes an update includes changes to default.settings.php (this will be
-   noted in the release notes). If that's the case, follow these steps:
+   If there is a change to default.settings.php, follow these steps:
 
 
    - Locate your settings.php file in the /sites/* directory. (Typically
    - Locate your settings.php file in the /sites/* directory. (Typically
      sites/default.)
      sites/default.)
@@ -87,16 +123,32 @@ following the instructions in the INTRODUCTION section at the top of this file:
      database information, and you will also want to copy in any other
      database information, and you will also want to copy in any other
      customizations you have added.
      customizations you have added.
 
 
-   You can find the release notes for your version at
-   https://www.drupal.org/project/drupal. At bottom of the project page under
-   "Downloads" use the link for your version of Drupal to view the release
-   notes. If your version is not listed, use the 'View all releases' link. From
-   this page you can scroll down or use the filter to find your version and its
-   release notes.
+3. Determine if there are any modifications to files such as .htaccess or
+   robots.txt and re-apply them. The Drupal Scaffold composer plugin
+   (https://github.com/drupal-composer/drupal-scaffold) can help you with
+   excluding files you'd like to always preserve when updating Drupal.
+
+4. Go to the "UPLOADING THE CODE" section
+
+UPDATING CODE WITHOUT COMPOSER
+------------------------------
+1. Remove the 'core' and 'vendor' directories. Also remove all of the files
+   in the top-level directory, except any that you added manually.
 
 
-4. Download the latest Drupal 8.x.x release from https://www.drupal.org to a
-   directory outside of your web root. Extract the archive and copy the files
-   into your Drupal directory.
+   If you made modifications to files like .htaccess, composer.json, or
+   robots.txt you will need to re-apply them from your backup, after the new
+   files are in place.
+
+   This should leave you with the modules, profiles, sites, and themes
+   directories. These directories should only contain code that you've used to
+   extend Drupal.
+
+2. Download the latest Drupal 8.x.x release from https://www.drupal.org/download
+   to a directory outside of your web root. Extract the archive and copy the
+   files into your Drupal directory.
+
+   Copy all the files, but do not accidentally overwrite your modules, profiles,
+   sites, or themes directories.
 
 
    On a typical Unix/Linux command line, use the following commands to download
    On a typical Unix/Linux command line, use the following commands to download
    and extract:
    and extract:
@@ -110,13 +162,60 @@ following the instructions in the INTRODUCTION section at the top of this file:
      cp -R drupal-x.y.z/* drupal-x.y.z/.htaccess /path/to/your/installation
      cp -R drupal-x.y.z/* drupal-x.y.z/.htaccess /path/to/your/installation
 
 
    If you do not have command line access to your server, download the archive
    If you do not have command line access to your server, download the archive
-   from https://www.drupal.org using your web browser, extract it, and then use
-   an FTP client to upload the files to your web root.
+   from https://www.drupal.org using your web browser and extract it locally.
+
+3. Check the release notes for the updated version of Drupal to find out if
+   there is a change to default.settings.php.
+
+   You can find the release notes for your updated version at
+   https://www.drupal.org/project/drupal. At bottom of the project page under
+   "Downloads" use the link for your updated version of Drupal to view the
+   release notes. If your updated version is not listed, use the 'View all
+   releases' link. From this page you can scroll down or use the filter to find
+   your updated version and its release notes.
+
+   If there is a change to default.settings.php, follow these steps:
+
+   - Locate your settings.php file in the /sites/* directory. (Typically
+     sites/default.)
+
+   - Make a backup copy of your settings.php file, with a different file name.
 
 
-5. Re-apply any modifications to files such as .htaccess, composer.json, or
-   robots.txt.
+   - Make a copy of the new default.settings.php file, and name the copy
+     settings.php (overwriting your previous settings.php file).
+
+   - Copy the custom and site-specific entries from the backup you made into the
+     new settings.php file. You will definitely need the lines giving the
+     database information, and you will also want to copy in any other
+     customizations you have added.
+
+4. Re-apply any modifications to files such as .htaccess or robots.txt.
 
 
-6. Run update.php by visiting http://www.example.com/update.php (replace
+   If you have added requirements in composer.json, it is recommended that you
+   re-add the requirements using Composer instead of applying the changes by
+   hand. For example, on a typical Unix/Linux command line, to reinstall the
+   Address module and its dependencies run (replace /PATH/TO/composer with the
+   appropriate location for your system):
+
+     /PATH/TO/composer require drupal/address
+
+   If you do not have command line access to your server, you will need to run
+   the Composer commands locally before uploading the file system to your
+   server.
+
+5. Go to the "UPLOADING THE CODE" section
+
+UPLOADING THE CODE
+------------------
+1. If you updated the code in a different environment from where it is running
+   you need to upload the files to your web root including the vendor/
+   directory.
+
+2. Go to the "UPDATING THE DATABASE" section
+
+UPDATING THE DATABASE
+---------------------
+1. Run update.php by visiting http://www.example.com/update.php (replace
    www.example.com with your domain name). This will update the core database
    www.example.com with your domain name). This will update the core database
    tables.
    tables.
 
 
@@ -133,12 +232,11 @@ following the instructions in the INTRODUCTION section at the top of this file:
    - Once the update is done, $settings['update_free_access'] must be reverted
    - Once the update is done, $settings['update_free_access'] must be reverted
      to FALSE.
      to FALSE.
 
 
-7. Go to Administration > Reports > Status report. Verify that everything is
+2. Go to Administration > Reports > Status report. Verify that everything is
    working as expected.
    working as expected.
 
 
-8. Ensure that $settings['update_free_access'] is FALSE in settings.php.
+3. Ensure that $settings['update_free_access'] is FALSE in settings.php.
 
 
-9. Go to Administration > Configuration > Development > Maintenance mode.
+4. Go to Administration > Configuration > Development > Maintenance mode.
    Disable the "Put site into maintenance mode" checkbox and save the
    Disable the "Put site into maintenance mode" checkbox and save the
    configuration.
    configuration.
-

File diff suppressed because it is too large
+ 48 - 18
core/assets/vendor/ckeditor/CHANGES.md


File diff suppressed because it is too large
+ 0 - 0
core/assets/vendor/ckeditor/ckeditor.js


File diff suppressed because it is too large
+ 0 - 0
core/assets/vendor/ckeditor/lang/af.js


File diff suppressed because it is too large
+ 0 - 0
core/assets/vendor/ckeditor/lang/fa.js


File diff suppressed because it is too large
+ 0 - 0
core/assets/vendor/ckeditor/lang/fr.js


File diff suppressed because it is too large
+ 0 - 0
core/assets/vendor/ckeditor/lang/lv.js


File diff suppressed because it is too large
+ 0 - 0
core/assets/vendor/ckeditor/lang/ro.js


File diff suppressed because it is too large
+ 0 - 0
core/assets/vendor/ckeditor/lang/sk.js


BIN
core/assets/vendor/ckeditor/plugins/icons.png


BIN
core/assets/vendor/ckeditor/plugins/icons_hidpi.png


File diff suppressed because it is too large
+ 0 - 0
core/assets/vendor/ckeditor/plugins/image2/dialogs/image2.js


File diff suppressed because it is too large
+ 0 - 0
core/assets/vendor/ckeditor/skins/moono-lisa/editor.css


File diff suppressed because it is too large
+ 0 - 0
core/assets/vendor/ckeditor/skins/moono-lisa/editor_gecko.css


File diff suppressed because it is too large
+ 0 - 0
core/assets/vendor/ckeditor/skins/moono-lisa/editor_ie.css


File diff suppressed because it is too large
+ 0 - 0
core/assets/vendor/ckeditor/skins/moono-lisa/editor_ie8.css


File diff suppressed because it is too large
+ 0 - 0
core/assets/vendor/ckeditor/skins/moono-lisa/editor_iequirks.css


BIN
core/assets/vendor/ckeditor/skins/moono-lisa/icons.png


BIN
core/assets/vendor/ckeditor/skins/moono-lisa/icons_hidpi.png


+ 1 - 1
core/authorize.php

@@ -125,7 +125,7 @@ if ($is_allowed) {
       $links = $results['tasks'];
       $links = $results['tasks'];
     }
     }
     else {
     else {
-      // Since this is being called outsite of the primary front controller,
+      // Since this is being called outside of the primary front controller,
       // the base_url needs to be set explicitly to ensure that links are
       // the base_url needs to be set explicitly to ensure that links are
       // relative to the site root.
       // relative to the site root.
       // @todo Simplify with https://www.drupal.org/node/2548095
       // @todo Simplify with https://www.drupal.org/node/2548095

+ 1 - 1
core/composer.json

@@ -59,7 +59,7 @@
         "jcalderonzumba/mink-phantomjs-driver": "^0.3.1",
         "jcalderonzumba/mink-phantomjs-driver": "^0.3.1",
         "mikey179/vfsStream": "^1.2",
         "mikey179/vfsStream": "^1.2",
         "phpunit/phpunit": "^4.8.35 || ^6.5",
         "phpunit/phpunit": "^4.8.35 || ^6.5",
-        "phpspec/prophecy": "^1.4",
+        "phpspec/prophecy": "^1.7",
         "symfony/css-selector": "^3.4.0",
         "symfony/css-selector": "^3.4.0",
         "symfony/phpunit-bridge": "^3.4.3",
         "symfony/phpunit-bridge": "^3.4.3",
         "symfony/debug": "^3.4.0"
         "symfony/debug": "^3.4.0"

+ 2 - 2
core/core.libraries.yml

@@ -24,10 +24,10 @@ classList:
 
 
 ckeditor:
 ckeditor:
   remote: https://github.com/ckeditor/ckeditor-dev
   remote: https://github.com/ckeditor/ckeditor-dev
-  version: "4.10.0"
+  version: "4.10.1"
   license:
   license:
     name: GNU-GPL-2.0-or-later
     name: GNU-GPL-2.0-or-later
-    url: https://github.com/ckeditor/ckeditor-dev/blob/4.10.0/LICENSE.md
+    url: https://github.com/ckeditor/ckeditor-dev/blob/4.10.1/LICENSE.md
     gpl-compatible: true
     gpl-compatible: true
   js:
   js:
     assets/vendor/ckeditor/ckeditor.js: { preprocess: false, minified: true }
     assets/vendor/ckeditor/ckeditor.js: { preprocess: false, minified: true }

+ 1 - 3
core/core.services.yml

@@ -431,9 +431,7 @@ services:
     factory: Drupal\Core\Site\Settings::getInstance
     factory: Drupal\Core\Site\Settings::getInstance
   state:
   state:
     class: Drupal\Core\State\State
     class: Drupal\Core\State\State
-    arguments: ['@keyvalue', '@cache.bootstrap', '@lock']
-    tags:
-      - { name: needs_destruction }
+    arguments: ['@keyvalue']
   queue:
   queue:
     class: Drupal\Core\Queue\QueueFactory
     class: Drupal\Core\Queue\QueueFactory
     arguments: ['@settings']
     arguments: ['@settings']

+ 10 - 6
core/includes/bootstrap.inc

@@ -572,14 +572,14 @@ function drupal_get_user_timezone() {
  * @param $message
  * @param $message
  *   The error message.
  *   The error message.
  * @param $filename
  * @param $filename
- *   The filename that the error was raised in.
+ *   (optional) The filename that the error was raised in.
  * @param $line
  * @param $line
- *   The line number the error was raised at.
+ *   (optional) The line number the error was raised at.
  * @param $context
  * @param $context
- *   An array that points to the active symbol table at the point the error
- *   occurred.
+ *   (optional) An array that points to the active symbol table at the point the
+ *   error occurred.
  */
  */
-function _drupal_error_handler($error_level, $message, $filename, $line, $context) {
+function _drupal_error_handler($error_level, $message, $filename = NULL, $line = NULL, $context = NULL) {
   require_once __DIR__ . '/errors.inc';
   require_once __DIR__ . '/errors.inc';
   _drupal_error_handler_real($error_level, $message, $filename, $line, $context);
   _drupal_error_handler_real($error_level, $message, $filename, $line, $context);
 }
 }
@@ -1033,8 +1033,12 @@ function _drupal_shutdown_function() {
   chdir(DRUPAL_ROOT);
   chdir(DRUPAL_ROOT);
 
 
   try {
   try {
-    foreach ($callbacks as &$callback) {
+    reset($callbacks);
+    // Do not use foreach() here because it is possible that the callback will
+    // add to the $callbacks array via drupal_register_shutdown_function().
+    while ($callback = current($callbacks)) {
       call_user_func_array($callback['callback'], $callback['arguments']);
       call_user_func_array($callback['callback'], $callback['arguments']);
+      next($callbacks);
     }
     }
   }
   }
   // PHP 7 introduces Throwable, which covers both Error and
   // PHP 7 introduces Throwable, which covers both Error and

+ 16 - 7
core/includes/install.core.inc

@@ -1270,12 +1270,19 @@ function install_select_profile(&$install_state) {
 /**
 /**
  * Determines the installation profile to use in the installer.
  * Determines the installation profile to use in the installer.
  *
  *
- * A profile will be selected in the following order of conditions:
+ * Depending on the context from which it's being called, this method
+ * may be used to:
+ * - Automatically select a profile under certain conditions.
+ * - Indicate which profile has already been selected.
+ * - Indicate that a profile still needs to be selected.
+ *
+ * A profile will be selected automatically if one of the following conditions
+ * is met. They are checked in the given order:
  * - Only one profile is available.
  * - Only one profile is available.
  * - A specific profile name is requested in installation parameters:
  * - A specific profile name is requested in installation parameters:
  *   - For interactive installations via request query parameters.
  *   - For interactive installations via request query parameters.
  *   - For non-interactive installations via install_drupal() settings.
  *   - For non-interactive installations via install_drupal() settings.
- * - A discovered profile that is a distribution. If multiple profiles are
+ * - One of the available profiles is a distribution. If multiple profiles are
  *   distributions, then the first discovered profile will be selected.
  *   distributions, then the first discovered profile will be selected.
  * - Only one visible profile is available.
  * - Only one visible profile is available.
  *
  *
@@ -1283,35 +1290,37 @@ function install_select_profile(&$install_state) {
  *   The current installer state, containing a 'profiles' key, which is an
  *   The current installer state, containing a 'profiles' key, which is an
  *   associative array of profiles with the machine-readable names as keys.
  *   associative array of profiles with the machine-readable names as keys.
  *
  *
- * @return
+ * @return string|null
  *   The machine-readable name of the selected profile or NULL if no profile was
  *   The machine-readable name of the selected profile or NULL if no profile was
  *   selected.
  *   selected.
+ *
+ *  @see install_select_profile()
  */
  */
 function _install_select_profile(&$install_state) {
 function _install_select_profile(&$install_state) {
-  // Don't need to choose profile if only one available.
+  // If there is only one profile available it will always be the one selected.
   if (count($install_state['profiles']) == 1) {
   if (count($install_state['profiles']) == 1) {
     return key($install_state['profiles']);
     return key($install_state['profiles']);
   }
   }
+  // If a valid profile has already been selected, return the selection.
   if (!empty($install_state['parameters']['profile'])) {
   if (!empty($install_state['parameters']['profile'])) {
     $profile = $install_state['parameters']['profile'];
     $profile = $install_state['parameters']['profile'];
     if (isset($install_state['profiles'][$profile])) {
     if (isset($install_state['profiles'][$profile])) {
       return $profile;
       return $profile;
     }
     }
   }
   }
-  // Check for a distribution profile.
+  // If any of the profiles are distribution profiles, return the first one.
   foreach ($install_state['profiles'] as $profile) {
   foreach ($install_state['profiles'] as $profile) {
     $profile_info = install_profile_info($profile->getName());
     $profile_info = install_profile_info($profile->getName());
     if (!empty($profile_info['distribution'])) {
     if (!empty($profile_info['distribution'])) {
       return $profile->getName();
       return $profile->getName();
     }
     }
   }
   }
-
   // Get all visible (not hidden) profiles.
   // Get all visible (not hidden) profiles.
   $visible_profiles = array_filter($install_state['profiles'], function ($profile) {
   $visible_profiles = array_filter($install_state['profiles'], function ($profile) {
     $profile_info = install_profile_info($profile->getName());
     $profile_info = install_profile_info($profile->getName());
     return !isset($profile_info['hidden']) || !$profile_info['hidden'];
     return !isset($profile_info['hidden']) || !$profile_info['hidden'];
   });
   });
-
+  // If there is only one visible profile, return it.
   if (count($visible_profiles) == 1) {
   if (count($visible_profiles) == 1) {
     return (key($visible_profiles));
     return (key($visible_profiles));
   }
   }

+ 17 - 0
core/includes/install.inc

@@ -11,6 +11,7 @@ use Drupal\Component\Utility\Crypt;
 use Drupal\Component\Utility\OpCodeCache;
 use Drupal\Component\Utility\OpCodeCache;
 use Drupal\Component\Utility\UrlHelper;
 use Drupal\Component\Utility\UrlHelper;
 use Drupal\Core\Extension\ExtensionDiscovery;
 use Drupal\Core\Extension\ExtensionDiscovery;
+use Drupal\Core\Extension\ModuleHandler;
 use Drupal\Core\Site\Settings;
 use Drupal\Core\Site\Settings;
 
 
 /**
 /**
@@ -990,6 +991,12 @@ function drupal_check_profile($profile) {
     }
     }
   }
   }
 
 
+  // Add the profile requirements.
+  $function = $profile . '_requirements';
+  if (function_exists($function)) {
+    $requirements = array_merge($requirements, $function('install'));
+  }
+
   return $requirements;
   return $requirements;
 }
 }
 
 
@@ -1113,6 +1120,16 @@ function install_profile_info($profile, $langcode = 'en') {
     $info = \Drupal::service('info_parser')->parse("$profile_path/$profile.info.yml");
     $info = \Drupal::service('info_parser')->parse("$profile_path/$profile.info.yml");
     $info += $defaults;
     $info += $defaults;
 
 
+    // Convert dependencies in [project:module] format.
+    $info['dependencies'] = array_map(function ($dependency) {
+      return ModuleHandler::parseDependency($dependency)['name'];
+    }, $info['dependencies']);
+
+    // Convert install key in [project:module] format.
+    $info['install'] = array_map(function ($dependency) {
+      return ModuleHandler::parseDependency($dependency)['name'];
+    }, $info['install']);
+
     // drupal_required_modules() includes the current profile as a dependency.
     // drupal_required_modules() includes the current profile as a dependency.
     // Remove that dependency, since a module cannot depend on itself.
     // Remove that dependency, since a module cannot depend on itself.
     $required = array_diff(drupal_required_modules(), [$profile]);
     $required = array_diff(drupal_required_modules(), [$profile]);

+ 2 - 2
core/lib/Drupal.php

@@ -82,7 +82,7 @@ class Drupal {
   /**
   /**
    * The current system version.
    * The current system version.
    */
    */
-  const VERSION = '8.6.2';
+  const VERSION = '8.6.3';
 
 
   /**
   /**
    * Core API compatibility.
    * Core API compatibility.
@@ -229,7 +229,7 @@ class Drupal {
   }
   }
 
 
   /**
   /**
-   * Retrives the request stack.
+   * Retrieves the request stack.
    *
    *
    * @return \Symfony\Component\HttpFoundation\RequestStack
    * @return \Symfony\Component\HttpFoundation\RequestStack
    *   The request stack
    *   The request stack

+ 1 - 1
core/lib/Drupal/Component/DependencyInjection/PhpArrayContainer.php

@@ -200,7 +200,7 @@ class PhpArrayContainer extends Container {
         // The PhpArrayDumper just uses the hash of the private service
         // The PhpArrayDumper just uses the hash of the private service
         // definition to generate a unique ID.
         // definition to generate a unique ID.
         //
         //
-        // @see \Drupal\Component\DependecyInjection\Dumper\OptimizedPhpArrayDumper::getPrivateServiceCall
+        // @see \Drupal\Component\DependencyInjection\Dumper\OptimizedPhpArrayDumper::getPrivateServiceCall
         if ($type == 'private_service') {
         if ($type == 'private_service') {
           $id = $argument->id;
           $id = $argument->id;
 
 

+ 1 - 1
core/lib/Drupal/Component/Gettext/PoStreamReader.php

@@ -394,7 +394,7 @@ class PoStreamReader implements PoStreamInterface, PoReaderInterface {
             ($this->context != 'MSGCTXT') &&
             ($this->context != 'MSGCTXT') &&
             ($this->context != 'MSGID_PLURAL') &&
             ($this->context != 'MSGID_PLURAL') &&
             ($this->context != 'MSGSTR_ARR')) {
             ($this->context != 'MSGSTR_ARR')) {
-          // Plural message strings must come after msgid, msgxtxt,
+          // Plural message strings must come after msgid, msgctxt,
           // msgid_plural, or other msgstr[] entries.
           // msgid_plural, or other msgstr[] entries.
           $this->errors[] = new FormattableMarkup('The translation stream %uri contains an error: "msgstr[]" is unexpected on line %line.', $log_vars);
           $this->errors[] = new FormattableMarkup('The translation stream %uri contains an error: "msgstr[]" is unexpected on line %line.', $log_vars);
           return FALSE;
           return FALSE;

+ 1 - 1
core/lib/Drupal/Component/HttpFoundation/SecuredRedirectResponse.php

@@ -36,7 +36,7 @@ abstract class SecuredRedirectResponse extends RedirectResponse {
    * Copies over the values from the given response.
    * Copies over the values from the given response.
    *
    *
    * @param \Symfony\Component\HttpFoundation\RedirectResponse $response
    * @param \Symfony\Component\HttpFoundation\RedirectResponse $response
-   *   The redirect reponse object.
+   *   The redirect response object.
    */
    */
   protected function fromResponse(RedirectResponse $response) {
   protected function fromResponse(RedirectResponse $response) {
     $this->setProtocolVersion($response->getProtocolVersion());
     $this->setProtocolVersion($response->getProtocolVersion());

+ 4 - 1
core/lib/Drupal/Component/Plugin/PluginManagerBase.php

@@ -29,7 +29,7 @@ abstract class PluginManagerBase implements PluginManagerInterface {
   /**
   /**
    * The object that returns the preconfigured plugin instance appropriate for a particular runtime condition.
    * The object that returns the preconfigured plugin instance appropriate for a particular runtime condition.
    *
    *
-   * @var \Drupal\Component\Plugin\Mapper\MapperInterface
+   * @var \Drupal\Component\Plugin\Mapper\MapperInterface|null
    */
    */
   protected $mapper;
   protected $mapper;
 
 
@@ -104,6 +104,9 @@ abstract class PluginManagerBase implements PluginManagerInterface {
    * {@inheritdoc}
    * {@inheritdoc}
    */
    */
   public function getInstance(array $options) {
   public function getInstance(array $options) {
+    if (!$this->mapper) {
+      throw new \BadMethodCallException(sprintf('%s does not support this method unless %s::$mapper is set.', static::class, static::class));
+    }
     return $this->mapper->getInstance($options);
     return $this->mapper->getInstance($options);
   }
   }
 
 

+ 3 - 3
core/lib/Drupal/Core/Archiver/ArchiveTar.php

@@ -49,7 +49,7 @@
  * The following changes have been done:
  * The following changes have been done:
  *  Added namespace Drupal\Core\Archiver.
  *  Added namespace Drupal\Core\Archiver.
  *  Removed require_once 'PEAR.php'.
  *  Removed require_once 'PEAR.php'.
- *  Added defintion of OS_WINDOWS taken from PEAR.php.
+ *  Added definition of OS_WINDOWS taken from PEAR.php.
  *  Renamed class to ArchiveTar.
  *  Renamed class to ArchiveTar.
  *  Removed extends PEAR from class.
  *  Removed extends PEAR from class.
  *  Removed call parent:: __construct().
  *  Removed call parent:: __construct().
@@ -181,7 +181,7 @@ class ArchiveTar
                     if ($data == "\37\213") {
                     if ($data == "\37\213") {
                         $this->_compress = true;
                         $this->_compress = true;
                         $this->_compress_type = 'gz';
                         $this->_compress_type = 'gz';
-                        // No sure it's enought for a magic code ....
+                        // Not sure it's enough for a magic code ....
                     } elseif ($data == "BZ") {
                     } elseif ($data == "BZ") {
                         $this->_compress = true;
                         $this->_compress = true;
                         $this->_compress_type = 'bz2';
                         $this->_compress_type = 'bz2';
@@ -2385,7 +2385,7 @@ class ArchiveTar
 
 
     /**
     /**
      * Compress path by changing for example "/dir/foo/../bar" to "/dir/bar",
      * Compress path by changing for example "/dir/foo/../bar" to "/dir/bar",
-     * rand emove double slashes.
+     * and remove double slashes.
      *
      *
      * @param string $p_dir path to reduce
      * @param string $p_dir path to reduce
      *
      *

+ 1 - 1
core/lib/Drupal/Core/Cache/CacheBackendInterface.php

@@ -9,7 +9,7 @@ namespace Drupal\Core\Cache;
  * Drupal\Core\Cache\DatabaseBackend provides the default implementation, which
  * Drupal\Core\Cache\DatabaseBackend provides the default implementation, which
  * can be consulted as an example.
  * can be consulted as an example.
  *
  *
- * The cache indentifiers are case sensitive.
+ * The cache identifiers are case sensitive.
  *
  *
  * @ingroup cache
  * @ingroup cache
  */
  */

+ 1 - 3
core/lib/Drupal/Core/Cache/MemoryBackend.php

@@ -157,9 +157,7 @@ class MemoryBackend implements CacheBackendInterface, CacheTagsInvalidatorInterf
    */
    */
   public function invalidateMultiple(array $cids) {
   public function invalidateMultiple(array $cids) {
     foreach ($cids as $cid) {
     foreach ($cids as $cid) {
-      if (isset($this->cache[$cid])) {
-        $this->cache[$cid]->expire = $this->getRequestTime() - 1;
-      }
+      $this->cache[$cid]->expire = $this->getRequestTime() - 1;
     }
     }
   }
   }
 
 

+ 16 - 5
core/lib/Drupal/Core/Config/ConfigInstaller.php

@@ -160,7 +160,10 @@ class ConfigInstaller implements ConfigInstallerInterface {
    */
    */
   public function installOptionalConfig(StorageInterface $storage = NULL, $dependency = []) {
   public function installOptionalConfig(StorageInterface $storage = NULL, $dependency = []) {
     $profile = $this->drupalGetProfile();
     $profile = $this->drupalGetProfile();
-    $optional_profile_config = [];
+    $enabled_extensions = $this->getEnabledExtensions();
+    $existing_config = $this->getActiveStorages()->listAll();
+
+    // Create the storages to read configuration from.
     if (!$storage) {
     if (!$storage) {
       // Search the install profile's optional configuration too.
       // Search the install profile's optional configuration too.
       $storage = new ExtensionInstallStorage($this->getActiveStorages(StorageInterface::DEFAULT_COLLECTION), InstallStorage::CONFIG_OPTIONAL_DIRECTORY, StorageInterface::DEFAULT_COLLECTION, TRUE, $this->installProfile);
       $storage = new ExtensionInstallStorage($this->getActiveStorages(StorageInterface::DEFAULT_COLLECTION), InstallStorage::CONFIG_OPTIONAL_DIRECTORY, StorageInterface::DEFAULT_COLLECTION, TRUE, $this->installProfile);
@@ -171,7 +174,6 @@ class ConfigInstaller implements ConfigInstallerInterface {
       // Creates a profile storage to search for overrides.
       // Creates a profile storage to search for overrides.
       $profile_install_path = $this->drupalGetPath('module', $profile) . '/' . InstallStorage::CONFIG_OPTIONAL_DIRECTORY;
       $profile_install_path = $this->drupalGetPath('module', $profile) . '/' . InstallStorage::CONFIG_OPTIONAL_DIRECTORY;
       $profile_storage = new FileStorage($profile_install_path, StorageInterface::DEFAULT_COLLECTION);
       $profile_storage = new FileStorage($profile_install_path, StorageInterface::DEFAULT_COLLECTION);
-      $optional_profile_config = $profile_storage->listAll();
     }
     }
     else {
     else {
       // Profile has not been set yet. For example during the first steps of the
       // Profile has not been set yet. For example during the first steps of the
@@ -179,10 +181,17 @@ class ConfigInstaller implements ConfigInstallerInterface {
       $profile_storage = NULL;
       $profile_storage = NULL;
     }
     }
 
 
-    $enabled_extensions = $this->getEnabledExtensions();
-    $existing_config = $this->getActiveStorages()->listAll();
+    // Build the list of possible configuration to create.
+    $list = $storage->listAll();
+    if ($profile_storage && !empty($dependency)) {
+      // Only add the optional profile configuration into the list if we are
+      // have a dependency to check. This ensures that optional profile
+      // configuration is not unexpectedly re-created after being deleted.
+      $list = array_unique(array_merge($list, $profile_storage->listAll()));
+    }
 
 
-    $list = array_unique(array_merge($storage->listAll(), $optional_profile_config));
+    // Filter the list of configuration to only include configuration that
+    // should be created.
     $list = array_filter($list, function ($config_name) use ($existing_config) {
     $list = array_filter($list, function ($config_name) use ($existing_config) {
       // Only list configuration that:
       // Only list configuration that:
       // - does not already exist
       // - does not already exist
@@ -226,6 +235,8 @@ class ConfigInstaller implements ConfigInstallerInterface {
         unset($all_config[$config_name]);
         unset($all_config[$config_name]);
       }
       }
     }
     }
+
+    // Create the optional configuration if there is any left after filtering.
     if (!empty($config_to_create)) {
     if (!empty($config_to_create)) {
       $this->createConfiguration(StorageInterface::DEFAULT_COLLECTION, $config_to_create, TRUE);
       $this->createConfiguration(StorageInterface::DEFAULT_COLLECTION, $config_to_create, TRUE);
     }
     }

+ 2 - 1
core/lib/Drupal/Core/Config/ConfigInstallerInterface.php

@@ -43,7 +43,8 @@ interface ConfigInstallerInterface {
    * @param \Drupal\Core\Config\StorageInterface $storage
    * @param \Drupal\Core\Config\StorageInterface $storage
    *   (optional) The configuration storage to search for optional
    *   (optional) The configuration storage to search for optional
    *   configuration. If not provided, all enabled extension's optional
    *   configuration. If not provided, all enabled extension's optional
-   *   configuration directories will be searched.
+   *   configuration directories including the install profile's will be
+   *   searched.
    * @param array $dependency
    * @param array $dependency
    *   (optional) If set, ensures that the configuration being installed has
    *   (optional) If set, ensures that the configuration being installed has
    *   this dependency. The format is dependency type as the key ('module',
    *   this dependency. The format is dependency type as the key ('module',

+ 1 - 1
core/lib/Drupal/Core/Config/Entity/ConfigEntityInterface.php

@@ -189,7 +189,7 @@ interface ConfigEntityInterface extends EntityInterface, ThirdPartySettingsInter
    * For example, a default view might not be installable if the base table
    * For example, a default view might not be installable if the base table
    * doesn't exist.
    * doesn't exist.
    *
    *
-   * @retun bool
+   * @return bool
    *   TRUE if the entity is installable, FALSE otherwise.
    *   TRUE if the entity is installable, FALSE otherwise.
    */
    */
   public function isInstallable();
   public function isInstallable();

+ 6 - 2
core/lib/Drupal/Core/Config/FileStorage.php

@@ -46,7 +46,7 @@ class FileStorage implements StorageInterface {
     $this->collection = $collection;
     $this->collection = $collection;
 
 
     // Use a NULL File Cache backend by default. This will ensure only the
     // Use a NULL File Cache backend by default. This will ensure only the
-    // internal statc caching of FileCache is used and thus avoids blowing up
+    // internal static caching of FileCache is used and thus avoids blowing up
     // the APCu cache.
     // the APCu cache.
     $this->fileCache = FileCacheFactory::get('config', ['cache_backend_class' => NULL]);
     $this->fileCache = FileCacheFactory::get('config', ['cache_backend_class' => NULL]);
   }
   }
@@ -279,6 +279,9 @@ class FileStorage implements StorageInterface {
    * {@inheritdoc}
    * {@inheritdoc}
    */
    */
   public function getAllCollectionNames() {
   public function getAllCollectionNames() {
+    if (!is_dir($this->directory)) {
+      return [];
+    }
     $collections = $this->getAllCollectionNamesHelper($this->directory);
     $collections = $this->getAllCollectionNamesHelper($this->directory);
     sort($collections);
     sort($collections);
     return $collections;
     return $collections;
@@ -305,7 +308,8 @@ class FileStorage implements StorageInterface {
    * @param string $directory
    * @param string $directory
    *   The directory to check for sub directories. This allows this
    *   The directory to check for sub directories. This allows this
    *   function to be used recursively to discover all the collections in the
    *   function to be used recursively to discover all the collections in the
-   *   storage.
+   *   storage. It is the responsibility of the caller to ensure the directory
+   *   exists.
    *
    *
    * @return array
    * @return array
    *   A list of collection names contained within the provided directory.
    *   A list of collection names contained within the provided directory.

+ 1 - 1
core/lib/Drupal/Core/Database/Driver/mysql/Install/Tasks.php

@@ -65,7 +65,7 @@ class Tasks extends InstallTasks {
         Database::getConnection();
         Database::getConnection();
       }
       }
       catch (\Exception $e) {
       catch (\Exception $e) {
-        // Detect utf8mb4 incompability.
+        // Detect utf8mb4 incompatibility.
         if ($e->getCode() == Connection::UNSUPPORTED_CHARSET || ($e->getCode() == Connection::SQLSTATE_SYNTAX_ERROR && $e->errorInfo[1] == Connection::UNKNOWN_CHARSET)) {
         if ($e->getCode() == Connection::UNSUPPORTED_CHARSET || ($e->getCode() == Connection::SQLSTATE_SYNTAX_ERROR && $e->errorInfo[1] == Connection::UNKNOWN_CHARSET)) {
           $this->fail(t('Your MySQL server and PHP MySQL driver must support utf8mb4 character encoding. Make sure to use a database system that supports this (such as MySQL/MariaDB/Percona 5.5.3 and up), and that the utf8mb4 character set is compiled in. See the <a href=":documentation" target="_blank">MySQL documentation</a> for more information.', [':documentation' => 'https://dev.mysql.com/doc/refman/5.0/en/cannot-initialize-character-set.html']));
           $this->fail(t('Your MySQL server and PHP MySQL driver must support utf8mb4 character encoding. Make sure to use a database system that supports this (such as MySQL/MariaDB/Percona 5.5.3 and up), and that the utf8mb4 character set is compiled in. See the <a href=":documentation" target="_blank">MySQL documentation</a> for more information.', [':documentation' => 'https://dev.mysql.com/doc/refman/5.0/en/cannot-initialize-character-set.html']));
           $info = Database::getConnectionInfo();
           $info = Database::getConnectionInfo();

+ 1 - 1
core/lib/Drupal/Core/Database/Driver/pgsql/Connection.php

@@ -38,7 +38,7 @@ class Connection extends DatabaseConnection {
   /**
   /**
    * A map of condition operators to PostgreSQL operators.
    * A map of condition operators to PostgreSQL operators.
    *
    *
-   * In PostgreSQL, 'LIKE' is case-sensitive. ILKE should be used for
+   * In PostgreSQL, 'LIKE' is case-sensitive. ILIKE should be used for
    * case-insensitive statements.
    * case-insensitive statements.
    */
    */
   protected static $postgresqlConditionOperatorMap = [
   protected static $postgresqlConditionOperatorMap = [

+ 7 - 0
core/lib/Drupal/Core/Database/Query/Select.php

@@ -914,6 +914,8 @@ class Select extends Query implements SelectInterface {
    * {@inheritdoc}
    * {@inheritdoc}
    */
    */
   public function __clone() {
   public function __clone() {
+    parent::__clone();
+
     // On cloning, also clone the dependent objects. However, we do not
     // On cloning, also clone the dependent objects. However, we do not
     // want to clone the database connection object as that would duplicate the
     // want to clone the database connection object as that would duplicate the
     // connection itself.
     // connection itself.
@@ -923,6 +925,11 @@ class Select extends Query implements SelectInterface {
     foreach ($this->union as $key => $aggregate) {
     foreach ($this->union as $key => $aggregate) {
       $this->union[$key]['query'] = clone($aggregate['query']);
       $this->union[$key]['query'] = clone($aggregate['query']);
     }
     }
+    foreach ($this->tables as $alias => $table) {
+      if ($table['table'] instanceof SelectInterface) {
+        $this->tables[$alias]['table'] = clone $table['table'];
+      }
+    }
   }
   }
 
 
 }
 }

+ 6 - 6
core/lib/Drupal/Core/Database/StatementPrefetch.php

@@ -20,7 +20,7 @@ class StatementPrefetch implements \Iterator, StatementInterface {
   /**
   /**
    * Driver-specific options. Can be used by child classes.
    * Driver-specific options. Can be used by child classes.
    *
    *
-   * @var Array
+   * @var array
    */
    */
   protected $driverOptions;
   protected $driverOptions;
 
 
@@ -41,14 +41,14 @@ class StatementPrefetch implements \Iterator, StatementInterface {
   /**
   /**
    * Main data store.
    * Main data store.
    *
    *
-   * @var Array
+   * @var array
    */
    */
   protected $data = [];
   protected $data = [];
 
 
   /**
   /**
    * The current row, retrieved in \PDO::FETCH_ASSOC format.
    * The current row, retrieved in \PDO::FETCH_ASSOC format.
    *
    *
-   * @var Array
+   * @var array
    */
    */
   protected $currentRow = NULL;
   protected $currentRow = NULL;
 
 
@@ -62,7 +62,7 @@ class StatementPrefetch implements \Iterator, StatementInterface {
   /**
   /**
    * The list of column names in this result set.
    * The list of column names in this result set.
    *
    *
-   * @var Array
+   * @var array
    */
    */
   protected $columnNames = NULL;
   protected $columnNames = NULL;
 
 
@@ -91,7 +91,7 @@ class StatementPrefetch implements \Iterator, StatementInterface {
   /**
   /**
    * Holds supplementary current fetch options (which will be used by the next fetch).
    * Holds supplementary current fetch options (which will be used by the next fetch).
    *
    *
-   * @var Array
+   * @var array
    */
    */
   protected $fetchOptions = [
   protected $fetchOptions = [
     'class' => 'stdClass',
     'class' => 'stdClass',
@@ -110,7 +110,7 @@ class StatementPrefetch implements \Iterator, StatementInterface {
   /**
   /**
    * Holds supplementary default fetch options.
    * Holds supplementary default fetch options.
    *
    *
-   * @var Array
+   * @var array
    */
    */
   protected $defaultFetchOptions = [
   protected $defaultFetchOptions = [
     'class' => 'stdClass',
     'class' => 'stdClass',

+ 1 - 1
core/lib/Drupal/Core/DependencyInjection/Compiler/CorsCompilerPass.php

@@ -22,7 +22,7 @@ class CorsCompilerPass implements CompilerPassInterface {
       $enabled = !empty($cors_config['enabled']);
       $enabled = !empty($cors_config['enabled']);
     }
     }
 
 
-    // Remove the CORS middleware completly in case it was not enabled.
+    // Remove the CORS middleware completely in case it was not enabled.
     if (!$enabled) {
     if (!$enabled) {
       $container->removeDefinition('http_middleware.cors');
       $container->removeDefinition('http_middleware.cors');
     }
     }

+ 6 - 2
core/lib/Drupal/Core/DrupalKernel.php

@@ -298,12 +298,16 @@ class DrupalKernel implements DrupalKernelInterface, TerminableInterface {
   }
   }
 
 
   /**
   /**
-   * Determine the application root directory based on assumptions.
+   * Determine the application root directory based on this file's location.
    *
    *
    * @return string
    * @return string
    *   The application root.
    *   The application root.
    */
    */
   protected static function guessApplicationRoot() {
   protected static function guessApplicationRoot() {
+    // Determine the application root by:
+    // - Removing the namespace directories from the path.
+    // - Getting the path to the directory two levels up from the path
+    //   determined in the previous step.
     return dirname(dirname(substr(__DIR__, 0, -strlen(__NAMESPACE__))));
     return dirname(dirname(substr(__DIR__, 0, -strlen(__NAMESPACE__))));
   }
   }
 
 
@@ -1091,7 +1095,7 @@ class DrupalKernel implements DrupalKernelInterface, TerminableInterface {
         // misses.
         // misses.
         $old_loader = $this->classLoader;
         $old_loader = $this->classLoader;
         $this->classLoader = $loader;
         $this->classLoader = $loader;
-        // Our class loaders are preprended to ensure they come first like the
+        // Our class loaders are prepended to ensure they come first like the
         // class loader they are replacing.
         // class loader they are replacing.
         $old_loader->register(TRUE);
         $old_loader->register(TRUE);
         $loader->register(TRUE);
         $loader->register(TRUE);

+ 2 - 1
core/lib/Drupal/Core/Entity/ContentEntityDeleteForm.php

@@ -60,6 +60,7 @@ class ContentEntityDeleteForm extends ContentEntityConfirmFormBase {
   public function submitForm(array &$form, FormStateInterface $form_state) {
   public function submitForm(array &$form, FormStateInterface $form_state) {
     /** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
     /** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
     $entity = $this->getEntity();
     $entity = $this->getEntity();
+    $message = $this->getDeletionMessage();
 
 
     // Make sure that deleting a translation does not delete the whole entity.
     // Make sure that deleting a translation does not delete the whole entity.
     if (!$entity->isDefaultTranslation()) {
     if (!$entity->isDefaultTranslation()) {
@@ -73,7 +74,7 @@ class ContentEntityDeleteForm extends ContentEntityConfirmFormBase {
       $form_state->setRedirectUrl($this->getRedirectUrl());
       $form_state->setRedirectUrl($this->getRedirectUrl());
     }
     }
 
 
-    $this->messenger()->addStatus($this->getDeletionMessage());
+    $this->messenger()->addStatus($message);
     $this->logDeletionMessage();
     $this->logDeletionMessage();
   }
   }
 
 

+ 2 - 2
core/lib/Drupal/Core/Entity/Element/EntityAutocomplete.php

@@ -251,8 +251,8 @@ class EntityAutocomplete extends Textfield {
   /**
   /**
    * Finds an entity from an autocomplete input without an explicit ID.
    * Finds an entity from an autocomplete input without an explicit ID.
    *
    *
-   * The method will return an entity ID if one single entity unambuguously
-   * matches the incoming input, and sill assign form errors otherwise.
+   * The method will return an entity ID if one single entity unambiguously
+   * matches the incoming input, and assign form errors otherwise.
    *
    *
    * @param \Drupal\Core\Entity\EntityReferenceSelection\SelectionInterface $handler
    * @param \Drupal\Core\Entity\EntityReferenceSelection\SelectionInterface $handler
    *   Entity reference selection plugin.
    *   Entity reference selection plugin.

+ 1 - 0
core/lib/Drupal/Core/Entity/EntityBundleListener.php

@@ -68,6 +68,7 @@ class EntityBundleListener implements EntityBundleListenerInterface {
     }
     }
     // Invoke hook_entity_bundle_create() hook.
     // Invoke hook_entity_bundle_create() hook.
     $this->moduleHandler->invokeAll('entity_bundle_create', [$entity_type_id, $bundle]);
     $this->moduleHandler->invokeAll('entity_bundle_create', [$entity_type_id, $bundle]);
+    $this->entityFieldManager->clearCachedFieldDefinitions();
   }
   }
 
 
   /**
   /**

+ 1 - 1
core/lib/Drupal/Core/Entity/EntityFieldManager.php

@@ -497,7 +497,7 @@ class EntityFieldManager implements EntityFieldManagerInterface {
           }
           }
         }
         }
 
 
-        $this->cacheSet($cid, $this->fieldMap, Cache::PERMANENT, ['entity_types']);
+        $this->cacheSet($cid, $this->fieldMap, Cache::PERMANENT, ['entity_types', 'entity_field_info']);
       }
       }
     }
     }
     return $this->fieldMap;
     return $this->fieldMap;

+ 1 - 1
core/lib/Drupal/Core/Entity/EntityStorageBase.php

@@ -194,7 +194,7 @@ abstract class EntityStorageBase extends EntityHandlerBase implements EntityStor
    * Invokes a hook on behalf of the entity.
    * Invokes a hook on behalf of the entity.
    *
    *
    * @param string $hook
    * @param string $hook
-   *   One of 'presave', 'insert', 'update', 'predelete', 'delete', or
+   *   One of 'create', 'presave', 'insert', 'update', 'predelete', 'delete', or
    *   'revision_delete'.
    *   'revision_delete'.
    * @param \Drupal\Core\Entity\EntityInterface $entity
    * @param \Drupal\Core\Entity\EntityInterface $entity
    *   The entity object.
    *   The entity object.

+ 1 - 1
core/lib/Drupal/Core/Entity/Plugin/DataType/EntityReference.php

@@ -16,7 +16,7 @@ use Drupal\Core\TypedData\DataReferenceBase;
  * or the entity ID may be passed.
  * or the entity ID may be passed.
  *
  *
  * Note that the definition of the referenced entity's type is required, whereas
  * Note that the definition of the referenced entity's type is required, whereas
- * defining referencable entity bundle(s) is optional. A reference defining the
+ * defining referenceable entity bundle(s) is optional. A reference defining the
  * type and bundle of the referenced entity can be created as following:
  * type and bundle of the referenced entity can be created as following:
  * @code
  * @code
  * $definition = \Drupal\Core\Entity\EntityDefinition::create($entity_type)
  * $definition = \Drupal\Core\Entity\EntityDefinition::create($entity_type)

+ 1 - 1
core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorage.php

@@ -518,7 +518,7 @@ class SqlContentEntityStorage extends ContentEntityStorageBase implements SqlEnt
           // Some fields can have more then one columns in the data table so
           // Some fields can have more then one columns in the data table so
           // column names are needed.
           // column names are needed.
           foreach ($data_fields as $data_field) {
           foreach ($data_fields as $data_field) {
-            // \Drupal\Core\Entity\Sql\TableMappingInterface:: getColumNames()
+            // \Drupal\Core\Entity\Sql\TableMappingInterface::getColumnNames()
             // returns an array keyed by property names so remove the keys
             // returns an array keyed by property names so remove the keys
             // before array_merge() to avoid losing data with fields having the
             // before array_merge() to avoid losing data with fields having the
             // same columns i.e. value.
             // same columns i.e. value.

+ 21 - 9
core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorageSchema.php

@@ -1174,12 +1174,12 @@ class SqlContentEntityStorageSchema implements DynamicallyFieldableEntityStorage
    *   The entity type.
    *   The entity type.
    * @param array $schema
    * @param array $schema
    *   The table schema, passed by reference.
    *   The table schema, passed by reference.
-   *
-   * @return array
-   *   A partial schema array for the base table.
    */
    */
   protected function processBaseTable(ContentEntityTypeInterface $entity_type, array &$schema) {
   protected function processBaseTable(ContentEntityTypeInterface $entity_type, array &$schema) {
-    $this->processIdentifierSchema($schema, $entity_type->getKey('id'));
+    // Process the schema for the 'id' entity key only if it exists.
+    if ($entity_type->hasKey('id')) {
+      $this->processIdentifierSchema($schema, $entity_type->getKey('id'));
+    }
   }
   }
 
 
   /**
   /**
@@ -1189,12 +1189,12 @@ class SqlContentEntityStorageSchema implements DynamicallyFieldableEntityStorage
    *   The entity type.
    *   The entity type.
    * @param array $schema
    * @param array $schema
    *   The table schema, passed by reference.
    *   The table schema, passed by reference.
-   *
-   * @return array
-   *   A partial schema array for the base table.
    */
    */
   protected function processRevisionTable(ContentEntityTypeInterface $entity_type, array &$schema) {
   protected function processRevisionTable(ContentEntityTypeInterface $entity_type, array &$schema) {
-    $this->processIdentifierSchema($schema, $entity_type->getKey('revision'));
+    // Process the schema for the 'revision' entity key only if it exists.
+    if ($entity_type->hasKey('revision')) {
+      $this->processIdentifierSchema($schema, $entity_type->getKey('revision'));
+    }
   }
   }
 
 
   /**
   /**
@@ -1333,11 +1333,23 @@ class SqlContentEntityStorageSchema implements DynamicallyFieldableEntityStorage
           // Create field columns.
           // Create field columns.
           $schema[$table_name] = $this->getSharedTableFieldSchema($storage_definition, $table_name, $column_names);
           $schema[$table_name] = $this->getSharedTableFieldSchema($storage_definition, $table_name, $column_names);
           if (!$only_save) {
           if (!$only_save) {
+            // The entity schema needs to be checked because the field schema is
+            // potentially incomplete.
+            // @todo Fix this in https://www.drupal.org/node/2929120.
+            $entity_schema = $this->getEntitySchema($this->entityType);
             foreach ($schema[$table_name]['fields'] as $name => $specifier) {
             foreach ($schema[$table_name]['fields'] as $name => $specifier) {
+              // Check if the field is part of the primary keys and pass along
+              // this information when adding the field.
+              // @see \Drupal\Core\Database\Schema::addField()
+              $new_keys = [];
+              if (isset($entity_schema[$table_name]['primary key']) && array_intersect($column_names, $entity_schema[$table_name]['primary key'])) {
+                $new_keys = ['primary key' => $entity_schema[$table_name]['primary key']];
+              }
+
               // Check if the field exists because it might already have been
               // Check if the field exists because it might already have been
               // created as part of the earlier entity type update event.
               // created as part of the earlier entity type update event.
               if (!$schema_handler->fieldExists($table_name, $name)) {
               if (!$schema_handler->fieldExists($table_name, $name)) {
-                $schema_handler->addField($table_name, $name, $specifier);
+                $schema_handler->addField($table_name, $name, $specifier, $new_keys);
               }
               }
             }
             }
             if (!empty($schema[$table_name]['indexes'])) {
             if (!empty($schema[$table_name]['indexes'])) {

+ 1 - 4
core/lib/Drupal/Core/EventSubscriber/KernelDestructionSubscriber.php

@@ -60,10 +60,7 @@ class KernelDestructionSubscriber implements EventSubscriberInterface, Container
    *   An array of event listener definitions.
    *   An array of event listener definitions.
    */
    */
   public static function getSubscribedEvents() {
   public static function getSubscribedEvents() {
-    // Run this subscriber after others as those might use services that need
-    // to be terminated as well or run code that needs to run before
-    // termination.
-    $events[KernelEvents::TERMINATE][] = ['onKernelTerminate', -100];
+    $events[KernelEvents::TERMINATE][] = ['onKernelTerminate', 100];
     return $events;
     return $events;
   }
   }
 
 

+ 17 - 7
core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/TimestampAgoFormatter.php

@@ -5,6 +5,7 @@ namespace Drupal\Core\Field\Plugin\Field\FieldFormatter;
 use Drupal\Component\Render\FormattableMarkup;
 use Drupal\Component\Render\FormattableMarkup;
 use Drupal\Core\Cache\CacheableMetadata;
 use Drupal\Core\Cache\CacheableMetadata;
 use Drupal\Core\Datetime\DateFormatterInterface;
 use Drupal\Core\Datetime\DateFormatterInterface;
+use Drupal\Core\Datetime\DrupalDateTime;
 use Drupal\Core\Field\FieldDefinitionInterface;
 use Drupal\Core\Field\FieldDefinitionInterface;
 use Drupal\Core\Field\FieldItemListInterface;
 use Drupal\Core\Field\FieldItemListInterface;
 use Drupal\Core\Field\FormatterBase;
 use Drupal\Core\Field\FormatterBase;
@@ -104,7 +105,7 @@ class TimestampAgoFormatter extends FormatterBase implements ContainerFactoryPlu
    * {@inheritdoc}
    * {@inheritdoc}
    */
    */
   public function settingsForm(array $form, FormStateInterface $form_state) {
   public function settingsForm(array $form, FormStateInterface $form_state) {
-    $elements = parent::settingsForm($form, $form_state);
+    $form = parent::settingsForm($form, $form_state);
 
 
     $form['future_format'] = [
     $form['future_format'] = [
       '#type' => 'textfield',
       '#type' => 'textfield',
@@ -120,7 +121,7 @@ class TimestampAgoFormatter extends FormatterBase implements ContainerFactoryPlu
       '#description' => $this->t('Use <em>@interval</em> where you want the formatted interval text to appear.'),
       '#description' => $this->t('Use <em>@interval</em> where you want the formatted interval text to appear.'),
     ];
     ];
 
 
-    $elements['granularity'] = [
+    $form['granularity'] = [
       '#type' => 'number',
       '#type' => 'number',
       '#title' => $this->t('Granularity'),
       '#title' => $this->t('Granularity'),
       '#description' => $this->t('How many time interval units should be shown in the formatted output.'),
       '#description' => $this->t('How many time interval units should be shown in the formatted output.'),
@@ -129,7 +130,7 @@ class TimestampAgoFormatter extends FormatterBase implements ContainerFactoryPlu
       '#max' => 6,
       '#max' => 6,
     ];
     ];
 
 
-    return $elements;
+    return $form;
   }
   }
 
 
   /**
   /**
@@ -138,10 +139,19 @@ class TimestampAgoFormatter extends FormatterBase implements ContainerFactoryPlu
   public function settingsSummary() {
   public function settingsSummary() {
     $summary = parent::settingsSummary();
     $summary = parent::settingsSummary();
 
 
-    $future_date = strtotime('1 year 1 month 1 week 1 day 1 hour 1 minute');
-    $past_date = strtotime('-1 year -1 month -1 week -1 day -1 hour -1 minute');
-    $summary[] = $this->t('Future date: %display', ['%display' => $this->formatTimestamp($future_date)]);
-    $summary[] = $this->t('Past date: %display', ['%display' => $this->formatTimestamp($past_date)]);
+    $future_date = new DrupalDateTime('1 year 1 month 1 week 1 day 1 hour 1 minute');
+    $past_date = new DrupalDateTime('-1 year -1 month -1 week -1 day -1 hour -1 minute');
+    $granularity = $this->getSetting('granularity');
+    $options = [
+      'granularity' => $granularity,
+      'return_as_object' => FALSE,
+    ];
+
+    $future_date_interval = new FormattableMarkup($this->getSetting('future_format'), ['@interval' => $this->dateFormatter->formatTimeDiffUntil($future_date->getTimestamp(), $options)]);
+    $past_date_interval = new FormattableMarkup($this->getSetting('past_format'), ['@interval' => $this->dateFormatter->formatTimeDiffSince($past_date->getTimestamp(), $options)]);
+
+    $summary[] = $this->t('Future date: %display', ['%display' => $future_date_interval]);
+    $summary[] = $this->t('Past date: %display', ['%display' => $past_date_interval]);
 
 
     return $summary;
     return $summary;
   }
   }

+ 4 - 4
core/lib/Drupal/Core/Form/ConfirmFormInterface.php

@@ -10,7 +10,7 @@ interface ConfirmFormInterface extends FormInterface {
   /**
   /**
    * Returns the question to ask the user.
    * Returns the question to ask the user.
    *
    *
-   * @return string
+   * @return \Drupal\Core\StringTranslation\TranslatableMarkup
    *   The form question. The page title will be set to this value.
    *   The form question. The page title will be set to this value.
    */
    */
   public function getQuestion();
   public function getQuestion();
@@ -26,7 +26,7 @@ interface ConfirmFormInterface extends FormInterface {
   /**
   /**
    * Returns additional text to display as a description.
    * Returns additional text to display as a description.
    *
    *
-   * @return string
+   * @return \Drupal\Core\StringTranslation\TranslatableMarkup
    *   The form description.
    *   The form description.
    */
    */
   public function getDescription();
   public function getDescription();
@@ -34,7 +34,7 @@ interface ConfirmFormInterface extends FormInterface {
   /**
   /**
    * Returns a caption for the button that confirms the action.
    * Returns a caption for the button that confirms the action.
    *
    *
-   * @return string
+   * @return \Drupal\Core\StringTranslation\TranslatableMarkup
    *   The form confirmation text.
    *   The form confirmation text.
    */
    */
   public function getConfirmText();
   public function getConfirmText();
@@ -42,7 +42,7 @@ interface ConfirmFormInterface extends FormInterface {
   /**
   /**
    * Returns a caption for the link which cancels the action.
    * Returns a caption for the link which cancels the action.
    *
    *
-   * @return string
+   * @return \Drupal\Core\StringTranslation\TranslatableMarkup
    *   The form cancellation text.
    *   The form cancellation text.
    */
    */
   public function getCancelText();
   public function getCancelText();

+ 1 - 1
core/lib/Drupal/Core/Form/FormBuilder.php

@@ -687,7 +687,7 @@ class FormBuilder implements FormBuilderInterface, FormValidatorInterface, FormS
       // will be replaced at the very last moment. This ensures forms with
       // will be replaced at the very last moment. This ensures forms with
       // dynamically generated action URLs don't have poor cacheability.
       // dynamically generated action URLs don't have poor cacheability.
       // Use the proper API to generate the placeholder, when we have one. See
       // Use the proper API to generate the placeholder, when we have one. See
-      // https://www.drupal.org/node/2562341. The placholder uses a fixed string
+      // https://www.drupal.org/node/2562341. The placeholder uses a fixed string
       // that is Crypt::hashBase64('Drupal\Core\Form\FormBuilder::prepareForm');
       // that is Crypt::hashBase64('Drupal\Core\Form\FormBuilder::prepareForm');
       $placeholder = 'form_action_p_pvdeGsVG5zNF_XLGPTvYSKCf43t8qZYSwcfZl2uzM';
       $placeholder = 'form_action_p_pvdeGsVG5zNF_XLGPTvYSKCf43t8qZYSwcfZl2uzM';
 
 

+ 2 - 2
core/lib/Drupal/Core/Form/FormState.php

@@ -87,8 +87,8 @@ class FormState implements FormStateInterface {
    * copy of the form is immediately built and sent to the browser, instead of a
    * copy of the form is immediately built and sent to the browser, instead of a
    * redirect. This is used for multi-step forms, such as wizards and
    * redirect. This is used for multi-step forms, such as wizards and
    * confirmation forms. Normally, self::$rebuild is set by a submit handler,
    * confirmation forms. Normally, self::$rebuild is set by a submit handler,
-   * since its is usually logic within a submit handler that determines whether
-   * a form is done or requires another step. However, a validation handler may
+   * since it is usually logic within a submit handler that determines whether a
+   * form is done or requires another step. However, a validation handler may
    * already set self::$rebuild to cause the form processing to bypass submit
    * already set self::$rebuild to cause the form processing to bypass submit
    * handlers and rebuild the form instead, even if there are no validation
    * handlers and rebuild the form instead, even if there are no validation
    * errors.
    * errors.

+ 1 - 1
core/lib/Drupal/Core/Http/HandlerStackConfigurator.php

@@ -43,7 +43,7 @@ class HandlerStackConfigurator {
   protected $container;
   protected $container;
 
 
   /**
   /**
-   * Contructs a new HandlerStackConfigurator object.
+   * Constructs a new HandlerStackConfigurator object.
    *
    *
    * @param \Symfony\Component\DependencyInjection\ContainerInterface $container
    * @param \Symfony\Component\DependencyInjection\ContainerInterface $container
    *   The service container.
    *   The service container.

+ 1 - 1
core/lib/Drupal/Core/Http/TrustedHostsRequestFactory.php

@@ -44,7 +44,7 @@ class TrustedHostsRequestFactory {
    * @param array $request
    * @param array $request
    *   (optional) An array of request variables.
    *   (optional) An array of request variables.
    * @param array $attributes
    * @param array $attributes
-   *   (optioanl) An array of attributes.
+   *   (optional) An array of attributes.
    * @param array $cookies
    * @param array $cookies
    *   (optional) The request cookies ($_COOKIE).
    *   (optional) The request cookies ($_COOKIE).
    * @param array $files
    * @param array $files

+ 1 - 1
core/lib/Drupal/Core/Installer/Exception/InstallProfileMismatchException.php

@@ -19,7 +19,7 @@ class InstallProfileMismatchException extends InstallerException {
    * @param string $settings_profile
    * @param string $settings_profile
    *   The profile in settings.php.
    *   The profile in settings.php.
    * @param string $settings_file
    * @param string $settings_file
-   *   The path to settigns.php.
+   *   The path to settings.php.
    * @param \Drupal\Core\StringTranslation\TranslationInterface $string_translation
    * @param \Drupal\Core\StringTranslation\TranslationInterface $string_translation
    *   The string translation manager.
    *   The string translation manager.
    *
    *

+ 10 - 0
core/lib/Drupal/Core/Mail/MailManager.php

@@ -2,7 +2,9 @@
 
 
 namespace Drupal\Core\Mail;
 namespace Drupal\Core\Mail;
 
 
+use Drupal\Component\Render\MarkupInterface;
 use Drupal\Component\Render\PlainTextOutput;
 use Drupal\Component\Render\PlainTextOutput;
+use Drupal\Component\Utility\Html;
 use Drupal\Component\Utility\Unicode;
 use Drupal\Component\Utility\Unicode;
 use Drupal\Core\Logger\LoggerChannelFactoryInterface;
 use Drupal\Core\Logger\LoggerChannelFactoryInterface;
 use Drupal\Core\Messenger\MessengerTrait;
 use Drupal\Core\Messenger\MessengerTrait;
@@ -10,6 +12,7 @@ use Drupal\Core\Plugin\DefaultPluginManager;
 use Drupal\Core\Cache\CacheBackendInterface;
 use Drupal\Core\Cache\CacheBackendInterface;
 use Drupal\Core\Extension\ModuleHandlerInterface;
 use Drupal\Core\Extension\ModuleHandlerInterface;
 use Drupal\Core\Config\ConfigFactoryInterface;
 use Drupal\Core\Config\ConfigFactoryInterface;
+use Drupal\Core\Render\Markup;
 use Drupal\Core\Render\RenderContext;
 use Drupal\Core\Render\RenderContext;
 use Drupal\Core\Render\RendererInterface;
 use Drupal\Core\Render\RendererInterface;
 use Drupal\Core\StringTranslation\StringTranslationTrait;
 use Drupal\Core\StringTranslation\StringTranslationTrait;
@@ -277,6 +280,13 @@ class MailManager extends DefaultPluginManager implements MailManagerInterface {
     // Retrieve the responsible implementation for this message.
     // Retrieve the responsible implementation for this message.
     $system = $this->getInstance(['module' => $module, 'key' => $key]);
     $system = $this->getInstance(['module' => $module, 'key' => $key]);
 
 
+    // Attempt to convert relative URLs to absolute.
+    foreach ($message['body'] as &$body_part) {
+      if ($body_part instanceof MarkupInterface) {
+        $body_part = Markup::create(Html::transformRootRelativeUrlsToAbsolute((string) $body_part, \Drupal::request()->getSchemeAndHttpHost()));
+      }
+    }
+
     // Format the message body.
     // Format the message body.
     $message = $system->format($message);
     $message = $system->format($message);
 
 

+ 15 - 12
core/lib/Drupal/Core/Menu/LocalActionDefault.php

@@ -83,33 +83,36 @@ class LocalActionDefault extends PluginBase implements LocalActionInterface, Con
    * {@inheritdoc}
    * {@inheritdoc}
    */
    */
   public function getRouteParameters(RouteMatchInterface $route_match) {
   public function getRouteParameters(RouteMatchInterface $route_match) {
-    $parameters = isset($this->pluginDefinition['route_parameters']) ? $this->pluginDefinition['route_parameters'] : [];
+    $route_parameters = isset($this->pluginDefinition['route_parameters']) ? $this->pluginDefinition['route_parameters'] : [];
     $route = $this->routeProvider->getRouteByName($this->getRouteName());
     $route = $this->routeProvider->getRouteByName($this->getRouteName());
     $variables = $route->compile()->getVariables();
     $variables = $route->compile()->getVariables();
 
 
     // Normally the \Drupal\Core\ParamConverter\ParamConverterManager has
     // Normally the \Drupal\Core\ParamConverter\ParamConverterManager has
-    // processed the Request attributes, and in that case the _raw_variables
-    // attribute holds the original path strings keyed to the corresponding
-    // slugs in the path patterns. For example, if the route's path pattern is
+    // run, and the route parameters have been upcast. The original values can
+    // be retrieved from the raw parameters. For example, if the route's path is
     // /filter/tips/{filter_format} and the path is /filter/tips/plain_text then
     // /filter/tips/{filter_format} and the path is /filter/tips/plain_text then
-    // $raw_variables->get('filter_format') == 'plain_text'.
-    $raw_variables = $route_match->getRawParameters();
+    // $raw_parameters->get('filter_format') == 'plain_text'. Parameters that
+    // are not represented in the route path as slugs might be added by a route
+    // enhancer and will not be present in the raw parameters.
+    $raw_parameters = $route_match->getRawParameters();
+    $parameters = $route_match->getParameters();
 
 
     foreach ($variables as $name) {
     foreach ($variables as $name) {
-      if (isset($parameters[$name])) {
+      if (isset($route_parameters[$name])) {
         continue;
         continue;
       }
       }
 
 
-      if ($raw_variables && $raw_variables->has($name)) {
-        $parameters[$name] = $raw_variables->get($name);
+      if ($raw_parameters->has($name)) {
+        $route_parameters[$name] = $raw_parameters->get($name);
       }
       }
-      elseif ($value = $route_match->getRawParameter($name)) {
-        $parameters[$name] = $value;
+      elseif ($parameters->has($name)) {
+        $route_parameters[$name] = $parameters->get($name);
       }
       }
     }
     }
+
     // The UrlGenerator will throw an exception if expected parameters are
     // The UrlGenerator will throw an exception if expected parameters are
     // missing. This method should be overridden if that is possible.
     // missing. This method should be overridden if that is possible.
-    return $parameters;
+    return $route_parameters;
   }
   }
 
 
   /**
   /**

+ 3 - 3
core/lib/Drupal/Core/Menu/LocalActionManager.php

@@ -196,9 +196,10 @@ class LocalActionManager extends DefaultPluginManager implements LocalActionMana
       }
       }
     }
     }
     $links = [];
     $links = [];
+    $cacheability = new CacheableMetadata();
+    $cacheability->addCacheContexts(['route']);
     /** @var $plugin \Drupal\Core\Menu\LocalActionInterface */
     /** @var $plugin \Drupal\Core\Menu\LocalActionInterface */
     foreach ($this->instances[$route_appears] as $plugin_id => $plugin) {
     foreach ($this->instances[$route_appears] as $plugin_id => $plugin) {
-      $cacheability = new CacheableMetadata();
       $route_name = $plugin->getRouteName();
       $route_name = $plugin->getRouteName();
       $route_parameters = $plugin->getRouteParameters($this->routeMatch);
       $route_parameters = $plugin->getRouteParameters($this->routeMatch);
       $access = $this->accessManager->checkNamedRoute($route_name, $route_parameters, $this->account, TRUE);
       $access = $this->accessManager->checkNamedRoute($route_name, $route_parameters, $this->account, TRUE);
@@ -213,9 +214,8 @@ class LocalActionManager extends DefaultPluginManager implements LocalActionMana
         '#weight' => $plugin->getWeight(),
         '#weight' => $plugin->getWeight(),
       ];
       ];
       $cacheability->addCacheableDependency($access)->addCacheableDependency($plugin);
       $cacheability->addCacheableDependency($access)->addCacheableDependency($plugin);
-      $cacheability->applyTo($links[$plugin_id]);
     }
     }
-    $links['#cache']['contexts'][] = 'route';
+    $cacheability->applyTo($links);
 
 
     return $links;
     return $links;
   }
   }

+ 15 - 13
core/lib/Drupal/Core/Menu/LocalTaskDefault.php

@@ -41,34 +41,36 @@ class LocalTaskDefault extends PluginBase implements LocalTaskInterface, Cacheab
    * {@inheritdoc}
    * {@inheritdoc}
    */
    */
   public function getRouteParameters(RouteMatchInterface $route_match) {
   public function getRouteParameters(RouteMatchInterface $route_match) {
-    $parameters = isset($this->pluginDefinition['route_parameters']) ? $this->pluginDefinition['route_parameters'] : [];
+    $route_parameters = isset($this->pluginDefinition['route_parameters']) ? $this->pluginDefinition['route_parameters'] : [];
     $route = $this->routeProvider()->getRouteByName($this->getRouteName());
     $route = $this->routeProvider()->getRouteByName($this->getRouteName());
     $variables = $route->compile()->getVariables();
     $variables = $route->compile()->getVariables();
 
 
     // Normally the \Drupal\Core\ParamConverter\ParamConverterManager has
     // Normally the \Drupal\Core\ParamConverter\ParamConverterManager has
-    // processed the Request attributes, and in that case the _raw_variables
-    // attribute holds the original path strings keyed to the corresponding
-    // slugs in the path patterns. For example, if the route's path pattern is
+    // run, and the route parameters have been upcast. The original values can
+    // be retrieved from the raw parameters. For example, if the route's path is
     // /filter/tips/{filter_format} and the path is /filter/tips/plain_text then
     // /filter/tips/{filter_format} and the path is /filter/tips/plain_text then
-    // $raw_variables->get('filter_format') == 'plain_text'.
-
-    $raw_variables = $route_match->getRawParameters();
+    // $raw_parameters->get('filter_format') == 'plain_text'. Parameters that
+    // are not represented in the route path as slugs might be added by a route
+    // enhancer and will not be present in the raw parameters.
+    $raw_parameters = $route_match->getRawParameters();
+    $parameters = $route_match->getParameters();
 
 
     foreach ($variables as $name) {
     foreach ($variables as $name) {
-      if (isset($parameters[$name])) {
+      if (isset($route_parameters[$name])) {
         continue;
         continue;
       }
       }
 
 
-      if ($raw_variables && $raw_variables->has($name)) {
-        $parameters[$name] = $raw_variables->get($name);
+      if ($raw_parameters->has($name)) {
+        $route_parameters[$name] = $raw_parameters->get($name);
       }
       }
-      elseif ($value = $route_match->getRawParameter($name)) {
-        $parameters[$name] = $value;
+      elseif ($parameters->has($name)) {
+        $route_parameters[$name] = $parameters->get($name);
       }
       }
     }
     }
+
     // The UrlGenerator will throw an exception if expected parameters are
     // The UrlGenerator will throw an exception if expected parameters are
     // missing. This method should be overridden if that is possible.
     // missing. This method should be overridden if that is possible.
-    return $parameters;
+    return $route_parameters;
   }
   }
 
 
   /**
   /**

+ 1 - 1
core/lib/Drupal/Core/Menu/MenuLinkTreeInterface.php

@@ -30,7 +30,7 @@ interface MenuLinkTreeInterface {
    *
    *
    * Builds menu link tree parameters that:
    * Builds menu link tree parameters that:
    * - Expand all links in the active trail based on route being viewed.
    * - Expand all links in the active trail based on route being viewed.
-   * - Expand the descendents of the links in the active trail whose
+   * - Expand the descendants of the links in the active trail whose
    *   'expanded' flag is enabled.
    *   'expanded' flag is enabled.
    *
    *
    * This only sets the (relatively complex) parameters to achieve the two above
    * This only sets the (relatively complex) parameters to achieve the two above

+ 1 - 1
core/lib/Drupal/Core/PageCache/RequestPolicy/NoSessionOpen.php

@@ -11,7 +11,7 @@ use Symfony\Component\HttpFoundation\Request;
  *
  *
  * Do not serve cached pages to authenticated users, or to anonymous users when
  * Do not serve cached pages to authenticated users, or to anonymous users when
  * $_SESSION is non-empty. $_SESSION may contain status messages from a form
  * $_SESSION is non-empty. $_SESSION may contain status messages from a form
- * submission, the contents of a shopping cart, or other userspecific content
+ * submission, the contents of a shopping cart, or other user-specific content
  * that should not be cached and displayed to other users.
  * that should not be cached and displayed to other users.
  */
  */
 class NoSessionOpen implements RequestPolicyInterface {
 class NoSessionOpen implements RequestPolicyInterface {

+ 8 - 1
core/lib/Drupal/Core/PathProcessor/InboundPathProcessorInterface.php

@@ -12,10 +12,17 @@ interface InboundPathProcessorInterface {
   /**
   /**
    * Processes the inbound path.
    * Processes the inbound path.
    *
    *
+   * Implementations may make changes to the request object passed in but should
+   * avoid all other side effects. This method can be called to process requests
+   * other than the current request.
+   *
    * @param string $path
    * @param string $path
    *   The path to process, with a leading slash.
    *   The path to process, with a leading slash.
    * @param \Symfony\Component\HttpFoundation\Request $request
    * @param \Symfony\Component\HttpFoundation\Request $request
-   *   The HttpRequest object representing the current request.
+   *   The HttpRequest object representing the request to process. Note, if this
+   *   method is being called via the path_processor_manager service and is not
+   *   part of routing, the current request object must be cloned before being
+   *   passed in.
    *
    *
    * @return string
    * @return string
    *   The processed path.
    *   The processed path.

+ 2 - 0
core/lib/Drupal/Core/Plugin/Context/Context.php

@@ -6,6 +6,7 @@ use Drupal\Component\Plugin\Context\Context as ComponentContext;
 use Drupal\Component\Plugin\Exception\ContextException;
 use Drupal\Component\Plugin\Exception\ContextException;
 use Drupal\Core\Cache\CacheableDependencyInterface;
 use Drupal\Core\Cache\CacheableDependencyInterface;
 use Drupal\Core\Cache\CacheableMetadata;
 use Drupal\Core\Cache\CacheableMetadata;
+use Drupal\Core\DependencyInjection\DependencySerializationTrait;
 use Drupal\Core\TypedData\TypedDataInterface;
 use Drupal\Core\TypedData\TypedDataInterface;
 use Drupal\Core\TypedData\TypedDataTrait;
 use Drupal\Core\TypedData\TypedDataTrait;
 
 
@@ -15,6 +16,7 @@ use Drupal\Core\TypedData\TypedDataTrait;
 class Context extends ComponentContext implements ContextInterface {
 class Context extends ComponentContext implements ContextInterface {
 
 
   use TypedDataTrait;
   use TypedDataTrait;
+  use DependencySerializationTrait;
 
 
   /**
   /**
    * The data associated with the context.
    * The data associated with the context.

+ 1 - 1
core/lib/Drupal/Core/ProxyBuilder/ProxyBuilder.php

@@ -5,7 +5,7 @@ namespace Drupal\Core\ProxyBuilder;
 use Drupal\Component\ProxyBuilder\ProxyBuilder as BaseProxyBuilder;
 use Drupal\Component\ProxyBuilder\ProxyBuilder as BaseProxyBuilder;
 
 
 /**
 /**
- * Extend the component proxy builder by using the DependencySerialziationTrait.
+ * Extend the component proxy builder by using the DependencySerializationTrait.
  */
  */
 class ProxyBuilder extends BaseProxyBuilder {
 class ProxyBuilder extends BaseProxyBuilder {
 
 

+ 2 - 0
core/lib/Drupal/Core/Render/Element/Email.php

@@ -11,12 +11,14 @@ use Drupal\Core\Render\Element;
  * Properties:
  * Properties:
  * - #default_value: An RFC-compliant email address.
  * - #default_value: An RFC-compliant email address.
  * - #size: The size of the input element in characters.
  * - #size: The size of the input element in characters.
+ * - #pattern: A string for the native HTML5 pattern attribute.
  *
  *
  * Example usage:
  * Example usage:
  * @code
  * @code
  * $form['email'] = array(
  * $form['email'] = array(
  *   '#type' => 'email',
  *   '#type' => 'email',
  *   '#title' => $this->t('Email'),
  *   '#title' => $this->t('Email'),
+ *   '#pattern' => '*@example.com',
  * );
  * );
  * @end
  * @end
  *
  *

+ 2 - 0
core/lib/Drupal/Core/Render/Element/Password.php

@@ -10,6 +10,7 @@ use Drupal\Core\Render\Element;
  *
  *
  * Properties:
  * Properties:
  * - #size: The size of the input element in characters.
  * - #size: The size of the input element in characters.
+ * - #pattern: A string for the native HTML5 pattern attribute.
  *
  *
  * Usage example:
  * Usage example:
  * @code
  * @code
@@ -17,6 +18,7 @@ use Drupal\Core\Render\Element;
  *   '#type' => 'password',
  *   '#type' => 'password',
  *   '#title' => $this->t('Password'),
  *   '#title' => $this->t('Password'),
  *   '#size' => 25,
  *   '#size' => 25,
+ *   '#pattern' => '[01]+',
  * );
  * );
  * @endcode
  * @endcode
  *
  *

+ 1 - 1
core/lib/Drupal/Core/Render/Element/RenderElement.php

@@ -32,7 +32,7 @@ use Drupal\Core\Url;
  * strings, if they are literals provided by your module, should be
  * strings, if they are literals provided by your module, should be
  * internationalized and translated; see the
  * internationalized and translated; see the
  * @link i18n Internationalization topic @endlink for more information. Note
  * @link i18n Internationalization topic @endlink for more information. Note
- * that although in the properies list that follows, they are designated to be
+ * that although in the properties list that follows, they are designated to be
  * of type string, they would generally end up being
  * of type string, they would generally end up being
  * \Drupal\Core\StringTranslation\TranslatableMarkup objects instead.
  * \Drupal\Core\StringTranslation\TranslatableMarkup objects instead.
  *
  *

+ 2 - 0
core/lib/Drupal/Core/Render/Element/Tel.php

@@ -12,12 +12,14 @@ use Drupal\Core\Render\Element;
  *
  *
  * Properties:
  * Properties:
  * - #size: The size of the input element in characters.
  * - #size: The size of the input element in characters.
+ * - #pattern: A string for the native HTML5 pattern attribute.
  *
  *
  * Usage example:
  * Usage example:
  * @code
  * @code
  * $form['phone'] = array(
  * $form['phone'] = array(
  *   '#type' => 'tel',
  *   '#type' => 'tel',
  *   '#title' => $this->t('Phone'),
  *   '#title' => $this->t('Phone'),
+ *   '#pattern' => '[^\d]*',
  * );
  * );
  * @endcode
  * @endcode
  *
  *

+ 3 - 1
core/lib/Drupal/Core/Render/Element/Textfield.php

@@ -15,6 +15,7 @@ use Drupal\Core\Render\Element;
  *   autocomplete JavaScript library.
  *   autocomplete JavaScript library.
  * - #autocomplete_route_parameters: An array of parameters to be used in
  * - #autocomplete_route_parameters: An array of parameters to be used in
  *   conjunction with the route name.
  *   conjunction with the route name.
+ * - #pattern: A string for the native HTML5 pattern attribute.
  *
  *
  * Usage example:
  * Usage example:
  * @code
  * @code
@@ -24,7 +25,8 @@ use Drupal\Core\Render\Element;
  *   '#default_value' => $node->title,
  *   '#default_value' => $node->title,
  *   '#size' => 60,
  *   '#size' => 60,
  *   '#maxlength' => 128,
  *   '#maxlength' => 128,
- * '#required' => TRUE,
+ *   '#pattern' => 'some-prefix-[a-z]+',
+ *   '#required' => TRUE,
  * );
  * );
  * @endcode
  * @endcode
  *
  *

+ 2 - 0
core/lib/Drupal/Core/Render/Element/Url.php

@@ -12,6 +12,7 @@ use Drupal\Core\Render\Element;
  * Properties:
  * Properties:
  * - #default_value: A valid URL string.
  * - #default_value: A valid URL string.
  * - #size: The size of the input element in characters.
  * - #size: The size of the input element in characters.
+ * - #pattern: A string for the native HTML5 pattern attribute.
  *
  *
  * Usage example:
  * Usage example:
  * @code
  * @code
@@ -19,6 +20,7 @@ use Drupal\Core\Render\Element;
  *   '#type' => 'url',
  *   '#type' => 'url',
  *   '#title' => $this->t('Home Page'),
  *   '#title' => $this->t('Home Page'),
  *   '#size' => 30,
  *   '#size' => 30,
+ *   '#pattern' => '*.example.com',
  *   ...
  *   ...
  * );
  * );
  * @endcode
  * @endcode

+ 1 - 1
core/lib/Drupal/Core/Session/SessionConfiguration.php

@@ -106,7 +106,7 @@ class SessionConfiguration implements SessionConfigurationInterface {
    * Return the session cookie domain.
    * Return the session cookie domain.
    *
    *
    * The Set-Cookie response header and its domain attribute are defined in RFC
    * The Set-Cookie response header and its domain attribute are defined in RFC
-   * 2109, RFC 2965 and RFC 6265 each one superseeding the previous version.
+   * 2109, RFC 2965 and RFC 6265 each one superseding the previous version.
    *
    *
    * @see http://tools.ietf.org/html/rfc2109
    * @see http://tools.ietf.org/html/rfc2109
    * @see http://tools.ietf.org/html/rfc2965
    * @see http://tools.ietf.org/html/rfc2965

+ 2 - 4
core/lib/Drupal/Core/Session/WriteSafeSessionHandlerInterface.php

@@ -14,8 +14,7 @@ interface WriteSafeSessionHandlerInterface {
    * only capable of forcibly disabling that session data is written to storage.
    * only capable of forcibly disabling that session data is written to storage.
    *
    *
    * @param bool $flag
    * @param bool $flag
-   *   TRUE if the session the session is allowed to be written, FALSE
-   *   otherwise.
+   *   TRUE if the session is allowed to be written, FALSE otherwise.
    */
    */
   public function setSessionWritable($flag);
   public function setSessionWritable($flag);
 
 
@@ -23,8 +22,7 @@ interface WriteSafeSessionHandlerInterface {
    * Returns whether or not a session may be written to storage.
    * Returns whether or not a session may be written to storage.
    *
    *
    * @return bool
    * @return bool
-   *   TRUE if the session the session is allowed to be written, FALSE
-   *   otherwise.
+   *   TRUE if the session is allowed to be written, FALSE otherwise.
    */
    */
   public function isSessionWritable();
   public function isSessionWritable();
 
 

+ 41 - 29
core/lib/Drupal/Core/State/State.php

@@ -2,15 +2,12 @@
 
 
 namespace Drupal\Core\State;
 namespace Drupal\Core\State;
 
 
-use Drupal\Core\Cache\CacheBackendInterface;
-use Drupal\Core\Cache\CacheCollector;
 use Drupal\Core\KeyValueStore\KeyValueFactoryInterface;
 use Drupal\Core\KeyValueStore\KeyValueFactoryInterface;
-use Drupal\Core\Lock\LockBackendInterface;
 
 
 /**
 /**
  * Provides the state system using a key value store.
  * Provides the state system using a key value store.
  */
  */
-class State extends CacheCollector implements StateInterface {
+class State implements StateInterface {
 
 
   /**
   /**
    * The key value store to use.
    * The key value store to use.
@@ -19,18 +16,20 @@ class State extends CacheCollector implements StateInterface {
    */
    */
   protected $keyValueStore;
   protected $keyValueStore;
 
 
+  /**
+   * Static state cache.
+   *
+   * @var array
+   */
+  protected $cache = [];
+
   /**
   /**
    * Constructs a State object.
    * Constructs a State object.
    *
    *
    * @param \Drupal\Core\KeyValueStore\KeyValueFactoryInterface $key_value_factory
    * @param \Drupal\Core\KeyValueStore\KeyValueFactoryInterface $key_value_factory
    *   The key value store to use.
    *   The key value store to use.
-   * @param \Drupal\Core\Cache\CacheBackendInterface $cache
-   *   The cache backend.
-   * @param \Drupal\Core\Lock\LockBackendInterface $lock
-   *   The lock backend.
    */
    */
-  public function __construct(KeyValueFactoryInterface $key_value_factory, CacheBackendInterface $cache, LockBackendInterface $lock) {
-    parent::__construct('state', $cache, $lock);
+  public function __construct(KeyValueFactoryInterface $key_value_factory) {
     $this->keyValueStore = $key_value_factory->get('state');
     $this->keyValueStore = $key_value_factory->get('state');
   }
   }
 
 
@@ -38,18 +37,8 @@ class State extends CacheCollector implements StateInterface {
    * {@inheritdoc}
    * {@inheritdoc}
    */
    */
   public function get($key, $default = NULL) {
   public function get($key, $default = NULL) {
-    $value = parent::get($key);
-    return $value !== NULL ? $value : $default;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  protected function resolveCacheMiss($key) {
-    $value = $this->keyValueStore->get($key);
-    $this->storage[$key] = $value;
-    $this->persist($key);
-    return $value;
+    $values = $this->getMultiple([$key]);
+    return isset($values[$key]) ? $values[$key] : $default;
   }
   }
 
 
   /**
   /**
@@ -57,9 +46,33 @@ class State extends CacheCollector implements StateInterface {
    */
    */
   public function getMultiple(array $keys) {
   public function getMultiple(array $keys) {
     $values = [];
     $values = [];
+    $load = [];
     foreach ($keys as $key) {
     foreach ($keys as $key) {
-      $values[$key] = $this->get($key);
+      // Check if we have a value in the cache.
+      if (isset($this->cache[$key])) {
+        $values[$key] = $this->cache[$key];
+      }
+      // Load the value if we don't have an explicit NULL value.
+      elseif (!array_key_exists($key, $this->cache)) {
+        $load[] = $key;
+      }
     }
     }
+
+    if ($load) {
+      $loaded_values = $this->keyValueStore->getMultiple($load);
+      foreach ($load as $key) {
+        // If we find a value, even one that is NULL, add it to the cache and
+        // return it.
+        if (isset($loaded_values[$key]) || array_key_exists($key, $loaded_values)) {
+          $values[$key] = $loaded_values[$key];
+          $this->cache[$key] = $loaded_values[$key];
+        }
+        else {
+          $this->cache[$key] = NULL;
+        }
+      }
+    }
+
     return $values;
     return $values;
   }
   }
 
 
@@ -67,7 +80,7 @@ class State extends CacheCollector implements StateInterface {
    * {@inheritdoc}
    * {@inheritdoc}
    */
    */
   public function set($key, $value) {
   public function set($key, $value) {
-    parent::set($key, $value);
+    $this->cache[$key] = $value;
     $this->keyValueStore->set($key, $value);
     $this->keyValueStore->set($key, $value);
   }
   }
 
 
@@ -76,7 +89,7 @@ class State extends CacheCollector implements StateInterface {
    */
    */
   public function setMultiple(array $data) {
   public function setMultiple(array $data) {
     foreach ($data as $key => $value) {
     foreach ($data as $key => $value) {
-      parent::set($key, $value);
+      $this->cache[$key] = $value;
     }
     }
     $this->keyValueStore->setMultiple($data);
     $this->keyValueStore->setMultiple($data);
   }
   }
@@ -85,8 +98,7 @@ class State extends CacheCollector implements StateInterface {
    * {@inheritdoc}
    * {@inheritdoc}
    */
    */
   public function delete($key) {
   public function delete($key) {
-    parent::delete($key);
-    $this->keyValueStore->delete($key);
+    $this->deleteMultiple([$key]);
   }
   }
 
 
   /**
   /**
@@ -94,7 +106,7 @@ class State extends CacheCollector implements StateInterface {
    */
    */
   public function deleteMultiple(array $keys) {
   public function deleteMultiple(array $keys) {
     foreach ($keys as $key) {
     foreach ($keys as $key) {
-      parent::delete($key);
+      unset($this->cache[$key]);
     }
     }
     $this->keyValueStore->deleteMultiple($keys);
     $this->keyValueStore->deleteMultiple($keys);
   }
   }
@@ -103,7 +115,7 @@ class State extends CacheCollector implements StateInterface {
    * {@inheritdoc}
    * {@inheritdoc}
    */
    */
   public function resetCache() {
   public function resetCache() {
-    $this->clear();
+    $this->cache = [];
   }
   }
 
 
 }
 }

+ 1 - 1
core/lib/Drupal/Core/StreamWrapper/StreamWrapperInterface.php

@@ -82,7 +82,7 @@ interface StreamWrapperInterface extends PhpStreamWrapperInterface {
   const READ_VISIBLE = 0x0014;
   const READ_VISIBLE = 0x0014;
 
 
   /**
   /**
-   * This is the default 'type' falg. This does not include
+   * This is the default 'type' flag. This does not include
    * StreamWrapperInterface::LOCAL, because PHP grants a greater trust level to
    * StreamWrapperInterface::LOCAL, because PHP grants a greater trust level to
    * local files (for example, they can be used in an "include" statement,
    * local files (for example, they can be used in an "include" statement,
    * regardless of the "allow_url_include" setting), so stream wrappers need to
    * regardless of the "allow_url_include" setting), so stream wrappers need to

+ 29 - 1
core/lib/Drupal/Core/TempStore/PrivateTempStore.php

@@ -123,6 +123,7 @@ class PrivateTempStore {
     if ($this->currentUser->isAnonymous()) {
     if ($this->currentUser->isAnonymous()) {
       // @todo when https://www.drupal.org/node/2865991 is resolved, use force
       // @todo when https://www.drupal.org/node/2865991 is resolved, use force
       //   start session API rather than setting an arbitrary value directly.
       //   start session API rather than setting an arbitrary value directly.
+      $this->startSession();
       $this->requestStack
       $this->requestStack
         ->getCurrentRequest()
         ->getCurrentRequest()
         ->getSession()
         ->getSession()
@@ -219,7 +220,34 @@ class PrivateTempStore {
    *   The owner.
    *   The owner.
    */
    */
   protected function getOwner() {
   protected function getOwner() {
-    return $this->currentUser->id() ?: $this->requestStack->getCurrentRequest()->getSession()->getId();
+    $owner = $this->currentUser->id();
+    if ($this->currentUser->isAnonymous()) {
+      $this->startSession();
+      $owner = $this->requestStack->getCurrentRequest()->getSession()->getId();
+    }
+    return $owner;
+  }
+
+  /**
+   * Start session because it is required for a private temp store.
+   *
+   * Ensures that an anonymous user has a session created for them, as
+   * otherwise subsequent page loads will not be able to retrieve their
+   * tempstore data.
+   *
+   * @todo when https://www.drupal.org/node/2865991 is resolved, use force
+   * start session API.
+   */
+  protected function startSession() {
+    $has_session = $this->requestStack
+      ->getCurrentRequest()
+      ->hasSession();
+    if (!$has_session) {
+      /** @var \Symfony\Component\HttpFoundation\Session\SessionInterface $session */
+      $session = \Drupal::service('session');
+      $this->requestStack->getCurrentRequest()->setSession($session);
+      $session->start();
+    }
   }
   }
 
 
 }
 }

+ 1 - 1
core/lib/Drupal/Core/TypedData/TypedDataManager.php

@@ -188,7 +188,7 @@ class TypedDataManager extends DefaultPluginManager implements TypedDataManagerI
         throw new \InvalidArgumentException("Property $property_name is unknown.");
         throw new \InvalidArgumentException("Property $property_name is unknown.");
       }
       }
       // Create the prototype without any value, but with initial parenting
       // Create the prototype without any value, but with initial parenting
-      // so that constructors can set up the objects correclty.
+      // so that constructors can set up the objects correctly.
       $this->prototypes[$key] = $this->create($definition, NULL, $property_name, $object);
       $this->prototypes[$key] = $this->create($definition, NULL, $property_name, $object);
     }
     }
 
 

+ 1 - 1
core/lib/Drupal/Core/Update/UpdateRegistry.php

@@ -197,7 +197,7 @@ class UpdateRegistry {
   }
   }
 
 
   /**
   /**
-   * Registers that update fucntions got executed.
+   * Registers that update functions were executed.
    *
    *
    * @param string[] $function_names
    * @param string[] $function_names
    *   The executed update functions.
    *   The executed update functions.

+ 1 - 1
core/lib/Drupal/Core/Updater/Module.php

@@ -105,7 +105,7 @@ class Module extends Updater implements UpdaterInterface {
    * {@inheritdoc}
    * {@inheritdoc}
    */
    */
   public function postInstallTasks() {
   public function postInstallTasks() {
-    // Since this is being called outsite of the primary front controller,
+    // Since this is being called outside of the primary front controller,
     // the base_url needs to be set explicitly to ensure that links are
     // the base_url needs to be set explicitly to ensure that links are
     // relative to the site root.
     // relative to the site root.
     // @todo Simplify with https://www.drupal.org/node/2548095
     // @todo Simplify with https://www.drupal.org/node/2548095

+ 1 - 1
core/lib/Drupal/Core/Updater/Theme.php

@@ -87,7 +87,7 @@ class Theme extends Updater implements UpdaterInterface {
    * {@inheritdoc}
    * {@inheritdoc}
    */
    */
   public function postInstallTasks() {
   public function postInstallTasks() {
-    // Since this is being called outsite of the primary front controller,
+    // Since this is being called outside of the primary front controller,
     // the base_url needs to be set explicitly to ensure that links are
     // the base_url needs to be set explicitly to ensure that links are
     // relative to the site root.
     // relative to the site root.
     // @todo Simplify with https://www.drupal.org/node/2548095
     // @todo Simplify with https://www.drupal.org/node/2548095

+ 0 - 1
core/lib/Drupal/Core/Utility/UnroutedUrlAssembler.php

@@ -78,7 +78,6 @@ class UnroutedUrlAssembler implements UnroutedUrlAssemblerInterface {
     $options += ['query' => []];
     $options += ['query' => []];
 
 
     $options['query'] = NestedArray::mergeDeep($parsed['query'], $options['query']);
     $options['query'] = NestedArray::mergeDeep($parsed['query'], $options['query']);
-    ksort($options['query']);
 
 
     if ($parsed['fragment'] && !$options['fragment']) {
     if ($parsed['fragment'] && !$options['fragment']) {
       $options['fragment'] = '#' . $parsed['fragment'];
       $options['fragment'] = '#' . $parsed['fragment'];

+ 1 - 1
core/misc/announce.es6.js

@@ -28,7 +28,7 @@
    * @type {Drupal~behavior}
    * @type {Drupal~behavior}
    *
    *
    * @prop {Drupal~behaviorAttach} attach
    * @prop {Drupal~behaviorAttach} attach
-   *   Attaches the behavior for drupalAnnouce.
+   *   Attaches the behavior for drupalAnnounce.
    */
    */
   Drupal.behaviors.drupalAnnounce = {
   Drupal.behaviors.drupalAnnounce = {
     attach(context) {
     attach(context) {

+ 1 - 1
core/misc/autocomplete.es6.js

@@ -218,7 +218,7 @@
         .find('input.form-autocomplete')
         .find('input.form-autocomplete')
         .once('autocomplete');
         .once('autocomplete');
       if ($autocomplete.length) {
       if ($autocomplete.length) {
-        // Allow options to be overriden per instance.
+        // Allow options to be overridden per instance.
         const blacklist = $autocomplete.attr(
         const blacklist = $autocomplete.attr(
           'data-autocomplete-first-character-blacklist',
           'data-autocomplete-first-character-blacklist',
         );
         );

+ 1 - 1
core/misc/normalize-fixes.css

@@ -6,7 +6,7 @@
 /**
 /**
  * Fix problem with details/summary lines missing the drop arrows.
  * Fix problem with details/summary lines missing the drop arrows.
  */
  */
-@-moz-document url-prefix() {
+@media (min--moz-device-pixel-ratio: 0) {
   summary {
   summary {
     display: list-item;
     display: list-item;
   }
   }

+ 38 - 12
core/misc/states.es6.js

@@ -59,6 +59,30 @@
     return typeof a === 'undefined' || typeof b === 'undefined';
     return typeof a === 'undefined' || typeof b === 'undefined';
   }
   }
 
 
+  /**
+   * Bitwise AND with a third undefined state.
+   *
+   * @function Drupal.states~ternary
+   *
+   * @param {*} a
+   *   Value a.
+   * @param {*} b
+   *   Value b
+   *
+   * @return {bool}
+   *   The result.
+   */
+  function ternary(a, b) {
+    if (typeof a === 'undefined') {
+      return b;
+    }
+    if (typeof b === 'undefined') {
+      return a;
+    }
+
+    return a && b;
+  }
+
   /**
   /**
    * Attaches the states.
    * Attaches the states.
    *
    *
@@ -305,18 +329,20 @@
       // bogus, we don't want to end up with an infinite loop.
       // bogus, we don't want to end up with an infinite loop.
       else if ($.isPlainObject(constraints)) {
       else if ($.isPlainObject(constraints)) {
         // This constraint is an object (AND).
         // This constraint is an object (AND).
-        result = Object.keys(constraints).every(constraint => {
-          const check = this.checkConstraints(
-            constraints[constraint],
-            selector,
-            constraint,
-          );
-          /**
-           * The checkConstraints() function's return value can be undefined. If
-           * this so, consider it to have returned true.
-           */
-          return typeof check === 'undefined' ? true : check;
-        });
+        // eslint-disable-next-line no-restricted-syntax
+        for (const n in constraints) {
+          if (constraints.hasOwnProperty(n)) {
+            result = ternary(
+              result,
+              this.checkConstraints(constraints[n], selector, n),
+            );
+            // False and anything else will evaluate to false, so return when
+            // any false condition is found.
+            if (result === false) {
+              return false;
+            }
+          }
+        }
       }
       }
       return result;
       return result;
     },
     },

+ 21 - 8
core/misc/states.js

@@ -24,6 +24,17 @@
     return typeof a === 'undefined' || typeof b === 'undefined';
     return typeof a === 'undefined' || typeof b === 'undefined';
   }
   }
 
 
+  function ternary(a, b) {
+    if (typeof a === 'undefined') {
+      return b;
+    }
+    if (typeof b === 'undefined') {
+      return a;
+    }
+
+    return a && b;
+  }
+
   Drupal.behaviors.states = {
   Drupal.behaviors.states = {
     attach: function attach(context, settings) {
     attach: function attach(context, settings) {
       var $states = $(context).find('[data-drupal-states]');
       var $states = $(context).find('[data-drupal-states]');
@@ -127,8 +138,6 @@
       }
       }
     },
     },
     verifyConstraints: function verifyConstraints(constraints, selector) {
     verifyConstraints: function verifyConstraints(constraints, selector) {
-      var _this3 = this;
-
       var result = void 0;
       var result = void 0;
       if ($.isArray(constraints)) {
       if ($.isArray(constraints)) {
         var hasXor = $.inArray('xor', constraints) === -1;
         var hasXor = $.inArray('xor', constraints) === -1;
@@ -144,11 +153,15 @@
           }
           }
         }
         }
       } else if ($.isPlainObject(constraints)) {
       } else if ($.isPlainObject(constraints)) {
-          result = Object.keys(constraints).every(function (constraint) {
-            var check = _this3.checkConstraints(constraints[constraint], selector, constraint);
+          for (var n in constraints) {
+            if (constraints.hasOwnProperty(n)) {
+              result = ternary(result, this.checkConstraints(constraints[n], selector, n));
 
 
-            return typeof check === 'undefined' ? true : check;
-          });
+              if (result === false) {
+                return false;
+              }
+            }
+          }
         }
         }
       return result;
       return result;
     },
     },
@@ -197,7 +210,7 @@
 
 
   states.Trigger.prototype = {
   states.Trigger.prototype = {
     initialize: function initialize() {
     initialize: function initialize() {
-      var _this4 = this;
+      var _this3 = this;
 
 
       var trigger = states.Trigger.states[this.state];
       var trigger = states.Trigger.states[this.state];
 
 
@@ -205,7 +218,7 @@
         trigger.call(window, this.element);
         trigger.call(window, this.element);
       } else {
       } else {
         Object.keys(trigger || {}).forEach(function (event) {
         Object.keys(trigger || {}).forEach(function (event) {
-          _this4.defaultTrigger(event, trigger[event]);
+          _this3.defaultTrigger(event, trigger[event]);
         });
         });
       }
       }
 
 

+ 1 - 1
core/modules/action/src/Plugin/Action/GotoAction.php

@@ -40,7 +40,7 @@ class GotoAction extends ConfigurableActionBase implements ContainerFactoryPlugi
   protected $unroutedUrlAssembler;
   protected $unroutedUrlAssembler;
 
 
   /**
   /**
-   * Constructs a new DeleteNode object.
+   * Constructs a GotoAction object.
    *
    *
    * @param array $configuration
    * @param array $configuration
    *   A configuration array containing information about the plugin instance.
    *   A configuration array containing information about the plugin instance.

+ 1 - 1
core/modules/aggregator/src/ItemsImporter.php

@@ -94,7 +94,7 @@ class ItemsImporter implements ItemsImporterInterface {
       watchdog_exception('aggregator', $e);
       watchdog_exception('aggregator', $e);
     }
     }
 
 
-    // Store instances in an array so we dont have to instantiate new objects.
+    // Store instances in an array so we don't have to instantiate new objects.
     $processor_instances = [];
     $processor_instances = [];
     foreach ($this->config->get('processors') as $processor) {
     foreach ($this->config->get('processors') as $processor) {
       try {
       try {

+ 1 - 1
core/modules/ban/src/Plugin/migrate/destination/BlockedIp.php

@@ -33,7 +33,7 @@ class BlockedIP extends DestinationBase implements ContainerFactoryPluginInterfa
    * @param string $plugin_id
    * @param string $plugin_id
    *   The plugin ID.
    *   The plugin ID.
    * @param mixed $plugin_definition
    * @param mixed $plugin_definition
-   *   The plugin definiiton.
+   *   The plugin definition.
    * @param \Drupal\migrate\Plugin\MigrationInterface $migration
    * @param \Drupal\migrate\Plugin\MigrationInterface $migration
    *   The current migration.
    *   The current migration.
    * @param \Drupal\ban\BanIpManagerInterface $ban_manager
    * @param \Drupal\ban\BanIpManagerInterface $ban_manager

+ 5 - 91
core/modules/block_content/src/Plugin/migrate/source/d6/BoxTranslation.php

@@ -2,8 +2,7 @@
 
 
 namespace Drupal\block_content\Plugin\migrate\source\d6;
 namespace Drupal\block_content\Plugin\migrate\source\d6;
 
 
-use Drupal\migrate\Row;
-use Drupal\migrate_drupal\Plugin\migrate\source\DrupalSqlBase;
+use Drupal\block_content\Plugin\migrate\source\d7\BlockCustomTranslation as D7BlockCustomTranslation;
 
 
 /**
 /**
  * Gets Drupal 6 i18n custom block translations from database.
  * Gets Drupal 6 i18n custom block translations from database.
@@ -13,97 +12,12 @@ use Drupal\migrate_drupal\Plugin\migrate\source\DrupalSqlBase;
  *   source_module = "i18nblocks"
  *   source_module = "i18nblocks"
  * )
  * )
  */
  */
-class BoxTranslation extends DrupalSqlBase {
+class BoxTranslation extends D7BlockCustomTranslation {
 
 
   /**
   /**
-   * {@inheritdoc}
+   * Drupal 6 table names.
    */
    */
-  public function query() {
-    // Build a query based on i18n_strings table where each row has the
-    // translation for only one property, either title or description. The
-    // method prepareRow() is then used to obtain the translation for the
-    // other property.
-    $query = $this->select('boxes', 'b')
-      ->fields('b', ['bid', 'format', 'body'])
-      ->fields('i18n', ['property'])
-      ->fields('lt', ['lid', 'translation', 'language'])
-      ->orderBy('b.bid')
-      ->isNotNull('lt.lid');
-
-    // Use 'title' for the info field to match the property name in the
-    // i18n_strings table.
-    $query->addField('b', 'info', 'title');
-
-    // Add in the property, which is either title or body. Cast the bid to text
-    // so PostgreSQL can make the join.
-    $query->leftJoin('i18n_strings', 'i18n', 'i18n.objectid = CAST(b.bid as CHAR(255))');
-    $query->condition('i18n.type', 'block');
-
-    // Add in the translation for the property.
-    $query->leftJoin('locales_target', 'lt', 'lt.lid = i18n.lid');
-    return $query;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function prepareRow(Row $row) {
-    $language = $row->getSourceProperty('language');
-    $bid = $row->getSourceProperty('bid');
-
-    // If this row has been migrated it is a duplicate then skip it.
-    if ($this->idMap->lookupDestinationIds(['bid' => $bid, 'language' => $language])) {
-      return FALSE;
-    }
-
-    // Save the translation for this property.
-    $property = $row->getSourceProperty('property');
-    $row->setSourceProperty($property . '_translated', $row->getSourceProperty('translation'));
-
-    // Get the translation for the property not already in the row.
-    $translation = ($property === 'title') ? 'body' : 'title';
-    $query = $this->select('i18n_strings', 'i18n')
-      ->fields('i18n', ['lid'])
-      ->condition('i18n.property', $translation)
-      ->condition('i18n.objectid', $bid);
-    $query->leftJoin('locales_target', 'lt', 'i18n.lid = lt.lid');
-    $query->condition('lt.language', $language)
-      ->addField('lt', 'translation');
-    $results = $query->execute()->fetchAssoc();
-    if (!$results) {
-      $row->setSourceProperty($translation . '_translated', NULL);
-    }
-    else {
-      $row->setSourceProperty($translation . '_translated', $results['translation']);
-    }
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function fields() {
-    return [
-      'bid' => $this->t('The block numeric identifier.'),
-      'format' => $this->t('Input format of the custom block/box content.'),
-      'lid' => $this->t('i18n_string table id'),
-      'language' => $this->t('Language for this field.'),
-      'property' => $this->t('Block property'),
-      'translation' => $this->t('The translation of the value of "property".'),
-      'title' => $this->t('Block title.'),
-      'title_translated' => $this->t('Block title translation.'),
-      'body' => $this->t('Block body.'),
-      'body_translated' => $this->t('Block body translation.'),
-    ];
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getIds() {
-    $ids['bid']['type'] = 'integer';
-    $ids['bid']['alias'] = 'b';
-    $ids['language']['type'] = 'string';
-    return $ids;
-  }
+  const CUSTOM_BLOCK_TABLE = 'boxes';
+  const I18N_STRING_TABLE = 'i18n_strings';
 
 
 }
 }

+ 99 - 0
core/modules/block_content/src/Plugin/migrate/source/d7/BlockCustomTranslation.php

@@ -0,0 +1,99 @@
+<?php
+
+namespace Drupal\block_content\Plugin\migrate\source\d7;
+
+use Drupal\migrate\Row;
+use Drupal\migrate_drupal\Plugin\migrate\source\DrupalSqlBase;
+use Drupal\content_translation\Plugin\migrate\source\I18nQueryTrait;
+
+/**
+ * Gets Drupal 7 custom block translation from database.
+ *
+ * @MigrateSource(
+ *   id = "d7_block_custom_translation",
+ *   source_module = "block"
+ * )
+ */
+class BlockCustomTranslation extends DrupalSqlBase {
+
+  use I18nQueryTrait;
+
+  /**
+   * Drupal 7 table names.
+   */
+  const CUSTOM_BLOCK_TABLE = 'block_custom';
+  const I18N_STRING_TABLE = 'i18n_string';
+
+  /**
+   * {@inheritdoc}
+   */
+  public function query() {
+    // Build a query based on blockCustomTable table where each row has the
+    // translation for only one property, either title or description. The
+    // method prepareRow() is then used to obtain the translation for the
+    // other property.
+    $query = $this->select(static::CUSTOM_BLOCK_TABLE, 'b')
+      ->fields('b', ['bid', 'format', 'body'])
+      ->fields('i18n', ['property'])
+      ->fields('lt', ['lid', 'translation', 'language'])
+      ->orderBy('b.bid')
+      ->isNotNull('lt.lid');
+
+    // Use 'title' for the info field to match the property name in
+    // i18nStringTable.
+    $query->addField('b', 'info', 'title');
+
+    // Add in the property, which is either title or body. Cast the bid to text
+    // so PostgreSQL can make the join.
+    $query->leftJoin(static::I18N_STRING_TABLE, 'i18n', 'i18n.objectid = CAST(b.bid as CHAR(255))');
+    $query->condition('i18n.type', 'block');
+
+    // Add in the translation for the property.
+    $query->leftJoin('locales_target', 'lt', 'lt.lid = i18n.lid');
+    return $query;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function prepareRow(Row $row) {
+    parent::prepareRow($row);
+    // Set the i18n string table for use in I18nQueryTrait.
+    $this->i18nStringTable = static::I18N_STRING_TABLE;
+    // Save the translation for this property.
+    $property_in_row = $row->getSourceProperty('property');
+    // Get the translation for the property not already in the row and save it
+    // in the row.
+    $property_not_in_row = ($property_in_row === 'title') ? 'body' : 'title';
+    return $this->getPropertyNotInRowTranslation($row, $property_not_in_row, 'bid', $this->idMap);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function fields() {
+    return [
+      'bid' => $this->t('The block numeric identifier.'),
+      'format' => $this->t('Input format of the custom block/box content.'),
+      'lid' => $this->t('i18n_string table id'),
+      'language' => $this->t('Language for this field.'),
+      'property' => $this->t('Block property'),
+      'translation' => $this->t('The translation of the value of "property".'),
+      'title' => $this->t('Block title.'),
+      'title_translated' => $this->t('Block title translation.'),
+      'body' => $this->t('Block body.'),
+      'body_translated' => $this->t('Block body translation.'),
+    ];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getIds() {
+    $ids['bid']['type'] = 'integer';
+    $ids['bid']['alias'] = 'b';
+    $ids['language']['type'] = 'string';
+    return $ids;
+  }
+
+}

+ 69 - 0
core/modules/block_content/tests/src/Kernel/Migrate/d7/MigrateCustomBlockContentTranslationTest.php

@@ -0,0 +1,69 @@
+<?php
+
+namespace Drupal\Tests\block_content\Kernel\Migrate\d7;
+
+use Drupal\block_content\Entity\BlockContent;
+use Drupal\Tests\migrate_drupal\Kernel\d7\MigrateDrupal7TestBase;
+
+/**
+ * Tests migration of i18n custom block strings.
+ *
+ * @group migrate_drupal_7
+ */
+class MigrateCustomBlockContentTranslationTest extends MigrateDrupal7TestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static $modules = [
+    'block_content',
+    'content_translation',
+    'filter',
+    'language',
+    'text',
+    // Required for translation migrations.
+    'migrate_drupal_multilingual',
+  ];
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+    $this->installConfig(['block_content']);
+    $this->installEntitySchema('block_content');
+    $this->executeMigrations([
+      'language',
+      'd7_filter_format',
+      'block_content_type',
+      'block_content_body_field',
+      'd7_custom_block',
+      'd7_custom_block_translation',
+    ]);
+  }
+
+  /**
+   * Tests the Drupal 7 i18n custom block strings to Drupal 8 migration.
+   */
+  public function testCustomBlockContentTranslation() {
+    /** @var \Drupal\block_content\Entity\BlockContent $block */
+    $block = BlockContent::load(1)->getTranslation('fr');
+    $this->assertSame('fr - Mildly amusing limerick of the day', $block->label());
+    $this->assertGreaterThanOrEqual($block->getChangedTime(), \Drupal::time()->getRequestTime());
+    $this->assertLessThanOrEqual(time(), $block->getChangedTime());
+    $this->assertSame('fr', $block->language()->getId());
+    $translation = "fr - A fellow jumped off a high wall\r\nAnd had a most terrible fall\r\nHe went back to bed\r\nWith a bump on his head\r\nThat's why you don't jump off a wall";
+    $this->assertSame($translation, $block->body->value);
+    $this->assertSame('filtered_html', $block->body->format);
+
+    $block = $block->getTranslation('is');
+    $this->assertSame('is - Mildly amusing limerick of the day', $block->label());
+    $this->assertGreaterThanOrEqual($block->getChangedTime(), \Drupal::time()->getRequestTime());
+    $this->assertLessThanOrEqual(time(), $block->getChangedTime());
+    $this->assertSame('is', $block->language()->getId());
+    $text = "A fellow jumped off a high wall\r\nAnd had a most terrible fall\r\nHe went back to bed\r\nWith a bump on his head\r\nThat's why you don't jump off a wall";
+    $this->assertSame($text, $block->body->value);
+    $this->assertSame('filtered_html', $block->body->format);
+  }
+
+}

+ 148 - 0
core/modules/block_content/tests/src/Kernel/Plugin/migrate/source/d7/BlockCustomTranslationTest.php

@@ -0,0 +1,148 @@
+<?php
+
+namespace Drupal\Tests\block_content\Kernel\Plugin\migrate\source\d7;
+
+use Drupal\Tests\migrate\Kernel\MigrateSqlSourceTestBase;
+
+/**
+ * Tests i18n custom block translations source plugin.
+ *
+ * @covers \Drupal\block_content\Plugin\migrate\source\d7\BlockCustomTranslation
+ *
+ * @group content_translation
+ */
+class BlockCustomTranslationTest extends MigrateSqlSourceTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static $modules = ['block_content', 'migrate_drupal'];
+
+  /**
+   * {@inheritdoc}
+   */
+  public function providerSource() {
+    $tests = [];
+
+    // The source data.
+    $tests[0]['database']['block_custom'] = [
+      [
+        'bid' => 1,
+        'body' => 'box 1 body',
+        'info' => 'box 1 title',
+        'format' => '2',
+      ],
+      [
+        'bid' => 2,
+        'body' => 'box 2 body',
+        'info' => 'box 2 title',
+        'format' => '2',
+      ],
+    ];
+
+    $tests[0]['database']['i18n_string'] = [
+      [
+        'lid' => 1,
+        'objectid' => 1,
+        'type' => 'block',
+        'property' => 'title',
+        'objectindex' => 1,
+        'format' => 0,
+      ],
+      [
+        'lid' => 2,
+        'objectid' => 1,
+        'type' => 'block',
+        'property' => 'body',
+        'objectindex' => 1,
+        'format' => 0,
+      ],
+      [
+        'lid' => 3,
+        'objectid' => 2,
+        'type' => 'block',
+        'property' => 'body',
+        'objectindex' => 2,
+        'format' => 2,
+      ],
+    ];
+
+    $tests[0]['database']['locales_target'] = [
+      [
+        'lid' => 1,
+        'language' => 'fr',
+        'translation' => 'fr - title translation',
+        'plid' => 0,
+        'plural' => 0,
+        'i18n_status' => 0,
+      ],
+      [
+        'lid' => 2,
+        'language' => 'fr',
+        'translation' => 'fr - body translation',
+        'plid' => 0,
+        'plural' => 0,
+        'i18n_status' => 0,
+      ],
+      [
+        'lid' => 3,
+        'language' => 'zu',
+        'translation' => 'zu - body translation',
+        'plid' => 0,
+        'plural' => 0,
+        'i18n_status' => 0,
+      ],
+    ];
+
+    $tests[0]['database']['system'] = [
+      [
+        'type' => 'module',
+        'name' => 'system',
+        'schema_version' => '7001',
+        'status' => '1',
+      ],
+    ];
+
+    $tests[0]['expected_results'] = [
+      [
+        'lid' => '1',
+        'property' => 'title',
+        'language' => 'fr',
+        'translation' => 'fr - title translation',
+        'bid' => '1',
+        'format' => '2',
+        'title_translated' => 'fr - title translation',
+        'body_translated' => 'fr - body translation',
+        'title' => 'box 1 title',
+        'body' => 'box 1 body',
+      ],
+      [
+        'lid' => '2',
+        'property' => 'body',
+        'language' => 'fr',
+        'translation' => 'fr - body translation',
+        'bid' => '1',
+        'format' => '2',
+        'title_translated' => 'fr - title translation',
+        'body_translated' => 'fr - body translation',
+        'title' => 'box 1 title',
+        'body' => 'box 1 body',
+      ],
+      [
+        'lid' => '3',
+        'property' => 'body',
+        'language' => 'zu',
+        'translation' => 'zu - body translation',
+        'bid' => '2',
+        'format' => '2',
+        'title_translated' => NULL,
+        'body_translated' => 'zu - body translation',
+        'title' => 'box 2 title',
+        'body' => 'box 2 body',
+      ],
+    ];
+
+    return $tests;
+  }
+
+}

Some files were not shown because too many files changed in this diff